perf(@angular/build): memoize package-level sideEffects checks in compiler plugin - #34047
Open
alan-agius4 wants to merge 1 commit into
Open
perf(@angular/build): memoize package-level sideEffects checks in compiler plugin#34047alan-agius4 wants to merge 1 commit into
alan-agius4 wants to merge 1 commit into
Conversation
alan-agius4
marked this pull request as ready for review
September 9, 2026 08:21
There was a problem hiding this comment.
Code Review
This pull request extracts the side-effects resolution logic from the Angular compiler plugin into a dedicated SideEffectsResolver utility, introducing package-level and file-level memoization to optimize bundling performance along with comprehensive unit tests. The review feedback suggests safely parsing package.json by avoiding direct destructuring of JSON.parse output, which could throw a TypeError if the JSON parses to null, and instead using optional chaining to avoid unnecessary performance overhead from the try/catch block.
…piler plugin When advancedOptimizations is enabled, the compiler plugin checks each emitted and transformed JavaScript file via hasSideEffects() to determine whether pure annotations should be added during bundling. Previously, hasSideEffects() invoked build.resolve() across the esbuild Go <-> Node.js IPC boundary for every individual file. In an application with hundreds of files loaded from node_modules (e.g. 226 files from rxjs), this resulted in hundreds of redundant IPC round-trips to evaluate the exact same package-level sideEffects configuration. To eliminate redundant resolution calls: - Introduce a dedicated SideEffectsResolver class and createSideEffectsResolver() factory in a separate module. - Memoize resolved sideEffects booleans at both the package level and the individual file level. - When an imported file resides in node_modules, extract the package directory and inspect its package.json sideEffects property once. - If sideEffects is a boolean, memoize it for all files originating from that package. - If sideEffects is an array of globs, string, or omitted, bypass package-level memoization and resolve via build.resolve() per file, memoizing the resolved file result to prevent duplicate calls. - Return an async noop when advancedOptimizations is disabled to avoid overhead. - Provide comprehensive unit tests covering package memoization, non-boolean sideEffects, scoped packages, pnpm virtual stores, and Windows/relative paths. In benchmarks on an application with 253 JS dependency files across 6 packages, build.resolve IPC calls dropped from 253 to 6 (-97.6%) and cumulative resolve duration dropped by 99.7%.
alan-agius4
force-pushed
the
perf/compiler-side-effects-cache
branch
from
September 9, 2026 08:28
e727ea9 to
8d482a1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When advancedOptimizations is enabled, the compiler plugin checks each emitted and transformed JavaScript file via hasSideEffects() to determine whether pure annotations should be added during bundling.
Previously, hasSideEffects() invoked build.resolve() across the esbuild Go <-> Node.js IPC boundary for every individual file. In an application with hundreds of files loaded from node_modules (e.g. 226 files from rxjs), this resulted in hundreds of redundant IPC round-trips to evaluate the exact same package-level sideEffects configuration.
To eliminate redundant resolution calls:
In benchmarks on an application with 253 JS dependency files across 6 packages, build.resolve IPC calls dropped from 253 to 6 (-97.6%) and cumulative resolve duration dropped by 99.7%.