diff --git a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts index b71b6262e1f009..87764c7d13ba47 100644 --- a/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts +++ b/packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts @@ -743,6 +743,19 @@ console.log("it can parse the hashbang")`, `) }) +test('import hoisted after hashbang', async () => { + expect( + await ssrTransformSimpleCode( + `#!/usr/bin/env node +import "foo"`, + ), + ).toMatchInlineSnapshot(` + "#!/usr/bin/env node + const __vite_ssr_import_0__ = await __vite_ssr_import__(\\"foo\\"); + " + `) +}) + // #10289 test('track scope by class, function, condition blocks', async () => { const code = ` diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts index 96ee465e2a0ebe..3c0992f41a82a1 100644 --- a/packages/vite/src/node/ssr/ssrTransform.ts +++ b/packages/vite/src/node/ssr/ssrTransform.ts @@ -33,6 +33,8 @@ export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__` export const ssrExportAllKey = `__vite_ssr_exportAll__` export const ssrImportMetaKey = `__vite_ssr_import_meta__` +const hashbangRE = /^#!.*\n/ + export async function ssrTransform( code: string, inMap: SourceMap | null, @@ -92,13 +94,16 @@ async function ssrTransformScript( const idToImportMap = new Map() const declaredConst = new Set() + // hoist at the start of the file, after the hashbang + const hoistIndex = code.match(hashbangRE)?.[0].length ?? 0 + function defineImport(source: string) { deps.add(source) const importId = `__vite_ssr_import_${uid++}__` // There will be an error if the module is called before it is imported, // so the module import statement is hoisted to the top s.appendLeft( - 0, + hoistIndex, `const ${importId} = await ${ssrImportKey}(${JSON.stringify(source)});\n`, ) return importId @@ -165,7 +170,7 @@ async function ssrTransformScript( // hoist re-exports near the defined import so they are immediately exported for (const spec of node.specifiers) { defineExport( - 0, + hoistIndex, spec.exported.name, `${importId}.${spec.local.name}`, ) @@ -214,9 +219,9 @@ async function ssrTransformScript( const importId = defineImport(node.source.value as string) // hoist re-exports near the defined import so they are immediately exported if (node.exported) { - defineExport(0, node.exported.name, `${importId}`) + defineExport(hoistIndex, node.exported.name, `${importId}`) } else { - s.appendLeft(0, `${ssrExportAllKey}(${importId});\n`) + s.appendLeft(hoistIndex, `${ssrExportAllKey}(${importId});\n`) } } }