Skip to content

Commit 80a91ff

Browse files
authored
fix(ssr): fix live binding of default export declaration and hoist exports getter (#19842)
1 parent 60fed67 commit 80a91ff

File tree

16 files changed

+170
-27
lines changed

16 files changed

+170
-27
lines changed

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

+1-1
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(`61`)
238+
expect(json?.code.length).toMatchInlineSnapshot(`208`)
239239
})
240240

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

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

+29-21
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,11 @@ test('export as from arbitrary module namespace identifier', async () => {
221221
})
222222

223223
test('export default', async () => {
224-
expect(
225-
await ssrTransformSimpleCode(`export default {}`),
226-
).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = {}"`)
224+
expect(await ssrTransformSimpleCode(`export default {}`))
225+
.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+
`)
227229
})
228230

229231
test('export then import minified', async () => {
@@ -505,11 +507,11 @@ test('should declare variable for imported super class', async () => {
505507
`export class B extends Foo {}`,
506508
),
507509
).toMatchInlineSnapshot(`
508-
"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(){ return A }});
511+
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }});
509512
const __vite_ssr_import_0__ = await __vite_ssr_import__("./dependency", {"importedNames":["Foo"]});const Foo = __vite_ssr_import_0__.Foo;
510513
class A extends Foo {};
511-
class B extends Foo {}
512-
Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: A });"
514+
class B extends Foo {}"
513515
`)
514516
})
515517

@@ -518,13 +520,15 @@ test('should handle default export variants', async () => {
518520
// default anonymous functions
519521
expect(await ssrTransformSimpleCode(`export default function() {}\n`))
520522
.toMatchInlineSnapshot(`
521-
"__vite_ssr_exports__.default = function() {}
523+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
524+
const __vite_ssr_export_default__ = function() {}
522525
"
523526
`)
524527
// default anonymous class
525528
expect(await ssrTransformSimpleCode(`export default class {}\n`))
526529
.toMatchInlineSnapshot(`
527-
"__vite_ssr_exports__.default = class {}
530+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
531+
const __vite_ssr_export_default__ = class {}
528532
"
529533
`)
530534
// default named functions
@@ -534,20 +538,20 @@ test('should handle default export variants', async () => {
534538
`foo.prototype = Object.prototype;`,
535539
),
536540
).toMatchInlineSnapshot(`
537-
"function foo() {};
538-
foo.prototype = Object.prototype;
539-
Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: foo });"
541+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return foo }});
542+
function foo() {};
543+
foo.prototype = Object.prototype;"
540544
`)
541545
// default named classes
542546
expect(
543547
await ssrTransformSimpleCode(
544548
`export default class A {}\n` + `export class B extends A {}`,
545549
),
546550
).toMatchInlineSnapshot(`
547-
"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(){ return A }});
552+
Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }});
548553
class A {};
549-
class B extends A {}
550-
Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: A });"
554+
class B extends A {}"
551555
`)
552556
})
553557

@@ -1007,14 +1011,17 @@ export default (function getRandom() {
10071011
`.trim()
10081012

10091013
expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(`
1010-
"__vite_ssr_exports__.default = (function getRandom() {
1014+
"Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }});
1015+
const __vite_ssr_export_default__ = (function getRandom() {
10111016
return Math.random();
10121017
});"
10131018
`)
10141019

