Skip to content

fix(browser): the published bundle parses on webpack 4 - #867

Closed
divshekhar wants to merge 1 commit into
mainfrom
fix/browser-legacy-syntax
Closed

fix(browser): the published bundle parses on webpack 4#867
divshekhar wants to merge 1 commit into
mainfrom
fix/browser-legacy-syntax

Conversation

@divshekhar

Copy link
Copy Markdown
Contributor

Closes #680, on the option the report argued for.

What shipped before, and why it was the weaker half

init already prints a transpilation recipe when it detects react-scripts@<5. That was option 2 of the report. Option 1 — publish a build their bundler can read — is the one it argued for, and I agree with the argument: "an install path that requires the user to edit their bundler config to run our dev-only SDK is an install path most people abandon."

dist shipped untranspiled optional chaining, nullish coalescing (ES2020) and logical assignment (ES2021). react-scripts 4 excludes node_modules from Babel, so webpack parses our output as-is with acorn and all three are parse errors. The app fails to compile at dist/index.js before a dev session could ever connect, with no diagnostic, because it simply does not build.

The change

target: ES2019 in packages/browser/tsconfig.json only — deliberately below the repo's ES2022 base, because this is the one package whose build output another project's bundler parses. ES2019 is the last target before all three operators. lib stays at ES2023: this downlevels syntax, and the runtime is a modern browser either way.

Cost: 2.6% of bundle size, 5600 KB → 5744 KB, measured from a clean build on both sides. (My first measurement said 48x smaller, which was a half-finished build rather than a result — flagging it because the number was nonsense and I nearly reported it.)

Three test files used BigInt literals (2n), which ES2019 cannot express. BigInt(2) is the same value with no syntax requirement, so those tests keep testing BigInt and the package keeps building.

The guard parses rather than greps

Its first version matched operators with regexes and produced two false failures on a dist that was already correct — both hits were inside comments, one of them a comment quoting the very form?.textContent an earlier fix had removed. A regex cannot tell code from prose about code.

dist-parses-on-webpack4.test.ts now runs acorn at ecmaVersion: 2019 — the same parser webpack 4 uses — so it fails exactly where a user's build would and nowhere else. It also:

  • scans every emitted module, not the entry: dist/index.js is a 3 KB barrel that was clean while 52 siblings were not;
  • carries a negative control asserting the parser still rejects a?.b, a ?? b and a ??= b, because a check that cannot fail proves nothing;
  • guards against a vacuous pass on an unbuilt dist.

acorn is added as a devDependency of this package. It was already in the tree transitively; this makes it resolvable rather than adding anything new.

Gates

  • pnpm format:check, pnpm lint 17/17, pnpm typecheck 22/22, pnpm test:unit (140 browser files, 669 server)
  • pnpm test:e2e — 36/36 + soak
  • pnpm test:integration — 12/12 (the example apps consume this bundle, so this is the tier that matters here)

`dist` shipped untranspiled optional chaining, nullish coalescing (ES2020) and logical assignment
(ES2021). react-scripts 4 excludes node_modules from Babel, so webpack reads our output as-is with
acorn and all three are parse errors — the app fails to compile at dist/index.js before a dev
session could ever connect, with NO diagnostic, because it simply does not build.

`init` already printed a transpilation recipe on `react-scripts@<5`. That was the weaker of the
two fixes the report offered, and the report's own argument for the other one stands: an install
path that requires editing your bundler config to run our dev-only SDK is one most people abandon.

`target: ES2019` for this package only, deliberately below the repo's ES2022 base. This is the one
package whose build output is parsed by somebody else's bundler, and ES2019 is the last target
before all three operators. `lib` stays high: this downlevels SYNTAX, and the runtime is a modern
browser either way. Costs 2.6% of bundle size, 5600 KB -> 5744 KB, measured clean on both sides —
my first measurement said 48x smaller, which was a half-finished build, not a result.

Three test files used BigInt LITERALS (`2n`), which ES2019 cannot express. `BigInt(2)` is the same
value with no syntax requirement, so the tests keep testing BigInt and the package keeps building.

The guard PARSES rather than greps, and that is the design. Its first version matched operators
with regexes and produced two false failures on a dist that was already correct: the hits were in
COMMENTS, one of them a comment quoting the very `form?.textContent` a previous fix had removed. A
regex cannot tell code from prose about code. acorn is what webpack 4 uses, so the check fails
exactly where a user's build would and nowhere else. It scans every emitted module, not the entry
-- the entry is a 3 KB barrel and was clean while 52 siblings were not -- and carries a negative
control, because a parser check that cannot fail proves nothing.

Closes #680

Signed-off-by: Divyanshu Shekhar <imdshekhar@gmail.com>
@divshekhar

Copy link
Copy Markdown
Contributor Author

Closing this as a duplicate of #745, which is better, and I should have found it before writing a line.

#745 has been open since 3 September, fully green — including both install gates and the 28-minute Windows one — waiting on a review. I searched the issue for claims and went straight to building; its title says "webpack 4" and never mentions the issue number, so my grep missed it. That is my process failing, not theirs.

It is also materially more correct than this branch. Three things it does that this one does not:

  1. It fixes @reticlehq/core too. That is not a nicety — @reticlehq/browser imports core, so core ends up in the user's bundle. I checked after reading their PR: core's dist fails an ES2019 parse in 10 files (constants.js, impact.js, daemon-registry.js, …). This branch would have claimed to close @reticlehq/browser needs a legacy-compatible build for webpack 4 / react-scripts 4 #680 while a webpack 4 app still failed to compile — a green claim over a defect that is still reachable, which is the exact thing this repo exists to prevent.
  2. It handles /\p{L}/u, a Unicode property escape that tsc never downlevels regardless of target, because it does not rewrite regex bodies. They build it from a string with a letter-block fallback covering Thai, Georgian and astral-plane scripts. I did not know that limitation and would have shipped it broken.
  3. It scans .cjs as well as .js, and the zod that core actually resolves.

We independently converged on parsing at ES2019 as the real webpack-4 ceiling rather than grepping for operators, which is at least a good sign about the check.

Nothing here is worth salvaging on top of theirs.

@divshekhar divshekhar closed this Sep 8, 2026
auto-merge was automatically disabled September 8, 2026 16:48

Pull request was closed

@divshekhar
divshekhar deleted the fix/browser-legacy-syntax branch September 8, 2026 16:48
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.

@reticlehq/browser needs a legacy-compatible build for webpack 4 / react-scripts 4

1 participant