Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
9 changes: 9 additions & 0 deletions crates/next-api/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,9 @@ impl Project {
source_maps: self.next_config().client_source_maps(self.next_mode()),
no_mangling: self.no_mangling(),
scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
minify_before_chunking: self
.next_config()
.turbopack_minify_before_chunking(self.next_mode()),
nested_async_chunking: self
.next_config()
.turbo_nested_async_chunking(self.next_mode(), true),
Expand Down Expand Up @@ -1817,6 +1820,9 @@ impl Project {
source_maps: self.next_config().server_source_maps(),
no_mangling: self.no_mangling(),
scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
minify_before_chunking: self
.next_config()
.turbopack_minify_before_chunking(self.next_mode()),
nested_async_chunking: self
.next_config()
.turbo_nested_async_chunking(self.next_mode(), false),
Expand Down Expand Up @@ -1864,6 +1870,9 @@ impl Project {
turbo_source_maps: self.next_config().server_source_maps(),
no_mangling: self.no_mangling(),
scope_hoisting: self.next_config().turbo_scope_hoisting(self.next_mode()),
minify_before_chunking: self
.next_config()
.turbopack_minify_before_chunking(self.next_mode()),
nested_async_chunking: self
.next_config()
.turbo_nested_async_chunking(self.next_mode(), false),
Expand Down
3 changes: 3 additions & 0 deletions crates/next-core/src/next_client/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ pub struct ClientChunkingContextOptions {
pub source_maps: Vc<SourceMapsType>,
pub no_mangling: Vc<bool>,
pub scope_hoisting: Vc<bool>,
pub minify_before_chunking: Vc<bool>,
pub nested_async_chunking: Vc<bool>,
pub shared_runtime: Vc<bool>,
pub per_page_module_graph: Vc<bool>,
Expand Down Expand Up @@ -544,6 +545,7 @@ pub async fn get_client_chunking_context(
source_maps,
no_mangling,
scope_hoisting,
minify_before_chunking,
nested_async_chunking,
shared_runtime,
per_page_module_graph,
Expand Down Expand Up @@ -656,6 +658,7 @@ pub async fn get_client_chunking_context(
)
.chunk_content_hashing(ContentHashing::Direct { length: 13 })
.module_merging(*scope_hoisting.await?)
.minify_before_chunking(*minify_before_chunking.await?)
.shared_runtime(*shared_runtime.await?);
}

Expand Down
14 changes: 14 additions & 0 deletions crates/next-core/src/next_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1185,6 +1185,7 @@ pub struct ExperimentalConfig {
turbopack_input_source_maps: Option<bool>,
turbopack_module_fragments: Option<bool>,
turbopack_scope_hoisting: Option<bool>,
turbopack_minify_before_chunking: Option<bool>,
turbopack_shared_runtime: Option<bool>,
/// Custom URL prefix for Web Worker URLs (the entrypoint and the module
/// chunks loaded inside the worker) produced by
Expand Down Expand Up @@ -2482,6 +2483,19 @@ impl NextConfig {
}))
}

/// Whether to minify each module factory as it is generated rather than minifying each
/// finished chunk in one pass. Disabled in development, where nothing is minified anyway.
#[turbo_tasks::function]
pub async fn turbopack_minify_before_chunking(&self, mode: Vc<NextMode>) -> Result<Vc<bool>> {
Ok(Vc::cell(match *mode.await? {
NextMode::Development => false,
NextMode::Build => self
.experimental
.turbopack_minify_before_chunking
.unwrap_or(false),
}))
}

#[turbo_tasks::function]
pub fn turbopack_generate_component_chunks(&self) -> Vc<bool> {
Vc::cell(
Expand Down
9 changes: 7 additions & 2 deletions crates/next-core/src/next_edge/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ pub struct EdgeChunkingContextOptions {
pub turbo_source_maps: Vc<SourceMapsType>,
pub no_mangling: Vc<bool>,
pub scope_hoisting: Vc<bool>,
pub minify_before_chunking: Vc<bool>,
pub nested_async_chunking: Vc<bool>,
pub client_root: FileSystemPath,
pub client_static_folder_name: RcStr,
Expand Down Expand Up @@ -213,6 +214,7 @@ pub async fn get_edge_chunking_context_with_client_assets(
turbo_source_maps,
no_mangling,
scope_hoisting,
minify_before_chunking,
nested_async_chunking,
client_root,
client_static_folder_name,
Expand Down Expand Up @@ -285,7 +287,8 @@ pub async fn get_edge_chunking_context_with_client_assets(
..Default::default()
},
)
.module_merging(*scope_hoisting.await?);
.module_merging(*scope_hoisting.await?)
.minify_before_chunking(*minify_before_chunking.await?);
}

Ok(Vc::upcast(builder.build()))
Expand All @@ -309,6 +312,7 @@ pub async fn get_edge_chunking_context(
turbo_source_maps,
no_mangling,
scope_hoisting,
minify_before_chunking,
nested_async_chunking,
client_root,
client_static_folder_name,
Expand Down Expand Up @@ -398,7 +402,8 @@ pub async fn get_edge_chunking_context(
..Default::default()
},
)
.module_merging(*scope_hoisting.await?);
.module_merging(*scope_hoisting.await?)
.minify_before_chunking(*minify_before_chunking.await?);
}

Ok(Vc::upcast(builder.build()))
Expand Down
9 changes: 7 additions & 2 deletions crates/next-core/src/next_server/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,7 @@ pub struct ServerChunkingContextOptions {
pub source_maps: Vc<SourceMapsType>,
pub no_mangling: Vc<bool>,
pub scope_hoisting: Vc<bool>,
pub minify_before_chunking: Vc<bool>,
pub nested_async_chunking: Vc<bool>,
pub debug_ids: Vc<bool>,
pub client_root: FileSystemPath,
Expand Down Expand Up @@ -1055,6 +1056,7 @@ pub async fn get_server_chunking_context_with_client_assets(
source_maps,
no_mangling,
scope_hoisting,
minify_before_chunking,
nested_async_chunking,
debug_ids,
client_root,
Expand Down Expand Up @@ -1143,7 +1145,8 @@ pub async fn get_server_chunking_context_with_client_assets(
..Default::default()
},
)
.module_merging(*scope_hoisting.await?);
.module_merging(*scope_hoisting.await?)
.minify_before_chunking(*minify_before_chunking.await?);
}

Ok(builder.build())
Expand All @@ -1167,6 +1170,7 @@ pub async fn get_server_chunking_context(
source_maps,
no_mangling,
scope_hoisting,
minify_before_chunking,
nested_async_chunking,
debug_ids,
client_root,
Expand Down Expand Up @@ -1256,7 +1260,8 @@ pub async fn get_server_chunking_context(
..Default::default()
},
)
.module_merging(*scope_hoisting.await?);
.module_merging(*scope_hoisting.await?)
.minify_before_chunking(*minify_before_chunking.await?);
}

Ok(builder.build())
Expand Down
1 change: 1 addition & 0 deletions packages/next/src/server/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ export const experimentalSchema = {
turbopackRemoveUnusedImports: z.boolean().optional(),
turbopackRemoveUnusedExports: z.boolean().optional(),
turbopackScopeHoisting: z.boolean().optional(),
turbopackMinifyBeforeChunking: z.boolean().optional(),
turbopackSharedRuntime: z.boolean().optional(),
turbopackChunking: z
.object({
Expand Down
14 changes: 14 additions & 0 deletions packages/next/src/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,20 @@ export interface ExperimentalConfig {
*/
turbopackScopeHoisting?: boolean

/**
* Minify each module factory as it is generated, instead of minifying each finished chunk in
* one pass. Defaults to false. Always disabled in development mode, where nothing is minified.
*
* A chunk is a single top-level statement holding every module factory, so minifying the
* finished chunk cannot be split across cores and becomes a serial tail at the end of a build.
* Minifying per factory spreads that work across the task pool, and lets each factory's
* minified output be cached so an incremental build only re-minifies what changed.
*
* The trade-off is marginally larger output: optimizations that require seeing every factory
* at once are not performed, and each factory's mangled names are chosen independently.
*/
turbopackMinifyBeforeChunking?: boolean

/**
* Share the browser runtime across routes in a single `runtime.js` asset and inline the
* per-route chunk-group bootstrap into the HTML, dropping the per-route runtime. Defaults to
Expand Down
18 changes: 17 additions & 1 deletion turbopack/crates/turbopack-browser/src/chunking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,9 @@ pub enum CurrentChunkMethod {
DocumentCurrentScript,
}

/// Written in minified form, like the rest of the chunk scaffolding.
pub const CURRENT_CHUNK_METHOD_DOCUMENT_CURRENT_SCRIPT_EXPR: &str =
"typeof document === \"object\" ? document.currentScript : undefined";
"\"object\"==typeof document?document.currentScript:void 0";

pub struct BrowserChunkingContextBuilder {
chunking_context: BrowserChunkingContext,
Expand Down Expand Up @@ -142,6 +143,12 @@ impl BrowserChunkingContextBuilder {
self
}

/// EXPERIMENTAL. See [`ChunkingContext::minify_before_chunking`].
pub fn minify_before_chunking(mut self, minify_before_chunking: bool) -> Self {
self.chunking_context.minify_before_chunking = minify_before_chunking;
self
}

pub fn source_maps(mut self, source_maps: SourceMapsType) -> Self {
self.chunking_context.source_maps_type = source_maps;
self
Expand Down Expand Up @@ -374,6 +381,9 @@ pub struct BrowserChunkingContext {
runtime_type: RuntimeType,
/// Whether to minify resulting chunks
minify_type: MinifyType,
/// EXPERIMENTAL: minify each chunk item before the chunk is assembled, rather than minifying
/// the finished chunk. See [`ChunkingContext::minify_before_chunking`].
minify_before_chunking: bool,
/// Whether content hashing is enabled for chunk filenames.
chunk_content_hashing: Option<ContentHashing>,
/// Content hashing for asset filenames.
Expand Down Expand Up @@ -451,6 +461,7 @@ impl BrowserChunkingContext {
environment,
runtime_type,
minify_type: MinifyType::NoMinify,
minify_before_chunking: false,
chunk_content_hashing: None,
asset_content_hashing: ContentHashing::Direct { length: 13 },
source_maps_type: SourceMapsType::Full,
Expand Down Expand Up @@ -869,6 +880,11 @@ impl ChunkingContext for BrowserChunkingContext {
self.minify_type.cell()
}

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

#[turbo_tasks::function]
fn should_use_absolute_url_references(&self) -> Vc<bool> {
Vc::cell(self.should_use_absolute_url_references)
Expand Down
49 changes: 24 additions & 25 deletions turbopack/crates/turbopack-browser/src/ecmascript/content.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,22 @@ use turbo_tasks::{ResolvedVc, Vc, turbobail};
use turbo_tasks_fs::{File, FileContent};
use turbopack_core::{
asset::AssetContent,
chunk::{ChunkingContext, MinifyType, ModuleId},
chunk::{ChunkingContext, ModuleId},
code_builder::{Code, CodeBuilder},
output::OutputAsset,
source_map::{GenerateSourceMap, SourceMapAsset},
version::{MergeableVersionedContent, Version, VersionedContent, VersionedContentMerger},
};
use turbopack_ecmascript::{
chunk::{
EcmascriptChunkContent, EcmascriptChunkContentEntries, strict_chunk_wrapper,
strict_factory_mode, write_module_factories,
ChunkMinification, EcmascriptChunkContent, EcmascriptChunkContentEntries,
strict_chunk_prefix, strict_factory_mode, write_module_factories,
},
hmr::{
EcmascriptHmrChunkContent, merger::EcmascriptChunkContentMerger,
version::EcmascriptChunkVersion,
},
minify::minify,
utils::StringifyJs,
utils::{PropertyAccessJs, StringifyJs},
};

use super::chunk::EcmascriptBrowserChunk;
Expand Down Expand Up @@ -95,12 +94,16 @@ impl EcmascriptBrowserChunkContent {
.supports_arrow_functions()
.await?;
let content = this.content.await?;
let chunk_items = content.chunk_item_code_module_ids_and_paths().await?;
let strict_factory_mode = strict_factory_mode(&chunk_items, supports_arrow_functions);

let strict_chunk_wrapper =
strict_chunk_wrapper(strict_factory_mode, supports_arrow_functions);
if let Some((prefix, _)) = strict_chunk_wrapper {
let minification =
ChunkMinification::for_chunking_context(Vc::upcast(*this.chunking_context)).await?;
let chunk_items = content
.chunk_item_code_module_ids_and_paths(minification, source_maps)
.await?;
let strict_factory_mode = strict_factory_mode(
&chunk_items,
minification.factories_have_strict_directives(),
);
if let Some(prefix) = strict_chunk_prefix(strict_factory_mode) {
code += prefix;
}

Expand All @@ -111,33 +114,29 @@ impl EcmascriptBrowserChunkContent {
// When the runtime executes (see the `evaluate` module), it will pick up and
// register all pending chunks, and replace the list of pending chunks
// with itself so later chunks can register directly with it.
//
// The scaffolding is written in minified form, so it needs no minification pass of its
// own when the factories are minified individually.
let chunk_loading_global = this.chunking_context.chunk_loading_global().await?;
let global = PropertyAccessJs("globalThis", &chunk_loading_global);
write!(
code,
// `||=` would be better but we need to be es2020 compatible
//`x || (x = default)` is better than `x = x || default` simply because we avoid _writing_ the property in the common case.
r#"(globalThis[{chunk_loading_global}] || (globalThis[{chunk_loading_global}] = [])).push([{script_or_path},"#,
chunk_loading_global = StringifyJs(&chunk_loading_global),
"({global}||({global}=[])).push([{script_or_path}",
)?;
if !chunk_items.is_empty() {
code += ",";
}
write_module_factories(
&mut code,
&chunk_items,
strict_factory_mode,
supports_arrow_functions,
)?;
write!(code, "\n]);")?;

if let Some((_, suffix)) = strict_chunk_wrapper {
code += suffix;
}

let mut code = code.build();

if let MinifyType::Minify { mangle } = *this.chunking_context.minify_type().await? {
code = minify(code, source_maps, mangle)?;
}
code += "]);";

Ok(code.cell())
Ok(minification.finish_chunk(code.build(), source_maps)?.cell())
}

#[turbo_tasks::function]
Expand Down
15 changes: 15 additions & 0 deletions turbopack/crates/turbopack-core/src/chunk/chunking_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,21 @@ pub trait ChunkingContext {
MinifyType::NoMinify.cell()
}

/// EXPERIMENTAL: minify each chunk item on its own, before the chunk is assembled, instead of
/// minifying the finished chunk in one pass.
///
/// A chunk is a single top-level statement holding every module factory, so the whole-chunk
/// pass cannot be parallelised and becomes a serial tail at the end of the build. Minifying
/// per item spreads that work across the task pool and lets each item's minified output be
/// cached, so an incremental build only re-minifies what changed.
///
/// The trade-off is slightly larger output: no optimisation that requires seeing every factory
/// at once is performed, and each factory's mangled names are chosen independently.
#[turbo_tasks::function]
fn minify_before_chunking(self: Vc<Self>) -> Vc<bool> {
Vc::cell(false)
}

#[turbo_tasks::function]
fn should_use_absolute_url_references(self: Vc<Self>) -> Vc<bool> {
Vc::cell(false)
Expand Down
Loading
Loading