1015-
expect(
1016-
await ssrTransformSimpleCode(`export default (class A {});`),
1017-
).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = (class A {});"`)
1020+
expect(await ssrTransformSimpleCode(`export default (class A {});`))
1021+
.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+
`)
10181025
})
10191026

10201027
// #8002
@@ -1278,10 +1285,11 @@ export * as bar from './bar'
12781285
console.log(bar)
12791286
`),
12801287
).toMatchInlineSnapshot(`
1281-
"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(){ return __vite_ssr_export_default__ }});
1289+
Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }});
12821290
12831291
const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]});
1284-
__vite_ssr_exports__.default = (0,__vite_ssr_import_0__.foo)();
1292+
const __vite_ssr_export_default__ = (0,__vite_ssr_import_0__.foo)();
12851293
const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar");;
12861294
console.log(bar)
12871295
"
@@ -1503,7 +1511,7 @@ test('combine mappings', async () => {
15031511
expect(result?.map).toMatchInlineSnapshot(`
15041512
SourceMap {
15051513
"file": undefined,
1506-
"mappings": "AAAA,8BAAc,CAAC,CAAC,IAAI,CAAC;",
1514+
"mappings": ";AAAA,mCAAc,CAAC,CAAC,IAAI,CAAC;",
15071515
"names": [],
15081516
"sources": [
15091517
"virtual:test-mappings:null",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import dep from "./index.js"
2+
export default dep
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import dep from "./dep.js";
2+
export default dep
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"type": "module"
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export default function f() {
2+
return 0;
3+
}
4+
5+
f = () => 1;
6+
7+
f = () => 2;
8+
9+
export function update() {
10+
f = () => 3;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import f, { update } from "./dep.js";
2+
3+
const x = f();
4+
update();
5+
const y = f();
6+
export default [x, y];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function f() {
2+
return 0;
3+
}
4+
5+
f = () => 1;
6+
7+
export default f;
8+
9+
f = () => 2;
10+
11+
export function update() {
12+
f = () => 3;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import f, { update } from "./dep.js";
2+
3+
const x = f();
4+
update();
5+
const y = f();
6+
export default [x, y];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
function f() {
2+
return 0;
3+
}
4+
5+
f = () => 1
6+
7+
export { f as default }
8+
9+
f = () => 2
10+
11+
export function update() {
12+
f = () => 3;
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import f, { update } from "./dep.js";
2+
3+
const x = f();
4+
update();
5+
const y = f();
6+
export default [x, y];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export default class C {
2+
static f = () => 0;
3+
}
4+
5+
C = class {
6+
static f = () => 1;
7+
}
8+
9+
C = class {
10+
static f = () => 2;
11+
}
12+
13+
export function update() {
14+
C = class {
15+
static f = () => 3;
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import C, { update } from "./dep.js";
2+
3+
const x = C.f();
4+
update();
5+
const y = C.f();
6+
export default [x, y];

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

+49
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,55 @@ describe('module runner initialization', async () => {
301301
`[ReferenceError: Cannot access 'dep1' before initialization]`,
302302
)
303303
})
304+
305+
it(`live binding (export default function f)`, async ({ runner }) => {
306+
const mod = await runner.import('/fixtures/live-binding/test1/index.js')
307+
expect(mod.default).toMatchInlineSnapshot(`
308+
[
309+
2,
310+
3,
311+
]
312+
`)
313+
})
314+
315+
it(`live binding (export default f)`, async ({ runner }) => {
316+
const mod = await runner.import('/fixtures/live-binding/test2/index.js')
317+
expect(mod.default).toMatchInlineSnapshot(`
318+
[
319+
1,
320+
1,
321+
]
322+
`)
323+
})
324+
325+
it(`live binding (export { f as default })`, async ({ runner }) => {
326+
const mod = await runner.import('/fixtures/live-binding/test3/index.js')
327+
expect(mod.default).toMatchInlineSnapshot(`
328+
[
329+
2,
330+
3,
331+
]
332+
`)
333+
})
334+
335+
it(`live binding (export default class C)`, async ({ runner }) => {
336+
const mod = await runner.import('/fixtures/live-binding/test4/index.js')
337+
expect(mod.default).toMatchInlineSnapshot(`
338+
[
339+
2,
340+
3,
341+
]
342+
`)
343+
})
344+
345+
it(`export default getter is hoisted`, async ({ runner }) => {
346+
// Node error is `ReferenceError: Cannot access 'dep' before initialization`
347+
await expect(() =>
348+
runner.import('/fixtures/cyclic2/test9/index.js'),
349+
).rejects.toMatchInlineSnapshot(
350+
`[ReferenceError: Cannot access '__vite_ssr_export_default__' before initialization]`,
351+
)
352+
})
304353
})
305354

306355
describe('optimize-deps', async () => {

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -318,17 +318,16 @@ async function ssrTransformScript(
318318
// export default class A {}
319319
const { name } = node.declaration.id
320320
s.remove(node.start, node.start + 15 /* 'export default '.length */)
321-
s.append(
322-
`\nObject.defineProperty(${ssrModuleExportsKey}, "default", ` +
323-
`{ enumerable: true, configurable: true, value: ${name} });`,
324-
)
321+
defineExport('default', name)
325322
} else {
326323
// anonymous default exports
324+
const name = `__vite_ssr_export_default__`
327325
s.update(
328326
node.start,
329327
node.start + 14 /* 'export default'.length */,
330-
`${ssrModuleExportsKey}.default =`,
328+
`const ${name} =`,
331329
)
330+
defineExport('default', name)
332331
}
333332
}
334333

pnpm-lock.yaml

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

0 commit comments

Comments
 (0)