diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index e027594de9dfe0..7de3900648d768 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -121,6 +121,15 @@ test('import.meta', async () => { ).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\\", { enumerable: true, get(){ return i }})" + `) +}) + test('do not rewrite method definition', async () => { expect( ( @@ -135,11 +144,37 @@ test('do not rewrite method definition', async () => { `) }) -test('dynamic import', async () => { +// #2221 +test('should declare variable for imported super class', async () => { expect( - (await ssrTransform(`export const i = () => import('./foo')`, null)).code + ( + await ssrTransform( + `import { Foo } from './dep';` + `class A extends Foo {}`, + null + ) + ).code ).toMatchInlineSnapshot(` - "const i = () => __vite_ssr_dynamic_import__('./foo') - Object.defineProperty(__vite_ssr_exports__, \\"i\\", { enumerable: true, get(){ return i }})" + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"./dep\\") + const Foo = __vite_ssr_import_0__.Foo; + class A extends Foo {}" + `) + + // exported classes: should prepend the declaration at root level, before the + // first class that uses the binding + expect( + ( + await ssrTransform( + `import { Foo } from './dep';` + + `export default class A extends Foo {}\n` + + `export class B extends Foo {}`, + null + ) + ).code + ).toMatchInlineSnapshot(` + "const __vite_ssr_import_0__ = __vite_ssr_import__(\\"./dep\\") + const Foo = __vite_ssr_import_0__.Foo; + __vite_ssr_exports__.default = class A extends Foo {} + class B extends Foo {} + Object.defineProperty(__vite_ssr_exports__, \\"B\\", { enumerable: true, get(){ return B }})" `) }) diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 43b1f9f6aab52b..b6267802156567 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -38,6 +38,7 @@ export async function ssrTransform( let uid = 0 const deps = new Set() const idToImportMap = new Map() + const declaredConst = new Set() function defineImport(node: Node, source: string) { deps.add(source) @@ -153,6 +154,16 @@ export async function ssrTransform( ) { s.appendLeft(id.end, `: ${binding}`) } + } else if ( + parent.type === 'ClassDeclaration' && + id === parent.superClass + ) { + if (!declaredConst.has(id.name)) { + declaredConst.add(id.name) + // locate the top-most node containing the class declaration + const topNode = parentStack[1] + s.prependRight(topNode.start, `const ${id.name} = ${binding};\n`) + } } else { s.overwrite(id.start, id.end, binding) } @@ -208,12 +219,12 @@ function walk( ;(eswalk as any)(root, { enter(node: Node, parent: Node | null) { - parent && parentStack.push(parent) - if (node.type === 'ImportDeclaration') { return this.skip() } + parent && parentStack.push(parent) + if (node.type === 'MetaProperty' && node.meta.name === 'import') { onImportMeta(node) } else if (node.type === 'ImportExpression') { @@ -302,7 +313,7 @@ function isRefIdentifier(id: Identifier, parent: _Node, parentStack: _Node[]) { return false } } - + // class method name if (parent.type === 'MethodDefinition') { return false