From 17eede2d59af75c05567f53a4cad5e2bcd7594ed Mon Sep 17 00:00:00 2001 From: dominikg Date: Sun, 2 Mar 2025 21:03:05 +0100 Subject: [PATCH 1/7] feat: scope css to js module to allow treeshaking it (requires vite 6.2) --- .changeset/plenty-eyes-talk.md | 5 +++ .../__tests__/css-treeshake.spec.ts | 37 +++++++++++++++++++ packages/e2e-tests/css-treeshake/index.html | 13 +++++++ packages/e2e-tests/css-treeshake/package.json | 17 +++++++++ packages/e2e-tests/css-treeshake/src/A.svelte | 7 ++++ .../e2e-tests/css-treeshake/src/App.svelte | 13 +++++++ packages/e2e-tests/css-treeshake/src/B.svelte | 10 +++++ packages/e2e-tests/css-treeshake/src/C.svelte | 14 +++++++ .../e2e-tests/css-treeshake/src/barrel.js | 4 ++ packages/e2e-tests/css-treeshake/src/main.js | 3 ++ .../e2e-tests/css-treeshake/src/vite-env.d.ts | 2 + .../e2e-tests/css-treeshake/svelte.config.js | 5 +++ .../e2e-tests/css-treeshake/vite.config.js | 7 ++++ packages/vite-plugin-svelte/package.json | 3 +- packages/vite-plugin-svelte/src/index.js | 15 +++++++- .../vite-plugin-svelte/src/types/compile.d.ts | 3 ++ .../vite-plugin-svelte/src/utils/compile.js | 27 +++++++++++++- 17 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 .changeset/plenty-eyes-talk.md create mode 100644 packages/e2e-tests/css-treeshake/__tests__/css-treeshake.spec.ts create mode 100644 packages/e2e-tests/css-treeshake/index.html create mode 100644 packages/e2e-tests/css-treeshake/package.json create mode 100644 packages/e2e-tests/css-treeshake/src/A.svelte create mode 100644 packages/e2e-tests/css-treeshake/src/App.svelte create mode 100644 packages/e2e-tests/css-treeshake/src/B.svelte create mode 100644 packages/e2e-tests/css-treeshake/src/C.svelte create mode 100644 packages/e2e-tests/css-treeshake/src/barrel.js create mode 100644 packages/e2e-tests/css-treeshake/src/main.js create mode 100644 packages/e2e-tests/css-treeshake/src/vite-env.d.ts create mode 100644 packages/e2e-tests/css-treeshake/svelte.config.js create mode 100644 packages/e2e-tests/css-treeshake/vite.config.js diff --git a/.changeset/plenty-eyes-talk.md b/.changeset/plenty-eyes-talk.md new file mode 100644 index 000000000..f753bc4f2 --- /dev/null +++ b/.changeset/plenty-eyes-talk.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': minor +--- + +scope css to js module to enable treeshaking scoped css from unused components. Requires vite 6.2 diff --git a/packages/e2e-tests/css-treeshake/__tests__/css-treeshake.spec.ts b/packages/e2e-tests/css-treeshake/__tests__/css-treeshake.spec.ts new file mode 100644 index 000000000..d9edac1c8 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/__tests__/css-treeshake.spec.ts @@ -0,0 +1,37 @@ +import { browserLogs, findAssetFile, getColor, getEl, getText, isBuild } from '~utils'; +import { expect } from 'vitest'; + +test('should not have failed requests', async () => { + browserLogs.forEach((msg) => { + expect(msg).not.toMatch('404'); + }); +}); + +test('should apply css from used components', async () => { + expect(await getText('#app')).toBe('App'); + expect(await getColor('#app')).toBe('blue'); + expect(await getText('#a')).toBe('A'); + expect(await getColor('#a')).toBe('red'); +}); + +test('should apply css from unused components that contain global styles', async () => { + expect(await getEl('head style[src]')); + expect(await getColor('#test')).toBe('green'); // from B.svelte +}); + +test('should not render unused components', async () => { + expect(await getEl('#b')).toBeNull(); + expect(await getEl('#c')).toBeNull(); +}); + +if (isBuild) { + test('should include unscoped global styles from unused components', async () => { + const cssOutput = findAssetFile(/index-.*\.css/); + expect(cssOutput).toContain('#test{color:green}'); // from B.svelte + }); + test('should not include scoped styles from unused components', async () => { + const cssOutput = findAssetFile(/index-.*\.css/); + // from C.svelte + expect(cssOutput).not.toContain('.unused'); + }); +} diff --git a/packages/e2e-tests/css-treeshake/index.html b/packages/e2e-tests/css-treeshake/index.html new file mode 100644 index 000000000..5ec38e6d2 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/index.html @@ -0,0 +1,13 @@ + + + + + + + Svelte app + + + + + + diff --git a/packages/e2e-tests/css-treeshake/package.json b/packages/e2e-tests/css-treeshake/package.json new file mode 100644 index 000000000..7119507d4 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/package.json @@ -0,0 +1,17 @@ +{ + "name": "e2e-tests-css-treeshake", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "workspace:^", + "sass": "^1.85.1", + "svelte": "^5.20.5", + "vite": "^6.2.0" + } +} diff --git a/packages/e2e-tests/css-treeshake/src/A.svelte b/packages/e2e-tests/css-treeshake/src/A.svelte new file mode 100644 index 000000000..ef94d98f3 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/A.svelte @@ -0,0 +1,7 @@ +

