fix(vite-plugin-angular): hash JIT inline style virtual module ids - #2470
Conversation
Angular's JIT resource transformer emits inline component styles as `angular:jit:style:inline;<base64 of the whole stylesheet>`. The plugin rewrote only the prefix, so the full base64 stayed in the module id and the bundler derived the emitted chunk name from it — a large inline stylesheet pushed the filename past the 255-byte limit and the build died with ENAMETOOLONG. Hash the payload into the id at rewrite time and keep the base64 in a lookup table the JIT plugin's `load` reads back, so the id stays a fixed length regardless of stylesheet size. Closes #2459 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
✅ Deploy Preview for analog-docs ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-blog ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for analog-app ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
View your CI Pipeline Execution ↗ for commit 25522e6
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe change adds shared utilities that map encoded JIT inline styles to truncated SHA-256 virtual module IDs. The Vite plugin uses these IDs when rewriting inline stylesheet references. The JIT loader extracts the hash, retrieves the encoded stylesheet, and preprocesses it. Tests cover ID length, successful lookup, and unknown hashes. The agent skills list also gains a Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts`:
- Around line 767-772: Update the JIT inline-style rewrite in
packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts lines 767-772 to
allow percent-encoded characters in the payload match, ensuring
toJitInlineStyleId hashes the complete URI-escaped stylesheet. Add a regression
test in packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts
lines 10-28 covering a payload containing %3D, %2B, or %2F through the rewrite
and loader flow; both sites require changes.
In `@packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts`:
- Around line 10-28: Extend the JIT inline style tests to cover URI-escaped
Base64 payloads rather than only raw encoded styles. Exercise the escaped
payload through the rewrite and loader path in angular-jit-plugin.ts, then
verify it produces the expected bounded ID and resolves back to the original
encoded styles using toJitInlineStyleId and getJitInlineStyles.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 81fc7206-452e-4d35-9c9a-d7fe924bc375
📒 Files selected for processing (4)
packages/vite-plugin-angular/src/lib/angular-jit-plugin.tspackages/vite-plugin-angular/src/lib/angular-vite-plugin.tspackages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.tspackages/vite-plugin-angular/src/lib/utils/jit-inline-styles.ts
| // The emitted id carries the base64 of the entire stylesheet, | ||
| // and the bundler derives chunk names from it — hash it so a | ||
| // large inline style can't produce an over-long filename (#2459). | ||
| data = data.replace( | ||
| /angular:jit:style:inline;/g, | ||
| 'virtual:angular:jit:style:inline;', | ||
| /angular:jit:style:inline;([A-Za-z0-9+/=]*)/g, | ||
| (_match, encodedStyles) => toJitInlineStyleId(encodedStyles), |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Handle URI-escaped Base64 in the JIT inline-style contract.
The loader decodes URI-escaped payloads, but the producer regex excludes %. The rewrite can create an ID with a valid hash followed by an unmatched escaped suffix. The lookup then fails and the JIT inline style does not load.
packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts#L767-L772: include%in the payload match so the hash covers the complete encoded stylesheet.packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts#L10-L28: add a regression test for%3D,%2B, or%2Fthrough the rewrite and loader flow.
Based on the PR objectives, the rewrite must retain the complete stylesheet payload. As per coding guidelines, “Include tests which validate behavior for any new functionality added.”
📍 Affects 2 files
packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts#L767-L772(this comment)packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts#L10-L28
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts` around lines 767
- 772, Update the JIT inline-style rewrite in
packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts lines 767-772 to
allow percent-encoded characters in the payload match, ensuring
toJitInlineStyleId hashes the complete URI-escaped stylesheet. Add a regression
test in packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts
lines 10-28 covering a payload containing %3D, %2B, or %2F through the rewrite
and loader flow; both sites require changes.
Source: Coding guidelines
| it('keeps the id bounded for large stylesheets (#2459)', () => { | ||
| const encodedStyles = Buffer.from( | ||
| `.a { color: red; }`.repeat(1000), | ||
| ).toString('base64'); | ||
|
|
||
| const id = toJitInlineStyleId(encodedStyles); | ||
|
|
||
| expect(id.startsWith(JIT_INLINE_STYLE_PREFIX)).toBe(true); | ||
| expect(id.length).toBeLessThan(255); | ||
| }); | ||
|
|
||
| it('resolves the hashed id back to the encoded styles', () => { | ||
| const encodedStyles = Buffer.from(`.b { color: blue; }`).toString('base64'); | ||
|
|
||
| const id = toJitInlineStyleId(encodedStyles); | ||
| const hash = id.split('style:inline;')[1]; | ||
|
|
||
| expect(getJitInlineStyles(hash)).toBe(encodedStyles); | ||
| }); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Test the URI-escaped JIT style payload.
Lines 10 through 28 pass raw Base64 to the utility. They do not cover the URI-escaped payload handled by angular-jit-plugin.ts. Add regression coverage that sends an escaped payload through the rewrite and loader path.
As per coding guidelines, “Include tests which validate behavior for any new functionality added.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/vite-plugin-angular/src/lib/utils/jit-inline-styles.spec.ts` around
lines 10 - 28, Extend the JIT inline style tests to cover URI-escaped Base64
payloads rather than only raw encoded styles. Exercise the escaped payload
through the rewrite and loader path in angular-jit-plugin.ts, then verify it
produces the expected bounded ID and resolves back to the original encoded
styles using toJitInlineStyleId and getJitInlineStyles.
Source: Coding guidelines
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud has identified a possible root cause for your failed CI:
We classified this failure as an environment state issue rather than a code change. The tests-vitest-angular:test task fails because @analogjs/vitest-angular has not been built in this environment, making its sub-path exports (e.g. setup-testbed) unresolvable — this is entirely unrelated to the PR's changes in vite-plugin-angular. Running nx build vitest-angular before the test suite would resolve the failure.
No code changes were suggested for this issue.
Trigger a rerun:
🎓 Learn more about Self-Healing CI on nx.dev
Captures the flow for taking a PR from failing checks to green: pull the real CI output (Nx distributed execution hides task logs behind the summary table that `--log-failed` shows), classify each failure against the branch diff and the base branch before touching anything, then fix, re-run once, or escalate. Documents the two failure modes that read as real but aren't: Nx DTE ordering flakes on workspace subpath exports, and local-only failures from a Node version drifting off `.node-version`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PR Checklist
In JIT mode, Angular's resource transformer emits inline component styles as
angular:jit:style:inline;<base64 of the whole stylesheet>. The plugin rewrote only the prefix, leaving the full base64 in the module id. The bundler derives the emitted chunk name from that id, so a large inline stylesheet pushed the filename past the 255-byte limit and the build failed withENAMETOOLONG/File name too long (os error 63).Closes #2459
Affected scope
vite-plugin-angularRecommended merge strategy for maintainer [optional]
What is the new behavior?
The base64 payload is hashed to 16 chars at rewrite time and kept in a lookup table (
utils/jit-inline-styles.ts) that the JIT plugin'sloadhook reads back, so the virtual module id is a fixed length regardless of stylesheet size. The JIT plugin no longer re-hashes for thepreprocessCSSfilename — the id it receives is already bounded.Emitted chunk before:
_virtual_angular_jit_style_inline_<entire base64>.jsEmitted chunk after:
_virtual_angular_jit_style_inline_db5785c80e45692c-Np9tqdeL.jsTest plan
nx format:checkpnpm build(nx build vite-plugin-angular)pnpm test(nx test vite-plugin-angular— 39 files / 1238 tests pass, incl. newjit-inline-styles.spec.ts)Manual verification used a minimal JIT app (Angular 22, Vite 8 / rolldown) with a ~20KB inline
stylesblock andpreserveModulesso each module gets its own chunk name:vite buildfails withFile name too long (os error 63)./@id/__x00__virtual:angular:jit:style:inline;db5785c80e45692c, which serves the complete stylesheet.Does this PR introduce a breaking change?
Other information
The reported workaround (
build.rollupOptions.output.chunkFileNames) is no longer needed. The fast-compile JIT path is unaffected — it keeps inlinestylesin the decorator metadata and never creates a virtual module for them.🤖 Generated with Claude Code