Skip to content

fix(babel-plugin): keep evaluated negative/non-finite numbers static - #4321

Merged
ryansolid merged 2 commits into
nextfrom
fix/babel-evaluated-number-literals
Oct 27, 2026
Merged

ryansolid merged 2 commits into
nextfrom
fix/babel-evaluated-number-literals

Conversation

@ryansolid

Copy link
Copy Markdown
Member

Summary

#4309 (Babel 8 support) changed evaluateAndInline to build evaluated numbers with t.valueToNode, because @babel/types 8's numericLiteral builder rejects negative and non-finite values. valueToNode(-1) builds a UnaryExpression and valueToNode(NaN) builds 0 / 0, so they are no longer NumericLiterals. The SSR and universal transforms then treat c={-1} d={NaN} as dynamic:

  • SSR emits ssrAttribute("c", -1) instead of inlining c="-1".
  • Universal prints d: 0 / 0.

Both differ from @solidjs/compiler. The compiler's parity probes ("exotic literal holes", 6 modes) catch the difference, but that workflow only runs on packages/compiler/** changes, so it never ran on #4309. It first showed up on #4320.

This change keeps the result a NumericLiteral. Values the v8 builder accepts still go through t.numericLiteral. Negative values, -0 and non-finite values are built as plain nodes, which both majors' generators print as -1 / NaN / Infinity, as Babel 7's builder did. The prop: path in dom/element.ts keeps valueToNode, because there the value is a runtime expression anyway.

How did you test this change?

  • packages/babel-plugin: pnpm test gives 411/411 on @babel/core 7, and 411/411 with @babel/core 8.0.9 swapped in as the babel-8 CI job does.
  • On Babel 8, <div c={-1} d={NaN} e={-0}> gives c="-1" d="NaN" e="0" in SSR, c: -1, d: NaN, e: 0 in universal, and c=-1 d=NaN e=0 in the dom template.
  • packages/compiler __tests__/parity-probes.test.js with the rebuilt plugin: 4080/4080. On next without this change, the 6 "exotic literal holes" probes fail.
  • This PR doesn't trigger the compiler-binaries workflow, so the same commit is also on fix(compiler): secondary passes accept any file extension #4320, where that workflow runs.

Generated by Claude Code

#4309 switched evaluateAndInline to t.valueToNode for numbers, which builds
-1 / 0/0 expressions; the SSR and universal transforms then treated them
as dynamic, diverging from the oxc compiler (parity-probes "exotic literal
holes"). Build NumericLiteral nodes directly where @babel/types 8 rejects
the value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0117BoWMdD4K7882vob4J9vq
@changeset-bot

changeset-bot Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5ba6b98

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 15 packages
Name Type
@solidjs/babel-plugin Patch
test-integration Patch
@solidjs/compiler Patch
@solidjs/diagnostics Patch
@solidjs/element Patch
@solidjs/h Patch
@solidjs/html Patch
@solidjs/signals Patch
solid-js Patch
@solidjs/universal Patch
@solidjs/web Patch
board-example Patch
canvas-example Patch
server-dbmon-example Patch
todos-server-example Patch

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

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.42 KB +4 B (+0.1%) 0 B −119 B 7.44 KB ✅
signals: + createStore 14.87 KB −3 B (−0.0%) 0 B −1463 B 15.04 KB ✅
signals: + isPending/latest 10.38 KB +10 B (+0.1%) 0 B −8 B 10.38 KB ✅
signals: + until/refresh (truth readers, no optimism) 8.05 KB 0 B 0 B −123 B 8.05 KB ✅
app: render + one signal (the simple-app floor) 9.78 KB +9 B (+0.1%) 0 B −107 B 9.75 KB ⚠️ over by 29 B, 127 B minified headroom
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 18.60 KB +39 B (+0.2%) +47 B −194 B 18.49 KB ⚠️ over by 110 B, 214 B minified headroom lazy-page.js 0.04 KB
app: hydrating + every store primitive family 31.04 KB +113 B (+0.4%) +48 B +16 B 30.86 KB ⚠️ over by 182 B, 4 B minified headroom lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 13.39 KB +21 B (+0.2%) 0 B −186 B 13.40 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 15.10 KB +8 B (+0.1%) 0 B −189 B 15.14 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 29.69 KB +11 B (+0.0%) 0 B −190 B 29.70 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 9.96 KB −12 B (−0.1%) 0 B −106 B 9.93 KB ⚠️ over by 26 B, 126 B minified headroom
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 26.14 KB +30 B (+0.1%) 0 B −736 B 26.27 KB ✅ stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 32.24 KB +94 B (+0.3%) +48 B −691 B 32.25 KB ✅ stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 11.75 KB 0 B 0 B 0 B 11.76 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 33.49 KB +55 B (+0.2%) +52 B −188 B 33.37 KB ⚠️ over by 121 B, 208 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 8.92 KB, wire.js 3.47 KB
page: live server components (base + live/GET + action + isPending/latest) 37.97 KB +153 B (+0.4%) +48 B +3 B 37.75 KB ⚠️ over by 216 B, 17 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, trace.js 8.93 KB, wire.js 3.47 KB
page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread) 35.49 KB −1 B (−0.0%) +47 B −185 B 35.43 KB ⚠️ over by 65 B, 205 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.06 KB, sc-comments.js 0.20 KB, trace.js 8.94 KB, wire.js 3.47 KB
page: compiled live server components (the compiled base page + live/GET + action + isPending/latest) 41.81 KB +135 B (+0.3%) +45 B −10 B 41.56 KB ⚠️ over by 255 B, 30 B minified headroom eager (counted): web.js 22.37 KB; assets.js 0.78 KB, decode.js 7.09 KB, regions.js 1.06 KB, sc-comments.js 0.19 KB, trace.js 8.94 KB, wire.js 3.47 KB
page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate) 41.03 KB +32 B (+0.1%) +44 B −189 B 40.81 KB ⚠️ over by 216 B, 209 B minified headroom assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.06 KB, server.js 1.03 KB, serverForms.js 3.58 KB, trace.js 8.97 KB, wire.js 3.48 KB
page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate) 47.35 KB +15 B (+0.0%) +48 B −4 B 47.16 KB ⚠️ over by 190 B, 24 B minified headroom eager (counted): client.js 27.45 KB; assets.js 0.78 KB, decode.js 7.09 KB, lazy-page.js 0.04 KB, regions.js 1.07 KB, server.js 1.03 KB, serverForms.js 3.32 KB, trace.js 8.95 KB, wire.js 3.49 KB
server: floor (getRequestEvent + isServer) 1.27 KB 0 B 0 B −69 B 1.34 KB ✅
server: renderToString (the server-render floor) 22.79 KB 0 B 0 B 0 B 22.80 KB ✅

