Repository navigation
Conversation
… memo (#4256) A component spread whose expression is not a plain object compiles to merge(fn), and merge() wraps the function in a memo with no options. The idiom returns a fresh key bag every run, so the attribution engine reported UNSTABLE_MEMO_OUTPUT for memo "computed" with advice (`equals`) nobody can apply. Observe builds now create that memo as `merge` with a new CONFIG_KEY_BAG bit, and the unstable-output check skips it. Production creates the memo exactly as before. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 78dab45 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Size (brotli, eager entry chunk)
|
Coverage Report for CI Build 40149257064Warning No base build found for commit Coverage: 77.764%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
Merging this PR will degrade performance by 13.38%2 warnings
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
|
CodSpeed flags one benchmark: the mount-rows "memo + sync render effect only (reference)" bench, 29.6 → 34.3 ms. The benches run the prod tier, and this PR leaves that tier's Generated by Claude Code |
Before: a component spread whose expression isn't a plain object, such as
<Child {...(on() ? { tone: "info" } : {})} />, compiles tomergeProps(() => …).merge()wraps that function in a memo with no options. The idiom returns a fresh key bag on every run, so after four runs the attribution engine reportsUNSTABLE_MEMO_OUTPUTfor memo "computed" and says to passequals. Nobody wrote that memo, so nobody can pass it options.After: observe builds create that memo with the name
mergeand a newCONFIG_KEY_BAGbit. The unstable-output check skips any memo carrying that bit. Production output is unchanged: the prod branch is stillcreateMemo(s).How:
merge()passes{ name: "merge", _extraConfig: CONFIG_KEY_BAG }behind__OBSERVE__, using the existing_extraConfigroute.checkUnstableOutputreturns early for that bit. This follows the existing exemptions (CONFIG_WIDE,CONFIG_SUPERSEDES).The issue also proposed a shallow
equalson the merge memo. I left that out because it would add bytes to every app that spreads into a component.Fixes #4256
How did you test this change?
packages/signals/tests/attribution-unstable-merge-4256.test.tsuses the issue's repro shape withmerge(() => …)and a reader ofprops.tone, run eight times. It fails onnext(oneUNSTABLE_MEMO_OUTPUT) and passes with this change.attribution-unstable-memo.test.tsstill passes, so user memos still warn.dist/prod/store/utils.jsstill emitscreateMemo(o).Generated by Claude Code