diff --git a/packages/playground/tsconfig.json b/packages/playground/tsconfig.json index 14bc3bf1ce2e18..5f1c7aa8a50c38 100644 --- a/packages/playground/tsconfig.json +++ b/packages/playground/tsconfig.json @@ -2,6 +2,7 @@ "include": ["."], "exclude": ["**/dist/**"], "compilerOptions": { + "target": "esnext", "outDir": "dist", "allowJs": true, "esModuleInterop": true, diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts new file mode 100644 index 00000000000000..e948f0b7fbb5e5 --- /dev/null +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -0,0 +1,131 @@ +import { traverseHtml } from '../../plugins/html' +import { ssrTransform } from '../ssrTransform' + +test('default import', async () => { + expect( + (await ssrTransform(`import foo from 'vue';console.log(foo.bar)`, null)) + .code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + console.log(__vite_ssr_import_0__.default.bar)" + `) +}) + +test('named import', async () => { + expect( + ( + await ssrTransform( + `import { ref } from 'vue';function foo() { return ref(0) }`, + null + ) + ).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + function foo() { return __vite_ssr_import_0__.ref(0) }" + `) +}) + +test('namespace import', async () => { + expect( + ( + await ssrTransform( + `import * as vue from 'vue';function foo() { return vue.ref(0) }`, + null + ) + ).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + function foo() { return __vite_ssr_import_0__.ref(0) }" + `) +}) + +test('export function decl', async () => { + expect((await ssrTransform(`export function foo() {}`, null)).code) + .toMatchInlineSnapshot(` + "function foo() {} + Object.defineProperty(__vite_ssr_exports__, \\"foo\\", { get(){ return foo }})" + `) +}) + +test('export class decl', async () => { + expect((await ssrTransform(`export class foo {}`, null)).code) + .toMatchInlineSnapshot(` + "class foo {} + Object.defineProperty(__vite_ssr_exports__, \\"foo\\", { get(){ return foo }})" + `) +}) + +test('export var decl', async () => { + expect((await ssrTransform(`export const a = 1, b = 2`, null)).code) + .toMatchInlineSnapshot(` + "const a = 1, b = 2 + Object.defineProperty(__vite_ssr_exports__, \\"a\\", { get(){ return a }}) + Object.defineProperty(__vite_ssr_exports__, \\"b\\", { get(){ return b }})" + `) +}) + +test('export named', async () => { + expect( + (await ssrTransform(`const a = 1, b = 2; export { a, b as c }`, null)).code + ).toMatchInlineSnapshot(` + "const a = 1, b = 2; + Object.defineProperty(__vite_ssr_exports__, \\"a\\", { get(){ return a }}) + Object.defineProperty(__vite_ssr_exports__, \\"c\\", { get(){ return b }})" + `) +}) + +test('export named from', async () => { + expect( + (await ssrTransform(`export { ref, computed as c } from 'vue'`, null)).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + + Object.defineProperty(__vite_ssr_exports__, \\"ref\\", { get(){ return __vite_ssr_import_0__.ref }}) + Object.defineProperty(__vite_ssr_exports__, \\"c\\", { get(){ return __vite_ssr_import_0__.computed }})" + `) +}) + +test('named exports of imported binding', async () => { + expect( + ( + await ssrTransform( + `import {createApp} from 'vue';export {createApp}`, + null + ) + ).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + + Object.defineProperty(__vite_ssr_exports__, \\"createApp\\", { get(){ return __vite_ssr_import_0__.createApp }})" + `) +}) + +test('export * from', async () => { + expect((await ssrTransform(`export * from 'vue'`, null)).code) + .toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"vue\\") + + __vite_ssr_exportAll__(__vite_ssr_import_0__)" + `) +}) + +test('export default', async () => { + expect( + (await ssrTransform(`export default {}`, null)).code + ).toMatchInlineSnapshot(`"__vite_ssr_exports__.default = {}"`) +}) + +test('import.meta', async () => { + expect( + (await ssrTransform(`console.log(import.meta.url)`, null)).code + ).toMatchInlineSnapshot(`"console.log(__vite_ssr_import_meta__.url)"`) +}) + +test('dynamic import', async () => { + expect( + (await ssrTransform(`export const i = () => import('./foo')`, null)).code + ).toMatchInlineSnapshot(` + "const i = () => __vite_ssr_dynamic_import__('./foo') + Object.defineProperty(__vite_ssr_exports__, \\"i\\", { get(){ return i }})" + `) +}) diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 6ee39987dbe905..460c9202e1b44b 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -56,7 +56,7 @@ export async function ssrTransform( ) } - // 1. check all import/export statements + // 1. check all import statements and record id -> importName map for (const node of ast.body as Node[]) { // import foo from 'foo' --> foo -> __import_foo__.default // import { baz } from 'foo' --> baz -> __import_foo__.baz @@ -78,7 +78,10 @@ export async function ssrTransform( } s.remove(node.start, node.end) } + } + // 2. check all export statements and define exports + for (const node of ast.body as Node[]) { // named exports if (node.type === 'ExportNamedDeclaration') { if (node.declaration) { @@ -104,10 +107,13 @@ export async function ssrTransform( for (const spec of node.specifiers) { defineExport(spec.exported.name, `${importId}.${spec.local.name}`) } + s.remove(node.start, node.end) } else { // export { foo, bar } for (const spec of node.specifiers) { - defineExport(spec.exported.name, spec.local.name) + const local = spec.local.name + const binding = idToImportMap.get(local) + defineExport(spec.exported.name, binding || local) } s.remove(node.start, node.end) } @@ -125,6 +131,7 @@ export async function ssrTransform( // export * from './foo' if (node.type === 'ExportAllDeclaration') { const importId = defineImport(node, node.source.value as string) + s.remove(node.start, node.end) s.append(`\n${ssrExportAllKey}(${importId})`) } } @@ -319,6 +326,10 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { return false } + if (parent.type === 'ExportSpecifier') { + return false + } + // is a special keyword but parsed as identifier if (id.name === 'arguments') { return false