diff --git a/.changeset/fuzzy-pets-drop.md b/.changeset/fuzzy-pets-drop.md new file mode 100644 index 000000000..9ed9db752 --- /dev/null +++ b/.changeset/fuzzy-pets-drop.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': patch +--- + +when prebundleSvelteLibraries is true and a dependency is manually excluded, generate reincludes for it's cjs deps diff --git a/packages/e2e-tests/prebundle-svelte-deps/__tests__/prebundle-svelte-deps.spec.ts b/packages/e2e-tests/prebundle-svelte-deps/__tests__/prebundle-svelte-deps.spec.ts index a04ab6c82..8d199b7fd 100644 --- a/packages/e2e-tests/prebundle-svelte-deps/__tests__/prebundle-svelte-deps.spec.ts +++ b/packages/e2e-tests/prebundle-svelte-deps/__tests__/prebundle-svelte-deps.spec.ts @@ -29,8 +29,8 @@ if (!isBuild) { const metadata = JSON.parse(metadataFile); const optimizedPaths = Object.keys(metadata.optimized); expect(optimizedPaths).toContain('e2e-test-dep-svelte-simple'); - expect(optimizedPaths).toContain('e2e-test-dep-svelte-hybrid'); expect(optimizedPaths).toContain('e2e-test-dep-svelte-api-only'); + expect(optimizedPaths).toContain('e2e-test-dep-svelte-nested'); }); test('should not optimize excluded svelte dependencies', () => { @@ -38,5 +38,7 @@ if (!isBuild) { const metadata = JSON.parse(metadataFile); const optimizedPaths = Object.keys(metadata.optimized); expect(optimizedPaths).not.toContain('e2e-test-dep-scss-only'); + expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-hybrid'); + expect(optimizedPaths).toContain('e2e-test-dep-svelte-hybrid > e2e-test-dep-cjs-only'); }); } diff --git a/packages/e2e-tests/prebundle-svelte-deps/vite.config.js b/packages/e2e-tests/prebundle-svelte-deps/vite.config.js index d6ba95770..9a7199cfc 100644 --- a/packages/e2e-tests/prebundle-svelte-deps/vite.config.js +++ b/packages/e2e-tests/prebundle-svelte-deps/vite.config.js @@ -5,9 +5,12 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'; export default defineConfig({ plugins: [svelte()], optimizeDeps: { - // TODO this must be excluded because nested has an scss dep that prebundle can't handle! - // figure out how to exclude it automatically or at least tell the user about it in a more friendly way - exclude: ['e2e-test-dep-scss-only'] + exclude: [ + // TODO this must be excluded because nested has an scss dep that prebundle can't handle! + // figure out how to exclude it automatically or at least tell the user about it in a more friendly way + 'e2e-test-dep-scss-only', + 'e2e-test-dep-svelte-hybrid' + ] }, build: { // make build faster by skipping transforms and minification diff --git a/packages/vite-plugin-svelte/src/utils/options.ts b/packages/vite-plugin-svelte/src/utils/options.ts index 34f43ffb3..a010807b5 100644 --- a/packages/vite-plugin-svelte/src/utils/options.ts +++ b/packages/vite-plugin-svelte/src/utils/options.ts @@ -334,49 +334,47 @@ export async function buildExtraViteConfig( // knownJsSrcExtensions: options.extensions }; - const optimizeDepsConfig = buildOptimizeDepsForSvelte(config); - const ssrConfig = buildSSROptionsForSvelte(config); - const crawlConfig = await getCrawlPkgsConfig(options, config); - + const extraSvelteConfig = buildExtraConfigForSvelte(config); + const extraDepsConfig = await buildExtraConfigForDependencies(options, config); + // merge extra svelte and deps config, but make sure dep values are not contradicting svelte extraViteConfig.optimizeDeps = { include: [ - ...optimizeDepsConfig.include, - ...crawlConfig.optimizeDeps.include.filter( - (dep) => !isDepExcluded(dep, optimizeDepsConfig.exclude) + ...extraSvelteConfig.optimizeDeps.include, + ...extraDepsConfig.optimizeDeps.include.filter( + (dep) => !isDepExcluded(dep, extraSvelteConfig.optimizeDeps.exclude) ) ], exclude: [ - ...optimizeDepsConfig.exclude, - ...crawlConfig.optimizeDeps.exclude.filter( - (dep) => !isDepIncluded(dep, optimizeDepsConfig.include) + ...extraSvelteConfig.optimizeDeps.exclude, + ...extraDepsConfig.optimizeDeps.exclude.filter( + (dep) => !isDepIncluded(dep, extraSvelteConfig.optimizeDeps.include) ) ] }; extraViteConfig.ssr = { - noExternal: [ - ...ssrConfig.noExternal, - ...crawlConfig.ssr.noExternal.filter((dep) => !isDepExternaled(dep, ssrConfig.external)) - ], external: [ - ...ssrConfig.external, - ...crawlConfig.ssr.external.filter((dep) => !isDepNoExternaled(dep, ssrConfig.noExternal)) + ...extraSvelteConfig.ssr.external, + ...extraDepsConfig.ssr.external.filter( + (dep) => !isDepNoExternaled(dep, extraSvelteConfig.ssr.noExternal) + ) + ], + noExternal: [ + ...extraSvelteConfig.ssr.noExternal, + ...extraDepsConfig.ssr.noExternal.filter( + (dep) => !isDepExternaled(dep, extraSvelteConfig.ssr.external) + ) ] }; // handle prebundling for svelte files if (options.prebundleSvelteLibraries) { - extraViteConfig.optimizeDeps = { - ...extraViteConfig.optimizeDeps, - // Experimental Vite API to allow these extensions to be scanned and prebundled - // @ts-ignore - extensions: options.extensions ?? ['.svelte'], - // Add esbuild plugin to prebundle Svelte files. - // Currently a placeholder as more information is needed after Vite config is resolved, - // the real Svelte plugin is added in `patchResolvedViteConfig()` - esbuildOptions: { - plugins: [{ name: facadeEsbuildSveltePluginName, setup: () => {} }] - } + extraViteConfig.optimizeDeps.extensions = options.extensions ?? ['.svelte']; + // Add esbuild plugin to prebundle Svelte files. + // Currently a placeholder as more information is needed after Vite config is resolved, + // the real Svelte plugin is added in `patchResolvedViteConfig()` + extraViteConfig.optimizeDeps.esbuildOptions = { + plugins: [{ name: facadeEsbuildSveltePluginName, setup: () => {} }] }; } @@ -393,7 +391,7 @@ export async function buildExtraViteConfig( return extraViteConfig; } -async function getCrawlPkgsConfig(options: PreResolvedOptions, config: UserConfig) { +async function buildExtraConfigForDependencies(options: PreResolvedOptions, config: UserConfig) { // extra handling for svelte dependencies in the project const depsConfig = await crawlFrameworkPkgs({ root: options.root, @@ -415,11 +413,28 @@ async function getCrawlPkgsConfig(options: PreResolvedOptions, config: UserConfi } }); - log.debug('crawl svelte packages result', depsConfig); + log.debug('extra config for dependencies generated by vitefu', depsConfig); if (options.prebundleSvelteLibraries) { - depsConfig.optimizeDeps = { include: [], exclude: [] }; - } else if (options.disableDependencyReinclusion === true) { + // prebundling enabled, so we don't need extra dependency excludes + depsConfig.optimizeDeps.exclude = []; + // but keep dependency reinclusions of explicit user excludes + const userExclude = config.optimizeDeps?.exclude; + depsConfig.optimizeDeps.include = !userExclude + ? [] + : depsConfig.optimizeDeps.include.filter((dep: string) => { + // reincludes look like this: foo > bar > baz + // in case foo or bar are excluded, we have to retain the reinclude even with prebundling + return ( + dep.includes('>') && + dep + .split('>') + .slice(0, -1) + .some((d) => isDepExcluded(d.trim(), userExclude)) + ); + }); + } + if (options.disableDependencyReinclusion === true) { depsConfig.optimizeDeps.include = depsConfig.optimizeDeps.include.filter( (dep) => !dep.includes('>') ); @@ -432,12 +447,12 @@ async function getCrawlPkgsConfig(options: PreResolvedOptions, config: UserConfi }); } - log.debug('processed crawl svelte packages result', depsConfig); + log.debug('post-processed extra config for dependencies', depsConfig); return depsConfig; } -function buildOptimizeDepsForSvelte(config: UserConfig) { +function buildExtraConfigForSvelte(config: UserConfig) { // include svelte imports for optimization unless explicitly excluded const include: string[] = []; const exclude: string[] = ['svelte-hmr']; @@ -450,10 +465,6 @@ function buildOptimizeDepsForSvelte(config: UserConfig) { } else { log.debug('"svelte" is excluded in optimizeDeps.exclude, skipped adding it to include.'); } - return { include, exclude }; -} - -function buildSSROptionsForSvelte(config: UserConfig) { const noExternal: (string | RegExp)[] = []; const external: string[] = []; // add svelte to ssr.noExternal unless it is present in ssr.external @@ -461,7 +472,7 @@ function buildSSROptionsForSvelte(config: UserConfig) { if (!isDepExternaled('svelte', config.ssr?.external ?? [])) { noExternal.push('svelte', /^svelte\//); } - return { noExternal, external }; + return { optimizeDeps: { include, exclude }, ssr: { noExternal, external } }; } export function patchResolvedViteConfig(viteConfig: ResolvedConfig, options: ResolvedOptions) {