Skip to content

Turbopack: scope hoisting #79459

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

Draft
wants to merge 47 commits into
base: canary
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
447c445
RefData and ExportUsage in module graph edges
mischnic May 16, 2025
a14bb5b
Always set exports on reference
mischnic May 21, 2025
43fc8ba
__turbopack_esm_other__
mischnic May 5, 2025
2a30a47
Support additional ids in runtime
mischnic May 9, 2025
21db28d
MergeableModule
mischnic May 8, 2025
483f0d9
MergedModules Graph visitor
mischnic Apr 29, 2025
50fb787
MergedModules Graph visitor
mischnic May 8, 2025
c850310
Convert modules in chunk_group_content
mischnic May 9, 2025
691edf8
codegen
mischnic Apr 29, 2025
dcac9e6
MergedEcmascriptModule
mischnic May 8, 2025
61747e6
Emit additional id list
mischnic May 9, 2025
462d8dc
MergedModules per group bitmaps
mischnic May 12, 2025
7121aa1
MergedModules global bitmap
mischnic May 13, 2025
bdee413
Don't merge CJS exports
mischnic May 13, 2025
4a84d59
MergedModules
mischnic May 13, 2025
4658898
fixups
mischnic May 13, 2025
c5bc68c
fixup codegen
mischnic May 13, 2025
6f10a63
WIP MergedModules externals
mischnic May 13, 2025
5ee9b6f
fixup codegen
mischnic May 13, 2025
917b74d
fixup MergedEcmascriptModule
mischnic May 13, 2025
6171dda
fixup MergedModules infinite loop
mischnic May 14, 2025
49f07bf
fix namespace imports inside merged group
mischnic May 14, 2025
c678843
Implement __turbopack_esm_other__ differently
mischnic May 14, 2025
a6b9b6f
fixup codegen
mischnic May 14, 2025
694de60
Require missing exports
mischnic May 14, 2025
cb4829f
Don't expose all modules
mischnic May 21, 2025
fe46126
cleanup
mischnic May 15, 2025
7135b6e
Async modules aren't mergeable
mischnic May 15, 2025
8850af1
Revert "undo merged_modules"
mischnic May 21, 2025
aa629ec
Expose namespace imported modules
mischnic May 16, 2025
b3c22de
Support ImportedNamespace reexports
mischnic May 16, 2025
df6a32f
Fix reexports from unmerged modules
mischnic May 16, 2025
ed67fa4
Fix reexport-star inside of merged group
mischnic May 16, 2025
6a88895
Fix local renaming when they are exports
mischnic May 16, 2025
588206d
Fix local renaming when they are exports
mischnic May 19, 2025
dbaad7d
Refactor: MergeableModule.is_mergable
mischnic May 19, 2025
0bec59d
Correct order of merged and unmerged references
mischnic May 19, 2025
c1454a3
Fix list traversal with cycles
mischnic May 20, 2025
3895725
Ensure correct ordering
mischnic May 21, 2025
0f450bb
Multiple entries in a merged group
mischnic May 21, 2025
ed34f3b
Correctly compute merged group entries
mischnic May 21, 2025
ad83509
Expose entry modules (assuming they are required)
mischnic May 21, 2025
2659885
is_module_merging_enabled on context, not in dev
mischnic May 21, 2025
e109a45
Turbopack: remove dead code in runtime
mischnic May 23, 2025
b5b56ff
Fix module exposing due to splitting
mischnic May 23, 2025
6a59879
Proper stubs for snapshots
mischnic May 23, 2025
fabac28
Update snapshots
mischnic May 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 19 additions & 17 deletions crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,23 +470,25 @@ pub async fn get_client_chunking_context(
if next_mode.is_development() {
builder = builder.hot_module_replacement().use_file_source_map_uris();
} else {
builder = builder.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 50_000,
max_chunk_count_per_group: 40,
max_merge_chunk_size: 200_000,
..Default::default()
},
);
builder = builder.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder.use_content_hashing(ContentHashing::Direct { length: 16 })
builder = builder
.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 50_000,
max_chunk_count_per_group: 40,
max_merge_chunk_size: 200_000,
..Default::default()
},
)
.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.use_content_hashing(ContentHashing::Direct { length: 16 })
.module_merging(true);
}

