Skip to content

Commit 82a5b11

Browse files
authored
fix(ssr): skip esm proxy guard for namespace imports (#14988)
1 parent bedfcfa commit 82a5b11

File tree

3 files changed

+76
-50
lines changed

3 files changed

+76
-50
lines changed

packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test('default import', async () => {
1111
expect(
1212
await ssrTransformSimpleCode(`import foo from 'vue';console.log(foo.bar)`),
1313
).toMatchInlineSnapshot(`
14-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\");
14+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"default\\"]});
1515
console.log(__vite_ssr_import_0__.default.bar)"
1616
`)
1717
})
@@ -22,7 +22,7 @@ test('named import', async () => {
2222
`import { ref } from 'vue';function foo() { return ref(0) }`,
2323
),
2424
).toMatchInlineSnapshot(`
25-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"ref\\"]});
25+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"ref\\"]});
2626
function foo() { return __vite_ssr_import_0__.ref(0) }"
2727
`)
2828
})
@@ -77,7 +77,7 @@ test('export named from', async () => {
7777
expect(
7878
await ssrTransformSimpleCode(`export { ref, computed as c } from 'vue'`),
7979
).toMatchInlineSnapshot(`
80-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"ref\\",\\"computed\\"]});
80+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"ref\\",\\"computed\\"]});
8181
8282
Object.defineProperty(__vite_ssr_exports__, \\"ref\\", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.ref }});
8383
Object.defineProperty(__vite_ssr_exports__, \\"c\\", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.computed }});"
@@ -90,7 +90,7 @@ test('named exports of imported binding', async () => {
9090
`import {createApp} from 'vue';export {createApp}`,
9191
),
9292
).toMatchInlineSnapshot(`
93-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"createApp\\"]});
93+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"createApp\\"]});
9494
9595
Object.defineProperty(__vite_ssr_exports__, \\"createApp\\", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.createApp }});"
9696
`)
@@ -132,7 +132,7 @@ test('export then import minified', async () => {
132132
`export * from 'vue';import {createApp} from 'vue';`,
133133
),
134134
).toMatchInlineSnapshot(`
135-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"createApp\\"]});
135+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"createApp\\"]});
136136
const __vite_ssr_import_1__ = await __vite_ssr_import__(\\"vue\\");
137137
__vite_ssr_exportAll__(__vite_ssr_import_1__);
138138
"
@@ -145,7 +145,7 @@ test('hoist import to top', async () => {
145145
`path.resolve('server.js');import path from 'node:path';`,
146146
),
147147
).toMatchInlineSnapshot(`
148-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"node:path\\");
148+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"node:path\\", {\\"importedNames\\":[\\"default\\"]});
149149
__vite_ssr_import_0__.default.resolve('server.js');"
150150
`)
151151
})
@@ -173,7 +173,7 @@ test('do not rewrite method definition', async () => {
173173
`import { fn } from 'vue';class A { fn() { fn() } }`,
174174
)
175175
expect(result?.code).toMatchInlineSnapshot(`
176-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
176+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
177177
class A { fn() { __vite_ssr_import_0__.fn() } }"
178178
`)
179179
expect(result?.deps).toEqual(['vue'])
@@ -184,7 +184,7 @@ test('do not rewrite when variable is in scope', async () => {
184184
`import { fn } from 'vue';function A(){ const fn = () => {}; return { fn }; }`,
185185
)
186186
expect(result?.code).toMatchInlineSnapshot(`
187-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
187+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
188188
function A(){ const fn = () => {}; return { fn }; }"
189189
`)
190190
expect(result?.deps).toEqual(['vue'])
@@ -196,7 +196,7 @@ test('do not rewrite when variable is in scope with object destructuring', async
196196
`import { fn } from 'vue';function A(){ let {fn, test} = {fn: 'foo', test: 'bar'}; return { fn }; }`,
197197
)
198198
expect(result?.code).toMatchInlineSnapshot(`
199-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
199+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
200200
function A(){ let {fn, test} = {fn: 'foo', test: 'bar'}; return { fn }; }"
201201
`)
202202
expect(result?.deps).toEqual(['vue'])
@@ -208,7 +208,7 @@ test('do not rewrite when variable is in scope with array destructuring', async
208208
`import { fn } from 'vue';function A(){ let [fn, test] = ['foo', 'bar']; return { fn }; }`,
209209
)
210210
expect(result?.code).toMatchInlineSnapshot(`
211-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
211+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
212212
function A(){ let [fn, test] = ['foo', 'bar']; return { fn }; }"
213213
`)
214214
expect(result?.deps).toEqual(['vue'])
@@ -220,7 +220,7 @@ test('rewrite variable in string interpolation in function nested arguments', as
220220
`import { fn } from 'vue';function A({foo = \`test\${fn}\`} = {}){ return {}; }`,
221221
)
222222
expect(result?.code).toMatchInlineSnapshot(`
223-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
223+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
224224
function A({foo = \`test\${__vite_ssr_import_0__.fn}\`} = {}){ return {}; }"
225225
`)
226226
expect(result?.deps).toEqual(['vue'])
@@ -232,7 +232,7 @@ test('rewrite variables in default value of destructuring params', async () => {
232232
`import { fn } from 'vue';function A({foo = fn}){ return {}; }`,
233233
)
234234
expect(result?.code).toMatchInlineSnapshot(`
235-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
235+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
236236
function A({foo = __vite_ssr_import_0__.fn}){ return {}; }"
237237
`)
238238
expect(result?.deps).toEqual(['vue'])
@@ -243,7 +243,7 @@ test('do not rewrite when function declaration is in scope', async () => {
243243
`import { fn } from 'vue';function A(){ function fn() {}; return { fn }; }`,
244244
)
245245
expect(result?.code).toMatchInlineSnapshot(`
246-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"fn\\"]});
246+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"fn\\"]});
247247
function A(){ function fn() {}; return { fn }; }"
248248
`)
249249
expect(result?.deps).toEqual(['vue'])
@@ -254,7 +254,7 @@ test('do not rewrite catch clause', async () => {
254254
`import {error} from './dependency';try {} catch(error) {}`,
255255
)
256256
expect(result?.code).toMatchInlineSnapshot(`
257-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"namedImportSpecifiers\\":[\\"error\\"]});
257+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"importedNames\\":[\\"error\\"]});
258258
try {} catch(error) {}"
259259
`)
260260
expect(result?.deps).toEqual(['./dependency'])
@@ -267,7 +267,7 @@ test('should declare variable for imported super class', async () => {
267267
`import { Foo } from './dependency';` + `class A extends Foo {}`,
268268
),
269269
).toMatchInlineSnapshot(`
270-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"namedImportSpecifiers\\":[\\"Foo\\"]});
270+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"importedNames\\":[\\"Foo\\"]});
271271
const Foo = __vite_ssr_import_0__.Foo;
272272
class A extends Foo {}"
273273
`)
@@ -281,7 +281,7 @@ test('should declare variable for imported super class', async () => {
281281
`export class B extends Foo {}`,
282282
),
283283
).toMatchInlineSnapshot(`
284-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"namedImportSpecifiers\\":[\\"Foo\\"]});
284+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./dependency\\", {\\"importedNames\\":[\\"Foo\\"]});
285285
const Foo = __vite_ssr_import_0__.Foo;
286286
class A extends Foo {}
287287
class B extends Foo {}
@@ -354,7 +354,7 @@ test('overwrite bindings', async () => {
354354
`function g() { const f = () => { const inject = true }; console.log(inject) }\n`,
355355
),
356356
).toMatchInlineSnapshot(`
357-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"inject\\"]});
357+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"inject\\"]});
358358
const a = { inject: __vite_ssr_import_0__.inject }
359359
const b = { test: __vite_ssr_import_0__.inject }
360360
function c() { const { test: inject } = { test: true }; console.log(inject) }
@@ -383,7 +383,7 @@ function c({ _ = bar() + foo() }) {}
383383
`,
384384
),
385385
).toMatchInlineSnapshot(`
386-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"namedImportSpecifiers\\":[\\"foo\\",\\"bar\\"]});
386+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"foo\\",\\"bar\\"]});
387387
388388
389389
const a = ({ _ = __vite_ssr_import_0__.foo() }) => {}
@@ -405,7 +405,7 @@ const a = () => {
405405
`,
406406
),
407407
).toMatchInlineSnapshot(`
408-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"namedImportSpecifiers\\":[\\"n\\"]});
408+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"n\\"]});
409409
410410
411411
const a = () => {
@@ -428,7 +428,7 @@ const foo = {}
428428
`,
429429
),
430430
).toMatchInlineSnapshot(`
431-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"namedImportSpecifiers\\":[\\"n\\",\\"m\\"]});
431+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"n\\",\\"m\\"]});
432432
433433
434434
const foo = {}
@@ -471,7 +471,7 @@ objRest()
471471
`,
472472
),
473473
).toMatchInlineSnapshot(`
474-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"remove\\",\\"add\\",\\"get\\",\\"set\\",\\"rest\\",\\"objRest\\"]});
474+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"remove\\",\\"add\\",\\"get\\",\\"set\\",\\"rest\\",\\"objRest\\"]});
475475
476476
477477
@@ -521,7 +521,7 @@ const obj = {
521521
`,
522522
),
523523
).toMatchInlineSnapshot(`
524-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\");
524+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"default\\"]});
525525
526526
527527
@@ -553,7 +553,7 @@ class A {
553553
`,
554554
),
555555
).toMatchInlineSnapshot(`
556-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"remove\\",\\"add\\"]});
556+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"remove\\",\\"add\\"]});
557557
558558
559559
@@ -585,7 +585,7 @@ class A {
585585
`,
586586
),
587587
).toMatchInlineSnapshot(`
588-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\");
588+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"default\\"]});
589589
590590
591591
@@ -631,7 +631,7 @@ bbb()
631631
`,
632632
),
633633
).toMatchInlineSnapshot(`
634-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"namedImportSpecifiers\\":[\\"aaa\\",\\"bbb\\",\\"ccc\\",\\"ddd\\"]});
634+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"vue\\", {\\"importedNames\\":[\\"aaa\\",\\"bbb\\",\\"ccc\\",\\"ddd\\"]});
635635
636636
637637
@@ -676,8 +676,8 @@ test('jsx', async () => {
676676
const result = await transformWithEsbuild(code, id)
677677
expect(await ssrTransformSimpleCode(result.code, '/foo.jsx'))
678678
.toMatchInlineSnapshot(`
679-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"react\\");
680-
const __vite_ssr_import_1__ = await __vite_ssr_import__(\\"foo\\", {\\"namedImportSpecifiers\\":[\\"Foo\\",\\"Slot\\"]});
679+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"react\\", {\\"importedNames\\":[\\"default\\"]});
680+
const __vite_ssr_import_1__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"Foo\\",\\"Slot\\"]});
681681
682682
683683
function Bar({ Slot: Slot2 = /* @__PURE__ */ __vite_ssr_import_0__.default.createElement(__vite_ssr_import_1__.Foo, null) }) {
@@ -752,7 +752,7 @@ import foo from "foo"`,
752752
),
753753
).toMatchInlineSnapshot(`
754754
"#!/usr/bin/env node
755-
const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\");
755+
const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\", {\\"importedNames\\":[\\"default\\"]});
756756
console.log(__vite_ssr_import_0__.default);
757757
"
758758
`)
@@ -788,7 +788,7 @@ export class Test {
788788
};`.trim()
789789

790790
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
791-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"namedImportSpecifiers\\":[\\"foo\\",\\"bar\\"]});
791+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"importedNames\\":[\\"foo\\",\\"bar\\"]});
792792
793793
if (false) {
794794
const foo = 'foo'
@@ -830,7 +830,7 @@ function test() {
830830
return [foo, bar]
831831
}`),
832832
).toMatchInlineSnapshot(`
833-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"namedImportSpecifiers\\":[\\"foo\\",\\"bar\\"]});
833+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"importedNames\\":[\\"foo\\",\\"bar\\"]});
834834
835835
836836
function test() {
@@ -857,7 +857,7 @@ function test() {
857857
return bar;
858858
}`),
859859
).toMatchInlineSnapshot(`
860-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"namedImportSpecifiers\\":[\\"foo\\",\\"bar\\",\\"baz\\"]});
860+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foobar\\", {\\"importedNames\\":[\\"foo\\",\\"bar\\",\\"baz\\"]});
861861
862862
863863
function test() {
@@ -889,7 +889,7 @@ for (const test in tests) {
889889
console.log(test)
890890
}`),
891891
).toMatchInlineSnapshot(`
892-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./test.js\\", {\\"namedImportSpecifiers\\":[\\"test\\"]});
892+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./test.js\\", {\\"importedNames\\":[\\"test\\"]});
893893
894894
895895
@@ -921,7 +921,7 @@ const Baz = class extends Foo {}
921921
`,
922922
)
923923
expect(result?.code).toMatchInlineSnapshot(`
924-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./foo\\", {\\"namedImportSpecifiers\\":[\\"Bar\\"]});
924+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./foo\\", {\\"importedNames\\":[\\"default\\",\\"Bar\\"]});
925925
926926
927927
@@ -963,7 +963,7 @@ export * from './b'
963963
console.log(foo + 2)
964964
`),
965965
).toMatchInlineSnapshot(`
966-
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./foo\\", {\\"namedImportSpecifiers\\":[\\"foo\\"]});
966+
"const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"./foo\\", {\\"importedNames\\":[\\"foo\\"]});
967967
const __vite_ssr_import_1__ = await __vite_ssr_import__(\\"./a\\");
968968
__vite_ssr_exportAll__(__vite_ssr_import_1__);
969969
const __vite_ssr_import_2__ = await __vite_ssr_import__(\\"./b\\");

packages/vite/src/node/ssr/ssrModuleLoader.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,16 @@ interface NodeImportResolveOptions
3131

3232
interface SSRImportMetadata {
3333
isDynamicImport?: boolean
34-
namedImportSpecifiers?: string[]
34+
/**
35+
* Imported names before being transformed to `ssrImportKey`
36+
*
37+
* import foo, { bar as baz, qux } from 'hello'
38+
* => ['default', 'bar', 'qux']
39+
*
40+
* import * as namespace from 'world
41+
* => undefined
42+
*/
43+
importedNames?: string[]
3544
}
3645

3746
// eslint-disable-next-line @typescript-eslint/no-empty-function
@@ -367,15 +376,13 @@ function analyzeImportedModDifference(
367376

368377
// For non-ESM, named imports is done via static analysis with cjs-module-lexer in Node.js.
369378
// If the user named imports a specifier that can't be analyzed, error.
370-
if (metadata?.namedImportSpecifiers?.length) {
371-
const missingBindings = metadata.namedImportSpecifiers.filter(
372-
(s) => !(s in mod),
373-
)
379+
if (metadata?.importedNames?.length) {
380+
const missingBindings = metadata.importedNames.filter((s) => !(s in mod))
374381
if (missingBindings.length) {
375382
const lastBinding = missingBindings[missingBindings.length - 1]
376383
// Copied from Node.js
377384
throw new SyntaxError(`\
378-
Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports.
385+
[vite] Named export '${lastBinding}' not found. The requested module '${rawId}' is a CommonJS module, which may not support all module.exports as named exports.
379386
CommonJS modules can always be imported via the default export, for example using:
380387
381388
import pkg from '${rawId}';
@@ -389,12 +396,20 @@ const {${missingBindings.join(', ')}} = pkg;
389396
* Guard invalid named exports only, similar to how Node.js errors for top-level imports.
390397
* But since we transform as dynamic imports, we need to emulate the error manually.
391398
*/
392-
function proxyGuardOnlyEsm(mod: any, rawId: string) {
399+
function proxyGuardOnlyEsm(
400+
mod: any,
401+
rawId: string,
402+
metadata?: SSRImportMetadata,
403+
) {
404+
// If the module doesn't import anything explicitly, e.g. `import 'foo'` or
405+
// `import * as foo from 'foo'`, we can skip the proxy guard.
406+
if (!metadata?.importedNames?.length) return mod
407+
393408
return new Proxy(mod, {
394409
get(mod, prop) {
395410
if (prop !== 'then' && !(prop in mod)) {
396411
throw new SyntaxError(
397-
`The requested module '${rawId}' does not provide an export named '${prop.toString()}'`,
412+
`[vite] The requested module '${rawId}' does not provide an export named '${prop.toString()}'`,
398413
)
399414
}
400415
return mod[prop]

0 commit comments

Comments
 (0)