Skip to content

Commit fd38d07

Browse files
authored
fix(ssr): handle uninitialized export access as undefined (#19959)
1 parent b040d54 commit fd38d07

File tree

5 files changed

+61
-54
lines changed

5 files changed

+61
-54
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ test('json', async () => {
235235
null,
236236
'/test.json',
237237
)
238-
expect(json?.code.length).toMatchInlineSnapshot(`208`)
238+
expect(json?.code.length).toMatchInlineSnapshot(`225`)
239239
})
240240

241241
test('file url', async () => {

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

Lines changed: 39 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ test('export function declaration', async () => {
5555
await ssrTransformSimpleCode(`export function foo() {}`),
5656
).toMatchInlineSnapshot(
5757
`
58-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
58+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return foo } catch {} }});
5959
function foo() {}"
6060
`,
6161
)
@@ -66,7 +66,7 @@ test('export class declaration', async () => {
6666
await ssrTransformSimpleCode(`export class foo {}`),
6767
).toMatchInlineSnapshot(
6868
`
69-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return foo }});
69+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return foo } catch {} }});
7070
class foo {}"
7171
`,
7272
)
@@ -77,8 +77,8 @@ test('export var declaration', async () => {
7777
await ssrTransformSimpleCode(`export const a = 1, b = 2`),
7878
).toMatchInlineSnapshot(
7979
`
80-
"Object.defineProperty(__vite_ssr_exports__, "a", { enumerable: true, configurable: true, get(){ return a }});
81-
Object.defineProperty(__vite_ssr_exports__, "b", { enumerable: true, configurable: true, get(){ return b }});
80+
"Object.defineProperty(__vite_ssr_exports__, "a", { enumerable: true, configurable: true, get(){ try { return a } catch {} }});
81+
Object.defineProperty(__vite_ssr_exports__, "b", { enumerable: true, configurable: true, get(){ try { return b } catch {} }});
8282
const a = 1, b = 2"
8383
`,
8484
)
@@ -89,8 +89,8 @@ test('export named', async () => {
8989
await ssrTransformSimpleCode(`const a = 1, b = 2; export { a, b as c }`),
9090
).toMatchInlineSnapshot(
9191
`
92-
"Object.defineProperty(__vite_ssr_exports__, "a", { enumerable: true, configurable: true, get(){ return a }});
93-
Object.defineProperty(__vite_ssr_exports__, "c", { enumerable: true, configurable: true, get(){ return b }});
92+
"Object.defineProperty(__vite_ssr_exports__, "a", { enumerable: true, configurable: true, get(){ try { return a } catch {} }});
93+
Object.defineProperty(__vite_ssr_exports__, "c", { enumerable: true, configurable: true, get(){ try { return b } catch {} }});
9494
const a = 1, b = 2; "
9595
`,
9696
)
@@ -101,8 +101,8 @@ test('export named from', async () => {
101101
await ssrTransformSimpleCode(`export { ref, computed as c } from 'vue'`),
102102
).toMatchInlineSnapshot(
103103
`
104-
"Object.defineProperty(__vite_ssr_exports__, "ref", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.ref }});
105-
Object.defineProperty(__vite_ssr_exports__, "c", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.computed }});
104+
"Object.defineProperty(__vite_ssr_exports__, "ref", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__.ref } catch {} }});
105+
Object.defineProperty(__vite_ssr_exports__, "c", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__.computed } catch {} }});
106106
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["ref","computed"]});"
107107
`,
108108
)
@@ -115,7 +115,7 @@ test('named exports of imported binding', async () => {
115115
),
116116
).toMatchInlineSnapshot(
117117
`
118-
"Object.defineProperty(__vite_ssr_exports__, "createApp", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__.createApp }});
118+
"Object.defineProperty(__vite_ssr_exports__, "createApp", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__.createApp } catch {} }});
119119
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["createApp"]});"
120120
`,
121121
)
@@ -139,7 +139,7 @@ test('export * as from', async () => {
139139
await ssrTransformSimpleCode(`export * as foo from 'vue'`),
140140
).toMatchInlineSnapshot(
141141
`
142-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__ }});
142+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__ } catch {} }});
143143
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue");"
144144
`,
145145
)
@@ -152,7 +152,7 @@ import * as foo from 'foo'
152152
export * as foo from 'foo'
153153
`),
154154
).toMatchInlineSnapshot(`
155-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }});
155+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_1__ } catch {} }});
156156
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo");
157157
const __vite_ssr_import_1__ = await __vite_ssr_import__("foo");
158158
"
@@ -164,7 +164,7 @@ import { foo } from 'foo'
164164
export { foo } from 'foo'
165165
`),
166166
).toMatchInlineSnapshot(`
167-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__.foo }});
167+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_1__.foo } catch {} }});
168168
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
169169
const __vite_ssr_import_1__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
170170
"
@@ -176,7 +176,7 @@ import { foo } from 'foo'
176176
export { foo as foo } from 'foo'
177177
`),
178178
).toMatchInlineSnapshot(`
179-
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__.foo }});
179+
"Object.defineProperty(__vite_ssr_exports__, "foo", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_1__.foo } catch {} }});
180180
const __vite_ssr_import_0__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
181181
const __vite_ssr_import_1__ = await __vite_ssr_import__("foo", {"importedNames":["foo"]});
182182
"
@@ -188,7 +188,7 @@ test('export * as from arbitrary module namespace identifier', async () => {
188188
await ssrTransformSimpleCode(`export * as "arbitrary string" from 'vue'`),
189189
).toMatchInlineSnapshot(
190190
`
191-
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__ }});
191+
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__ } catch {} }});
192192
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue");"
193193
`,
194194
)
@@ -201,7 +201,7 @@ test('export as arbitrary module namespace identifier', async () => {
201201
),
202202
).toMatchInlineSnapshot(
203203
`
204-
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ return something }});
204+
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ try { return something } catch {} }});
205205
const something = "Something";"
206206
`,
207207
)
@@ -214,7 +214,7 @@ test('export as from arbitrary module namespace identifier', async () => {
214214
),
215215
).toMatchInlineSnapshot(
216216
`
217-
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_0__["arbitrary string2"] }});
217+
"Object.defineProperty(__vite_ssr_exports__, "arbitrary string", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_0__["arbitrary string2"] } catch {} }});
218218
const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["arbitrary string2"]});"
219219
`,
220220
)
@@ -223,9 +223,9 @@ test('export as from arbitrary module namespace identifier', async () => {
223223
test('export default', async () => {
224224
expect(await ssrTransformSimpleCode(`export default {}`))
225225
.toMatchInlineSnapshot(`
226-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
227-
const __vite_ssr_export_default__ = {}"
228-
`)
226+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
227+
const __vite_ssr_export_default__ = {}"
228+
`)
229229
})
230230

231231
test('export then import minified', async () => {
@@ -347,7 +347,7 @@ test('dynamic import', async () => {
347347
)
348348
expect(result?.code).toMatchInlineSnapshot(
349349
`
350-
"Object.defineProperty(__vite_ssr_exports__, "i", { enumerable: true, configurable: true, get(){ return i }});
350+
"Object.defineProperty(__vite_ssr_exports__, "i", { enumerable: true, configurable: true, get(){ try { return i } catch {} }});
351351
const i = () => __vite_ssr_dynamic_import__('./foo')"
352352
`,
353353
)
@@ -507,8 +507,8 @@ test('should declare variable for imported super class', async () => {
507507
`export class B extends Foo {}`,
508508
),
509509
).toMatchInlineSnapshot(`
510-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return A }});
511-
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }});
510+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return A } catch {} }});
511+
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ try { return B } catch {} }});
512512
const __vite_ssr_import_0__ = await __vite_ssr_import__("./dependency", {"importedNames":["Foo"]});const Foo = __vite_ssr_import_0__.Foo;
513513
class A extends Foo {};
514514
class B extends Foo {}"
@@ -520,14 +520,14 @@ test('should handle default export variants', async () => {
520520
// default anonymous functions
521521
expect(await ssrTransformSimpleCode(`export default function() {}\n`))
522522
.toMatchInlineSnapshot(`
523-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
523+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
524524
const __vite_ssr_export_default__ = function() {}
525525
"
526526
`)
527527
// default anonymous class
528528
expect(await ssrTransformSimpleCode(`export default class {}\n`))
529529
.toMatchInlineSnapshot(`
530-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
530+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
531531
const __vite_ssr_export_default__ = class {}
532532
"
533533
`)
@@ -538,7 +538,7 @@ test('should handle default export variants', async () => {
538538
`foo.prototype = Object.prototype;`,
539539
),
540540
).toMatchInlineSnapshot(`
541-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return foo }});
541+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return foo } catch {} }});
542542
function foo() {};
543543
foo.prototype = Object.prototype;"
544544
`)
@@ -548,8 +548,8 @@ test('should handle default export variants', async () => {
548548
`export default class A {}\n` + `export class B extends A {}`,
549549
),
550550
).toMatchInlineSnapshot(`
551-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return A }});
552-
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }});
551+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return A } catch {} }});
552+
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ try { return B } catch {} }});
553553
class A {};
554554
class B extends A {}"
555555
`)
@@ -988,8 +988,8 @@ export function fn1() {
988988
`,
989989
),
990990
).toMatchInlineSnapshot(`
991-
"Object.defineProperty(__vite_ssr_exports__, "fn1", { enumerable: true, configurable: true, get(){ return fn1 }});
992-
Object.defineProperty(__vite_ssr_exports__, "fn2", { enumerable: true, configurable: true, get(){ return fn2 }});
991+
"Object.defineProperty(__vite_ssr_exports__, "fn1", { enumerable: true, configurable: true, get(){ try { return fn1 } catch {} }});
992+
Object.defineProperty(__vite_ssr_exports__, "fn2", { enumerable: true, configurable: true, get(){ try { return fn2 } catch {} }});
993993
994994
function fn1() {
995995
};function fn2() {
@@ -1011,17 +1011,17 @@ export default (function getRandom() {
10111011
`.trim()
10121012

10131013
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
1014-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
1014+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
10151015
const __vite_ssr_export_default__ = (function getRandom() {
10161016
return Math.random();
10171017
});"
10181018
`)
10191019

10201020
expect(await ssrTransformSimpleCode(`export default (class A {});`))
10211021
.toMatchInlineSnapshot(`
1022-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
1023-
const __vite_ssr_export_default__ = (class A {});"
1024-
`)
1022+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
1023+
const __vite_ssr_export_default__ = (class A {});"
1024+
`)
10251025
})
10261026

10271027
// #8002
@@ -1095,7 +1095,7 @@ export class Test {
10951095
};`.trim()
10961096

10971097
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
1098-
"Object.defineProperty(__vite_ssr_exports__, "Test", { enumerable: true, configurable: true, get(){ return Test }});
1098+
"Object.defineProperty(__vite_ssr_exports__, "Test", { enumerable: true, configurable: true, get(){ try { return Test } catch {} }});
10991099
const __vite_ssr_import_0__ = await __vite_ssr_import__("foobar", {"importedNames":["foo","bar"]});
11001100
if (false) {
11011101
const foo = 'foo';
@@ -1285,8 +1285,8 @@ export * as bar from './bar'
12851285
console.log(bar)
12861286
`),
12871287
).toMatchInlineSnapshot(`
1288-
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
1289-
Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }});
1288+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_export_default__ } catch {} }});
1289+
Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_1__ } catch {} }});
12901290
12911291
const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]});const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar");;
12921292
const __vite_ssr_export_default__ = (0,__vite_ssr_import_0__.foo)();
@@ -1542,9 +1542,9 @@ import("e")
15421542
export * as A from "a";
15431543
`)
15441544
expect(result?.code).toMatchInlineSnapshot(`
1545-
"Object.defineProperty(__vite_ssr_exports__, "b", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__.b }});
1546-
Object.defineProperty(__vite_ssr_exports__, "d", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_3__ }});
1547-
Object.defineProperty(__vite_ssr_exports__, "A", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_4__ }});
1545+
"Object.defineProperty(__vite_ssr_exports__, "b", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_1__.b } catch {} }});
1546+
Object.defineProperty(__vite_ssr_exports__, "d", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_3__ } catch {} }});
1547+
Object.defineProperty(__vite_ssr_exports__, "A", { enumerable: true, configurable: true, get(){ try { return __vite_ssr_import_4__ } catch {} }});
15481548
const __vite_ssr_import_0__ = await __vite_ssr_import__("a", {"importedNames":["default"]});
15491549
const __vite_ssr_import_1__ = await __vite_ssr_import__("b", {"importedNames":["b"]});
15501550
const __vite_ssr_import_2__ = await __vite_ssr_import__("c");__vite_ssr_exportAll__(__vite_ssr_import_2__);

packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,15 +290,20 @@ describe('module runner initialization', async () => {
290290
await expect(() =>
291291
runner.import('/fixtures/cyclic2/test5/index.js'),
292292
).rejects.toMatchInlineSnapshot(
293-
`[ReferenceError: Cannot access '__vite_ssr_import_1__' before initialization]`,
293+
`[TypeError: Cannot read properties of undefined (reading 'ok')]`,
294294
)
295295
})
296296

297297
it(`cyclic invalid 2`, async ({ runner }) => {
298-
await expect(() =>
299-
runner.import('/fixtures/cyclic2/test6/index.js'),
300-
).rejects.toMatchInlineSnapshot(
301-
`[ReferenceError: Cannot access 'dep1' before initialization]`,
298+
// It should be an error but currently `undefined` fallback.
299+
expect(
300+
await runner.import('/fixtures/cyclic2/test6/index.js'),
301+
).toMatchInlineSnapshot(
302+
`
303+
{
304+
"dep1": "dep1: dep2: undefined",
305+
}
306+
`,
302307
)
303308
})
304309

@@ -374,10 +379,15 @@ describe('module runner initialization', async () => {
374379

375380
it(`export default getter is hoisted`, async ({ runner }) => {
376381
// Node error is `ReferenceError: Cannot access 'dep' before initialization`
377-
await expect(() =>
378-
runner.import('/fixtures/cyclic2/test9/index.js'),
379-
).rejects.toMatchInlineSnapshot(
380-
`[ReferenceError: Cannot access '__vite_ssr_export_default__' before initialization]`,
382+
// It should be an error but currently `undefined` fallback.
383+
expect(
384+
await runner.import('/fixtures/cyclic2/test9/index.js'),
385+
).toMatchInlineSnapshot(
386+
`
387+
{
388+
"default": undefined,
389+
}
390+
`,
381391
)
382392
})
383393
})

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,11 @@ async function ssrTransformScript(
179179
}
180180

181181
function defineExport(name: string, local = name) {
182+
// wrap with try/catch to fallback to `undefined` for backward compat.
182183
s.appendLeft(
183184
fileStartIndex,
184185
`Object.defineProperty(${ssrModuleExportsKey}, ${JSON.stringify(name)}, ` +
185-
`{ enumerable: true, configurable: true, get(){ return ${local} }});\n`,
186+
`{ enumerable: true, configurable: true, get(){ try { return ${local} } catch {} }});\n`,
186187
)
187188
}
188189

vitest.config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ export default defineConfig({
1919
},
2020
testTimeout: 20000,
2121
isolate: false,
22-
// importing non-entry files (e.g. config.ts, build.ts, server/index.ts) is broken due to cyclic import
23-
// as it can be seen from tsx (try pnpm exec tsx packages/vite/src/node/server/index.ts).
24-
// we can use `setupFiles` to ensure the modules are evaluated via main node entry.
25-
setupFiles: ['./packages/vite/src/node/index.ts'],
2622
},
2723
esbuild: {
2824
target: 'node18',

0 commit comments

Comments
 (0)