A

+ + diff --git a/packages/e2e-tests/css-treeshake/src/App.svelte b/packages/e2e-tests/css-treeshake/src/App.svelte new file mode 100644 index 000000000..ca9ca9a52 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/App.svelte @@ -0,0 +1,13 @@ + + +
test
+

App

+ + + diff --git a/packages/e2e-tests/css-treeshake/src/B.svelte b/packages/e2e-tests/css-treeshake/src/B.svelte new file mode 100644 index 000000000..26f088447 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/B.svelte @@ -0,0 +1,10 @@ +

B

+ + diff --git a/packages/e2e-tests/css-treeshake/src/C.svelte b/packages/e2e-tests/css-treeshake/src/C.svelte new file mode 100644 index 000000000..b452d6408 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/C.svelte @@ -0,0 +1,14 @@ +

C

+ + diff --git a/packages/e2e-tests/css-treeshake/src/barrel.js b/packages/e2e-tests/css-treeshake/src/barrel.js new file mode 100644 index 000000000..718d05e83 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/barrel.js @@ -0,0 +1,4 @@ +export { default as A } from './A.svelte'; +// B and C are unused, their css should not be included +export { default as B } from './B.svelte'; +export { default as C } from './C.svelte'; diff --git a/packages/e2e-tests/css-treeshake/src/main.js b/packages/e2e-tests/css-treeshake/src/main.js new file mode 100644 index 000000000..071c75dc7 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/main.js @@ -0,0 +1,3 @@ +import App from './App.svelte'; +import { mount } from 'svelte'; +mount(App, { target: document.body }); diff --git a/packages/e2e-tests/css-treeshake/src/vite-env.d.ts b/packages/e2e-tests/css-treeshake/src/vite-env.d.ts new file mode 100644 index 000000000..4078e7476 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/packages/e2e-tests/css-treeshake/svelte.config.js b/packages/e2e-tests/css-treeshake/svelte.config.js new file mode 100644 index 000000000..76bab5483 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/svelte.config.js @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +export default { + preprocess: [vitePreprocess()] +}; diff --git a/packages/e2e-tests/css-treeshake/vite.config.js b/packages/e2e-tests/css-treeshake/vite.config.js new file mode 100644 index 000000000..6f9c7da64 --- /dev/null +++ b/packages/e2e-tests/css-treeshake/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite'; +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { env } from 'node:process'; +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()] +}); diff --git a/packages/vite-plugin-svelte/package.json b/packages/vite-plugin-svelte/package.json index 2b944ea11..cb0f18985 100644 --- a/packages/vite-plugin-svelte/package.json +++ b/packages/vite-plugin-svelte/package.json @@ -46,7 +46,8 @@ "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", - "vitefu": "^1.0.6" + "vitefu": "^1.0.6", + "zimmerframe": "^1.1.2" }, "peerDependencies": { "svelte": "^5.0.0", diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index 9ec43e9ec..cce486d77 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -118,9 +118,20 @@ export function svelte(inlineOptions) { }; } else { if (query.svelte && query.type === 'style') { - const css = cache.getCSS(svelteRequest); + // @ts-expect-error __meta does not exist + const { __meta, ...css } = cache.getCSS(svelteRequest); if (css) { - return css; + if (__meta?.hasUnscopedGlobalCss) { + return css; // css contains unscoped global, do not scope to component + } + return { + ...css, + meta: { + vite: { + cssScopeTo: [svelteRequest.filename, 'default'] + } + } + }; } } // prevent vite asset plugin from loading files as url that should be compiled in transform diff --git a/packages/vite-plugin-svelte/src/types/compile.d.ts b/packages/vite-plugin-svelte/src/types/compile.d.ts index d6ba48e0f..b2171155f 100644 --- a/packages/vite-plugin-svelte/src/types/compile.d.ts +++ b/packages/vite-plugin-svelte/src/types/compile.d.ts @@ -12,6 +12,9 @@ export interface Code { code: string; map?: any; dependencies?: any[]; + __meta?: { + hasUnscopedGlobalCss?: boolean; + }; } export interface CompileData { diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index 4c0e524c9..b47d2dbba 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -1,7 +1,7 @@ import * as svelte from 'svelte/compiler'; - import { safeBase64Hash } from './hash.js'; import { log } from './log.js'; +import { walk } from 'zimmerframe'; import { checkPreprocessDependencies, @@ -133,6 +133,31 @@ export function createCompileSvelte() { let compiled; try { compiled = svelte.compile(finalCode, { ...finalCompileOptions, filename }); + if (compiled.css) { + if (finalCode.includes(':global') && compiled.ast.css) { + walk( + compiled.ast.css, + {}, + { + Selector(node, { stop }) { + if ( + node.children?.[0].type === 'PseudoClassSelector' && + node.children[0].name === 'global' + ) { + Object.defineProperty(compiled.css, '__meta', { + value: { hasUnscopedGlobalCss: true }, + writable: false, + enumerable: false, + configurable: false + }); + stop(); + } + } + } + ); + } + } + // patch output with partial accept until svelte does it // TODO remove later if ( From b450fabb460ce2d50f1f1cef4d424d8555c92b3b Mon Sep 17 00:00:00 2001 From: dominikg Date: Wed, 5 Mar 2025 11:13:21 +0100 Subject: [PATCH 2/7] chore: improve condition and add comment --- .../vite-plugin-svelte/src/utils/compile.js | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index b47d2dbba..4c339b6a0 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -133,29 +133,30 @@ export function createCompileSvelte() { let compiled; try { compiled = svelte.compile(finalCode, { ...finalCompileOptions, filename }); - if (compiled.css) { - if (finalCode.includes(':global') && compiled.ast.css) { - walk( - compiled.ast.css, - {}, - { - Selector(node, { stop }) { - if ( - node.children?.[0].type === 'PseudoClassSelector' && - node.children[0].name === 'global' - ) { - Object.defineProperty(compiled.css, '__meta', { - value: { hasUnscopedGlobalCss: true }, - writable: false, - enumerable: false, - configurable: false - }); - stop(); - } + + // check if css has unscoped :global. + // This is later used to decide if css output can be scoped to the js module for treeshaking + if (compiled.css && compiled.ast.css && finalCode.includes(':global')) { + walk( + compiled.ast.css, + {}, + { + Selector(node, { stop }) { + if ( + node.children?.[0].type === 'PseudoClassSelector' && + node.children[0].name === 'global' + ) { + Object.defineProperty(compiled.css, '__meta', { + value: { hasUnscopedGlobalCss: true }, + writable: false, + enumerable: false, + configurable: false + }); + stop(); } } - ); - } + } + ); } // patch output with partial accept until svelte does it From d5fe8e5bbbf5ffa71d86daf4fa602dd60f028617 Mon Sep 17 00:00:00 2001 From: dominikg Date: Tue, 18 Mar 2025 13:56:32 +0100 Subject: [PATCH 3/7] refactor: replace zimmerframe+walk with regex test in preprocessor --- packages/vite-plugin-svelte/package.json | 3 +- .../vite-plugin-svelte/src/utils/compile.js | 62 +++++++++---------- pnpm-lock.yaml | 15 +++++ 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/packages/vite-plugin-svelte/package.json b/packages/vite-plugin-svelte/package.json index cb0f18985..2b944ea11 100644 --- a/packages/vite-plugin-svelte/package.json +++ b/packages/vite-plugin-svelte/package.json @@ -46,8 +46,7 @@ "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", - "vitefu": "^1.0.6", - "zimmerframe": "^1.1.2" + "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index 4c339b6a0..bd8b0f5bd 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -1,7 +1,6 @@ import * as svelte from 'svelte/compiler'; import { safeBase64Hash } from './hash.js'; import { log } from './log.js'; -import { walk } from 'zimmerframe'; import { checkPreprocessDependencies, @@ -69,18 +68,31 @@ export function createCompileSvelte() { } let preprocessed; - let preprocessors = options.preprocess; + let hasUnscopedGlobalCss = false; + const preprocessors = options.preprocess + ? Array.isArray(options.preprocess) + ? [...options.preprocess] + : [options.preprocess] + : []; + if (!options.isBuild && options.emitCss && compileOptions.hmr) { // inject preprocessor that ensures css hmr works better - if (!Array.isArray(preprocessors)) { - preprocessors = preprocessors - ? [preprocessors, devStylePreprocessor] - : [devStylePreprocessor]; - } else { - preprocessors = preprocessors.concat(devStylePreprocessor); - } + preprocessors.push(devStylePreprocessor); + } + + if (options.emitCss) { + // check if css has unscoped global rules + // This is later used to decide if css output can be scoped to the js module for treeshaking + preprocessors.push({ + name: 'test-has-global-style', + style({ content }) { + hasUnscopedGlobalCss = + content?.length > 0 && /(?:^|,)\s*(?::global[\s{(]|@keyframes -global-)/m.test(content); + } + }); } - if (preprocessors) { + + if (preprocessors.length > 0) { try { preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works } catch (e) { @@ -134,29 +146,13 @@ export function createCompileSvelte() { try { compiled = svelte.compile(finalCode, { ...finalCompileOptions, filename }); - // check if css has unscoped :global. - // This is later used to decide if css output can be scoped to the js module for treeshaking - if (compiled.css && compiled.ast.css && finalCode.includes(':global')) { - walk( - compiled.ast.css, - {}, - { - Selector(node, { stop }) { - if ( - node.children?.[0].type === 'PseudoClassSelector' && - node.children[0].name === 'global' - ) { - Object.defineProperty(compiled.css, '__meta', { - value: { hasUnscopedGlobalCss: true }, - writable: false, - enumerable: false, - configurable: false - }); - stop(); - } - } - } - ); + if (compiled.css && hasUnscopedGlobalCss) { + Object.defineProperty(compiled.css, '__meta', { + value: { hasUnscopedGlobalCss }, + writable: false, + enumerable: false, + configurable: false + }); } // patch output with partial accept until svelte does it diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cade5757..73339bed1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -262,6 +262,21 @@ importers: specifier: ^6.3.2 version: 6.3.2(@types/node@20.17.30)(sass@1.86.3)(stylus@0.64.0)(yaml@2.7.0) + packages/e2e-tests/css-treeshake: + devDependencies: + '@sveltejs/vite-plugin-svelte': + specifier: workspace:^ + version: link:../../vite-plugin-svelte + sass: + specifier: ^1.85.1 + version: 1.85.1 + svelte: + specifier: ^5.22.2 + version: 5.22.2 + vite: + specifier: ^6.2.0 + version: 6.2.0(@types/node@20.17.23)(sass@1.85.1)(stylus@0.64.0)(yaml@2.7.0) + packages/e2e-tests/custom-extensions: devDependencies: '@sveltejs/vite-plugin-svelte': From 1f8bfe4bdb5b3551cb4ad9e9fca08c3701539050 Mon Sep 17 00:00:00 2001 From: dominikg Date: Tue, 18 Mar 2025 13:59:45 +0100 Subject: [PATCH 4/7] chore: remove extraneous check --- packages/vite-plugin-svelte/src/utils/compile.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index bd8b0f5bd..58ef02807 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -86,8 +86,7 @@ export function createCompileSvelte() { preprocessors.push({ name: 'test-has-global-style', style({ content }) { - hasUnscopedGlobalCss = - content?.length > 0 && /(?:^|,)\s*(?::global[\s{(]|@keyframes -global-)/m.test(content); + hasUnscopedGlobalCss = /(?:^|,)\s*(?::global[\s{(]|@keyframes -global-)/m.test(content); } }); } From f46c6221a459c3b105069fa868b9600a849b174e Mon Sep 17 00:00:00 2001 From: dominikg Date: Mon, 21 Apr 2025 14:53:02 +0200 Subject: [PATCH 5/7] refactor: use svelte 5.26 hasGlobal flag instead of custom impl --- packages/vite-plugin-svelte/src/index.js | 22 ++++++++----------- .../vite-plugin-svelte/src/types/compile.d.ts | 6 +++-- .../vite-plugin-svelte/src/utils/compile.js | 22 +------------------ pnpm-lock.yaml | 10 ++++----- 4 files changed, 19 insertions(+), 41 deletions(-) diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index cce486d77..aed46b80c 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -118,20 +118,16 @@ export function svelte(inlineOptions) { }; } else { if (query.svelte && query.type === 'style') { - // @ts-expect-error __meta does not exist - const { __meta, ...css } = cache.getCSS(svelteRequest); - if (css) { - if (__meta?.hasUnscopedGlobalCss) { - return css; // css contains unscoped global, do not scope to component + const cachedCss = cache.getCSS(svelteRequest); + if (cachedCss) { + const { hasGlobal, ...css } = cachedCss; + if (hasGlobal === false) { + // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false + css.meta ??= {}; + css.meta.vite ??= {}; + css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default']; } - return { - ...css, - meta: { - vite: { - cssScopeTo: [svelteRequest.filename, 'default'] - } - } - }; + return css; } } // prevent vite asset plugin from loading files as url that should be compiled in transform diff --git a/packages/vite-plugin-svelte/src/types/compile.d.ts b/packages/vite-plugin-svelte/src/types/compile.d.ts index b2171155f..1f28fcfc1 100644 --- a/packages/vite-plugin-svelte/src/types/compile.d.ts +++ b/packages/vite-plugin-svelte/src/types/compile.d.ts @@ -1,6 +1,7 @@ import type { Processed, CompileResult } from 'svelte/compiler'; import type { SvelteRequest } from './id.d.ts'; import type { ResolvedOptions } from './options.d.ts'; +import type { CustomPluginOptionsVite } from 'vite'; export type CompileSvelte = ( svelteRequest: SvelteRequest, @@ -12,8 +13,9 @@ export interface Code { code: string; map?: any; dependencies?: any[]; - __meta?: { - hasUnscopedGlobalCss?: boolean; + hasGlobal?: boolean; + meta?: { + vite?: CustomPluginOptionsVite; }; } diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index 58ef02807..8da0eb246 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -68,7 +68,7 @@ export function createCompileSvelte() { } let preprocessed; - let hasUnscopedGlobalCss = false; + const preprocessors = options.preprocess ? Array.isArray(options.preprocess) ? [...options.preprocess] @@ -80,17 +80,6 @@ export function createCompileSvelte() { preprocessors.push(devStylePreprocessor); } - if (options.emitCss) { - // check if css has unscoped global rules - // This is later used to decide if css output can be scoped to the js module for treeshaking - preprocessors.push({ - name: 'test-has-global-style', - style({ content }) { - hasUnscopedGlobalCss = /(?:^|,)\s*(?::global[\s{(]|@keyframes -global-)/m.test(content); - } - }); - } - if (preprocessors.length > 0) { try { preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works @@ -145,15 +134,6 @@ export function createCompileSvelte() { try { compiled = svelte.compile(finalCode, { ...finalCompileOptions, filename }); - if (compiled.css && hasUnscopedGlobalCss) { - Object.defineProperty(compiled.css, '__meta', { - value: { hasUnscopedGlobalCss }, - writable: false, - enumerable: false, - configurable: false - }); - } - // patch output with partial accept until svelte does it // TODO remove later if ( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73339bed1..787f30008 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -269,13 +269,13 @@ importers: version: link:../../vite-plugin-svelte sass: specifier: ^1.85.1 - version: 1.85.1 + version: 1.86.3 svelte: - specifier: ^5.22.2 - version: 5.22.2 + specifier: ^5.28.1 + version: 5.28.1 vite: - specifier: ^6.2.0 - version: 6.2.0(@types/node@20.17.23)(sass@1.85.1)(stylus@0.64.0)(yaml@2.7.0) + specifier: ^6.3.2 + version: 6.3.2(@types/node@20.17.30)(sass@1.86.3)(stylus@0.64.0)(yaml@2.7.0) packages/e2e-tests/custom-extensions: devDependencies: From 3e9cc313db93db5691edd69981ca2103774455fc Mon Sep 17 00:00:00 2001 From: dominikg Date: Mon, 21 Apr 2025 14:58:45 +0200 Subject: [PATCH 6/7] chore: revert code change from previous impl --- .../vite-plugin-svelte/src/utils/compile.js | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/vite-plugin-svelte/src/utils/compile.js b/packages/vite-plugin-svelte/src/utils/compile.js index 8da0eb246..f7999fe72 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.js +++ b/packages/vite-plugin-svelte/src/utils/compile.js @@ -68,19 +68,18 @@ export function createCompileSvelte() { } let preprocessed; - - const preprocessors = options.preprocess - ? Array.isArray(options.preprocess) - ? [...options.preprocess] - : [options.preprocess] - : []; - + let preprocessors = options.preprocess; if (!options.isBuild && options.emitCss && compileOptions.hmr) { // inject preprocessor that ensures css hmr works better - preprocessors.push(devStylePreprocessor); + if (!Array.isArray(preprocessors)) { + preprocessors = preprocessors + ? [preprocessors, devStylePreprocessor] + : [devStylePreprocessor]; + } else { + preprocessors = preprocessors.concat(devStylePreprocessor); + } } - - if (preprocessors.length > 0) { + if (preprocessors) { try { preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works } catch (e) { From 3840f63a5189b4e00dd1255b50f047713842a270 Mon Sep 17 00:00:00 2001 From: dominikg Date: Mon, 21 Apr 2025 15:00:22 +0200 Subject: [PATCH 7/7] chore: update changeset --- .changeset/plenty-eyes-talk.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/plenty-eyes-talk.md b/.changeset/plenty-eyes-talk.md index f753bc4f2..1bb7c3946 100644 --- a/.changeset/plenty-eyes-talk.md +++ b/.changeset/plenty-eyes-talk.md @@ -2,4 +2,4 @@ '@sveltejs/vite-plugin-svelte': minor --- -scope css to js module to enable treeshaking scoped css from unused components. Requires vite 6.2 +scope css to js module to enable treeshaking scoped css from unused components. Requires vite 6.2 and svelte 5.26