Turbopack scope-hoisting emits wrong variable reference when inlining pre-minified ESM modules #97979
rmachado-hema
started this conversation in
Turbopack Error Report
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
We hit an SSR crash on Next.js 16.3.3 (Turbopack) where a component's
.map()call ends up referencing a destructured string parameter instead of a child component, causing React to try to render a plain string as an HTML tag (Error: Invalid tag: <some text>).The root cause is in how Turbopack renames variables during scope-hoisting inside
__turbopack_context__callbacks. We couldn't build a small standalone reproduction — the bug only triggers when enough modules are concatenated into a single callback, which doesn't happen in a minimal project.Environment
The pattern
We use a private UI component library that ships pre-minified ESM (standard Rollup output). Three component files each export a
function a(...)with short import aliases — a normal minification pattern:function a, a simple wrapper componentfunction a, imports ComponentA ase, uses it internallyfunction a, imports ComponentB asn, maps over a prop callingnComponentC looks roughly like this in the minified dist:
These are imported via a barrel index in a server component, with
optimizePackageImportsenabled.What Turbopack produces
After
next build, the server chunk contains a__turbopack_context__callback where all three functions are inlined. Turbopack renames the threeaexports toa,a1,a2:The
.map()emitsjsx(a, ...)instead ofjsx(a1, ...). Since{title: a}also shadowsain that function scope,aresolves to the title string at runtime, and React's SSR crashes trying to render it as an HTML tag.Why we couldn't build a standalone repro
In a small project, Turbopack uses a flat module scope with distinct variable names and handles the renaming correctly. The
__turbopack_context__concatenation pattern — where the bug occurs — only triggers when there are enough application-level modules in the module graph.Workaround
Wrapping the affected component in a
'use client'file prevents Turbopack from inlining it into the server chunk:Our hypothesis
When Turbopack renames three
function aexports toa,a1,a2during scope-hoisting, it correctly updates the cross-reference ina1(ComponentB → ComponentA). But it fails to update the reference ina2: the import aliasn(ComponentB) should be rewritten toa1, but is rewritten toainstead. This may be a conflict between the import-alias renaming and the destructuring alias{title: a}within the same function body.Possibly related issues:
All reactions