Skip to content

fix(angular-rspack): speed up watch-mode rebuilds#35282

Closed
leosvelperez wants to merge 1 commit intomasterfrom
gh-34936
Closed

fix(angular-rspack): speed up watch-mode rebuilds#35282
leosvelperez wants to merge 1 commit intomasterfrom
gh-34936

Conversation

@leosvelperez
Copy link
Copy Markdown
Member

Current Behavior

Watch-mode rebuilds in @nx/angular-rspack are significantly slower than expected — often several times slower than the Angular CLI's esbuild-based dev server on the same project. On large apps, a single-file change can take 10+ seconds to rebuild even though only one file changed.

Three root causes:

  1. buildAndAnalyze eagerly runs JavaScriptTransformer.transformData() on every file returned by emitAffectedFiles(). Due to Angular's .ngtypecheck.ts shim version mismatch between the first build (placeholder content) and subsequent rebuilds (real analysis content), TypeScript's incremental builder reports all ~7800 files as affected — so all ~7800 files go through the expensive transform on every rebuild.
  2. ComponentStylesheetBundler is constructed with incremental: false, which disables result caching, selective invalidation, and esbuild's incremental rebuild mode for component styles.
  3. JavaScriptTransformer.close() is called in the emit hook, which fires on every compilation — tearing down and re-spawning the worker thread pool on each rebuild.

Expected Behavior

Watch-mode rebuilds perform on par with the Angular CLI's esbuild pipeline. A single-file change should only transform the changed file and reuse cached output for everything else.

Changes:

  • Lazy transformbuildAndAnalyze now stores raw emitted content in a rawEmitCache and invalidates matching transformed cache entries only when the raw content actually changed. The Angular transform loader runs transformData() lazily when Rspack requests a module, mirroring esbuild's onLoad pattern. Files Rspack doesn't re-process are never re-transformed.
  • Incremental stylesheet bundlerComponentStylesheetBundler is now constructed with incremental: !!options.watch and invalidate(modifiedFiles) is called on rebuilds, matching the Angular CLI's setup-bundling.js behavior.
  • Persistent worker poolJavaScriptTransformer.close() moved from the per-build emit hook to the shutdown hook so workers live across rebuilds (mirrors esbuild plugin's onDispose).

Measured on a benchmark app with ~7800 files (Angular 21.2):

Pipeline Warm rebuild
rspack (before) ~12s
rspack (after) ~3.6s
esbuild (Angular CLI) ~3.8s

Related Issue(s)

Fixes #34936

@leosvelperez leosvelperez requested a review from a team as a code owner April 14, 2026 08:29
@leosvelperez leosvelperez requested a review from MaxKless April 14, 2026 08:29
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 14, 2026

Deploy Preview for nx-dev ready!

Name Link
🔨 Latest commit d0151f0
🔍 Latest deploy log https://app.netlify.com/projects/nx-dev/deploys/69ddfed7f3a5120008fea722
😎 Deploy Preview https://deploy-preview-35282--nx-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 14, 2026

Deploy Preview for nx-docs ready!

Name Link
🔨 Latest commit d0151f0
🔍 Latest deploy log https://app.netlify.com/projects/nx-docs/deploys/69ddfed7f3a5120008fea71e
😎 Deploy Preview https://deploy-preview-35282--nx-docs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud bot commented Apr 14, 2026

View your CI Pipeline Execution ↗ for commit d0151f0

Command Status Duration Result
nx affected --targets=lint,test,build,e2e,e2e-c... ✅ Succeeded 3m 34s View ↗
nx run-many -t check-imports check-lock-files c... ✅ Succeeded 3s View ↗
nx-cloud record -- pnpm nx conformance:check ✅ Succeeded 7s View ↗
nx build workspace-plugin ✅ Succeeded <1s View ↗
nx-cloud record -- nx format:check ✅ Succeeded 2s View ↗
nx-cloud record -- nx sync:check ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-04-14 08:55:05 UTC

Rebuilds transformed ALL Angular-emitted files through JavaScriptTransformer
eagerly in buildAndAnalyze, even when only one file changed. Now transform
lazily in the loader (esbuild's onLoad pattern) so only files Rspack actually
requests are transformed.

Also align component stylesheet bundler with Angular CLI by enabling
incremental caching in watch mode, and move JavaScriptTransformer.close()
from the per-build emit hook to the shutdown hook so worker threads persist
across rebuilds.
@Timebutt
Copy link
Copy Markdown

Timebutt commented Apr 14, 2026

Hey @leosvelperez! I was very excited about your changes and when reading them I thought 'this is it!' so wanted to try things out as fast as possible. I couldn't find any build artifacts in the Nx CI pipeline so built everything from scratch and dropped the updated versions of @nx/angular-rspack and @nx/angular-rspack-compiler into the node_modules of the big project I'm working on. I added some console.log() tracing statements to ensure what I'm seeing is using the new implementation.

Unfortunately I'm not seeing the performance difference you're mentioning :/ I'll do some averaging for rebuild times with the new implementation and then roll-back and compare with what it was before, but if there is a change it's definitely not close to the expected 3x or 4x faster. I'm hoping I made a mistake in setting up my local dev with the new changes but the tracing I added seems to disprove that.

I also still observe a long delay between saving a file and actually seeing the CLI pick up the file change, maybe that's where a lot of time is lost? I have an app that takes 40s for a rebuild, and most of it is just waiting for the CLI to react in any way (as in: showing the progress bar for instance).

Sometimes I'm also seeing a situation in which even when I change and save a file, nothing is picked up by the CLI. I'm not sure if after a very (!) long time the change gets picked up, or if that was after I hit Control+C but it did pick it up eventually. Very long time is 2min+, longer than the initial build.

Is there a chance something else (an earlier relevant change) is missing in your branch? If not, is there a published pre-release version I can use instead of what I built, to exclude the fact I may have made a mistake?

Apart from that: I'll gladly provide you with any information you might need from my setup to be able to debug this issue further.

@dan-winters
Copy link
Copy Markdown
Contributor

dan-winters commented Apr 15, 2026

I second that this PR is fairly important for the plugin. Is there any concern on addtional memory usage with the rawEmitCache ? Coming from a rather large repository space, this was my first thought; perhaps it's unfounded since this in only for local development?

@github-actions
Copy link
Copy Markdown
Contributor

Failed to publish a PR release of this pull request, triggered by @leosvelperez.
See the failed workflow run at: https://github.com/nrwl/nx/actions/runs/24458019335

@leosvelperez
Copy link
Copy Markdown
Member Author

@Timebutt I tested it against the repro you provided (https://github.com/Timebutt/ng-bundler-benchmark/tree/add-local-dev-configurations), but there was a bug in the benchmark script I used. I've rerun everything manually with more care, and you're right: this isn't improving anything. I'm closing it and will keep investigating.

@leosvelperez leosvelperez deleted the gh-34936 branch April 15, 2026 16:02
@dan-winters
Copy link
Copy Markdown
Contributor

@leosvelperez unsure if this helps, but from my experience the first watch mode rebuild is slow but it's significantly faster on additional updates

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

angular-rspack: poor local dev incremental build performance

3 participants