Skip to content

Commit dbe41bd

Browse files
o-m12aclaude
andauthored
fix: use precise regexes for transform filter to avoid backtracking (#21800)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6d97142 commit dbe41bd

File tree

4 files changed

+49
-13
lines changed

4 files changed

+49
-13
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, test } from 'vitest'
2+
import { assetImportMetaUrlRE } from '../plugins/assetImportMetaUrl'
3+
import { workerImportMetaUrlRE } from '../plugins/workerImportMetaUrl'
4+
5+
describe('filter regexes do not cause catastrophic backtracking', () => {
6+
const largeCode =
7+
`new URL('https://example.com');\n`.repeat(200) +
8+
`var a = 1;\n`.repeat(200_000)
9+
10+
test('assetImportMetaUrlRE completes without backtracking on large files', () => {
11+
assetImportMetaUrlRE.lastIndex = 0
12+
expect(assetImportMetaUrlRE.test(largeCode)).toBe(false)
13+
})
14+
15+
test('workerImportMetaUrlRE completes without backtracking on large files', () => {
16+
workerImportMetaUrlRE.lastIndex = 0
17+
expect(workerImportMetaUrlRE.test(largeCode)).toBe(false)
18+
})
19+
20+
test('assetImportMetaUrlRE still matches valid patterns', () => {
21+
assetImportMetaUrlRE.lastIndex = 0
22+
expect(
23+
assetImportMetaUrlRE.test(`new URL('./asset.png', import.meta.url)`),
24+
).toBe(true)
25+
})
26+
27+
test('workerImportMetaUrlRE still matches valid patterns', () => {
28+
workerImportMetaUrlRE.lastIndex = 0
29+
expect(
30+
workerImportMetaUrlRE.test(
31+
`new Worker(new URL('./worker.js', import.meta.url))`,
32+
),
33+
).toBe(true)
34+
})
35+
})

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import type { Environment } from '../environment'
2222
import { createBackCompatIdResolver } from '../idResolver'
2323
import { isWindows } from '../../shared/utils'
2424
import { hasViteIgnoreRE } from '../plugins/importAnalysis'
25+
import { assetImportMetaUrlRE } from '../plugins/assetImportMetaUrl'
2526

2627
const externalWithConversionNamespace =
2728
'vite:dep-pre-bundle:external-conversion'
@@ -313,16 +314,15 @@ export function rolldownDepPlugin(
313314
},
314315
transform: {
315316
filter: {
316-
code: /new\s+URL.+import\.meta\.url/s,
317+
code: assetImportMetaUrlRE,
317318
},
318319
async handler(code, id) {
319320
let s: MagicString | undefined
320-
const assetImportMetaUrlRE =
321-
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg
321+
const re = new RegExp(assetImportMetaUrlRE)
322322
const cleanString = stripLiteral(code)
323323

324324
let match: RegExpExecArray | null
325-
while ((match = assetImportMetaUrlRE.exec(cleanString))) {
325+
while ((match = re.exec(cleanString))) {
326326
const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices!
327327
if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue
328328

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import { hasViteIgnoreRE } from './importAnalysis'
3131
* import.meta.glob('./dir/**.png', { eager: true, import: 'default' })[`./dir/${name}.png`]
3232
* ```
3333
*/
34+
export const assetImportMetaUrlRE: RegExp =
35+
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg
36+
3437
export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
3538
const { publicDir } = config
3639
let assetResolver: ResolveIdFn
@@ -56,16 +59,15 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
5659
id: {
5760
exclude: [exactRegex(preloadHelperId), exactRegex(CLIENT_ENTRY)],
5861
},
59-
code: /new\s+URL.+import\.meta\.url/s,
62+
code: assetImportMetaUrlRE,
6063
},
6164
async handler(code, id) {
6265
let s: MagicString | undefined
63-
const assetImportMetaUrlRE =
64-
/\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg
66+
const re = new RegExp(assetImportMetaUrlRE)
6567
const cleanString = stripLiteral(code)
6668

6769
let match: RegExpExecArray | null
68-
while ((match = assetImportMetaUrlRE.exec(cleanString))) {
70+
while ((match = re.exec(cleanString))) {
6971
const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices!
7072
if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue
7173

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ async function getWorkerType(
181181
return 'classic'
182182
}
183183

184-
const workerImportMetaUrlRE =
185-
/new\s+(?:Worker|SharedWorker)\s*\(\s*new\s+URL.+?import\.meta\.url/s
184+
export const workerImportMetaUrlRE: RegExp =
185+
/\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\))/dg
186186

187187
export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
188188
const isBundled = config.isBundled
@@ -209,11 +209,10 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin {
209209
async handler(code, id) {
210210
let s: MagicString | undefined
211211
const cleanString = stripLiteral(code)
212-
const workerImportMetaUrlRE =
213-
/\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\))/dg
212+
const re = new RegExp(workerImportMetaUrlRE)
214213

215214
let match: RegExpExecArray | null
216-
while ((match = workerImportMetaUrlRE.exec(cleanString))) {
215+
while ((match = re.exec(cleanString))) {
217216
const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] =
218217
match.indices!
219218

0 commit comments

Comments
 (0)