diff --git a/.changeset/fuzzy-hairs-obey.md b/.changeset/fuzzy-hairs-obey.md new file mode 100644 index 000000000..e3f263a51 --- /dev/null +++ b/.changeset/fuzzy-hairs-obey.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': patch +--- + +fix: ensure compiled css is returned when reloading during dev with ssr (e.g. SvelteKit) diff --git a/packages/e2e-tests/build-watch/__tests__/build-watch.spec.ts b/packages/e2e-tests/build-watch/__tests__/build-watch.spec.ts index c99b5b8af..cf5cb0e6d 100644 --- a/packages/e2e-tests/build-watch/__tests__/build-watch.spec.ts +++ b/packages/e2e-tests/build-watch/__tests__/build-watch.spec.ts @@ -8,7 +8,8 @@ import { sleep, getColor, browserLogs, - e2eServer + e2eServer, + getWatchErrors } from '~utils'; import * as vite from 'vite'; @@ -63,17 +64,6 @@ describe.runIf(isBuildWatch)('build-watch', () => { const updateApp = editFileAndWaitForBuildWatchComplete.bind(null, 'src/App.svelte'); const updateStore = editFileAndWaitForBuildWatchComplete.bind(null, 'src/stores/hmr-stores.js'); - const getWatchErrors = () => - isRolldownVite - ? e2eServer.logs.watch.err.filter( - (m) => - ![ - 'Support for rolldown-vite in vite-plugin-svelte is experimental', - 'See https://github.com/sveltejs/vite-plugin-svelte/issues/1143' - ].some((s) => m.includes(s)) - ) - : e2eServer.logs.watch.err; - test('should have expected initial state', async () => { // initial state, both counters 0, both labels red expect(await getText('#hmr-test-1 .counter')).toBe('0'); diff --git a/packages/e2e-tests/kit-node/__tests__/kit.spec.ts b/packages/e2e-tests/kit-node/__tests__/kit.spec.ts index b33ea7914..6da8fffa2 100644 --- a/packages/e2e-tests/kit-node/__tests__/kit.spec.ts +++ b/packages/e2e-tests/kit-node/__tests__/kit.spec.ts @@ -12,7 +12,8 @@ import { fetchPageText, reloadPage, readFileContent, - IS_SVELTE_BASELINE + IS_SVELTE_BASELINE, + getServerErrors } from '~utils'; import glob from 'tiny-glob'; @@ -95,6 +96,11 @@ describe('kit-node', () => { expect(browserLogs.some((x) => x === 'onMount dynamic imported isSSR: false')).toBe(true); }); + it('should load dynamic import with css', async () => { + expect(await getText('#dynamic-imported')).toBe("i'm blue"); + expect(await getColor('#dynamic-imported')).toBe('blue'); + }); + it('should respect transforms', async () => { expect(await getText('#js-transform')).toBe('Hello world'); expect(await getColor('#css-transform')).toBe('red'); @@ -192,9 +198,15 @@ describe('kit-node', () => { it('should serve changes even after page reload', async () => { expect(await getColor('h1')).toBe('green'); expect(await getText('#hmr-test2')).toBe('bar'); + expect(await getText('#dynamic-imported')).toBe("i'm blue"); + expect(await getColor('#dynamic-imported')).toBe('blue'); await reloadPage(); expect(await getColor('h1')).toBe('green'); expect(await getText('#hmr-test2')).toBe('bar'); + await page.waitForSelector('#dynamic-imported', { strict: true }); + expect(await getText('#dynamic-imported')).toBe("i'm blue"); + expect(await getColor('#dynamic-imported')).toBe('blue'); + expect(getServerErrors(), 'error log of `vite dev` is not empty after reload').toEqual([]); }); describe('child component update', () => { diff --git a/packages/e2e-tests/kit-node/package.json b/packages/e2e-tests/kit-node/package.json index 18b31ffa8..ae6304d18 100644 --- a/packages/e2e-tests/kit-node/package.json +++ b/packages/e2e-tests/kit-node/package.json @@ -3,8 +3,8 @@ "version": "0.0.0", "private": true, "scripts": { - "dev": "vite dev", - "build": "vite build", + "dev": "svelte-kit sync && vite dev", + "build": "svelte-kit sync && vite build", "preview": "vite preview", "prepare": "svelte-kit sync || echo ''", "sync": "svelte-kit sync || echo ''", @@ -18,8 +18,9 @@ "@sveltejs/package": "^2.4.1", "@sveltejs/vite-plugin-svelte": "workspace:^", "e2e-test-dep-svelte-api-only": "file:../_test_dependencies/svelte-api-only", - "e2e-test-dep-vite-plugins": "file:../_test_dependencies/vite-plugins", "e2e-test-dep-svelte-nested-workspace-devdep": "../_test_dependencies/svelte-nested-workspace-devdep", + "e2e-test-dep-vite-plugins": "file:../_test_dependencies/vite-plugins", + "sass": "^1.90.0", "svelte": "^5.38.0", "svelte-check": "^4.3.1", "svelte-i18n": "^4.0.1", diff --git a/packages/e2e-tests/kit-node/src/routes/+page.svelte b/packages/e2e-tests/kit-node/src/routes/+page.svelte index 0b58211e2..339a1232f 100644 --- a/packages/e2e-tests/kit-node/src/routes/+page.svelte +++ b/packages/e2e-tests/kit-node/src/routes/+page.svelte @@ -4,6 +4,8 @@ import Counter from '$lib/Counter.svelte'; import Child from '$lib/Child.svelte'; import { setSomeContext } from 'e2e-test-dep-svelte-api-only'; + import { browser } from '$app/environment'; + export let data = {}; $: load_status = data?.load_status ?? 'NOT_LOADED'; const jsTransform = '__JS_TRANSFORM_1__'; @@ -38,6 +40,13 @@

