Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: missing source maps for hoisted imports (fix #16355) #16356

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { assert, expect, test } from 'vitest'
import type { SourceMap } from 'rollup'
import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping'
import { transformWithEsbuild } from '../../plugins/esbuild'
import { ssrTransform } from '../ssrTransform'

Expand Down Expand Up @@ -442,6 +443,36 @@ test('sourcemap source', async () => {
expect(map?.sourcesContent).toStrictEqual(['export const a = 1 /* */'])
})

test('sourcemap is correct for hoisted imports', async () => {
const code = `\n\n\nconsole.log(foo, bar);\nimport { foo } from 'vue';\nimport { bar } from 'vue2';`
const result = (await ssrTransform(code, null, 'input.js', code))!

expect(result.code).toMatchInlineSnapshot(`
"const __vite_ssr_import_0__ = await __vite_ssr_import__("vue", {"importedNames":["foo"]});
const __vite_ssr_import_1__ = await __vite_ssr_import__("vue2", {"importedNames":["bar"]});



console.log(__vite_ssr_import_0__.foo, __vite_ssr_import_1__.bar);

"
`)

const traceMap = new TraceMap(result.map as any)
expect(originalPositionFor(traceMap, { line: 1, column: 0 })).toStrictEqual({
source: 'input.js',
line: 5,
column: 0,
name: null,
})
expect(originalPositionFor(traceMap, { line: 2, column: 0 })).toStrictEqual({
source: 'input.js',
line: 6,
column: 0,
name: null,
})
})

test('sourcemap with multiple sources', async () => {
const code = readFixture('bundle.js')
const map = readFixture('bundle.js.map')
Expand Down
59 changes: 37 additions & 22 deletions packages/vite/src/node/ssr/ssrTransform.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'node:path'
import MagicString from 'magic-string'
import type { SourceMap } from 'rollup'
import type { RollupAstNode, SourceMap } from 'rollup'
import type {
ExportAllDeclaration,
ExportDefaultDeclaration,
Expand Down Expand Up @@ -98,13 +98,21 @@ async function ssrTransformScript(
const declaredConst = new Set<string>()

// hoist at the start of the file, after the hashbang
const hoistIndex = hashbangRE.exec(code)?.[0].length ?? 0
let hoistIndex = hashbangRE.exec(code)?.[0].length ?? 0

function defineImport(
index: number,
source: string,
importNode: (
| ImportDeclaration
| (ExportNamedDeclaration & { source: Literal })
| ExportAllDeclaration
) & {
start: number
end: number
},
metadata?: DefineImportMetadata,
) {
const source = importNode.source.value as string
deps.add(source)
const importId = `__vite_ssr_import_${uid++}__`

Expand All @@ -117,14 +125,23 @@ async function ssrTransformScript(
}
const metadataStr = metadata ? `, ${JSON.stringify(metadata)}` : ''

// 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(
index,
s.update(
importNode.start,
importNode.end,
`const ${importId} = await ${ssrImportKey}(${JSON.stringify(
source,
)}${metadataStr});\n`,
)

if (importNode.start === index) {
// no need to hoist, but update hoistIndex to keep the order
hoistIndex = importNode.end
} else {
// 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.move(importNode.start, importNode.end, index)
}

return importId
}

Expand All @@ -136,12 +153,12 @@ async function ssrTransformScript(
)
}

const imports: (ImportDeclaration & { start: number; end: number })[] = []
const exports: ((
| ExportNamedDeclaration
| ExportDefaultDeclaration
| ExportAllDeclaration
) & { start: number; end: number })[] = []
const imports: RollupAstNode<ImportDeclaration>[] = []
const exports: (
| RollupAstNode<ExportNamedDeclaration>
| RollupAstNode<ExportDefaultDeclaration>
| RollupAstNode<ExportAllDeclaration>
)[] = []

for (const node of ast.body as Node[]) {
if (node.type === 'ImportDeclaration') {
Expand All @@ -160,7 +177,7 @@ async function ssrTransformScript(
// import foo from 'foo' --> foo -> __import_foo__.default
// import { baz } from 'foo' --> baz -> __import_foo__.baz
// import * as ok from 'foo' --> ok -> __import_foo__
const importId = defineImport(hoistIndex, node.source.value as string, {
const importId = defineImport(hoistIndex, node, {
importedNames: node.specifiers
.map((s) => {
if (s.type === 'ImportSpecifier')
Expand All @@ -169,7 +186,6 @@ async function ssrTransformScript(
})
.filter(isDefined),
})
s.remove(node.start, node.end)
for (const spec of node.specifiers) {
if (spec.type === 'ImportSpecifier') {
if (spec.imported.type === 'Identifier') {
Expand Down Expand Up @@ -219,7 +235,7 @@ async function ssrTransformScript(
// export { foo, bar } from './foo'
const importId = defineImport(
node.start,
node.source.value as string,
node as RollupAstNode<ExportNamedDeclaration & { source: Literal }>,
{
importedNames: node.specifiers.map(
(s) => getIdentifierNameOrLiteralValue(s.local) as string,
Expand All @@ -233,13 +249,13 @@ async function ssrTransformScript(

if (spec.local.type === 'Identifier') {
defineExport(
node.start,
node.end,
exportedAs,
`${importId}.${spec.local.name}`,
)
} else {
defineExport(
node.start,
node.end,
exportedAs,
`${importId}[${JSON.stringify(spec.local.value as string)}]`,
)
Expand Down Expand Up @@ -290,15 +306,14 @@ async function ssrTransformScript(

// export * from './foo'
if (node.type === 'ExportAllDeclaration') {
s.remove(node.start, node.end)
const importId = defineImport(node.start, node.source.value as string)
const importId = defineImport(node.start, node)
if (node.exported) {
const exportedAs = getIdentifierNameOrLiteralValue(
node.exported,
) as string
defineExport(node.start, exportedAs, `${importId}`)
defineExport(node.end, exportedAs, `${importId}`)
} else {
s.appendLeft(node.start, `${ssrExportAllKey}(${importId});\n`)
s.appendLeft(node.end, `${ssrExportAllKey}(${importId});\n`)
}
}
}
Expand Down