Debug ID changes the bytes of an optimized chunk but not its file name, so cached files fail the SRI check
维护者通常 1 天内回复
评估
- 难度
- 4/5
- 预计耗时
- 3-5 天
- 新手友好度
- 55/100
- Issue 类型
- 缺陷
- 描述清晰度
- 描述清楚
- 活跃度
- 活跃
- 技术栈
- angular, typescript
- 领域
- build-system
调研方向
Start in execute-post-bundle.ts, where injectDebugIds changes output bytes, and chunk-optimizer.ts, where Rolldown assigns hashed chunk names. Trace how the chunk hash is computed and how debug IDs are injected, then check the build tests for optimized chunks and source maps. Done means a changed chunk byte sequence gets a different filename, including when only its source map changes.
由索引模型根据 Issue 内容生成。
描述
Command
build
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
22.0.x
Description
Since 22.1.0 (#33110), the application builder adds a //# debugId=<uuid> line to each browser JS file when script source maps are enabled. The ID comes from the bytes of the source map.
When the chunk optimizer runs (by default when the build has 3 or more lazy chunks), Rolldown gives the chunks their final file names. The [hash] in these names covers the code of the chunk only. It does not cover the source map. The debugId line is added after that.
A change that keeps the JS code of a chunk the same but changes its source map (for example, a change to a TypeScript type or to a comment) gives this result for a chunk that does not import other chunks:
- The file name stays the same.
- The debugId changes, because the source map changed.
- The file bytes change, because the debugId line changed.
- The SRI hash in
index.htmlchanges to match the new bytes.
In one build, the HTML and the files match. Across two builds, the rule "same hashed file name means same content" is no longer true.
Hosting setups depend on this rule: they cache hashed files as immutable. Any cache can keep the file from the previous build: a CDN such as Cloudflare, the browser cache, or a proxy. That cache then serves the old bytes together with the new index.html. The integrity check fails, the browser blocks the file, and the modules in it do not load.
Before 22.1.0 there was no debugId line, so the bytes of a hashed file changed only when its name changed.
Expected behavior: if the bytes of a hashed output file change, its file name changes too.
Minimal Reproduction
Repository: https://github.com/virgiliojr/angular-debugid-sri-repro
The app is a plain ng new app (Angular CLI 22.2.2) with 3 lazy routes, so the chunk optimizer runs. lazy-a and lazy-b use src/app/shared/label.ts. This module has no imports, so it becomes its own chunk.
- Run
npm ci. - Run
npx ng build --source-map --subresource-integrity. - In
dist/repro/browser/, find the chunk that containslabel:. Write down its file name and its SHA-384. - In
src/app/shared/label.ts, addextra?: number;to theLabelInputinterface. This is a type-only change: the JS output oflabel.tsstays the same. - Run
npx ng build --source-map --subresource-integrityagain. - Find the chunk that contains
label:again.
bash compare-builds.sh in the repository does steps 2 to 6 and prints the result below.
Actual result:
Build 1
file name: chunk-z-ytF2G0.js
sha384 of bytes: sha384-Y7f5K4V0FTCrXc6JQ2tGE+VllRWg501EixeFEPMjNMboDgwfeRc9E6l8oHlqTbX5
debugId line: //# debugId=105cedd6-2603-584f-8e8d-7801d6020b96
sha in index.html: yes
Build 2
file name: chunk-z-ytF2G0.js
sha384 of bytes: sha384-bgCbCJmJexbhKCHeS4kpofe2lKBPWJ1f3JpK9668+/jD2mfiEYADUy5xD1Usr1dZ
debugId line: //# debugId=9c547775-c029-5af8-ba7e-25c70949f1b6
sha in index.html: yes
Diff of the chunk between the two builds:
2c2
< //# debugId=105cedd6-2603-584f-8e8d-7801d6020b96
---
> //# debugId=9c547775-c029-5af8-ba7e-25c70949f1b6
The file name is the same, but the bytes and the SRI hash in index.html are different.
Expected result: if the bytes are different, the file name is different too.
To see the failure in a browser, serve the output of build 2, but serve chunk-z-ytF2G0.js from build 1. This is what a cache does after a deployment.
Exception or Error
Failed to find a valid digest in the 'integrity' attribute for resource 'https://<host>/chunk-z-ytF2G0.js' with computed SHA-384 integrity '<hash>'. The resource has been blocked.
Your Environment
Angular CLI : 22.2.5
Angular : 22.2.3
Node.js : 24.18.0
Package Manager : npm 11.16.0
Operating System : darwin arm64
┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build │ 22.2.5 │ ^22.2.2 │
│ @angular/cli │ 22.2.5 │ ^22.2.2 │
│ @angular/common │ 22.2.3 │ ^22.2.0 │
│ @angular/compiler │ 22.2.3 │ ^22.2.0 │
│ @angular/compiler-cli │ 22.2.3 │ ^22.2.0 │
│ @angular/core │ 22.2.3 │ ^22.2.0 │
│ @angular/forms │ 22.2.3 │ ^22.2.0 │
│ @angular/platform-browser │ 22.2.3 │ ^22.2.0 │
│ @angular/router │ 22.2.3 │ ^22.2.0 │
│ rxjs │ 7.8.2 │ ~7.8.0 │
│ typescript │ 6.0.3 │ ~6.0.2 │
└───────────────────────────┴───────────────────┴───────────────────┘
Anything else relevant?
This does not depend on the browser. Any cache in front of the hashed files can cause it.
The defect needs the chunk optimizer. With NG_BUILD_OPTIMIZE_CHUNKS=false, the same type change gives a new esbuild file name for the chunk (for example chunk-P54LQIKA.js to chunk-5HS55H7R.js), so the name and the bytes change together.
A chunk that imports other chunks usually gets a new name anyway, because the names of the chunks it imports change. A chunk without such imports keeps its name.
Related: #33108, #33110, #33572. The current main still has this behavior: injectDebugIds (called from execute-post-bundle.ts when sourcemapOptions.scripts is true) changes the file contents but does not change the file name. The Rolldown pass in chunk-optimizer.ts uses chunkFileNames: '<name>-[hash].js'.
- 主要语言
- TypeScript
- 星标
- 27k
- 派生
- 11.8k
- 平均合并
- 1 天 5 小时
- 30 天内合并 PR
- 280
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
angular/angular-cli 的其他 Issue
-
Can't use an array of hostnames in --allowedHosts cli parameter in @angular/build:dev-server可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭area: @angular/build gemini-triaged
难度 2/5 1-3 小时 新手友好度 74/100
angular/angular-cli#33955 ·
维护者通常 1 天内回复
-
Allow tests to pass without any test files (e.g. via Vitest `passWithNoTests`)可能重新可做 关联的 PR 已关闭且未合并。 未关闭area: @angular/cli gemini-triaged
难度 2/5 1-3 小时 新手友好度 72/100
angular/angular-cli#33055 · 1 条评论 · 3 个 reaction ·
维护者通常 1 天内回复
-
@angular/build: source-map comment scanner corrupts valid JS containing a regex literal可能已有人在做 @alan-agius4 于 1 天前认领。 未关闭area: @angular/build
难度 3/5 1-2 天 新手友好度 76/100
angular/angular-cli#34590 ·
维护者通常 1 天内回复
-
Angular SSR prerender params allow build-time SSRF, credential leaks, and open redirects可能已有人在做 @SkyZeroZx 于 2 天前认领。 未关闭area: @angular/ssr
难度 4/5 3-5 天 新手友好度 35/100
angular/angular-cli#34569 ·
维护者通常 1 天内回复
-
build: ERR_REQUIRE_CYCLE_MODULE when loading oxc-parser in StackBlitz / WebContainer可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭area: @angular/build
难度 3/5 1-2 天 新手友好度 55/100
angular/angular-cli#34524 · 5 条评论 · 2 个 reaction ·
维护者通常 1 天内回复
查看 angular/angular-cli 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 78/100
components-web-app/docs#307 ·
-
难度 2/5 1-3 小时 新手友好度 78/100
localstack/localstack-mcp-server#112 ·
维护者通常 1 天内回复
-
area:frontend kind:bug priority:p1 ready
难度 2/5 1-3 小时 新手友好度 85/100
维护者通常 1 天内回复
-
bug ready-for-agent
难度 2/5 1-3 小时 新手友好度 73/100
PhyberApex/kuroshiro#1687 ·
维护者通常 1 天内回复
-
Performance (Web Vitals) collector: LCP/FCP from background tabs are not discarded, inflating p75未关闭
难度 2/5 1-3 小时 新手友好度 74/100
umami-software/umami#4720 ·
维护者通常 3 天内回复