{jsTransform}

Hello world

+ {#if browser} + {#await import('./DynamicImported.svelte').then((m) => m.default)} + loading... + {:then DynamicImported} + + {/await} + {/if} diff --git a/packages/e2e-tests/kit-node/src/routes/DynamicImported.svelte b/packages/e2e-tests/kit-node/src/routes/DynamicImported.svelte new file mode 100644 index 000000000..8dd31c28a --- /dev/null +++ b/packages/e2e-tests/kit-node/src/routes/DynamicImported.svelte @@ -0,0 +1,7 @@ +
i'm blue
+ + diff --git a/packages/e2e-tests/kit-node/svelte.config.js b/packages/e2e-tests/kit-node/svelte.config.js index 17c5095f0..67b6c8747 100644 --- a/packages/e2e-tests/kit-node/svelte.config.js +++ b/packages/e2e-tests/kit-node/svelte.config.js @@ -1,6 +1,9 @@ import node from '@sveltejs/adapter-node'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + /** @type {import('@sveltejs/kit').Config} */ const config = { + preprocess: vitePreprocess(), kit: { adapter: node() } diff --git a/packages/e2e-tests/kit-node/vite.config.js b/packages/e2e-tests/kit-node/vite.config.js index c3d4fca29..4df72608e 100644 --- a/packages/e2e-tests/kit-node/vite.config.js +++ b/packages/e2e-tests/kit-node/vite.config.js @@ -19,5 +19,10 @@ export default { optimizeDeps: { // eagerly include these, otherwise vite optimizer might interfere with restarting while the test is running include: ['svelte-i18n', 'e2e-test-dep-svelte-api-only'] + }, + css: { + postcss: { + plugins: [{ postcssPlugin: 'noop' }] + } } }; diff --git a/packages/e2e-tests/testUtils.ts b/packages/e2e-tests/testUtils.ts index b749ee3d7..349376110 100644 --- a/packages/e2e-tests/testUtils.ts +++ b/packages/e2e-tests/testUtils.ts @@ -18,6 +18,10 @@ import { waitForViteConnect } from './vitestSetup.js'; +import * as vite from 'vite'; +//@ts-ignore +const isRolldownVite = !!vite.rolldownVersion; + import { VERSION } from 'svelte/compiler'; export const IS_SVELTE_BASELINE = VERSION === '5.0.0'; @@ -354,3 +358,28 @@ export function readVitePrebundleMetadata() { } throw new Error('Unable to find vite prebundle metadata'); } + +export function getServerErrors() { + return filterMessages(e2eServer.logs.server.err); +} + +export function getWatchErrors() { + return filterMessages(e2eServer.logs.watch.err); +} +function filterMessages(arr) { + if (arr.length === 0) { + return arr; + } + const excludes = []; + if (isRolldownVite) { + excludes.push( + 'Support for rolldown-vite in vite-plugin-svelte is experimental', + 'See https://github.com/sveltejs/vite-plugin-svelte/issues/1143' + ); + } + if (excludes.length > 0) { + return arr.filter((m) => !excludes.some((e) => m.includes(e))); + } else { + return arr; + } +} diff --git a/packages/vite-plugin-svelte/src/plugins/load-compiled-css.js b/packages/vite-plugin-svelte/src/plugins/load-compiled-css.js index b2616b198..0a9e9c233 100644 --- a/packages/vite-plugin-svelte/src/plugins/load-compiled-css.js +++ b/packages/vite-plugin-svelte/src/plugins/load-compiled-css.js @@ -8,14 +8,17 @@ const filter = { id: SVELTE_VIRTUAL_STYLE_ID_REGEX }; * @returns {import('vite').Plugin} */ export function loadCompiledCss(api) { - let isBuildWatch = false; + let useLocalCache = false; + /** @type{Map} */ const buildWatchCssCache = new Map(); return { name: 'vite-plugin-svelte:load-compiled-css', configResolved(c) { - isBuildWatch = !!c.build?.watch; + const isDev = c.command === 'serve'; + const isBuildWatch = !!c.build?.watch; + useLocalCache = isDev || isBuildWatch; }, resolveId: { @@ -34,9 +37,9 @@ export function loadCompiledCss(api) { return; } let cachedCss = this.getModuleInfo(svelteRequest.filename)?.meta.svelte?.css; - // in build --watch getModuleInfo only returns changed module data. + // in `build --watch` or dev ssr reloads getModuleInfo only returns changed module data. // To ensure virtual css is loaded unchanged, we cache it here separately - if (isBuildWatch) { + if (useLocalCache) { if (cachedCss) { buildWatchCssCache.set(svelteRequest.filename, cachedCss); } else { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ba96eac8..5a3872d63 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -257,7 +257,7 @@ importers: version: 7.1.1(@types/node@22.17.0)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0) vite-plugin-inspect: specifier: /home/dominikg/develop/vite-plugin-inspect - version: link:../../../../../../../../../home/dominikg/develop/vite-plugin-inspect + version: link:../../../../../vite-plugin-inspect packages/e2e-tests/configfile-custom: dependencies: @@ -489,6 +489,9 @@ importers: e2e-test-dep-vite-plugins: specifier: file:../_test_dependencies/vite-plugins version: file:packages/e2e-tests/_test_dependencies/vite-plugins + sass: + specifier: ^1.90.0 + version: 1.90.0 svelte: specifier: ^5.38.0 version: 5.38.0 @@ -4579,14 +4582,6 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.1.1(@types/node@20.19.9)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0))': - dependencies: - '@vitest/spy': 3.2.4 - estree-walker: 3.0.3 - magic-string: 0.30.17 - optionalDependencies: - vite: 7.1.1(@types/node@20.19.9)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0) - '@vitest/mocker@3.2.4(vite@7.1.1(@types/node@22.17.0)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 @@ -6410,7 +6405,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.1.1(@types/node@20.19.9)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@7.1.1(@types/node@22.17.0)(sass@1.90.0)(stylus@0.64.0)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4