From 74c7c5c6156b9d949f7edb328ea8410752160649 Mon Sep 17 00:00:00 2001 From: dominikg Date: Mon, 28 Jul 2025 16:30:29 +0200 Subject: [PATCH 1/4] feat: enable optimizer for server environments during dev --- .changeset/spotty-jokes-notice.md | 5 + .../src/plugins/configure.js | 14 +- .../src/plugins/setup-optimizer.js | 145 ++++++++---------- 3 files changed, 85 insertions(+), 79 deletions(-) create mode 100644 .changeset/spotty-jokes-notice.md diff --git a/.changeset/spotty-jokes-notice.md b/.changeset/spotty-jokes-notice.md new file mode 100644 index 000000000..abf6d4513 --- /dev/null +++ b/.changeset/spotty-jokes-notice.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': minor +--- + +feat: enable optimizer for server environments during dev diff --git a/packages/vite-plugin-svelte/src/plugins/configure.js b/packages/vite-plugin-svelte/src/plugins/configure.js index 68f5e080a..b897f1386 100644 --- a/packages/vite-plugin-svelte/src/plugins/configure.js +++ b/packages/vite-plugin-svelte/src/plugins/configure.js @@ -39,6 +39,11 @@ export function configure(api, inlineOptions) { */ let preOptions; + /** + * @type {unknown} + */ + let extraViteConfig; + /** @type {import('vite').Plugin} */ return { name: 'vite-plugin-svelte:config', @@ -57,11 +62,12 @@ export function configure(api, inlineOptions) { preOptions = await preResolveOptions(inlineOptions, config, configEnv); // extra vite config - const extraViteConfig = await buildExtraViteConfig(preOptions, config); + extraViteConfig = await buildExtraViteConfig(preOptions, config); log.debug('additional vite config', extraViteConfig, 'config'); return extraViteConfig; } }, + configResolved: { order: 'pre', handler(config) { @@ -87,6 +93,12 @@ export function configure(api, inlineOptions) { ensureConfigEnvironmentConditions(name, config, opts); // @ts-expect-error the function above should make `resolve.conditions` non-nullable config.resolve.conditions.push('svelte'); + if (config.consumer === 'server' && extraViteConfig?.optimizeDeps) { + // optimizeDeps is not inherited by server environments so return it here + return { + optimizeDeps: extraViteConfig.optimizeDeps + }; + } }, configureServer(server) { diff --git a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js index 56468a5ac..047d8a4aa 100644 --- a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js +++ b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js @@ -32,7 +32,8 @@ export function setupOptimizer(api) { return { name: 'vite-plugin-svelte:setup-optimizer', apply: 'serve', - config() { + configEnvironment(_name, config) { + const consumer = config.consumer ?? 'client'; /** @type {import('vite').UserConfig['optimizeDeps']} */ const optimizeDeps = { // Experimental Vite API to allow these extensions to be scanned and prebundled @@ -43,17 +44,18 @@ export function setupOptimizer(api) { // the added plugins are patched in configResolved below if (rolldownVersion) { //@ts-ignore rolldown types not finished + optimizeDeps.rollupOptions = { plugins: [ - placeholderRolldownOptimizerPlugin(optimizeSveltePluginName), - placeholderRolldownOptimizerPlugin(optimizeSvelteModulePluginName) + rolldownOptimizerPlugin(api, consumer, true), + rolldownOptimizerPlugin(api, consumer, false) ] }; } else { optimizeDeps.esbuildOptions = { plugins: [ - { name: optimizeSveltePluginName, setup: () => {} }, - { name: optimizeSvelteModulePluginName, setup: () => {} } + eSBuildOptimizerPlugin(api, consumer, true), + eSBuildOptimizerPlugin(api, consumer, false) ] }; } @@ -61,25 +63,6 @@ export function setupOptimizer(api) { }, configResolved(c) { viteConfig = c; - const optimizeDeps = c.optimizeDeps; - if (rolldownVersion) { - const plugins = - // @ts-expect-error not typed - optimizeDeps.rollupOptions?.plugins?.filter((p) => - [optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name) - ) ?? []; - for (const plugin of plugins) { - patchRolldownOptimizerPlugin(plugin, api.options); - } - } else { - const plugins = - optimizeDeps.esbuildOptions?.plugins?.filter((p) => - [optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name) - ) ?? []; - for (const plugin of plugins) { - patchESBuildOptimizerPlugin(plugin, api.options); - } - } }, async buildStart() { if (!api.options.prebundleSvelteLibraries) return; @@ -94,54 +77,72 @@ export function setupOptimizer(api) { } /** - * @param {EsbuildPlugin} plugin - * @param {import('../types/options.d.ts').ResolvedOptions} options + * @param {import('../types/plugin-api.d.ts').PluginAPI} api + * @param {'server'|'client'} consumer + * @param {boolean} components + * @return {EsbuildPlugin} */ -function patchESBuildOptimizerPlugin(plugin, options) { - const components = plugin.name === optimizeSveltePluginName; +function eSBuildOptimizerPlugin(api, consumer, components) { + const name = components ? optimizeSveltePluginName : optimizeSvelteModulePluginName; const compileFn = components ? compileSvelte : compileSvelteModule; const statsName = components ? 'prebundle library components' : 'prebundle library modules'; const filter = components ? /\.svelte(?:\?.*)?$/ : /\.svelte\.[jt]s(?:\?.*)?$/; - plugin.setup = (build) => { - if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return; + const generate = consumer === 'server' ? 'server' : 'client'; - /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */ - let statsCollection; - build.onStart(() => { - statsCollection = options.stats?.startCollection(statsName, { - logResult: (c) => c.stats.length > 1 + return { + name, + setup(build) { + if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return; + + /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */ + let statsCollection; + build.onStart(() => { + statsCollection = api.options.stats?.startCollection(statsName, { + logResult: (c) => c.stats.length > 1 + }); }); - }); - build.onLoad({ filter }, async ({ path: filename }) => { - const code = readFileSync(filename, 'utf8'); - try { - const result = await compileFn(options, { filename, code }, statsCollection); - const contents = result.map - ? result.code + '//# sourceMappingURL=' + result.map.toUrl() - : result.code; - return { contents }; - } catch (e) { - return { errors: [toESBuildError(e, options)] }; - } - }); - build.onEnd(() => { - statsCollection?.finish(); - }); + build.onLoad({ filter }, async ({ path: filename }) => { + const code = readFileSync(filename, 'utf8'); + try { + const result = await compileFn( + api.options, + { filename, code }, + generate, + statsCollection + ); + const contents = result.map + ? result.code + '//# sourceMappingURL=' + result.map.toUrl() + : result.code; + return { contents }; + } catch (e) { + return { errors: [toESBuildError(e, api.options)] }; + } + }); + build.onEnd(() => { + statsCollection?.finish(); + }); + } }; } /** - * @param {RollupPlugin} plugin - * @param {import('../types/options.d.ts').ResolvedOptions} options + * @param {import('../types/plugin-api.d.ts').PluginAPI} api + * @param {'server'|'client'} consumer + * @param {boolean} components + * @return {import('vite').Rollup.Plugin} */ -function patchRolldownOptimizerPlugin(plugin, options) { - const components = plugin.name === optimizeSveltePluginName; +function rolldownOptimizerPlugin(api, consumer, components) { + const name = components ? optimizeSveltePluginName : optimizeSvelteModulePluginName; const compileFn = components ? compileSvelte : compileSvelteModule; const statsName = components ? 'prebundle library components' : 'prebundle library modules'; const includeRe = components ? /^[^?#]+\.svelte(?:[?#]|$)/ : /^[^?#]+\.svelte\.[jt]s(?:[?#]|$)/; + const generate = consumer === 'server' ? 'server' : 'client'; /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */ let statsCollection; - + /**@type {import('vite').Rollup.Plugin}*/ + const plugin = { + name + }; plugin.options = (opts) => { // @ts-expect-error plugins is an array here const isScanner = opts.plugins.some( @@ -160,14 +161,14 @@ function patchRolldownOptimizerPlugin(plugin, options) { */ async handler(code, filename) { try { - return await compileFn(options, { filename, code }, statsCollection); + return await compileFn(api.options, { filename, code }, generate, statsCollection); } catch (e) { - throw toRollupError(e, options); + throw toRollupError(e, api.options); } } }; plugin.buildStart = () => { - statsCollection = options.stats?.startCollection(statsName, { + statsCollection = api.options.stats?.startCollection(statsName, { logResult: (c) => c.stats.length > 1 }); }; @@ -176,15 +177,17 @@ function patchRolldownOptimizerPlugin(plugin, options) { }; } }; + return plugin; } /** * @param {import('../types/options.d.ts').ResolvedOptions} options * @param {{ filename: string, code: string }} input + * @param {'client'|'server'} generate * @param {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} [statsCollection] * @returns {Promise} */ -async function compileSvelte(options, { filename, code }, statsCollection) { +async function compileSvelte(options, { filename, code }, generate, statsCollection) { let css = options.compilerOptions.css; if (css !== 'injected') { // TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js @@ -196,7 +199,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) { ...options.compilerOptions, css, filename, - generate: 'client' + generate }; if (compileOptions.hmr && options.emitCss) { @@ -252,15 +255,16 @@ async function compileSvelte(options, { filename, code }, statsCollection) { /** * @param {import('../types/options.d.ts').ResolvedOptions} options * @param {{ filename: string; code: string }} input + * @param {'client'|'server'} generate * @param {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection} [statsCollection] * @returns {Promise} */ -async function compileSvelteModule(options, { filename, code }, statsCollection) { +async function compileSvelteModule(options, { filename, code }, generate, statsCollection) { const endStat = statsCollection?.start(filename); const compiled = svelte.compileModule(code, { dev: options.compilerOptions?.dev ?? true, // default to dev: true because prebundling is only used in dev filename, - generate: 'client' + generate }); if (endStat) { endStat(); @@ -311,21 +315,6 @@ async function svelteMetadataChanged(cacheDir, options) { return currentSvelteMetadata !== existingSvelteMetadata; } -/** - * - * @param {string} name - * @returns {import('vite').Rollup.Plugin} - */ -function placeholderRolldownOptimizerPlugin(name) { - return { - name, - options() {}, - buildStart() {}, - buildEnd() {}, - transform: { filter: { id: /^$/ }, handler() {} } - }; -} - /** * @param {import('../types/options.d.ts').ResolvedOptions} options * @returns {Partial} From 730c995616fd89976a01e4b53d184affeca8812f Mon Sep 17 00:00:00 2001 From: "Dominik G." Date: Tue, 29 Jul 2025 08:34:23 +0000 Subject: [PATCH 2/4] Update packages/vite-plugin-svelte/src/plugins/setup-optimizer.js Co-authored-by: Tee Ming --- packages/vite-plugin-svelte/src/plugins/setup-optimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js index 047d8a4aa..0d9ebd29c 100644 --- a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js +++ b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js @@ -82,7 +82,7 @@ export function setupOptimizer(api) { * @param {boolean} components * @return {EsbuildPlugin} */ -function eSBuildOptimizerPlugin(api, consumer, components) { +function esbuildOptimizerPlugin(api, consumer, components) { const name = components ? optimizeSveltePluginName : optimizeSvelteModulePluginName; const compileFn = components ? compileSvelte : compileSvelteModule; const statsName = components ? 'prebundle library components' : 'prebundle library modules'; From ff27973d5247aa095923b9332f751869f680935a Mon Sep 17 00:00:00 2001 From: "Dominik G." Date: Tue, 29 Jul 2025 08:39:11 +0000 Subject: [PATCH 3/4] Apply suggestions from code review Co-authored-by: Tee Ming --- packages/vite-plugin-svelte/src/plugins/setup-optimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js index 0d9ebd29c..374722016 100644 --- a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js +++ b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js @@ -54,8 +54,8 @@ export function setupOptimizer(api) { } else { optimizeDeps.esbuildOptions = { plugins: [ - eSBuildOptimizerPlugin(api, consumer, true), - eSBuildOptimizerPlugin(api, consumer, false) + esbuildOptimizerPlugin(api, consumer, true), + esbuildOptimizerPlugin(api, consumer, false) ] }; } From 0cef83c0ef9bada9d5d4fad7fbe2e6a810ed8320 Mon Sep 17 00:00:00 2001 From: dominikg Date: Tue, 29 Jul 2025 11:02:07 +0200 Subject: [PATCH 4/4] improve typing and fix default handling of consumer --- packages/vite-plugin-svelte/src/plugins/configure.js | 11 ++++++----- .../vite-plugin-svelte/src/plugins/setup-optimizer.js | 5 +++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/vite-plugin-svelte/src/plugins/configure.js b/packages/vite-plugin-svelte/src/plugins/configure.js index b897f1386..b9cd6a0c5 100644 --- a/packages/vite-plugin-svelte/src/plugins/configure.js +++ b/packages/vite-plugin-svelte/src/plugins/configure.js @@ -40,9 +40,9 @@ export function configure(api, inlineOptions) { let preOptions; /** - * @type {unknown} + * @type {import('vite').DepOptimizationConfig | undefined} */ - let extraViteConfig; + let optimizeDeps = undefined; /** @type {import('vite').Plugin} */ return { @@ -62,8 +62,9 @@ export function configure(api, inlineOptions) { preOptions = await preResolveOptions(inlineOptions, config, configEnv); // extra vite config - extraViteConfig = await buildExtraViteConfig(preOptions, config); + const extraViteConfig = await buildExtraViteConfig(preOptions, config); log.debug('additional vite config', extraViteConfig, 'config'); + optimizeDeps = extraViteConfig.optimizeDeps; return extraViteConfig; } }, @@ -93,10 +94,10 @@ export function configure(api, inlineOptions) { ensureConfigEnvironmentConditions(name, config, opts); // @ts-expect-error the function above should make `resolve.conditions` non-nullable config.resolve.conditions.push('svelte'); - if (config.consumer === 'server' && extraViteConfig?.optimizeDeps) { + if (config.consumer === 'server' && optimizeDeps !== undefined) { // optimizeDeps is not inherited by server environments so return it here return { - optimizeDeps: extraViteConfig.optimizeDeps + optimizeDeps }; } }, diff --git a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js index 374722016..c09273a3a 100644 --- a/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js +++ b/packages/vite-plugin-svelte/src/plugins/setup-optimizer.js @@ -32,8 +32,9 @@ export function setupOptimizer(api) { return { name: 'vite-plugin-svelte:setup-optimizer', apply: 'serve', - configEnvironment(_name, config) { - const consumer = config.consumer ?? 'client'; + configEnvironment(name, config) { + // fall back to vite behavior when consumer isn't set + const consumer = (config.consumer ?? name === 'client') ? 'client' : 'server'; /** @type {import('vite').UserConfig['optimizeDeps']} */ const optimizeDeps = { // Experimental Vite API to allow these extensions to be scanned and prebundled