Skip to content

Fix blank production site from dev-only JSX runtime - #19

Merged
frewsxcv merged 1 commit into
mainfrom
claude/bump-dependencies-omi2m9
Sep 2, 2026
Merged

frewsxcv merged 1 commit into
mainfrom
claude/bump-dependencies-omi2m9

Conversation

@frewsxcv

@frewsxcv frewsxcv commented Sep 2, 2026

Copy link
Copy Markdown
Owner

The deployed site is currently blank. It throws on load:

TypeError: (0 , s(...).jsxDEV) is not a function

This is fallout from the Babel 7 → 8 upgrade in #17. Two problems combined.

What happened

Babel 8 changed @babel/preset-react's default runtime from "classic" to "automatic", so JSX now compiles to imports from a react/jsx-*-runtime module instead of React.createElement.

Which runtime it picks depends on Babel's environment — and Babel falls back to "development" whenever BABEL_ENV and NODE_ENV are both unset. That is exactly the case during npm run build: webpack's mode only defines NODE_ENV inside the bundle, not in the process babel-loader runs in.

So the production build emitted jsxDEV calls. React's production build of jsx-dev-runtime deliberately exports no jsxDEV, so the first render threw and left an empty page.

Setting NODE_ENV correctly then exposed a second, latent problem: .babelrc's env.production block pulls in a minify preset that is not, and never was, in devDependencies. It had simply never been reached, because the environment was always "development". Webpack's own minifier already covers this, so the block is removed rather than replaced.

The fix

  • The webpack configs set NODE_ENV to match their mode before babel-loader sees it. Production gets the production runtime; development keeps jsxDEV and its better error messages.
  • The dead env.production / minify block is removed from .babelrc.

Why CI didn't catch it, and what now does

npm run build compiling successfully says nothing about whether the bundle runs — and nothing in CI ever loaded it. The tests are no-ops, and my browser check on the previous PR ran against the dev server, where jsxDEV legitimately exists. That's precisely the gap that let a blank page ship green.

npm run build now also runs scripts/check-production-bundle.js, which fails the build if a development-only React runtime is present in the emitted output:

Production bundle contains development-only React runtime code:
  - bundle.f08ac142.min.js references jsxDEV

This bundle would throw on load and render a blank page. ...

I verified the guard fails on the broken bundle and passes on the fixed one.

Verification

Built the production bundle, served it, and loaded it headless with the iNaturalist API stubbed — the real (non-offline) code path, which had never been exercised end to end before:

before after
page body empty + TypeError renders
geolocation → nearby places — Berkeley, Oakland
search "yosemite" — Yosemite National Park, Yosemite Valley
select a place — advances to the taxa step

Also confirmed the dev server still compiles and mounts cleanly (3 children under #root, no page errors), plus npm run lint and the tests pass.

Note

This guard is deliberately narrow — it catches this specific class of "bundle throws on load". A real headless smoke test in CI that loads the built page and asserts it rendered would catch the general case, but it means adding a browser to CI, so I left that as your call.

🤖 Generated with Claude Code

https://claude.ai/code/session_01J7nU5T9UJU7ydrRdENiKuQ


Generated by Claude Code

The deployed site rendered nothing and threw on load:

    TypeError: (0 , s(...).jsxDEV) is not a function

Two problems from the Babel 7 -> 8 upgrade combined to cause it.

Babel 8 changed @babel/preset-react's default `runtime` from "classic" to
"automatic", so JSX now compiles to imports from a react/jsx-*-runtime
module instead of `React.createElement`. Which runtime it picks depends on
Babel's environment, and Babel falls back to "development" whenever
BABEL_ENV and NODE_ENV are both unset — which is exactly the case during
`npm run build`, since webpack's `mode` only defines NODE_ENV inside the
bundle, not in the process babel-loader runs in. The production build was
therefore emitting `jsxDEV` calls, and React's production build of
jsx-dev-runtime deliberately exports no `jsxDEV`, so the first render threw
and left an empty page.

Setting NODE_ENV correctly then exposed the second problem: .babelrc's
`env.production` block pulls in a `minify` preset that is not, and never
was, in devDependencies. It had simply never been reached, because the
environment was always "development". Webpack's own minifier already
handles this, so the block is removed rather than replaced.

The webpack configs now set NODE_ENV to match their mode before
babel-loader sees it, which fixes the production runtime and keeps the
development build on the dev runtime.

Verified by loading the built bundle in a headless browser with the
iNaturalist API stubbed: it now renders, geolocation lists nearby places,
searching returns matches, and selecting one advances to the taxa step.
Before this change the same check produced an empty body and the TypeError
above.

`npm run build` compiling clean is what let this reach production, so the
build now also runs scripts/check-production-bundle.js, which fails if a
development-only React runtime is present in the emitted bundle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J7nU5T9UJU7ydrRdENiKuQ
@frewsxcv
frewsxcv merged commit b8f3915 into main Sep 2, 2026
1 check 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.

2 participants