Repository navigation
fix(core): keep bundled HTML under V8's max string length - #6134
Conversation
7644f89 to
fdf538f
Compare
miguel-heygen
left a comment
There was a problem hiding this comment.
Nice catch, and thank you for the first contribution here. Counting per reference rather than per asset is the right call, since that is what actually lands in the output string, and routing it through the one inliner means the srcset, CSS url() and LUT paths are all covered. The test goes through bundleToSingleHtml and genuinely fails without the cap.
Two optional notes, neither blocking: the budget latches once, so a small font after the overflowing image keeps its relative URL too, which is worth a word in the comment since it is deliberate. And the test holds a 266 MB string and splits it twice. Making the cap injectable would let the same assertions run on a few MB.
jrusso1020
left a comment
There was a problem hiding this comment.
Approving. The crash is real: on main, createAssetInliner caches one data URL per asset but returns it for every reference, so an image under the 2 MB per-asset cap that is used hundreds of times builds a string past V8's limit and every caller of bundleToSingleHtml (check, snapshot, layout, studio preview, inspector) throws RangeError: Invalid string length.
A bundle-wide budget is the right shape for this, and it is placed at the one function every inlining path goes through (img/src, srcset, CSS url(), inline styles and LUTs), so nothing sidesteps it. Past the budget a reference keeps its relative URL, which is the same fallback the per-asset cap already uses, with one warning. Bundles under 256 MB are unchanged.
I ran the new test on main without the fix and it fails with RangeError: Invalid string length. With the fix the whole htmlBundler.test.ts file passes, 187/187.
The one red check, Studio edit accuracy shard 1, was cancelled in its browser ensure step after 25 minutes, so it is an infra timeout and not this change.
Need: yes, a repeated asset crashes every bundle consumer · Size: ok · Reuse: extends the existing per-asset inline cap in the same inliner · Simpler: already minimal; the dedup the author mentions would also shrink normal bundles but changes output shape, fine as a follow-up
— Rames
What
bundleToSingleHtmlstops inlining data URLs once the bundle has inlined 256 MB of them. References past that point keep their relative URL.Why
Each asset under 2 MB is inlined once per reference, so an image used hundreds of times can push the bundle past V8's max string length and
check/snapshot/renderthrowRangeError: Invalid string length. The per-asset limit cannot prevent this; only a limit on the total can.Related work
Fixes #6133. No overlapping open PRs found.
How
createAssetInlinercounts the characters it has inlined. Before returning a data URL that would exceedMAX_INLINED_BUNDLE_CHARS(256 MB, half of V8's limit, leaving room for the rest of the HTML), it warns once and returnsnull, so the reference falls through to its existing relative-URL path. Bundles under the cap are byte-for-byte unchanged.Deduplicating (write each asset once and reference it) would also shrink normal bundles, but changes the output shape; this PR keeps to the crash. Happy to follow up with dedup if you prefer that direction.
Test plan
htmlBundler.test.ts: 300 references to a 1.9 MB asset. Fails onmainwithRangeError: Invalid string length; passes here, with some references inlined, the rest relative, and one warning.packages/coretests, oxlint, oxfmt, typecheck and lefthook pre-commit pass.