feat(turbopack): support false values for resolveAlias config#91595
feat(turbopack): support false values for resolveAlias config#91595
false values for resolveAlias config#91595Conversation
Adds support for passing `false` as a value in `resolveAlias`, which
resolves the aliased import to an empty module (`{}` for CJS/namespace
imports, `undefined` for named bindings).
Chain: `false` → `SubpathValue::Empty` → `ImportMapping::Empty` →
`ReplacedImportMapping::Empty` → `ResolveResultItem::Empty` →
`ModuleResolveResultItem::Empty` → `SinglePatternMapping::Empty` /
`ReferencedAsset::Empty`.
Co-Authored-By: Claude <noreply@anthropic.com>
Tests ESM namespace/named/default imports, dynamic import(), and CommonJS require() all resolve to an empty module when aliased to false. Co-Authored-By: Claude <noreply@anthropic.com>
`SinglePatternMapping::Empty` had identical code generation to `Ignored` in all three paths (create_id/create_require/create_import), so the variant was redundant. Map `ModuleResolveResultItem::Empty` directly to `Ignored` and remove the unreachable explicit arm in `as_module`. Co-Authored-By: Claude <noreply@anthropic.com>
Failing test suitesCommit: 74a51a5 | About building and testing Next.js
Expand output● app-dir - server-action-period-hash › should have same manifest between continuous two builds ● app-dir - server-action-period-hash › should have different manifest between two builds with period hash |
Merging this PR will not alter performance
Comparing Footnotes
|
Stats from current PR🔴 1 regression, 1 improvement
📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📝 Changed Files (16 files)Files with changes:
View diffsapp-page-exp..ntime.dev.jsfailed to diffapp-page-exp..time.prod.jsDiff too large to display app-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsfailed to diffapp-page-tur..ntime.dev.jsfailed to diffapp-page-tur..time.prod.jsDiff too large to display app-page.runtime.dev.jsfailed to diffapp-page.runtime.prod.jsDiff too large to display app-route-ex..ntime.dev.jsDiff too large to display app-route-ex..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route-tu..ntime.dev.jsDiff too large to display app-route-tu..time.prod.jsDiff too large to display app-route.runtime.dev.jsDiff too large to display app-route.ru..time.prod.jsDiff too large to display 📎 Tarball URL |
SubpathValue::add_results duplicated the tree-walk logic already present in ReplacedSubpathValue::add_results. Replace the one call site (export_value_to_import_mapping) with value.convert().add_results() and make AliasKey, ReplacedSubpathValue, and ReplacedSubpathValueResult part of turbopack-core's public resolve API so callers can use them. Co-Authored-By: Claude <noreply@anthropic.com>
Introduce ReplacedSubpathValueResultType::{Path, Empty, Excluded} so
that add_results callers no longer need a boolean return value to detect
terminal cases. Excluded and Empty are now represented as explicit result
entries rather than implicit termination signals, which fixes silent
omission of Empty in the previous add_results logic.
Remove the bool return value from ReplacedSubpathValue::add_results;
callers detect whether a match was found by comparing the target length
before and after the call.
Co-Authored-By: Claude <noreply@anthropic.com>
Returns true when a definitive result covering all possible unknown condition values was found (Excluded, Empty, or a concrete path), so callers (Alternatives arms and the outer lookup loop) know to stop trying further alternatives. Co-Authored-By: Claude <noreply@anthropic.com>
…rminal A Result path might not resolve successfully (the file may not exist), so Alternatives should continue to the next entry rather than stopping. Only Excluded and Empty are definitively terminal for all possible unknown condition values. Co-Authored-By: Claude <noreply@anthropic.com>
…for null The Excluded arm already returns true to signal termination, so there is no need to push a result item. Callers simply produce no output for that case, which naturally leads to an unresolvable outcome. Co-Authored-By: Claude <noreply@anthropic.com>
What?
Adds support for
falseas a value inexperimental.turbopack.resolveAlias, which resolves the aliased module to an empty stub — matching webpack's long-standing behavior.With this alias in place:
import * as ns from 'some-server-only-module'→nsis{}import { foo } from 'some-server-only-module'→fooisundefinedimport def from 'some-server-only-module'→defisundefinedawait import('some-server-only-module')→ resolves to{}require('some-server-only-module')→ returns{}Why?
Webpack supports
falseas an alias target to stub out modules (e.g. to exclude Node-only packages from client/edge bundles). Turbopack did not, causing migration friction for projects that rely on this pattern.How?
Threads the
falsesentinel through the full resolution pipeline as a newEmptyvariant:For static ESM imports,
ReferencedAsset::Emptyis kept as a distinct variant so thatbinding.rscan distinguish a namespace import (yields{}) from a named/default import (yieldsundefined).For CJS
require()and dynamicimport(),ModuleResolveResultItem::Emptymaps to the existingSinglePatternMapping::Ignored, which already generates the correct{}/Promise.resolve({})stubs — so no new code-generation path was needed there (the redundantEmptyvariant introduced during development was removed in a follow-up cleanup commit).Files changed:
packages/next/src/server/config-shared.ts— addfalsetoresolveAliastypepackages/next/src/server/config-schema.ts— addz.literal(false)to schemacrates/next-core/src/next_import_map.rs— mapfalse→SubpathValue::Emptyturbopack/crates/turbopack-core/src/resolve/remap.rs— handleImportMapping::Emptyturbopack/crates/turbopack-core/src/resolve/mod.rs— propagateModuleResolveResultItem::Emptyturbopack/crates/turbopack-ecmascript/src/references/esm/base.rs—ReferencedAsset::Emptyturbopack/crates/turbopack-ecmascript/src/references/esm/binding.rs— empty-module binding code-genturbopack/crates/turbopack-ecmascript/src/references/esm/url.rs— handle Empty in URL refsturbopack/crates/turbopack-ecmascript/src/references/async_module.rs— skip Empty in async module analysisturbopack/crates/turbopack-ecmascript/src/references/pattern_mapping.rs— map Empty → IgnoredTests:
test/e2e/turbopack-resolve-alias-false/covers all five import styles (namespace, named, default, dynamic, CJS).