Repository navigation
fix(compiler): print numbers like JS String(number) - #4324
Conversation
format_number saturated whole numbers at i64::MAX (`{1e21}` gave
`9223372036854775807`) and never used exponent form, where Babel prints
`1e+21` / `1e-7`. Port JS Number::toString: shortest round-trip digits
from Rust's `{:e}`, laid out with the ECMA-262 rules (exponent form when
the decimal exponent is >= 21 or <= -7, `e+N` / `e-N`; NaN, Infinity,
-Infinity; -0 prints "0").
The child static-value path also folds `-`, `*` and `/` on numbers, as
the attribute evaluator does, so `<div>{1 / 0}</div>` inlines
`Infinity` instead of inserting `1 / 0` at runtime.
Fixes #4322
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015hPxMkC3jg3Uivh65gvaf2
🦋 Changeset detectedLatest commit: 4af5f10 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 40538821516Coverage remained the same at 78.727%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
|
The The earlier red Generated by Claude Code |
Merging this PR will not alter performance1 warning
Comparing Footnotes
|
Summary
Before: the Rust compiler printed inlined numbers differently from Babel. Any whole number of 2^63 or more was squeezed into an i64, so
<div k={1e21} />gavek=9223372036854775807where Babel givesk=1e+21, andk={1e19}gave the same wrong value instead of10000000000000000000. Fractions never switched to exponent form, sok={1e-7}gave0.0000001instead of1e-7. Also, a child{1 / 0}stayed a runtime_$insert(_el$, 1 / 0, …), even though the same expression as an attribute folds toInfinity, and Babel folds both.After: numbers print exactly as JS
String(number)prints them, in attributes and in children, in every mode. A child hole folds-,*and/on numbers the same way an attribute does, so<div>{1 / 0}</div>putsInfinityin the template, as Babel does.How
format_number(packages/compiler/src/shared/utils.rs) is now a port of JSNumber::toString(ECMA-262 §6.1.6.1.20). It takes the shortest round-trip digits from Rust's{:e}and lays them out with JS's rules: plain decimal when the decimal exponent is between −7 and 21, otherwise exponent form with a sign (1e+21,1.5e-7). It also handlesNaN,Infinityand-Infinity, and prints-0as"0".static_binary_expression, which the child path uses throughstatic_jsx_expression, now folds-,*and/when both operands are numbers. These are the same operatorsAttrPlanner::evaluate_confidentfolds for attributes.Relation to #4320 / #4321
No overlap. Neither PR touches these files. On
next, Babel's SSR and universal output keeps negative and non-finite attribute values dynamic until #4321 lands, and that is why the existing "exotic literal holes" probe fails in 6 modes onnext, with or without this change. So the new probes put-1e21and1 / 0in child position. With #4321'sbabel-plugin/src/shared/utils.tsapplied locally, the attribute forms (f={-1e21} g={1/0}) also match in every mode, and all 4110 probes pass.How did you test this change?
format_number_matches_js_number_to_string. It covers 26 values (0, -0, 0.1+0.2, 1e-6, 1e-7, 1.5e-7, 5e-324, 2^53+1, 2^63, 1e19, 1e20, 123456789012345680000, 1e21, -1e21, 1.5e21, MAX_VALUE, NaN, ±Infinity), and each expected string was checked against Node.packages/compiler/__tests__/parity-probes.test.js. Each runs in all 15 harness modes (dom, ssr, hydratable, universal, dynamic, tsrx, …):a={1e21} b={1e19} c={1e-7} d={1.5e-7} e={123456789012345680000} f={0.1 + 0.2}{1e21}{1e19}{1e-7}{1.5e-7}{123456789012345680000}{-1e21}{1 / 0}{-1 / 0}{2 * 3}{5 - 1}nextand pass with this change.cargo testin all three configs fromtest:rust(default,--no-default-features,--no-default-features --features tsrx): all pass.cargo clippy -- -D warnings: clean.cargo fmt --check: clean.nextuntil fix(babel-plugin): keep evaluated negative/non-finite numbers static #4321 lands. With fix(babel-plugin): keep evaluated negative/non-finite numbers static #4321's Babel fix applied, 4110/4110 probes pass.packages/babel-pluginpnpm test: 411/411. That package is not changed here.Client bundle impact: 0 B. This is a compiler-only change.
Fixes #4322
Generated by Claude Code