Skip to content

Commit ead758f

Browse files
authored
fix: only optimize nested cjs dependencies (#163)
* wip: fix nested optimization * feat: better heuristic * refactor(options): reuse createRequire * feat: add test * chore: add changeset * fix: update tests * chore: add github issue link * perf: skip optimizeDeps config in build
1 parent 649b9b0 commit ead758f

File tree

6 files changed

+43
-31
lines changed

6 files changed

+43
-31
lines changed

.changeset/tricky-clouds-grab.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': patch
3+
---
4+
5+
Only optimize nested cjs dependencies

packages/e2e-tests/package-json-svelte-field/__tests__/package-json-svelte-field.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ test('should render component imported via svelte field in package.json', async
77
});
88

99
if (!isBuild) {
10-
test('should optimize nested deps of excluded svelte deps', () => {
10+
test('should optimize nested cjs deps of excluded svelte deps', () => {
1111
const vitePrebundleMetadata = path.resolve(__dirname, '../node_modules/.vite/_metadata.json');
1212
const metadataFile = fs.readFileSync(vitePrebundleMetadata, 'utf8');
1313
const metadata = JSON.parse(metadataFile);
1414
const optimizedPaths = Object.keys(metadata.optimized);
1515
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-nested');
1616
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-simple');
17-
expect(optimizedPaths).toContain('e2e-test-dep-svelte-nested > e2e-test-dep-cjs-and-esm');
1817
expect(optimizedPaths).toContain(
1918
'e2e-test-dep-svelte-nested > e2e-test-dep-svelte-simple > e2e-test-dep-cjs-only'
2019
);

packages/e2e-tests/package-json-svelte-field/vite.config.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ const { defineConfig } = require('vite');
33

44
module.exports = defineConfig(({ command, mode }) => {
55
return {
6-
plugins: [
7-
svelte({
8-
disableDependencyReinclusion: ['e2e-test-dep-svelte-hybrid']
9-
})
10-
],
6+
plugins: [svelte()],
117
build: {
128
// make build faster by skipping transforms and minification
139
target: 'esnext',

packages/vite-plugin-svelte/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin {
5252
}
5353
options = await resolveOptions(inlineOptions, config, configEnv);
5454
// extra vite config
55-
const extraViteConfig = buildExtraViteConfig(options, config);
55+
const extraViteConfig = buildExtraViteConfig(options, config, configEnv);
5656
log.debug('additional vite config', extraViteConfig);
5757
return extraViteConfig as Partial<UserConfig>;
5858
},

packages/vite-plugin-svelte/src/utils/dependencies.ts

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ function getSvelteDependencies(
3939
const result = [];
4040
const localRequire = createRequire(`${pkgDir}/package.json`);
4141
const resolvedDeps = deps
42-
.map((dep) => resolveSvelteDependency(dep, localRequire))
43-
.filter(Boolean);
42+
.map((dep) => resolveDependencyData(dep, localRequire))
43+
.filter((depData) => !!depData && isSvelte(depData.pkg)) as DependencyData[];
4444
// @ts-ignore
4545
for (const { pkg, dir } of resolvedDeps) {
4646
result.push({ name: pkg.name, pkg, dir, path });
@@ -64,16 +64,10 @@ function getSvelteDependencies(
6464
return result;
6565
}
6666

67-
function resolveSvelteDependency(
68-
dep: string,
69-
localRequire: NodeRequire
70-
): { dir: string; pkg: Pkg } | void {
67+
function resolveDependencyData(dep: string, localRequire: NodeRequire): DependencyData | void {
7168
try {
7269
const pkgJson = `${dep}/package.json`;
7370
const pkg = localRequire(pkgJson);
74-
if (!isSvelte(pkg)) {
75-
return;
76-
}
7771
const dir = path.dirname(localRequire.resolve(pkgJson));
7872
return { dir, pkg };
7973
} catch (e) {
@@ -84,12 +78,6 @@ function resolveSvelteDependency(
8478
while (dir) {
8579
const pkg = parsePkg(dir, true);
8680
if (pkg && pkg.name === dep) {
87-
if (!isSvelte(pkg)) {
88-
return;
89-
}
90-
log.warn.once(
91-
`package.json of ${dep} has a "svelte" field but does not include itself in exports field. Please ask package maintainer to update`
92-
);
9381
return { dir, pkg };
9482
}
9583
const parent = path.dirname(dir);
@@ -174,6 +162,20 @@ function is_common_without_svelte_field(dependency: string): boolean {
174162
);
175163
}
176164

165+
export function needsOptimization(dep: string, localRequire: NodeRequire): boolean {
166+
const depData = resolveDependencyData(dep, localRequire);
167+
if (!depData) return false;
168+
const pkg = depData.pkg;
169+
// only optimize if is cjs, using the below as heuristic
170+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/162
171+
return pkg.main && !pkg.module && !pkg.exports;
172+
}
173+
174+
interface DependencyData {
175+
dir: string;
176+
pkg: Pkg;
177+
}
178+
177179
export interface SvelteDependency {
178180
name: string;
179181
dir: string;

packages/vite-plugin-svelte/src/utils/options.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import {
1313
// eslint-disable-next-line node/no-missing-import
1414
} from 'svelte/types/compiler/preprocess';
1515
import path from 'path';
16-
import { findRootSvelteDependencies, SvelteDependency } from './dependencies';
16+
import { findRootSvelteDependencies, needsOptimization, SvelteDependency } from './dependencies';
1717
import { DepOptimizationOptions } from 'vite/src/node/optimizer/index';
18+
import { createRequire } from 'module';
1819

1920
const knownOptions = new Set([
2021
'configFile',
@@ -180,12 +181,12 @@ function resolveViteRoot(viteConfig: UserConfig): string | undefined {
180181

181182
export function buildExtraViteConfig(
182183
options: ResolvedOptions,
183-
config: UserConfig
184+
config: UserConfig,
185+
configEnv: ConfigEnv
184186
): Partial<UserConfig> {
185187
// extra handling for svelte dependencies in the project
186188
const svelteDeps = findRootSvelteDependencies(options.root);
187189
const extraViteConfig: Partial<UserConfig> = {
188-
optimizeDeps: buildOptimizeDepsForSvelte(svelteDeps, options, config.optimizeDeps),
189190
resolve: {
190191
mainFields: [...SVELTE_RESOLVE_MAIN_FIELDS],
191192
dedupe: [...SVELTE_IMPORTS, ...SVELTE_HMR_IMPORTS]
@@ -196,6 +197,14 @@ export function buildExtraViteConfig(
196197
// knownJsSrcExtensions: options.extensions
197198
};
198199

200+
if (configEnv.command === 'serve') {
201+
extraViteConfig.optimizeDeps = buildOptimizeDepsForSvelte(
202+
svelteDeps,
203+
options,
204+
config.optimizeDeps
205+
);
206+
}
207+
199208
// @ts-ignore
200209
extraViteConfig.ssr = buildSSROptionsForSvelte(svelteDeps, options, config);
201210

@@ -253,11 +262,12 @@ function buildOptimizeDepsForSvelte(
253262
}
254263
const transitiveDepsToInclude = svelteDeps
255264
.filter((dep) => !disabledReinclusions.includes(dep.name) && isExcluded(dep.name))
256-
.flatMap((dep) =>
257-
Object.keys(dep.pkg.dependencies || {})
258-
.filter((depOfDep) => !isExcluded(depOfDep))
259-
.map((depOfDep) => dep.path.concat(dep.name, depOfDep).join(' > '))
260-
);
265+
.flatMap((dep) => {
266+
const localRequire = createRequire(`${dep.dir}/package.json`);
267+
return Object.keys(dep.pkg.dependencies || {})
268+
.filter((depOfDep) => !isExcluded(depOfDep) && needsOptimization(depOfDep, localRequire))
269+
.map((depOfDep) => dep.path.concat(dep.name, depOfDep).join(' > '));
270+
});
261271
log.debug(
262272
`reincluding transitive dependencies of excluded svelte dependencies`,
263273
transitiveDepsToInclude

0 commit comments

Comments
 (0)