diff --git a/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.spec.ts b/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.spec.ts index 924cff4f996..2c2455575c7 100644 --- a/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.spec.ts +++ b/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.spec.ts @@ -9,16 +9,20 @@ describe('transformPointerImports()', () => { it('basics', () => { expect(t('bla')).toMatchInlineSnapshot(`null`) expect(t("import { something } from './bla.js'")).toMatchInlineSnapshot( - `"const something = '​import:./bla.js:something';"`, + `"const something = '​import:./bla.js:something'; +"`, ) - expect(t("import def from './bla.js'")).toMatchInlineSnapshot(`"const def = '​import:./bla.js:default';"`) + expect(t("import def from './bla.js'")).toMatchInlineSnapshot(`"const def = '​import:./bla.js:default'; +"`) }) it('removes unused imports', () => { expect(t("import './style.css'")).toMatchInlineSnapshot(`""`) expect(t("import './script.js'")).toMatchInlineSnapshot(`""`) }) it('import as', () => { - expect(t("import { bla as blu } from './bla.js'")).toMatchInlineSnapshot(`"const blu = '​import:./bla.js:bla';"`) - expect(t("import * as blo from './bla.js'")).toMatchInlineSnapshot(`"const blo = '​import:./bla.js:*';"`) + expect(t("import { bla as blu } from './bla.js'")).toMatchInlineSnapshot(`"const blu = '​import:./bla.js:bla'; +"`) + expect(t("import * as blo from './bla.js'")).toMatchInlineSnapshot(`"const blo = '​import:./bla.js:*'; +"`) }) }) diff --git a/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.ts b/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.ts index 26ac1597fc6..d40f07a883d 100644 --- a/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.ts +++ b/packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.ts @@ -42,6 +42,10 @@ function transformPointerImports( skipWarnings?: true, ): string | null { const spliceOperations: SpliceOperation[] = [] + // Collect all const declarations to prepend at the top, so that they are + // available before any code runs (import declarations are hoisted but const + // is not, so we must place them at the top to avoid TDZ errors). + const constDeclarations: string[] = [] // Performance trick if (!code.includes('import')) return null @@ -93,7 +97,7 @@ function transformPointerImports( } } - let replacement = '' + let constDeclaration = '' node.specifiers.forEach((specifier) => { assert( specifier.type === 'ImportSpecifier' || @@ -111,18 +115,22 @@ function transformPointerImports( return importLocalName })() const importString = serializePointerImportData({ importPath, exportName, importStringWasGenerated: true }) - replacement += `const ${importLocalName} = '${importString}';` + constDeclaration += `const ${importLocalName} = '${importString}';` }) + if (constDeclaration) constDeclarations.push(constDeclaration) + // Replace the import with blank lines to preserve source map line numbers. spliceOperations.push({ start, end, - replacement, + replacement: '\n'.repeat(importStatementCode.split('\n').length - 1), }) }) - const codeMod = spliceMany(code, spliceOperations) - return codeMod + if (constDeclarations.length === 0 && spliceOperations.length === 0) return null + const codeWithImportsRemoved = spliceMany(code, spliceOperations) + if (constDeclarations.length === 0) return codeWithImportsRemoved + return constDeclarations.join('') + '\n' + codeWithImportsRemoved } function getImports(code: string): ImportDeclaration[] { const result = parseSync(code, {