⚠️ Over the brotli cap within the minified allowance (passes)

  • app: render + one signal (the simple-app floor): over brotli cap by 29 B; minified 26,922 B vs 27,029 B recorded with the cap (−107 B) — 127 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: hydrating (no stores) with Show/For/Loading/Errored/lazy: over brotli cap by 110 B; minified 53,426 B vs 53,620 B recorded with the cap (−194 B) — 214 B of the 20 B minified allowance left; +47 B minified over this PR's base
  • app: hydrating + every store primitive family: over brotli cap by 182 B; minified 95,129 B vs 95,113 B recorded with the cap (+16 B) — 4 B of the 20 B minified allowance left; +48 B minified over this PR's base
  • app: compiled floor (one template, one text hole, one delegated click): over brotli cap by 26 B; minified 27,454 B vs 27,560 B recorded with the cap (−106 B) — 126 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: base server components (hydrating + dynamic + frames + sf reference): over brotli cap by 121 B; minified 101,371 B vs 101,559 B recorded with the cap (−188 B) — 208 B of the 20 B minified allowance left; +52 B minified over this PR's base
  • page: live server components (base + live/GET + action + isPending/latest): over brotli cap by 216 B; minified 115,220 B vs 115,217 B recorded with the cap (+3 B) — 17 B of the 20 B minified allowance left; +48 B minified over this PR's base
  • page: compiled base server components (the base page as JSX: templates with class/style/attributes/events, For/Show; no spread): over brotli cap by 65 B; minified 107,733 B vs 107,918 B recorded with the cap (−185 B) — 205 B of the 20 B minified allowance left; +47 B minified over this PR's base
  • page: compiled live server components (the compiled base page + live/GET + action + isPending/latest): over brotli cap by 255 B; minified 123,206 B vs 123,216 B recorded with the cap (−10 B) — 30 B of the 20 B minified allowance left; +45 B minified over this PR's base
  • page: base + router (base page + @solidjs/router: createRouter, two routes, preload, useNavigate): over brotli cap by 216 B; minified 125,198 B vs 125,387 B recorded with the cap (−189 B) — 209 B of the 20 B minified allowance left; +44 B minified over this PR's base
  • page: live + router (live page + @solidjs/router: createRouter, two routes, preload, useNavigate): over brotli cap by 190 B; minified 140,476 B vs 140,480 B recorded with the cap (−4 B) — 24 B of the 20 B minified allowance left; +48 B minified over this PR's base

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

@coveralls

coveralls commented Oct 27, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 40532949656

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.04%) to 78.627%

Details

  • Coverage increased (+0.04%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 90 coverage regressions across 1 file.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

90 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
packages/solid/src/client/hydration.ts 90 78.63%

Coverage Stats

Coverage Status
Relevant Lines: 1353
Covered Lines: 1123
Line Coverage: 83.0%
Relevant Branches: 1080
Covered Branches: 790
Branch Coverage: 73.15%
Branches in Coverage %: Yes
Coverage Strength: 28.03 hits per line

💛 - Coveralls

@codspeed

codspeed Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 13.73%

2 warnings

⚠️ 2 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

❌ 2 regressed benchmarks
✅ 194 untouched benchmarks
⏩ 5 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ createStore setter: delete + set one root key (#3044 overlay) 1.8 ms 2.1 ms -14.29%
❌ memo + sync render effect only (reference) 29.7 ms 34.2 ms -13.18%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing fix/babel-evaluated-number-literals (5ba6b98) with next (0090fe3)

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Export numberLiteral and use it for evaluated prop: numbers, keep -0's
sign, and test the static attribute paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018LY7F9czoRqdMEvk2QwPr1
@ryansolid
ryansolid merged commit 8cff139 into next Oct 27, 2026
8 checks passed
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.

3 participants