[turbopack] Forward usage information across re-exports (#96282)
## What?
This PR improves tree-shaking across whole-namespace re-exports. It is
the middle PR in the stack: #98680 provides the independently reviewable
`EsmAssetReferenceOptions` constructor refactor requested during review,
and #96396 builds on this PR to narrow static member reads on
namespace-valued named exports.
A representative case is a CommonJS re-export followed by an ESM star
barrel:
```js
// index.js
const { m } = require('./cjs')
// cjs.js
module.exports = require('./barrel') // CJS re-export
// barrel.js
export * from './base' // ESM star barrel
// base.js
export const m = 1
export const n = 2 // nobody ever uses this
```
Only `m` is observed by the entrypoint, so that usage can now flow
through both re-export layers to `base.js` and allow `n` to be removed.
## Why?
Turbopack now has a generic `ExportUsage::Passthrough` mechanism,
introduced on `canary` for client-component and Next.js module proxies.
It carries the referencing module's accumulated used-export set into a
transparent target.
However, CommonJS whole-module forwarding and ordinary ESM star barrels
do not opt into that mechanism. Applying this PR's tests to plain
`canary` showed that all runtime values are already correct, but unused
exports remain retained in four important shapes:
- an ESM named consumer through `module.exports = require(ESM)`;
- a deep CommonJS forwarding chain;
- CommonJS forwarding into an ESM `export *` barrel;
- a CommonJS diamond with disjoint used exports.
This PR therefore remains a tree-shaking precision change rather than
introducing a second forwarding model.
There is also an independent name-observability concern. A CommonJS
forwarder exposes a raw namespace object's properties under their
original names, while named consumers of an ESM `export *` can be
rewritten if an export is mangled. Namespace provenance must survive
multiple passthrough hops without unnecessarily disabling mangling for
ordinary ESM named forwarding.
## How?
### Configure forwarding at construction time
#98680 replaces `EsmAssetReference`'s positional behavior arguments with
`EsmAssetReferenceOptions`. This PR extends that options object with an
optional passthrough mode. ESM star references receive `Some(false)`
directly when constructed; free-variable references receive `None`; and
the existing annotation-driven client-proxy path still contributes
`Some(true)`.
The two sources merge conservatively: an annotation that exposes the
namespace's original names cannot be weakened by a syntax-driven
`Some(false)` on the same reference. The former mutating
`mark_namespace_reexport()` API is removed.
### Reuse the generic passthrough fixed point
The implementation uses canary's existing model:
```rust
ExportUsage::Passthrough {
namespace_object_may_escape: bool,
}
```
The boolean describes whether the edge itself exposes the target
namespace's original property names:
- `module.exports = require('…')` records `Passthrough {
namespace_object_may_escape: true }` directly in the existing CommonJS
import-usage map;
- `export * from '…'` records `Passthrough {
namespace_object_may_escape: false }`, because statically known ESM
consumers can be rewritten when target exports are mangled.
No parallel `TargetExportUsage` type, graph field, call parameter, or
second fixed-point implementation is added.
Detection is limited to a simple assignment using the unresolved/global
`module` and `require` bindings. Shadowed bindings, member requires, and
compound assignments retain their existing behavior.
### Propagate keys and namespace provenance independently
A passthrough edge merges the parent module's resolved `Evaluation`,
`Exports`, or `All` state into its target. The merge remains monotonic
and idempotent, so chains, cycles, and diamonds converge.
Namespace provenance is carried independently of the used key set:
- an edge with `namespace_object_may_escape: true` makes the target's
original names observable;
- every passthrough also carries namespace provenance that already
reached its parent;
- a newly propagated provenance bit retriggers traversal even when the
used key set did not change.
This preserves original names through multi-hop namespace reads while
keeping unrelated ordinary named exports mangleable. Passthrough
references are also exposed correctly to scope-hoisted merged modules.
## Testing
Enabled execution coverage includes:
- deep CommonJS chains, CommonJS → ESM star barrels, cycles, diamonds,
and overwrite behavior;
- ESM named/default/namespace import interop through `module.exports =
require(ESM)`;
- opaque whole-namespace and evaluation-only consumption, including
single evaluation;
- native `export *`, default/local-shadow filtering, and the
optimization-disabled mode;
- namespace provenance across forwarding edges and a multi-hop diamond;
- both mangling safety and non-pessimization for ordinary named
forwarding;
- shadowed `module`/`require`, member-require, and non-simple assignment
boundaries;
- runtime counterparts for every nested precision case that remains
skipped.
Known optimization-only limitations remain under
`__skipped__/deep-reexports`: nested `export * as` through CJS,
namespace-property unwrapping in a CJS assignment, depth-two nested
namespaces, and unwrapping after a forwarding hop. Their runtime
semantics are covered by the enabled
`reexport-forwarding-nested-runtime` fixture.
Verification on the rebased stack:
- plain-canary reassessment: 7/9 fixtures, with only four expected
precision failures;
- integrated bottom layer: 9/9 forwarding fixtures;
- passthrough unit tests: 2/2, plus the six-case construction-option
merge lattice;
- combined stack: 14/14 focused execution/snapshot tests and 60/60
analyzer graph tests;
- affected Cargo checks, `next-core` check, `build-all`, and the
forwarding/namespace HMR test pass.
<!-- NEXT_JS_LLM -->
<!-- fleet 993ef5d6-cc80-4082-8e14-ff9ac871ce33 -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Co-authored-by: Luke Sandberg <210140+lukesandberg@users.noreply.github.com>