Skip to content

Commit

Permalink
fix: missing source maps for hoisted imports (fix vitejs#16355)
Browse files Browse the repository at this point in the history
  • Loading branch information
smcenlly committed Apr 5, 2024
1 parent e2658ad commit a9e9716
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 17 deletions.
58 changes: 58 additions & 0 deletions packages/vite/src/node/ssr/__tests__/ssrTransform.spec.ts
Expand Up @@ -341,6 +341,64 @@ test('sourcemap source', async () => {
expect(map?.sourcesContent).toStrictEqual(['export const a = 1 /* */'])
})

test('sourcemap is correct for hoisted imports', async () => {
// Mapping from Base64 character to integer
const base64Chars =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
const base64ToInt = Object.fromEntries(
base64Chars.split('').map((c, i) => [c, i]),
)

// Decode a single Base64 VLQ segment
function decodeVLQ(segment) {
const result: number[] = []
let cur = 0
let shift = 0
let continuation, sign, value

for (const char of segment) {
const integer = base64ToInt[char]

continuation = integer & 32
value = integer & 31
cur += value << shift
shift += 5

if (!continuation) {
sign = cur & 1
cur >>= 1
if (sign) {
cur = -cur
}
result.push(cur)
cur = 0
shift = 0
}
}

const [, , originalLine] = result
return originalLine
}

// Split the mappings string and decode each segment
function decodeMappings(mappings) {
const lines = mappings.split(';')
return lines.map((line) => line.split(',').map(decodeVLQ))
}

const map = (
await ssrTransform(
`\n\n\nimport { foo } from 'vue';`,
null,
'input.js',
'export const a = 1 /* */',
)
)?.map

const decodedMappings = decodeMappings(map?.mappings || '')
expect(decodedMappings[0]).toEqual([3])
})

test('overwrite bindings', async () => {
expect(
await ssrTransformSimpleCode(
Expand Down
57 changes: 40 additions & 17 deletions packages/vite/src/node/ssr/ssrTransform.ts
Expand Up @@ -92,10 +92,12 @@ async function ssrTransformScript(
const declaredConst = new Set<string>()

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

function defineImport(
index: number,
start: number,
end: number,
source: string,
metadata?: DefineImportMetadata,
) {
Expand All @@ -113,12 +115,22 @@ async function ssrTransformScript(

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

if (start !== index) {
s.move(start, end, index)
}

if (index === hoistIndex) {
hoistIndex = end
}

return importId
}

Expand All @@ -136,15 +148,20 @@ async function ssrTransformScript(
// import { baz } from 'foo' --> baz -> __import_foo__.baz
// import * as ok from 'foo' --> ok -> __import_foo__
if (node.type === 'ImportDeclaration') {
const importId = defineImport(hoistIndex, node.source.value as string, {
importedNames: node.specifiers
.map((s) => {
if (s.type === 'ImportSpecifier') return s.imported.name
else if (s.type === 'ImportDefaultSpecifier') return 'default'
})
.filter(isDefined),
})
s.remove(node.start, node.end)
const importId = defineImport(
hoistIndex,
node.start,
node.end,
node.source.value as string,
{
importedNames: node.specifiers
.map((s) => {
if (s.type === 'ImportSpecifier') return s.imported.name
else if (s.type === 'ImportDefaultSpecifier') return 'default'
})
.filter(isDefined),
},
)
for (const spec of node.specifiers) {
if (spec.type === 'ImportSpecifier') {
idToImportMap.set(
Expand Down Expand Up @@ -188,14 +205,16 @@ async function ssrTransformScript(
// export { foo, bar } from './foo'
const importId = defineImport(
node.start,
node.start,
node.end,
node.source.value as string,
{
importedNames: node.specifiers.map((s) => s.local.name),
},
)
for (const spec of node.specifiers) {
defineExport(
node.start,
node.end,
spec.exported.name,
`${importId}.${spec.local.name}`,
)
Expand Down Expand Up @@ -240,12 +259,16 @@ 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.start,
node.end,
node.source.value as string,
)
if (node.exported) {
defineExport(node.start, node.exported.name, `${importId}`)
defineExport(node.end, node.exported.name, `${importId}`)
} else {
s.appendLeft(node.start, `${ssrExportAllKey}(${importId});\n`)
s.appendLeft(node.end, `${ssrExportAllKey}(${importId});\n`)
}
}
}
Expand Down

0 comments on commit a9e9716

Please sign in to comment.