Skip to content

Commit bc71fe9

Browse files
authored
fix: hoist pointer imports (#3132)
1 parent 31aa0e3 commit bc71fe9

File tree

2 files changed

+21
-9
lines changed

2 files changed

+21
-9
lines changed

packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.spec.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,20 @@ describe('transformPointerImports()', () => {
99
it('basics', () => {
1010
expect(t('bla')).toMatchInlineSnapshot(`null`)
1111
expect(t("import { something } from './bla.js'")).toMatchInlineSnapshot(
12-
`"const something = '​import:./bla.js:something';"`,
12+
`"const something = '​import:./bla.js:something';
13+
"`,
1314
)
14-
expect(t("import def from './bla.js'")).toMatchInlineSnapshot(`"const def = '​import:./bla.js:default';"`)
15+
expect(t("import def from './bla.js'")).toMatchInlineSnapshot(`"const def = '​import:./bla.js:default';
16+
"`)
1517
})
1618
it('removes unused imports', () => {
1719
expect(t("import './style.css'")).toMatchInlineSnapshot(`""`)
1820
expect(t("import './script.js'")).toMatchInlineSnapshot(`""`)
1921
})
2022
it('import as', () => {
21-
expect(t("import { bla as blu } from './bla.js'")).toMatchInlineSnapshot(`"const blu = '​import:./bla.js:bla';"`)
22-
expect(t("import * as blo from './bla.js'")).toMatchInlineSnapshot(`"const blo = '​import:./bla.js:*';"`)
23+
expect(t("import { bla as blu } from './bla.js'")).toMatchInlineSnapshot(`"const blu = '​import:./bla.js:bla';
24+
"`)
25+
expect(t("import * as blo from './bla.js'")).toMatchInlineSnapshot(`"const blo = '​import:./bla.js:*';
26+
"`)
2327
})
2428
})

packages/vike/src/node/vite/shared/resolveVikeConfigInternal/pointerImports.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ function transformPointerImports(
4242
skipWarnings?: true,
4343
): string | null {
4444
const spliceOperations: SpliceOperation[] = []
45+
// Collect all const declarations to prepend at the top, so that they are
46+
// available before any code runs (import declarations are hoisted but const
47+
// is not, so we must place them at the top to avoid TDZ errors).
48+
const constDeclarations: string[] = []
4549

4650
// Performance trick
4751
if (!code.includes('import')) return null
@@ -93,7 +97,7 @@ function transformPointerImports(
9397
}
9498
}
9599

96-
let replacement = ''
100+
let constDeclaration = ''
97101
node.specifiers.forEach((specifier) => {
98102
assert(
99103
specifier.type === 'ImportSpecifier' ||
@@ -111,18 +115,22 @@ function transformPointerImports(
111115
return importLocalName
112116
})()
113117
const importString = serializePointerImportData({ importPath, exportName, importStringWasGenerated: true })
114-
replacement += `const ${importLocalName} = '${importString}';`
118+
constDeclaration += `const ${importLocalName} = '${importString}';`
115119
})
116120

121+
if (constDeclaration) constDeclarations.push(constDeclaration)
122+
// Replace the import with blank lines to preserve source map line numbers.
117123
spliceOperations.push({
118124
start,
119125
end,
120-
replacement,
126+
replacement: '\n'.repeat(importStatementCode.split('\n').length - 1),
121127
})
122128
})
123129

124-
const codeMod = spliceMany(code, spliceOperations)
125-
return codeMod
130+
if (constDeclarations.length === 0 && spliceOperations.length === 0) return null
131+
const codeWithImportsRemoved = spliceMany(code, spliceOperations)
132+
if (constDeclarations.length === 0) return codeWithImportsRemoved
133+
return constDeclarations.join('') + '\n' + codeWithImportsRemoved
126134
}
127135
function getImports(code: string): ImportDeclaration[] {
128136
const result = parseSync(code, {

0 commit comments

Comments
 (0)