Conversation
🦋 Changeset detectedLatest commit: 10b14b3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Automated review: Thanks for digging into this. The perf win is real on Hermes and I was able to reproduce it, but the change needs a few fixes before it can land. Details and evidence below. What
|
baseline (JSON.parse) |
PR (object literal) | |
|---|---|---|
locale require |
25.8 ms | 17.5 ms |
lottie require |
28.1 ms | 8.6 ms |
| total | 54.0 ms | 25.6 ms (−53%) |
| JS bytes allocated | 27.6 MB | 23.9 MB |
Hermes VM on the host (VM built from the matching tag, bytecode from RN's hermesc, hyperfine, 30 runs):
| baseline | PR | |
|---|---|---|
| locale 4.5 MB | 29.8 ms | 23.3 ms |
| locale 4.2 MB (ASCII only) | 28.5 ms | 18.7 ms |
| lottie 2 MB | 61.0 ms | 42.3 ms |
Peak memory was about 25–30% lower with the PR.
Semantics. __proto__ keys (top-level and nested), U+2028/U+2029, \u0000, quotes and backslashes, large numbers, -0, 5e-324, integer-like key ordering, nesting, 150k keys, a 1M-element array, and top-level arrays and strings all produce output identical to Node's JSON.parse, in both variants and after Terser.
Trade-offs worth documenting
-
Dev mode gets slower. When Hermes runs the unminified bundle from source (
hermes -lazy, like a dev bundle from the dev server), object literals are much slower thanJSON.parse(this is the V8 rationale for webpack's default):- locale: 75.8 → 175.0 ms
- ASCII locale: 62.8 → 144.2 ms
- lottie: 81.7 → 427.9 ms (peak RSS 48 → 194 MB)
-
hermesc memory at build time:
- 2 MB Lottie: 20 MB → 472 MB peak RSS
- 1M-element numeric array: 87 MB → 761 MB
- 10 MB Lottie: 59 MB → 1.67 GB, and 0.1 s → 5 s
Metro users already live with this, but it can matter on memory-limited CI runners.
-
Build time. Terser does more work: Rspack + Terser went from 630 → 1,700 ms for about 11 MB of JSON.
-
Bytecode size can go either way. Locale shrank from 8.4 to 6.3 MB, because a single non-ASCII character makes Hermes store the whole
JSON.parsestring as UTF-16. Lottie grew from 2.10 to 2.41 MB.
Blocking issues
-
Builds crash on Rspack 1.0–1.2. The peer range is
@rspack/core >=1, but thejsongenerator option only exists from Rspack 1.3.0. With the new default, 1.2.8 fails immediately:Error: unreachable: unknow module type: json at getRawGeneratorOptions (@rspack/core/dist/index.js:6869:9) at getRawModuleRule (...) -
The rule silently overrides users' own
.jsonrules. The templates spread...Repack.getAssetTransformRules()last, and the last matchingtypewins. For example, with{ test: /\.lottie\.json$/, type: 'asset/resource' }defined earlier inmodule.rules:- without this PR, the Lottie file is emitted as a separate asset (
0a5eb80f52ea67a9.json); - with this PR, it gets inlined as a JSON module.
Reproduced on Rspack 1.3.0, 1.6.0 and 2.2.7, and on webpack 5.105. A user rule that runs a custom loader over
.jsonand setstype: 'javascript/auto'would be forced back tojsonthe same way. - without this PR, the Lottie file is emitted as a separate asset (
-
No effect on webpack 5.90–5.98.
JSONParsewas added in webpack 5.99.0, and the peer range iswebpack >=5.90. On older versions the option is silently ignored, so the documented behaviour doesn't happen. -
The docs, JSDoc, changeset and option name are inverted (see above).
Compat matrix (with the PR rule appended last, as in the templates):
| bundler | result |
|---|---|
| rspack 1.0.0 / 1.2.8 | crash (unknow module type: json) |
| rspack 1.3.0 / 1.6.0 / 2.2.7 | object literal ✅, but user .json rule overridden ❌ |
| webpack 5.90.0 / 5.98.0 | ignored (still JSON.parse) |
| webpack 5.99.0 / 5.105.4 | object literal ✅, but user .json rule overridden ❌ |
Suggestion
Rather than a module rule in getAssetTransformRules, set the global generator default in Re.Pack's base config (getRepackConfig, or the bundle command's config), gated on bundler support:
module: { generator: { json: { JSONParse: false } } } // only for rspack >= 1.3 / webpack >= 5.99I tested this across the same matrix:
- output is identical (object literals);
- user
typerules are left alone (the Lottie file stays an emitted asset); - users can still opt out through their own
module.generator.json, since user config merges on top of the Re.Pack defaults; - it also covers configs that don't call
getAssetTransformRules.
Consider enabling it for production/bundle builds only, so dev-server bundles keep the faster source-parse path. If you keep a user-facing option, a name that mirrors the bundler option (e.g. jsonParse) would avoid the inversion.
Minor: the updated tests rely on hard-coded indices (rules[1], rules[2]), which will break on any reordering. expect(rules[0]?.test.source).not.toContain('json') is a weak assertion. None of the tests exercise an actual build, so none of the issues above are caught.
Checks run
On the PR branch: tsc --noEmit, biome check and all 337 @callstack/repack jest tests pass. The Release iOS build and simulator runs use the Re.Pack CLI end to end.
Summary
issue: #1461
AI Usage:
Apexhelps to update docs + tests and added a new.changesetTest plan