Ok(Vc::upcast(builder.build()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ impl Visit<VisitClientReferenceNode> for VisitClientReference {

let referenced_modules = referenced_modules
.iter()
.flat_map(|(chunking_type, modules)| match chunking_type {
.flat_map(|(chunking_type, _, modules)| match chunking_type {
ChunkingType::Traced => None,
_ => Some(modules.iter()),
})
Expand Down
3 changes: 3 additions & 0 deletions crates/next-core/src/next_dynamic/dynamic_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use turbopack_core::{
module::Module,
module_graph::ModuleGraph,
reference::{ModuleReferences, SingleChunkableModuleReference},
resolve::ExportUsage,
};
use turbopack_ecmascript::{
chunk::{
Expand Down Expand Up @@ -60,6 +61,7 @@ impl Module for NextDynamicEntryModule {
SingleChunkableModuleReference::new(
Vc::upcast(*self.module),
dynamic_ref_description(),
ExportUsage::all(),
)
.to_resolved()
.await?,
Expand Down Expand Up @@ -102,6 +104,7 @@ impl EcmascriptChunkPlaceable for NextDynamicEntryModule {
SingleChunkableModuleReference::new(
Vc::upcast(*self.module),
dynamic_ref_description(),
ExportUsage::all(),
)
.to_resolved()
.await?,
Expand Down
60 changes: 32 additions & 28 deletions crates/next-core/src/next_edge/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,20 +255,22 @@ pub async fn get_edge_chunking_context_with_client_assets(
.module_id_strategy(module_id_strategy);

if !next_mode.is_development() {
builder = builder.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
..Default::default()
},
);
builder = builder.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder
.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
..Default::default()
},
)
.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.module_merging(true);
}

Ok(Vc::upcast(builder.build()))
Expand Down Expand Up @@ -318,20 +320,22 @@ pub async fn get_edge_chunking_context(
.module_id_strategy(module_id_strategy);

if !next_mode.is_development() {
builder = builder.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
..Default::default()
},
);
builder = builder.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder
.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
..Default::default()
},
)
.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.module_merging(true);
}

Ok(Vc::upcast(builder.build()))
Expand Down
6 changes: 4 additions & 2 deletions crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1027,7 +1027,8 @@ pub async fn get_server_chunking_context_with_client_assets(
SourceMapsType::None
})
.module_id_strategy(module_id_strategy)
.file_tracing(next_mode.is_production());
.file_tracing(next_mode.is_production())
.module_merging(next_mode.is_production());

if next_mode.is_development() {
builder = builder.use_file_source_map_uris();
Expand Down Expand Up @@ -1092,7 +1093,8 @@ pub async fn get_server_chunking_context(
SourceMapsType::None
})
.module_id_strategy(module_id_strategy)
.file_tracing(next_mode.is_production());
.file_tracing(next_mode.is_production())
.module_merging(next_mode.is_production());

if next_mode.is_development() {
builder = builder.use_file_source_map_uris()
Expand Down
13 changes: 13 additions & 0 deletions turbopack/crates/turbopack-browser/src/chunking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ impl BrowserChunkingContextBuilder {
self
}

pub fn module_merging(mut self, enable_module_merging: bool) -> Self {
self.chunking_context.enable_module_merging = enable_module_merging;
self
}

pub fn asset_base_path(mut self, asset_base_path: ResolvedVc<Option<RcStr>>) -> Self {
self.chunking_context.asset_base_path = asset_base_path;
self
Expand Down Expand Up @@ -202,6 +207,8 @@ pub struct BrowserChunkingContext {
enable_hot_module_replacement: bool,
/// Enable tracing for this chunking
enable_tracing: bool,
/// Enable module merging
enable_module_merging: bool,
/// The environment chunks will be evaluated in.
environment: ResolvedVc<Environment>,
/// The kind of runtime to include in the output.
Expand Down Expand Up @@ -248,6 +255,7 @@ impl BrowserChunkingContext {
asset_base_path: ResolvedVc::cell(None),
enable_hot_module_replacement: false,
enable_tracing: false,
enable_module_merging: false,
environment,
runtime_type,
minify_type: MinifyType::NoMinify,
Expand Down Expand Up @@ -510,6 +518,11 @@ impl ChunkingContext for BrowserChunkingContext {
Vc::cell(self.enable_tracing)
}

#[turbo_tasks::function]
fn is_module_merging_enabled(&self) -> Vc<bool> {
Vc::cell(self.enable_module_merging)
}

#[turbo_tasks::function]
pub fn minify_type(&self) -> Vc<MinifyType> {
self.minify_type.cell()
Expand Down
70 changes: 37 additions & 33 deletions turbopack/crates/turbopack-cli/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,23 +343,25 @@ async fn build_internal(
match *node_env.await? {
NodeEnv::Development => {}
NodeEnv::Production => {
builder = builder.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 50_000,
max_chunk_count_per_group: 40,
max_merge_chunk_size: 200_000,
..Default::default()
},
);
builder = builder.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder.use_content_hashing(ContentHashing::Direct { length: 16 })
builder = builder
.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 50_000,
max_chunk_count_per_group: 40,
max_merge_chunk_size: 200_000,
..Default::default()
},
)
.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.use_content_hashing(ContentHashing::Direct { length: 16 })
.module_merging(true);
}
}

Expand Down Expand Up @@ -387,22 +389,24 @@ async fn build_internal(
match *node_env.await? {
NodeEnv::Development => {}
NodeEnv::Production => {
builder = builder.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
max_chunk_count_per_group: 100,
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
);
builder = builder
.chunking_config(
Vc::<EcmascriptChunkType>::default().to_resolved().await?,
ChunkingConfig {
min_chunk_size: 20_000,
max_chunk_count_per_group: 100,
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.chunking_config(
Vc::<CssChunkType>::default().to_resolved().await?,
ChunkingConfig {
max_merge_chunk_size: 100_000,
..Default::default()
},
)
.module_merging(true);
}
}

Expand Down
68 changes: 66 additions & 2 deletions turbopack/crates/turbopack-core/src/chunk/chunk_group.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::HashSet;
use std::{collections::HashSet, sync::atomic::AtomicBool};

use anyhow::{Context, Result};
use rustc_hash::FxHashMap;
use turbo_tasks::{FxIndexSet, ResolvedVc, TryJoinIterExt, Value, Vc};
use turbo_tasks::{FxIndexSet, ResolvedVc, TryFlatJoinIterExt, TryJoinIterExt, Value, Vc};

use super::{
Chunk, ChunkGroupContent, ChunkItem, ChunkItemWithAsyncModuleInfo, ChunkingContext,
Expand Down Expand Up @@ -45,6 +45,7 @@ pub async fn make_chunk_group(
ChunkLoading::Edge
);
let should_trace = *chunking_context.is_tracing_enabled().await?;
let should_merge_modules = *chunking_context.is_module_merging_enabled().await?;
let batching_config = chunking_context.batching_config();

let ChunkGroupContent {
Expand All @@ -58,6 +59,7 @@ pub async fn make_chunk_group(
availability_info,
can_split_async,
should_trace,
should_merge_modules,
batching_config,
)
.await?;
Expand Down Expand Up @@ -189,6 +191,7 @@ pub async fn chunk_group_content(
availability_info: AvailabilityInfo,
can_split_async: bool,
should_trace: bool,
should_merge_modules: bool,
batching_config: Vc<BatchingConfig>,
) -> Result<ChunkGroupContent> {
let module_batches_graph = module_graph.module_batches(batching_config).await?;
Expand Down Expand Up @@ -315,6 +318,67 @@ pub async fn chunk_group_content(
},
)?;

if should_merge_modules {
let merged_modules = module_graph.merged_modules().await?;
state.chunkable_items = state
.chunkable_items
.into_iter()
.map(async |chunkable_module| match chunkable_module {
ChunkableModuleOrBatch::Module(module) => {
if !merged_modules.should_create_chunk_item_for(ResolvedVc::upcast(module)) {
return Ok(vec![]);
}

Ok(vec![ChunkableModuleOrBatch::Module(
merged_modules
.should_replace_module(ResolvedVc::upcast(module))
.unwrap_or(module),
)])
}
ChunkableModuleOrBatch::Batch(batch) => {
let batch_ref = batch.await?;
let modules = &batch_ref.modules;

let modified = AtomicBool::new(false);
let modules = modules
.iter()
.filter(|module| {
if merged_modules
.should_create_chunk_item_for(ResolvedVc::upcast(**module))
{
true
} else {
modified.store(true, std::sync::atomic::Ordering::Release);
false
}
})
.map(|&module| {
if let Some(module) =
merged_modules.should_replace_module(ResolvedVc::upcast(module))
{
modified.store(true, std::sync::atomic::Ordering::Release);
module
} else {
module
}
})
.map(ChunkableModuleOrBatch::Module)
.collect::<Vec<_>>();

if modified.load(std::sync::atomic::Ordering::Acquire) {
Ok(modules)
} else {
Ok(vec![ChunkableModuleOrBatch::Batch(batch)])
}
}
ChunkableModuleOrBatch::None(i) => Ok(vec![ChunkableModuleOrBatch::None(i)]),
})
.try_flat_join()
.await?
.into_iter()
.collect();
}

let mut batch_groups = FxIndexSet::default();
for &module in &state.chunkable_items {
if let Some(batch_group) = module_batches_graph.get_batch_group(&module.into()) {
Expand Down
Loading
Loading