Repository navigation
fix(@angular/build): merge component stylesheet metafiles in a stable order - #34210
Conversation
There was a problem hiding this comment.
Code Review
This pull request ensures deterministic metafile generation by sorting additional compilation results by key order before merging, preventing non-deterministic output attribution caused by completion-order variations. A test has been added to verify this behavior. The feedback suggests that if the merging logic is eventually updated to merge duplicate output inputs rather than overwriting them, the test assertion should be updated to expect both input files.
|
Thanks for investigating this! However, there are a couple of concerns with this approach:
|
|
Both points taken, and this replaces what I said to the bot above. Pushed as two fixups.
Each spec fails against the code it guards, and the application target passes in full. One detail for the record: unminified CSS carries a source-path comment, so identical stylesheets share a hashed name only when styles are minified, which is why this stayed hidden in development builds. |
|
@alan-agius4 a gentle nudge on this one, in case it slipped: both of your points from 30 September are addressed in the fixups, and it still rebases cleanly onto current main with the |
There was a problem hiding this comment.
Thanks for the updates! There are still a couple of performance/memory concerns and some test cleanups needed. Please see the inline comments below. Also, when squashing the fixup commits, please make sure to update the commit message so it reflects the current approach.
…order Component stylesheet metafiles merged into the main metafile in bundle completion order, so the statsJson stats file varied between builds and, when two stylesheets bundled to the same output file, the last merged won and dropped the other's input attribution. Sort the dynamic path dictionaries (inputs, outputs, and each output's inputs) when serializing the stats file, and union the inputs of colliding outputs so every input is attributed. Both avoid a JSON.stringify replacer and per-collision object spreads, keeping V8 on its native serializer and avoiding the O(n^2) allocations raised in review.
1356bd1 to
56c42a4
Compare
|
You're right, the earlier revisions weren't surgical. I've applied all three suggestions:
One correction while I'm here: my earlier reply on the Gemini thread said this "only fixes the order, it doesn't change what the merge records." That's no longer accurate - the merge now unions the inputs of colliding outputs so every input is attributed, and the tests assert it. Squashed into one commit with an updated message. The statsJson integration specs pass locally. |
alan-agius4
left a comment
There was a problem hiding this comment.
LGTM, thanks for the updates!
Build output is non-deterministic: two builds of identical sources can write different stats files. Merging the component stylesheet metafiles in a stable order fixes it.
PR Checklist
PR Type
What is the current behavior?
Issue Number: #34209
outputs[<file>].inputsin the stats file changes between builds of the same sources. The cause is one loop inbuild.onEnd: the compiler plugin merges each component stylesheet's metafile into the main metafile in the order the stylesheets finished bundling, and when two stylesheets produce the same output file, the last one merged owns the entry. Every emitted file is byte-identical; only the metadata moves.What is the new behavior?
The entries are sorted by key before merging, so the merged metafile is the same on every build.
The new spec builds two components in different directories with identical stylesheets, builds twice, and asserts the shared output is attributed to the same input both times.
Does this PR introduce a breaking change?
Other information
Measured on 22.1.9 with the reproduction at https://github.com/sdjayna/angular-metafile-merge-order-repro (60 components, 12 identical-stylesheet pairs, 4 identical-image pairs): without the change 5 of 5 build pairs differed, in 7 to 10 of 54
outputsentries; with it, 0 of 3 differed and the stats file was byte-identical. The same loop is onmaintoday. The sort runs once per build over one entry per stylesheet, so its cost does not register against the bundling.