Skip to content

Commit 445c4f2

Browse files
authored
fix: fix sourcemap when using object as define value (#15805)
1 parent a78e265 commit 445c4f2

File tree

7 files changed

+124
-1
lines changed

7 files changed

+124
-1
lines changed

packages/vite/src/node/plugins/define.ts

+21
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { transform } from 'esbuild'
2+
import { TraceMap, decodedMap, encodedMap } from '@jridgewell/trace-mapping'
23
import type { ResolvedConfig } from '../config'
34
import type { Plugin } from '../plugin'
45
import { escapeRegex, getHash } from '../utils'
@@ -157,6 +158,26 @@ export async function replaceDefine(
157158
sourcemap: config.command === 'build' ? !!config.build.sourcemap : true,
158159
})
159160

161+
// remove esbuild's <define:...> source entries
162+
// since they would confuse source map remapping/collapsing which expects a single source
163+
if (result.map.includes('<define:')) {
164+
const originalMap = new TraceMap(result.map)
165+
if (originalMap.sources.length >= 2) {
166+
const sourceIndex = originalMap.sources.indexOf(id)
167+
const decoded = decodedMap(originalMap)
168+
decoded.sources = [id]
169+
decoded.mappings = decoded.mappings.map((segments) =>
170+
segments.filter((segment) => {
171+
// modify and filter
172+
const index = segment[1]
173+
segment[1] = 0
174+
return index === sourceIndex
175+
}),
176+
)
177+
result.map = JSON.stringify(encodedMap(new TraceMap(decoded as any)))
178+
}
179+
}
180+
160181
for (const marker in replacementMarkers) {
161182
result.code = result.code.replaceAll(marker, replacementMarkers[marker])
162183
}

playground/js-sourcemap/__tests__/js-sourcemap.spec.ts

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { URL } from 'node:url'
1+
import { URL, fileURLToPath } from 'node:url'
2+
import { promisify } from 'node:util'
3+
import { execFile } from 'node:child_process'
24
import { describe, expect, test } from 'vitest'
35
import { mapFileCommentRegex } from 'convert-source-map'
46
import { commentSourceMap } from '../foo-with-sourcemap-plugin'
@@ -170,4 +172,39 @@ describe.runIf(isBuild)('build tests', () => {
170172
const js = findAssetFile(/after-preload-dynamic-no-dep-[-\w]{8}\.js$/)
171173
expect(js).not.toMatch(/__vite__mapDeps/)
172174
})
175+
176+
test('sourcemap is correct when using object as "define" value', async () => {
177+
const map = findAssetFile(/with-define-object.*\.js\.map/)
178+
expect(formatSourcemapForSnapshot(JSON.parse(map))).toMatchInlineSnapshot(`
179+
{
180+
"mappings": "qBAEA,SAASA,GAAO,CACJC,GACZ,CAEA,SAASA,GAAY,CAEX,QAAA,MAAM,qBAAsBC,CAAkB,CACxD,CAEAF,EAAK",
181+
"sources": [
182+
"../../with-define-object.ts",
183+
],
184+
"sourcesContent": [
185+
"// test complicated stack since broken sourcemap
186+
// might still look correct with a simple case
187+
function main() {
188+
mainInner()
189+
}
190+
191+
function mainInner() {
192+
// @ts-expect-error "define"
193+
console.trace('with-define-object', __testDefineObject)
194+
}
195+
196+
main()
197+
",
198+
],
199+
"version": 3,
200+
}
201+
`)
202+
})
203+
204+
test('correct sourcemap during ssr dev when using object as "define" value', async () => {
205+
const execFileAsync = promisify(execFile)
206+
await execFileAsync('node', ['test-ssr-dev.js'], {
207+
cwd: fileURLToPath(new URL('..', import.meta.url)),
208+
})
209+
})
173210
})

playground/js-sourcemap/index.html

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ <h1>JS Sourcemap</h1>
1010
<script type="module" src="./after-preload-dynamic-no-dep.js"></script>
1111
<script type="module" src="./with-multiline-import.ts"></script>
1212
<script type="module" src="./zoo.js"></script>
13+
<script type="module" src="./with-define-object.ts"></script>
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import assert from 'node:assert'
2+
import { fileURLToPath } from 'node:url'
3+
import { createServer } from 'vite'
4+
5+
async function runTest() {
6+
const server = await createServer({
7+
root: fileURLToPath(new URL('.', import.meta.url)),
8+
configFile: false,
9+
optimizeDeps: {
10+
noDiscovery: true,
11+
},
12+
server: {
13+
middlewareMode: true,
14+
hmr: false,
15+
},
16+
define: {
17+
__testDefineObject: '{ "hello": "test" }',
18+
},
19+
})
20+
const mod = await server.ssrLoadModule('/with-define-object-ssr.ts')
21+
const error = await getError(() => mod.error())
22+
server.ssrFixStacktrace(error)
23+
assert.match(error.stack, /at errorInner (.*with-define-object-ssr.ts:7:9)/)
24+
await server.close()
25+
}
26+
27+
async function getError(f) {
28+
let error
29+
try {
30+
await f()
31+
} catch (e) {
32+
error = e
33+
}
34+
assert.ok(error)
35+
return error
36+
}
37+
38+
runTest()

playground/js-sourcemap/vite.config.js

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export default defineConfig({
2121
if (name.endsWith('after-preload-dynamic-no-dep.js')) {
2222
return 'after-preload-dynamic-no-dep'
2323
}
24+
if (name.includes('with-define-object')) {
25+
return 'with-define-object'
26+
}
2427
},
2528
banner(chunk) {
2629
if (chunk.name.endsWith('after-preload-dynamic-hashbang')) {
@@ -30,4 +33,7 @@ export default defineConfig({
3033
},
3134
},
3235
},
36+
define: {
37+
__testDefineObject: '{ "hello": "test" }',
38+
},
3339
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export function error() {
2+
errorInner()
3+
}
4+
5+
function errorInner() {
6+
// @ts-expect-error "define"
7+
throw new Error('with-define-object: ' + JSON.stringify(__testDefineObject))
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// test complicated stack since broken sourcemap
2+
// might still look correct with a simple case
3+
function main() {
4+
mainInner()
5+
}
6+
7+
function mainInner() {
8+
// @ts-expect-error "define"
9+
console.trace('with-define-object', __testDefineObject)
10+
}
11+
12+
main()

0 commit comments

Comments
 (0)