Skip to content

Commit 1298951

Browse files
authored
fix(optimizer): preserve sourcemaps for transformed optimized deps with follow-up transforms (#22428)
1 parent 28d8e12 commit 1298951

13 files changed

Lines changed: 477 additions & 10 deletions

File tree

packages/vite/src/node/optimizer/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ async function prepareRolldownOptimizerRun(
868868
return await bundle.write({
869869
...rolldownOptions.output,
870870
format: 'esm',
871-
sourcemap: true,
871+
sourcemap: 'hidden',
872872
dir: processingCacheDir,
873873
entryFileNames: '[name].js',
874874
})

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,20 @@ export function optimizedDepsPlugin(): Plugin {
8585
// load hooks to avoid race conditions, once processing is resolved,
8686
// we are sure that the file has been properly save to disk
8787
try {
88-
return await fsp.readFile(file, 'utf-8')
88+
const [code, map] = await Promise.all([
89+
fsp.readFile(file, 'utf-8'),
90+
fsp
91+
.readFile(`${file}.map`, 'utf-8')
92+
.then((map) => JSON.parse(map))
93+
.catch(() => null),
94+
])
95+
if (map) {
96+
return {
97+
code,
98+
map,
99+
}
100+
}
101+
return code
89102
} catch {
90103
if (
91104
browserHash &&

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

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { URL, fileURLToPath } from 'node:url'
22
import { promisify } from 'node:util'
33
import { execFile } from 'node:child_process'
44
import { existsSync } from 'node:fs'
5+
import { TraceMap, originalPositionFor } from '@jridgewell/trace-mapping'
56
import { describe, expect, test } from 'vitest'
67
import { mapFileCommentRegex } from 'convert-source-map'
78
import { commentSourceMap } from '../foo-with-sourcemap-plugin'
@@ -16,12 +17,55 @@ import {
1617
serverLogs,
1718
} from '~utils'
1819

19-
function createMapFileReader(moduleUrl: string) {
20-
return async (filename: string): Promise<string> => {
21-
const base = new URL(moduleUrl, page.url())
22-
const res = await page.request.get(new URL(filename, base).href)
23-
return res.text()
24-
}
20+
const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g
21+
function escapeRegex(str: string): string {
22+
return str.replace(escapeRegexRE, '\\$&')
23+
}
24+
25+
async function getDepJs(entry: string, depIdFragment: string) {
26+
const res = await page.request.get(new URL(entry, page.url()).href)
27+
const js = await res.text()
28+
const depUrlMatch = js.match(
29+
new RegExp(`from\\s+"([^"]*${depIdFragment}[^"]*)"`),
30+
)
31+
expect(depUrlMatch).toBeTruthy()
32+
33+
const depUrl = depUrlMatch![1]
34+
expect(depUrl).toContain('/deps/')
35+
36+
const depRes = await page.request.get(new URL(depUrl, page.url()).href)
37+
return depRes.text()
38+
}
39+
40+
function expectConsoleLogArgumentMapsToOriginalX(
41+
depJs: string,
42+
generatedName: string,
43+
) {
44+
const map = extractSourcemap(depJs)
45+
const depLines = depJs.split('\n')
46+
const consoleLogCallRE = new RegExp(
47+
`console[\\w$]*\\.\\s*log[\\w$]*\\(${escapeRegex(generatedName)}\\)`,
48+
)
49+
const generatedLine =
50+
depLines.findIndex((line) => consoleLogCallRE.test(line)) + 1
51+
expect(generatedLine).toBeGreaterThan(0)
52+
53+
const generatedColumn = depLines[generatedLine - 1].indexOf(generatedName)
54+
expect(generatedColumn).toBeGreaterThanOrEqual(0)
55+
56+
const position = originalPositionFor(new TraceMap(map), {
57+
line: generatedLine,
58+
column: generatedColumn,
59+
})
60+
61+
expect(depJs).toMatch(
62+
/^\/\/# sourceMappingURL=data:application\/json;base64,/m,
63+
)
64+
expect(position).toMatchObject({
65+
line: 6,
66+
column: 16,
67+
name: 'x',
68+
})
2569
}
2670

2771
if (!isBuild) {
@@ -184,12 +228,39 @@ if (!isBuild) {
184228
expect(depUrl).toContain('.vite/deps')
185229
const depRes = await page.request.get(new URL(depUrl, page.url()).href)
186230
const depJs = await depRes.text()
187-
const map = await extractSourcemap(depJs, createMapFileReader(depUrl))
231+
expect(depJs).toMatch(
232+
/^\/\/# sourceMappingURL=data:application\/json;base64,/m,
233+
)
234+
const map = extractSourcemap(depJs)
188235
expect(map.sourcesContent).toBeDefined()
189236
expect(map.sourcesContent).not.toContainEqual(
190237
expect.stringContaining('defineConfig'),
191238
)
192239
})
240+
241+
test('babel-transformed downleveled optimized dep maps to the correct original name', async () => {
242+
const depJs = await getDepJs(
243+
'./optimized-class-field-import-babel.js',
244+
'test-dep-class-field-sourcemap-babel',
245+
)
246+
247+
expect(depJs).toContain('x = () => 1')
248+
expect(depJs).toContain('constructor(_x)')
249+
expect(depJs).toContain('console.log(_x)')
250+
expectConsoleLogArgumentMapsToOriginalX(depJs, '_x')
251+
})
252+
253+
test('oxc-transformed downleveled optimized dep maps to the correct original name', async () => {
254+
const depJs = await getDepJs(
255+
'./optimized-class-field-import-oxc.js',
256+
'test-dep-class-field-sourcemap-oxc',
257+
)
258+
259+
expect(depJs).toContain('x$$$ = () => 1')
260+
expect(depJs).toContain('constructor$$$(_x$$$)')
261+
expect(depJs).toContain('console$$$.log$$$(_x$$$)')
262+
expectConsoleLogArgumentMapsToOriginalX(depJs, '_x$$$')
263+
})
193264
}
194265

195266
describe.runIf(isBuild)('build tests', () => {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const x = () => 1
2+
3+
class C {
4+
prop = x()
5+
constructor(x) {
6+
console.log(x)
7+
}
8+
}
9+
10+
export { C }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "@vitejs/test-dep-class-field-sourcemap-babel",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"main": "index.js"
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const x = () => 1
2+
3+
class C {
4+
prop = x()
5+
constructor(x) {
6+
console.log(x)
7+
}
8+
}
9+
10+
export { C }
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"name": "@vitejs/test-dep-class-field-sourcemap-oxc",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"main": "index.js"
7+
}

playground/js-sourcemap/index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ <h1>JS Sourcemap</h1>
1313
<script type="module" src="./with-define-object.ts"></script>
1414
<script type="module" src="./malicious-import.js"></script>
1515
<script type="module" src="./optimized-malicious-import.js"></script>
16+
<script type="module" src="./optimized-class-field-import-babel.js"></script>
17+
<script type="module" src="./optimized-class-field-import-oxc.js"></script>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { C } from '@vitejs/test-dep-class-field-sourcemap-babel'
2+
3+
console.log('optimized-class-field-import-babel', C)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { C } from '@vitejs/test-dep-class-field-sourcemap-oxc'
2+
3+
console.log('optimized-class-field-import-oxc', C)

0 commit comments

Comments
 (0)