fix(build): preserve commented Vite URL imports

This commit is contained in:
4gray committed 2026-08-08 02:41:38 +02:00
1 parent 9c92a616a8
commit bccbe8dcba
7 files changed
+71 -42

No files matched your search

+2 -2
View File
@@ -4,5 +4,5 @@ area: build
---
Electron development serve no longer crashes while Vite transforms a large
lazy chunk. The pinned Vite release now carries the upstream precise-filter
fix, with a regression check guarding dependency updates.
lazy chunk. The pinned Vite release now carries a backtracking-safe transform
filter fix, with a regression check guarding dependency updates.
+5 -4
View File
@@ -18,10 +18,11 @@ This file provides guidance to coding agents working in this repository.
- See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy.
- Keep `nx` and every official `@nx/*` package on the same exact version; run
`pnpm run deps:nx:validate` after dependency updates.
- Vite `7.3.5`, resolved through Angular's build tooling, is patched with the
upstream precise transform filters in `patches/vite@7.3.5.patch`. Keep the
patch until supported Angular tooling resolves a Vite version containing the
fix, and run `pnpm run deps:vite:test` after related dependency updates.
- Vite `7.3.5`, resolved through Angular's build tooling, is patched with
bounded transform prefilters and the upstream precise matchers in
`patches/vite@7.3.5.patch`. Keep the patch until supported Angular tooling
resolves a Vite version containing the fix, and run `pnpm run deps:vite:test`
after related dependency updates.
- A directory holding files consumed by other projects must be an Nx project.
Nx builds its graph from TypeScript imports only, so a relative SCSS `@use`
across project roots creates no edge and the imported file lands in no task
+5 -4
View File
@@ -76,10 +76,11 @@ pnpm nx show projects
- See `docs/architecture/nx-workspace-boundaries.md` for the current Nx tag and alias policy.
- Keep `nx` and every official `@nx/*` package on the same exact version; run
`pnpm run deps:nx:validate` after dependency updates.
- Vite `7.3.5`, resolved through Angular's build tooling, is patched with the
upstream precise transform filters in `patches/vite@7.3.5.patch`. Keep the
patch until supported Angular tooling resolves a Vite version containing the
fix, and run `pnpm run deps:vite:test` after related dependency updates.
- Vite `7.3.5`, resolved through Angular's build tooling, is patched with
bounded transform prefilters and the upstream precise matchers in
`patches/vite@7.3.5.patch`. Keep the patch until supported Angular tooling
resolves a Vite version containing the fix, and run `pnpm run deps:vite:test`
after related dependency updates.
- A directory holding files consumed by other projects must be an Nx project.
Nx builds its graph from TypeScript imports only, so a relative SCSS `@use`
across project roots creates no edge and the imported file lands in no task
+10 -8
View File
@@ -60,20 +60,22 @@ server can then fail a lazy chunk request with `Maximum call stack size
exceeded`, even though static builds succeed because they do not pass emitted
chunks through Vite's request-time plugin container.
`patches/vite@7.3.5.patch` backports Vite's upstream precise-filter fix from
[vitejs/vite#21800](https://github.com/vitejs/vite/pull/21800). Keep the patch
while the supported Angular toolchain resolves Vite `7.3.5`; remove it only
after the resolved Vite contains the upstream fix. Run the regression check
after any related manifest or lockfile update:
`patches/vite@7.3.5.patch` backports Vite's upstream precise-matcher fix from
[vitejs/vite#21800](https://github.com/vitejs/vite/pull/21800). Bounded
prefilters keep the request-time scan linear while allowing comment-bearing
asset and worker expressions to reach the precise matcher after Vite strips
comments. Keep the patch while the supported Angular toolchain resolves Vite
`7.3.5`; remove it only after the resolved Vite contains the upstream fix. Run
the regression check after any related manifest or lockfile update:
```bash
pnpm run deps:vite:test
```
CI runs the same check. It resolves Vite from `@angular/build`, verifies the
patched filter wiring and version pin, stress-tests the false-positive chunk
shape, and preserves valid asset and worker `new URL(..., import.meta.url)`
matches.
patched prefilter/matcher wiring and version pin, stress-tests the false-positive
chunk shape, and preserves ordinary and comment-bearing asset and worker
`new URL(..., import.meta.url)` matches.
## Placement Decision
+10 -7
View File
@@ -1,18 +1,20 @@
diff --git a/dist/node/chunks/config.js b/dist/node/chunks/config.js
index 9a45043275249a25101fd7991ffcfc316478840e..4089dbe5748076482589bd99dd9698ecd43a4f78 100644
index 9a45043275249a25101fd7991ffcfc316478840e..5435b17de9ca65b64a8abacb609b75587226b106 100644
--- a/dist/node/chunks/config.js
+++ b/dist/node/chunks/config.js
@@ -27667,7 +27667,7 @@ async function getWorkerType(raw, clean, i$1) {
@@ -27667,7 +27667,8 @@ async function getWorkerType(raw, clean, i$1) {
if (workerOpts.type && (workerOpts.type === "module" || workerOpts.type === "classic")) return workerOpts.type;
return "classic";
}
-const workerImportMetaUrlRE = /new\s+(?:Worker|SharedWorker)\s*\(\s*new\s+URL.+?import\.meta\.url/s;
+const workerImportMetaUrlFilterRE = /\bnew\s+(?:Worker|SharedWorker)\b/;
+const workerImportMetaUrlRE = /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\))/dg;
function workerImportMetaUrlPlugin(config$2) {
const isBuild = config$2.command === "build";
let workerResolver;
@@ -27688,10 +27688,10 @@ function workerImportMetaUrlPlugin(config$2) {
filter: { code: workerImportMetaUrlRE },
@@ -27688,10 +27689,10 @@ function workerImportMetaUrlPlugin(config$2) {
- filter: { code: workerImportMetaUrlRE },
+ filter: { code: workerImportMetaUrlFilterRE },
async handler(code, id) {
let s;
+ const re = new RegExp(workerImportMetaUrlRE);
@@ -24,20 +26,21 @@ index 9a45043275249a25101fd7991ffcfc316478840e..4089dbe5748076482589bd99dd9698ec
const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = match.indices;
const rawUrl = code.slice(urlStart, urlEnd);
if (rawUrl[0] === "`" && rawUrl.includes("${")) this.error(`\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, expStart);
@@ -27733,6 +27733,7 @@ function workerImportMetaUrlPlugin(config$2) {
@@ -27733,6 +27734,8 @@ function workerImportMetaUrlPlugin(config$2) {
}
//#endregion
+const assetImportMetaUrlFilterRE = /\bnew\s+URL\b/;
+const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg;
//#region src/node/plugins/assetImportMetaUrl.ts
/**
* Convert `new URL('./foo.png', import.meta.url)` to its resolved built URL
@@ -27763,14 +27764,14 @@ function assetImportMetaUrlPlugin(config$2) {
@@ -27763,14 +27765,14 @@ function assetImportMetaUrlPlugin(config$2) {
transform: {
filter: {
id: { exclude: [exactRegex(preloadHelperId), exactRegex(CLIENT_ENTRY)] },
- code: /new\s+URL.+import\.meta\.url/s
+ code: assetImportMetaUrlRE
+ code: assetImportMetaUrlFilterRE
},
async handler(code, id) {
let s;
+6 -6
View File
@@ -36,7 +36,7 @@ patchedDependencies:
hash: f4bbde1778360c4c462b255bec47a337c0465d59cdcab14ecb601161f3467002
path: patches/nx-electron@22.0.0.patch
vite@7.3.5:
hash: 8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4
hash: b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d
path: patches/vite@7.3.5.patch
importers:
@@ -11905,7 +11905,7 @@ snapshots:
'@babel/helper-annotate-as-pure': 7.27.3
'@babel/helper-split-export-declaration': 7.24.7
'@inquirer/confirm': 5.1.21(@types/node@20.19.9)
'@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.5(patch_hash=8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0))
'@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.5(patch_hash=b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0))
beasties: 0.4.1
browserslist: 4.28.2
esbuild: 0.28.1
@@ -11926,7 +11926,7 @@ snapshots:
tslib: 2.8.1
typescript: 5.9.3
undici: 7.24.4
vite: 7.3.5(patch_hash=8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)
vite: 7.3.5(patch_hash=b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)
watchpack: 2.5.1
optionalDependencies:
'@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)
@@ -16580,9 +16580,9 @@ snapshots:
global: 4.4.0
is-function: 1.0.2
'@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.5(patch_hash=8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0))':
'@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.5(patch_hash=b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0))':
dependencies:
vite: 7.3.5(patch_hash=8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)
vite: 7.3.5(patch_hash=b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0)
'@webassemblyjs/ast@1.14.1':
dependencies:
@@ -24380,7 +24380,7 @@ snapshots:
tsx: 4.23.1
yaml: 2.9.0
vite@7.3.5(patch_hash=8629229ab80a0812eb9a7c02d1302452448f102ebadc6c844ae598e0fb6d63a4)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0):
vite@7.3.5(patch_hash=b3a8f88ab420b6504a61ffb0f8e402f112c7fda7f3f3e802d21272da7969882d)(@types/node@20.19.9)(jiti@1.21.7)(less@4.5.1)(sass-embedded@1.97.1)(sass@1.97.3)(terser@5.44.1)(tsx@4.23.1)(yaml@2.9.0):
dependencies:
esbuild: 0.27.7
fdir: 6.5.0(picomatch@4.0.4)
@@ -30,16 +30,24 @@ test('pins the Vite version carrying the local transform-filter backport', () =>
assert.equal(vitePackage.version, '7.3.5');
});
test('uses precise Vite transform filters instead of backtracking prefilters', () => {
test('uses bounded Vite prefilters with precise handler matchers', () => {
assert.ok(
/filter: \{\s*id: \{[^}]+\},\s*code: assetImportMetaUrlRE\s*\}/s.test(
/filter: \{\s*id: \{[^}]+\},\s*code: assetImportMetaUrlFilterRE\s*\}/s.test(
viteConfig
),
'asset import-meta transform must use the precise shared filter'
'asset import-meta transform must use the bounded prefilter'
);
assert.ok(
/filter: \{ code: workerImportMetaUrlRE \}/.test(viteConfig),
'worker import-meta transform must use the precise shared filter'
/filter: \{ code: workerImportMetaUrlFilterRE \}/.test(viteConfig),
'worker import-meta transform must use the bounded prefilter'
);
assert.ok(
/const re = new RegExp\(assetImportMetaUrlRE\)/.test(viteConfig),
'asset handler must clone the precise matcher'
);
assert.ok(
/const re = new RegExp\(workerImportMetaUrlRE\)/.test(viteConfig),
'worker handler must clone the precise matcher'
);
assert.ok(
!/code: \/new\\s\+URL\.\+import\\\.meta\\\.url\/s/.test(viteConfig),
@@ -48,8 +56,8 @@ test('uses precise Vite transform filters instead of backtracking prefilters', (
});
test('rejects a large false-positive chunk without regex backtracking', () => {
const assetFilter = extractRegExp('assetImportMetaUrlRE');
const workerFilter = extractRegExp('workerImportMetaUrlRE');
const assetFilter = extractRegExp('assetImportMetaUrlFilterRE');
const workerFilter = extractRegExp('workerImportMetaUrlFilterRE');
const largeCode =
`new URLSearchParams();\n`.repeat(200) + `var a = 1;\n`.repeat(200_000);
const start = performance.now();
@@ -63,16 +71,30 @@ test('rejects a large false-positive chunk without regex backtracking', () => {
});
test('keeps valid asset and worker import-meta URL patterns eligible', () => {
const assetFilter = extractRegExp('assetImportMetaUrlRE');
const workerFilter = extractRegExp('workerImportMetaUrlRE');
const assetFilter = extractRegExp('assetImportMetaUrlFilterRE');
const workerFilter = extractRegExp('workerImportMetaUrlFilterRE');
const assetMatcher = extractRegExp('assetImportMetaUrlRE');
const workerMatcher = extractRegExp('workerImportMetaUrlRE');
const assetExpression = `new URL('./asset.png', import.meta.url)`;
const workerExpression = `new Worker(new URL('./worker.js', import.meta.url))`;
assert.equal(assetFilter.test(assetExpression), true);
assert.equal(workerFilter.test(workerExpression), true);
assert.equal(assetMatcher.test(assetExpression), true);
assert.equal(workerMatcher.test(workerExpression), true);
});
test('keeps comment-bearing import-meta URL patterns eligible', () => {
const assetFilter = extractRegExp('assetImportMetaUrlFilterRE');
const workerFilter = extractRegExp('workerImportMetaUrlFilterRE');
assert.equal(
assetFilter.test(`new URL('./asset.png', import.meta.url)`),
assetFilter.test(`new URL(/* keep */ './asset.png', import.meta.url)`),
true
);
assert.equal(
workerFilter.test(
`new Worker(new URL('./worker.js', import.meta.url))`
`new Worker(/* keep */ new URL('./worker.js', import.meta.url))`
),
true
);