diff --git a/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts index ce762fe41596fb..d215fbc436747a 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrLoadModule.spec.ts @@ -235,7 +235,7 @@ test('json', async () => { null, '/test.json', ) - expect(json?.code.length).toMatchInlineSnapshot(`61`) + expect(json?.code.length).toMatchInlineSnapshot(`208`) }) test('file url', async () => { diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index 0a4d0e3634aeb8..19dade4dceb130 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -221,9 +221,11 @@ test('export as from arbitrary module namespace identifier', async () => { }) test('export default', async () => { - expect( - await ssrTransformSimpleCode(`export default {}`), - ).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = {}"`) + expect(await ssrTransformSimpleCode(`export default {}`)) + .toMatchInlineSnapshot(` + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + const __vite_ssr_export_default__ = {}" + `) }) test('export then import minified', async () => { @@ -505,11 +507,11 @@ test('should declare variable for imported super class', async () => { `export class B extends Foo {}`, ), ).toMatchInlineSnapshot(` - "Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }}); + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return A }}); + Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }}); const __vite_ssr_import_0__ = await __vite_ssr_import__("./dependency", {"importedNames":["Foo"]});const Foo = __vite_ssr_import_0__.Foo; class A extends Foo {}; - class B extends Foo {} - Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: A });" + class B extends Foo {}" `) }) @@ -518,13 +520,15 @@ test('should handle default export variants', async () => { // default anonymous functions expect(await ssrTransformSimpleCode(`export default function() {}\n`)) .toMatchInlineSnapshot(` - "__vite_ssr_exports__.default = function() {} + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + const __vite_ssr_export_default__ = function() {} " `) // default anonymous class expect(await ssrTransformSimpleCode(`export default class {}\n`)) .toMatchInlineSnapshot(` - "__vite_ssr_exports__.default = class {} + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + const __vite_ssr_export_default__ = class {} " `) // default named functions @@ -534,9 +538,9 @@ test('should handle default export variants', async () => { `foo.prototype = Object.prototype;`, ), ).toMatchInlineSnapshot(` - "function foo() {}; - foo.prototype = Object.prototype; - Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: foo });" + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return foo }}); + function foo() {}; + foo.prototype = Object.prototype;" `) // default named classes expect( @@ -544,10 +548,10 @@ test('should handle default export variants', async () => { `export default class A {}\n` + `export class B extends A {}`, ), ).toMatchInlineSnapshot(` - "Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }}); + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return A }}); + Object.defineProperty(__vite_ssr_exports__, "B", { enumerable: true, configurable: true, get(){ return B }}); class A {}; - class B extends A {} - Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, value: A });" + class B extends A {}" `) }) @@ -1007,14 +1011,17 @@ export default (function getRandom() { `.trim() expect(await ssrTransformSimpleCode(code)).toMatchInlineSnapshot(` - "__vite_ssr_exports__.default = (function getRandom() { + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + const __vite_ssr_export_default__ = (function getRandom() { return Math.random(); });" `) - expect( - await ssrTransformSimpleCode(`export default (class A {});`), - ).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = (class A {});"`) + expect(await ssrTransformSimpleCode(`export default (class A {});`)) + .toMatchInlineSnapshot(` + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + const __vite_ssr_export_default__ = (class A {});" + `) }) // #8002 @@ -1278,10 +1285,11 @@ export * as bar from './bar' console.log(bar) `), ).toMatchInlineSnapshot(` - "Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }}); + "Object.defineProperty(__vite_ssr_exports__, "default", { enumerable: true, configurable: true, get(){ return __vite_ssr_export_default__ }}); + Object.defineProperty(__vite_ssr_exports__, "bar", { enumerable: true, configurable: true, get(){ return __vite_ssr_import_1__ }}); const __vite_ssr_import_0__ = await __vite_ssr_import__("./foo", {"importedNames":["foo"]}); - __vite_ssr_exports__.default = (0,__vite_ssr_import_0__.foo)(); + const __vite_ssr_export_default__ = (0,__vite_ssr_import_0__.foo)(); const __vite_ssr_import_1__ = await __vite_ssr_import__("./bar");; console.log(bar) " @@ -1503,7 +1511,7 @@ test('combine mappings', async () => { expect(result?.map).toMatchInlineSnapshot(` SourceMap { "file": undefined, - "mappings": "AAAA,8BAAc,CAAC,CAAC,IAAI,CAAC;", + "mappings": ";AAAA,mCAAc,CAAC,CAAC,IAAI,CAAC;", "names": [], "sources": [ "virtual:test-mappings:null", diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/dep.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/dep.js new file mode 100644 index 00000000000000..d5982ab8f99973 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/dep.js @@ -0,0 +1,2 @@ +import dep from "./index.js" +export default dep diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/index.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/index.js new file mode 100644 index 00000000000000..43cb4ad5b80a96 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/cyclic2/test9/index.js @@ -0,0 +1,2 @@ +import dep from "./dep.js"; +export default dep diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/package.json b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/package.json new file mode 100644 index 00000000000000..3dbc1ca591c055 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/dep.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/dep.js new file mode 100644 index 00000000000000..3e39b08e7ba159 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/dep.js @@ -0,0 +1,11 @@ +export default function f() { + return 0; +} + +f = () => 1; + +f = () => 2; + +export function update() { + f = () => 3; +} diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/index.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/index.js new file mode 100644 index 00000000000000..c45964af4f3158 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test1/index.js @@ -0,0 +1,6 @@ +import f, { update } from "./dep.js"; + +const x = f(); +update(); +const y = f(); +export default [x, y]; diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/dep.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/dep.js new file mode 100644 index 00000000000000..8e35680d4839fd --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/dep.js @@ -0,0 +1,13 @@ +function f() { + return 0; +} + +f = () => 1; + +export default f; + +f = () => 2; + +export function update() { + f = () => 3; +} diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/index.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/index.js new file mode 100644 index 00000000000000..c45964af4f3158 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test2/index.js @@ -0,0 +1,6 @@ +import f, { update } from "./dep.js"; + +const x = f(); +update(); +const y = f(); +export default [x, y]; diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/dep.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/dep.js new file mode 100644 index 00000000000000..128b476a3476e5 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/dep.js @@ -0,0 +1,13 @@ +function f() { + return 0; +} + +f = () => 1 + +export { f as default } + +f = () => 2 + +export function update() { + f = () => 3; +} diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/index.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/index.js new file mode 100644 index 00000000000000..c45964af4f3158 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test3/index.js @@ -0,0 +1,6 @@ +import f, { update } from "./dep.js"; + +const x = f(); +update(); +const y = f(); +export default [x, y]; diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/dep.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/dep.js new file mode 100644 index 00000000000000..ac42d55cd7b925 --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/dep.js @@ -0,0 +1,17 @@ +export default class C { + static f = () => 0; +} + +C = class { + static f = () => 1; +} + +C = class { + static f = () => 2; +} + +export function update() { + C = class { + static f = () => 3; + } +} diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/index.js b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/index.js new file mode 100644 index 00000000000000..4db35e71d5275a --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding/test4/index.js @@ -0,0 +1,6 @@ +import C, { update } from "./dep.js"; + +const x = C.f(); +update(); +const y = C.f(); +export default [x, y]; diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts index c07407084ce324..6b0def476551f3 100644 --- a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts +++ b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts @@ -301,6 +301,55 @@ describe('module runner initialization', async () => { `[ReferenceError: Cannot access 'dep1' before initialization]`, ) }) + + it(`live binding (export default function f)`, async ({ runner }) => { + const mod = await runner.import('/fixtures/live-binding/test1/index.js') + expect(mod.default).toMatchInlineSnapshot(` + [ + 2, + 3, + ] + `) + }) + + it(`live binding (export default f)`, async ({ runner }) => { + const mod = await runner.import('/fixtures/live-binding/test2/index.js') + expect(mod.default).toMatchInlineSnapshot(` + [ + 1, + 1, + ] + `) + }) + + it(`live binding (export { f as default })`, async ({ runner }) => { + const mod = await runner.import('/fixtures/live-binding/test3/index.js') + expect(mod.default).toMatchInlineSnapshot(` + [ + 2, + 3, + ] + `) + }) + + it(`live binding (export default class C)`, async ({ runner }) => { + const mod = await runner.import('/fixtures/live-binding/test4/index.js') + expect(mod.default).toMatchInlineSnapshot(` + [ + 2, + 3, + ] + `) + }) + + it(`export default getter is hoisted`, async ({ runner }) => { + // Node error is `ReferenceError: Cannot access 'dep' before initialization` + await expect(() => + runner.import('/fixtures/cyclic2/test9/index.js'), + ).rejects.toMatchInlineSnapshot( + `[ReferenceError: Cannot access '__vite_ssr_export_default__' before initialization]`, + ) + }) }) describe('optimize-deps', async () => { diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 925e2e037d6b7c..587d907cd423e1 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -318,17 +318,16 @@ async function ssrTransformScript( // export default class A {} const { name } = node.declaration.id s.remove(node.start, node.start + 15 /* 'export default '.length */) - s.append( - `\nObject.defineProperty(${ssrModuleExportsKey}, "default", ` + - `{ enumerable: true, configurable: true, value: ${name} });`, - ) + defineExport('default', name) } else { // anonymous default exports + const name = `__vite_ssr_export_default__` s.update( node.start, node.start + 14 /* 'export default'.length */, - `${ssrModuleExportsKey}.default =`, + `const ${name} =`, ) + defineExport('default', name) } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e01a88b79e17f4..20ecbb44c8686f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -509,6 +509,8 @@ importers: packages/vite/src/node/ssr/runtime/__tests__/fixtures/esm-external: {} + packages/vite/src/node/ssr/runtime/__tests__/fixtures/live-binding: {} + playground: devDependencies: convert-source-map: