Skip to content

Commit 9e1086b

Browse files
authoredJun 13, 2023
fix(ssr): fix crash when a pnpm/Yarn workspace depends on a CJS package (#9763)
1 parent 6a87c65 commit 9e1086b

File tree

11 files changed

+57
-28
lines changed

11 files changed

+57
-28
lines changed
 

‎packages/vite/src/node/plugins/importAnalysis.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ export function importAnalysisPlugin(config: ResolvedConfig): Plugin {
492492
) {
493493
return
494494
}
495-
} else if (shouldExternalizeForSSR(specifier, config)) {
495+
} else if (shouldExternalizeForSSR(specifier, importer, config)) {
496496
return
497497
}
498498
if (isBuiltin(specifier)) {

‎packages/vite/src/node/plugins/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export async function resolvePlugins(
6969
getDepsOptimizer: (ssr: boolean) => getDepsOptimizer(config, ssr),
7070
shouldExternalize:
7171
isBuild && config.build.ssr && config.ssr?.format !== 'cjs'
72-
? (id) => shouldExternalizeForSSR(id, config)
72+
? (id, importer) => shouldExternalizeForSSR(id, importer, config)
7373
: undefined,
7474
}),
7575
htmlInlineProxyPlugin(config),

‎packages/vite/src/node/plugins/preAlias.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function preAliasPlugin(config: ResolvedConfig): Plugin {
6969
(isInNodeModules(resolvedId) ||
7070
optimizeDeps.include?.includes(id)) &&
7171
isOptimizable(resolvedId, optimizeDeps) &&
72-
!(isBuild && ssr && isConfiguredAsExternal(id)) &&
72+
!(isBuild && ssr && isConfiguredAsExternal(id, importer)) &&
7373
(!ssr || optimizeAliasReplacementForSSR(resolvedId, optimizeDeps))
7474
) {
7575
// aliased dep has not yet been optimized

‎packages/vite/src/node/plugins/resolve.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export interface InternalResolveOptions extends Required<ResolveOptions> {
114114
ssrOptimizeCheck?: boolean
115115
// Resolve using esbuild deps optimization
116116
getDepsOptimizer?: (ssr: boolean) => DepsOptimizer | undefined
117-
shouldExternalize?: (id: string) => boolean | undefined
117+
shouldExternalize?: (id: string, importer?: string) => boolean | undefined
118118

119119
/**
120120
* Set by createResolver, we only care about the resolved id. moduleSideEffects
@@ -329,7 +329,7 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin {
329329

330330
// bare package imports, perform node resolve
331331
if (bareImportRE.test(id)) {
332-
const external = options.shouldExternalize?.(id)
332+
const external = options.shouldExternalize?.(id, importer)
333333
if (
334334
!external &&
335335
asSrc &&

‎packages/vite/src/node/ssr/ssrExternal.ts

+15-11
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,25 @@ const _require = createRequire(import.meta.url)
9292

9393
const isSsrExternalCache = new WeakMap<
9494
ResolvedConfig,
95-
(id: string) => boolean | undefined
95+
(id: string, importer?: string) => boolean | undefined
9696
>()
9797

9898
export function shouldExternalizeForSSR(
9999
id: string,
100+
importer: string | undefined,
100101
config: ResolvedConfig,
101102
): boolean | undefined {
102103
let isSsrExternal = isSsrExternalCache.get(config)
103104
if (!isSsrExternal) {
104105
isSsrExternal = createIsSsrExternal(config)
105106
isSsrExternalCache.set(config, isSsrExternal)
106107
}
107-
return isSsrExternal(id)
108+
return isSsrExternal(id, importer)
108109
}
109110

110111
export function createIsConfiguredAsSsrExternal(
111112
config: ResolvedConfig,
112-
): (id: string) => boolean {
113+
): (id: string, importer?: string) => boolean {
113114
const { ssr, root } = config
114115
const noExternal = ssr?.noExternal
115116
const noExternalFilter =
@@ -126,6 +127,7 @@ export function createIsConfiguredAsSsrExternal(
126127

127128
const isExternalizable = (
128129
id: string,
130+
importer?: string,
129131
configuredAsExternal?: boolean,
130132
): boolean => {
131133
if (!bareImportRE.test(id) || id.includes('\0')) {
@@ -134,7 +136,9 @@ export function createIsConfiguredAsSsrExternal(
134136
try {
135137
return !!tryNodeResolve(
136138
id,
137-
undefined,
139+
// Skip passing importer in build to avoid externalizing non-hoisted dependencies
140+
// unresolveable from root (which would be unresolvable from output bundles also)
141+
config.command === 'build' ? undefined : importer,
138142
resolveOptions,
139143
ssr?.target === 'webworker',
140144
undefined,
@@ -157,7 +161,7 @@ export function createIsConfiguredAsSsrExternal(
157161

158162
// Returns true if it is configured as external, false if it is filtered
159163
// by noExternal and undefined if it isn't affected by the explicit config
160-
return (id: string) => {
164+
return (id: string, importer?: string) => {
161165
const { ssr } = config
162166
if (ssr) {
163167
if (
@@ -169,14 +173,14 @@ export function createIsConfiguredAsSsrExternal(
169173
}
170174
const pkgName = getNpmPackageName(id)
171175
if (!pkgName) {
172-
return isExternalizable(id)
176+
return isExternalizable(id, importer)
173177
}
174178
if (
175179
// A package name in ssr.external externalizes every
176180
// externalizable package entry
177181
ssr.external?.includes(pkgName)
178182
) {
179-
return isExternalizable(id, true)
183+
return isExternalizable(id, importer, true)
180184
}
181185
if (typeof noExternal === 'boolean') {
182186
return !noExternal
@@ -185,24 +189,24 @@ export function createIsConfiguredAsSsrExternal(
185189
return false
186190
}
187191
}
188-
return isExternalizable(id)
192+
return isExternalizable(id, importer)
189193
}
190194
}
191195

192196
function createIsSsrExternal(
193197
config: ResolvedConfig,
194-
): (id: string) => boolean | undefined {
198+
): (id: string, importer?: string) => boolean | undefined {
195199
const processedIds = new Map<string, boolean | undefined>()
196200

197201
const isConfiguredAsExternal = createIsConfiguredAsSsrExternal(config)
198202

199-
return (id: string) => {
203+
return (id: string, importer?: string) => {
200204
if (processedIds.has(id)) {
201205
return processedIds.get(id)
202206
}
203207
let external = false
204208
if (id[0] !== '.' && !path.isAbsolute(id)) {
205-
external = isBuiltin(id) || isConfiguredAsExternal(id)
209+
external = isBuiltin(id) || isConfiguredAsExternal(id, importer)
206210
}
207211
processedIds.set(id, external)
208212
return external
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Module with state, to check that it is properly externalized and
2+
// not bundled in the optimized deps
3+
let msg
4+
5+
module.exports = {
6+
setMessage(externalMsg) {
7+
msg = externalMsg
8+
},
9+
getMessage() {
10+
return msg
11+
},
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "nested-external-cjs",
3+
"private": true,
4+
"version": "0.0.0",
5+
"main": "index.js",
6+
"type": "commonjs"
7+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import { setMessage } from 'nested-external'
2+
import external from 'nested-external-cjs'
23

34
setMessage('Hello World!')
5+
external.setMessage('Hello World!')

‎playground/ssr-deps/non-optimized-with-nested-external/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"type": "module",
66
"main": "index.js",
77
"dependencies": {
8-
"nested-external": "file:../nested-external"
8+
"nested-external": "file:../nested-external",
9+
"nested-external-cjs": "file:../nested-external-cjs"
910
}
1011
}

‎playground/ssr-deps/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"@vitejs/test-no-external-cjs": "file:./no-external-cjs",
2424
"@vitejs/test-import-builtin-cjs": "file:./import-builtin-cjs",
2525
"@vitejs/test-no-external-css": "file:./no-external-css",
26-
"@vitejs/test-non-optimized-with-nested-external": "file:./non-optimized-with-nested-external",
26+
"@vitejs/test-non-optimized-with-nested-external": "workspace:*",
2727
"@vitejs/test-optimized-with-nested-external": "file:./optimized-with-nested-external",
2828
"@vitejs/test-optimized-cjs-with-nested-external": "file:./optimized-with-nested-external",
2929
"@vitejs/test-external-using-external-entry": "file:./external-using-external-entry",

‎pnpm-lock.yaml

+13-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
Please sign in to comment.