-
Notifications
You must be signed in to change notification settings - Fork 29k
Turbopack: side effects optimization cleanups #82467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: canary
Are you sure you want to change the base?
Conversation
match part { | ||
ModulePart::Evaluation => { | ||
if *module.get_exports().split_locals_and_reexports().await? { | ||
if *module.get_exports().split_locals_and_reexports().await? { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tree shaking logic restructure breaks reexport tree shaking for modules that don't have reexports, causing them to bypass tree shaking entirely when they should still be processed.
View Details
Analysis
The refactored TreeShakingMode::ReexportsOnly
logic in lines 177-216 changes the conditional structure in a way that breaks tree shaking for certain modules.
Before the change: When part
is ModulePart::Export(_)
, the code would:
- If
split_locals_and_reexports()
returns true → apply tree shaking with facade module - If
split_locals_and_reexports()
returns false → still apply tree shaking directly on the module viaapply_reexport_tree_shaking(Vc::upcast(*module), part, side_effect_free_packages)
After the change: The logic now checks split_locals_and_reexports()
first:
- If true → apply appropriate tree shaking based on part type
- If false → always return
Vc::upcast(*module)
unchanged (line 215)
This means modules that don't have reexports (where split_locals_and_reexports()
returns false) will completely bypass tree shaking even when explicitly requested for export parts in ReexportsOnly
mode. This can result in larger bundles and dead code inclusion, as the tree shaking that should occur for these modules is never applied.
The split_locals_and_reexports()
method returns true only when a module has star exports or imported bindings that are re-exported, so regular modules with only local exports will be affected by this bug.
Recommendation
Restore the original conditional structure where apply_reexport_tree_shaking
is called for ModulePart::Export(_)
regardless of whether the module splits locals and reexports. The logic should be:
if let Some(part) = part {
match part {
ModulePart::Evaluation => {
if *module.get_exports().split_locals_and_reexports().await? {
Vc::upcast(EcmascriptModuleLocalsModule::new(*module))
} else {
Vc::upcast(*module)
}
}
ModulePart::Export(_) => {
let side_effect_free_packages = module_asset_context
.side_effect_free_packages()
.resolve()
.await?;
if *module.get_exports().split_locals_and_reexports().await? {
apply_reexport_tree_shaking(
Vc::upcast(EcmascriptModuleFacadeModule::new(
Vc::upcast(*module),
ModulePart::facade(),
).resolve().await?),
part,
side_effect_free_packages,
)
} else {
apply_reexport_tree_shaking(
Vc::upcast(*module),
part,
side_effect_free_packages,
)
}
}
_ => bail!("Invalid module part...")
}
} else if *module.get_exports().split_locals_and_reexports().await? {
Vc::upcast(EcmascriptModuleFacadeModule::new(
Vc::upcast(*module),
ModulePart::facade(),
))
} else {
Vc::upcast(*module)
}
Failing test suitesCommit: 84bf1ec
Expand output● Read-only source HMR › should detect a new page
Read more about building and testing Next.js in contributing.md. |
CodSpeed Performance ReportMerging #82467 will improve performances by 3.92%Comparing Summary
Benchmarks breakdown
|
9e3cf69
to
2fb7903
Compare
e4b1cde
to
2b7eb7d
Compare
_ => Ok(FollowExportsResult { | ||
module, | ||
export_name: Some(export_name), | ||
ty: FoundExportType::Dynamic, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code uses unreachable!()
for an empty dynamic modules array, but this case could occur due to logic bugs or edge cases, causing a panic instead of graceful error handling.
View Details
Analysis
In the Dynamic
match arm of follow_reexports
, the code uses [] => unreachable!(),
when handling dynamic_exporting_modules
. While the current logic in find_export_from_reexports
should prevent returning a Dynamic
variant with an empty vector (it returns NotFound
when dynamic_exporting_modules.is_empty()
is true), using unreachable!()
is risky in production code.
If there's any bug in the logic, race condition, or unexpected edge case that results in an empty vector being passed to the Dynamic
case, the application will panic rather than handling the error gracefully. The previous implementation handled this case by returning a NotFound
result, which was more defensive programming.
Recommendation
Replace the unreachable!()
with graceful error handling that matches the old behavior. Instead of [] => unreachable!(),
, use:
[] => Ok(FollowExportsResult {
module,
export_name: Some(export_name),
ty: FoundExportType::NotFound,
}.cell()),
This maintains the same behavior as the previous implementation while avoiding potential panics.
50c0d16
to
c5ae955
Compare
2b7eb7d
to
27af5f2
Compare
c5ae955
to
ac71779
Compare
27af5f2
to
ef31503
Compare
ef31503
to
67e7a37
Compare
ac71779
to
428616e
Compare
67e7a37
to
84bf1ec
Compare
84bf1ec
to
52d60b2
Compare
pass ModulePart correctly for FreeVarReference::EcmaScriptModule
code simplification
use EsmExport in FindExportFromReexportsResult