Skip to content

Commit 8c082c1

Browse files
authored
feat(pack): support optimization.removeUnusedExports & codegen modules without module graph (#2239)
* feat(pack): support optimization.removeUnusedExports & codegen modules without module graph * fix: clippy
1 parent 26043d2 commit 8c082c1

File tree

34 files changed

+286
-182
lines changed

34 files changed

+286
-182
lines changed

crates/pack-api/src/library.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pack_core::{
44
get_client_module_options_context, get_client_resolve_options_context,
55
get_client_runtime_entries,
66
},
7-
library::contexts::get_library_chunking_context,
7+
library::contexts::{LibraryChunkingContextOptions, get_library_chunking_context},
88
util::convert_to_project_relative,
99
};
1010
use qstring::QString;
@@ -238,17 +238,21 @@ impl LibraryEndpoint {
238238
runtime_export: Vc<Vec<RcStr>>,
239239
) -> Result<Vc<Box<dyn ChunkingContext>>> {
240240
let project = self.project();
241+
241242
Ok(get_library_chunking_context(
242-
project.project_root().owned().await?,
243-
project.dist_root().owned().await?,
244-
rcstr!("/ROOT"),
245-
project.client_compile_time_info().environment(),
246-
project.mode(),
247-
project.module_ids(),
248-
project.no_mangling(),
249-
runtime_root,
250-
runtime_export,
251-
project.config(),
243+
LibraryChunkingContextOptions {
244+
mode: project.mode(),
245+
root_path: project.project_root().owned().await?,
246+
output_root: project.dist_root().owned().await?,
247+
output_root_to_root_path: rcstr!("/ROOT"),
248+
environment: project.client_compile_time_info().environment(),
249+
module_id_strategy: project.module_ids(),
250+
no_mangling: project.no_mangling(),
251+
runtime_root,
252+
runtime_export,
253+
config: project.config(),
254+
export_usage: project.export_usage(),
255+
},
252256
))
253257
}
254258

crates/pack-api/src/project.rs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use anyhow::{Context, Result, bail};
22
use pack_core::{
33
all_assets_from_entries,
4-
client::context::{get_client_chunking_context, get_client_compile_time_info},
4+
client::context::{
5+
ClientChunkingContextOptions, get_client_chunking_context, get_client_compile_time_info,
6+
},
57
config::{Config, ModuleIds as ModuleIdStrategyConfig},
68
emit_assets,
79
mode::Mode,
@@ -46,6 +48,7 @@ use turbopack_core::{
4648
module_graph::{
4749
GraphEntries, ModuleGraph, SingleModuleGraph, VisitedModules,
4850
chunk_group_info::ChunkGroupEntry,
51+
export_usage::{OptionExportUsageInfo, compute_export_usage_info},
4952
},
5053
output::{OutputAsset, OutputAssets},
5154
raw_output::RawOutput,
@@ -892,17 +895,18 @@ impl Project {
892895

893896
#[turbo_tasks::function]
894897
pub async fn client_chunking_context(self: Vc<Self>) -> Result<Vc<Box<dyn ChunkingContext>>> {
895-
Ok(get_client_chunking_context(
896-
self.project_root().owned().await?,
897-
self.client_root().owned().await?,
898-
rcstr!("/ROOT"),
899-
Some(self.dist_dir().owned().await?),
900-
self.client_compile_time_info().environment(),
901-
self.mode(),
902-
self.module_ids(),
903-
self.no_mangling(),
904-
self.config(),
905-
))
898+
Ok(get_client_chunking_context(ClientChunkingContextOptions {
899+
mode: self.mode(),
900+
root_path: self.project_root().owned().await?,
901+
output_root: self.client_root().owned().await?,
902+
output_root_to_root_path: rcstr!("/ROOT"),
903+
chunk_base_path: Some(self.dist_dir().owned().await?),
904+
environment: self.client_compile_time_info().environment(),
905+
module_id_strategy: self.module_ids(),
906+
no_mangling: self.no_mangling(),
907+
config: self.config(),
908+
export_usage: self.export_usage(),
909+
}))
906910
}
907911

908912
#[turbo_tasks::function]
@@ -1161,6 +1165,21 @@ impl Project {
11611165
}
11621166
}
11631167

1168+
/// Computed the used exports for each module.
1169+
#[turbo_tasks::function]
1170+
pub async fn export_usage(self: Vc<Self>) -> Result<Vc<OptionExportUsageInfo>> {
1171+
if *self.config().remove_unused_exports(self.mode()).await? {
1172+
let module_graphs = self.whole_app_module_graphs().await?;
1173+
Ok(Vc::cell(Some(
1174+
compute_export_usage_info(module_graphs.full)
1175+
.resolve_strongly_consistent()
1176+
.await?,
1177+
)))
1178+
} else {
1179+
Ok(Vc::cell(None))
1180+
}
1181+
}
1182+
11641183
#[turbo_tasks::function]
11651184
pub async fn copy_output_assets(self: Vc<Self>) -> Result<Vc<OutputAssets>> {
11661185
let project_path = self.project_path().owned().await?;

crates/pack-core/src/client/context.rs

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::{collections::BTreeSet, str::FromStr};
22

33
use anyhow::Result;
4+
use serde::{Deserialize, Serialize};
45
use turbo_rcstr::{RcStr, rcstr};
5-
use turbo_tasks::{FxIndexMap, ResolvedVc, TryJoinIterExt, ValueToString, Vc};
6+
use turbo_tasks::{
7+
FxIndexMap, ResolvedVc, TaskInput, TryJoinIterExt, ValueToString, Vc, trace::TraceRawVcs,
8+
};
69
use turbo_tasks_env::EnvMap;
710
use turbo_tasks_fs::FileSystemPath;
811
use turbopack::{
@@ -26,6 +29,7 @@ use turbopack_core::{
2629
environment::{BrowserEnvironment, Environment, ExecutionEnvironment},
2730
file_source::FileSource,
2831
free_var_references,
32+
module_graph::export_usage::OptionExportUsageInfo,
2933
};
3034
use turbopack_ecmascript::{TypeofWindow, chunk::EcmascriptChunkType};
3135
use turbopack_node::{
@@ -470,18 +474,37 @@ pub async fn get_client_resolve_options_context(
470474
.cell())
471475
}
472476

477+
#[derive(Clone, Debug, PartialEq, Eq, Hash, TaskInput, TraceRawVcs, Serialize, Deserialize)]
478+
pub struct ClientChunkingContextOptions {
479+
pub mode: Vc<Mode>,
480+
pub root_path: FileSystemPath,
481+
pub output_root: FileSystemPath,
482+
pub output_root_to_root_path: RcStr,
483+
pub chunk_base_path: Option<RcStr>,
484+
pub environment: Vc<Environment>,
485+
pub module_id_strategy: Vc<Box<dyn ModuleIdStrategy>>,
486+
pub no_mangling: Vc<bool>,
487+
pub config: Vc<Config>,
488+
pub export_usage: Vc<OptionExportUsageInfo>,
489+
}
490+
473491
#[turbo_tasks::function]
474492
pub async fn get_client_chunking_context(
475-
root_path: FileSystemPath,
476-
output_root: FileSystemPath,
477-
output_root_to_root_path: RcStr,
478-
chunk_base_path: Option<RcStr>,
479-
environment: ResolvedVc<Environment>,
480-
mode: Vc<Mode>,
481-
module_id_strategy: ResolvedVc<Box<dyn ModuleIdStrategy>>,
482-
no_mangling: Vc<bool>,
483-
config: ResolvedVc<Config>,
493+
options: ClientChunkingContextOptions,
484494
) -> Result<Vc<Box<dyn ChunkingContext>>> {
495+
let ClientChunkingContextOptions {
496+
mode,
497+
root_path,
498+
output_root,
499+
output_root_to_root_path,
500+
chunk_base_path,
501+
environment,
502+
module_id_strategy,
503+
no_mangling,
504+
config,
505+
export_usage,
506+
} = options;
507+
485508
let minify = config.minify(mode);
486509
let concatenate_modules = config.concatenate_modules(mode);
487510
let mode = mode.await?;
@@ -509,7 +532,7 @@ pub async fn get_client_chunking_context(
509532
output_root.clone(),
510533
output_root.clone(),
511534
output_root,
512-
environment,
535+
environment.to_resolved().await?,
513536
runtime_type,
514537
)
515538
.minify_type(if mode.is_production() && *minify.await? {
@@ -526,10 +549,13 @@ pub async fn get_client_chunking_context(
526549
})
527550
.chunk_base_path(chunk_base_path)
528551
.current_chunk_method(CurrentChunkMethod::DocumentCurrentScript)
529-
.module_id_strategy(module_id_strategy);
552+
.export_usage(*export_usage.await?)
553+
.module_id_strategy(module_id_strategy.to_resolved().await?);
530554

531555
let output = config.output().await?;
532556

557+
builder = builder.asset_base_path(output.asset_prefix.clone());
558+
533559
if !mode.is_development() {
534560
if let Some(filename) = &output.filename {
535561
builder = builder.filename(filename.clone());

crates/pack-core/src/config.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ pub struct OptimizationConfig {
306306
/// improving caching.
307307
#[serde(default)]
308308
pub concatenate_modules: Option<bool>,
309+
/// Defaults to false in development mode, true in production mode.
310+
pub remove_unused_exports: Option<bool>,
309311
}
310312

311313
#[derive(
@@ -328,6 +330,8 @@ pub struct OutputConfig {
328330
pub r#type: Option<OutputType>,
329331
pub clean: Option<bool>,
330332
pub copy: Option<Vec<CopyItem>>,
333+
/// URL prefix that will be prepended to all static asset URLs when loading them.
334+
pub asset_prefix: Option<RcStr>,
331335
}
332336

333337
#[derive(
@@ -1342,6 +1346,17 @@ impl Config {
13421346
.cell()
13431347
}
13441348

1349+
#[turbo_tasks::function]
1350+
pub async fn remove_unused_exports(&self, mode: Vc<Mode>) -> Result<Vc<bool>> {
1351+
let is_prod = matches!(*mode.await?, Mode::Production);
1352+
let remove_unused_exports = self
1353+
.optimization
1354+
.as_ref()
1355+
.and_then(|op| op.remove_unused_exports)
1356+
.unwrap_or(is_prod);
1357+
Ok(Vc::cell(remove_unused_exports))
1358+
}
1359+
13451360
#[turbo_tasks::function]
13461361
pub fn module_ids(&self) -> Vc<OptionModuleIds> {
13471362
let Some(module_ids) = self.optimization.as_ref().and_then(|t| t.module_ids) else {

crates/pack-core/src/library/chunking_context.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ impl LibraryChunkingContextBuilder {
134134
self
135135
}
136136

137+
pub fn asset_base_path(mut self, asset_base_path: Option<RcStr>) -> Self {
138+
self.chunking_context.asset_base_path = asset_base_path;
139+
self
140+
}
141+
137142
pub fn build(self) -> Vc<LibraryChunkingContext> {
138143
LibraryChunkingContext::cell(self.chunking_context)
139144
}
@@ -161,6 +166,8 @@ pub struct LibraryChunkingContext {
161166
output_root: FileSystemPath,
162167
/// The relative path from the output_root to the root_path.
163168
output_root_to_root_path: RcStr,
169+
/// URL prefix that will be prepended to all static asset URLs when loading them.
170+
asset_base_path: Option<RcStr>,
164171
/// The environment chunks will be evaluated in.
165172
environment: ResolvedVc<Environment>,
166173
/// Enable module merging
@@ -198,6 +205,7 @@ impl LibraryChunkingContext {
198205
output_root,
199206
output_root_to_root_path,
200207
should_use_file_source_map_uris: false,
208+
asset_base_path: None,
201209
environment,
202210
runtime_type,
203211
minify_type: MinifyType::NoMinify,
@@ -410,7 +418,14 @@ impl ChunkingContext for LibraryChunkingContext {
410418
async fn asset_url(&self, ident: FileSystemPath) -> Result<Vc<RcStr>> {
411419
let asset_path = ident.to_string();
412420

413-
Ok(Vc::cell(asset_path.into()))
421+
Ok(Vc::cell(
422+
format!(
423+
"{}{}",
424+
self.asset_base_path.as_deref().unwrap_or(""),
425+
asset_path
426+
)
427+
.into(),
428+
))
414429
}
415430

416431
#[turbo_tasks::function]

crates/pack-core/src/library/contexts.rs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,53 @@
11
use anyhow::Result;
2+
use serde::{Deserialize, Serialize};
23
use turbo_rcstr::RcStr;
3-
use turbo_tasks::{ResolvedVc, Vc};
4+
use turbo_tasks::{TaskInput, Vc, trace::TraceRawVcs};
45
use turbo_tasks_fs::FileSystemPath;
56
use turbopack_core::{
67
chunk::{
78
ChunkingContext, MangleType, MinifyType, SourceMapsType,
89
module_id_strategies::ModuleIdStrategy,
910
},
1011
environment::Environment,
12+
module_graph::export_usage::OptionExportUsageInfo,
1113
};
1214

1315
use crate::{config::Config, mode::Mode};
1416

1517
use super::LibraryChunkingContext;
1618

19+
#[derive(Clone, Debug, PartialEq, Eq, Hash, TaskInput, TraceRawVcs, Serialize, Deserialize)]
20+
pub struct LibraryChunkingContextOptions {
21+
pub mode: Vc<Mode>,
22+
pub root_path: FileSystemPath,
23+
pub output_root: FileSystemPath,
24+
pub output_root_to_root_path: RcStr,
25+
pub environment: Vc<Environment>,
26+
pub module_id_strategy: Vc<Box<dyn ModuleIdStrategy>>,
27+
pub no_mangling: Vc<bool>,
28+
pub runtime_root: Vc<Option<RcStr>>,
29+
pub runtime_export: Vc<Vec<RcStr>>,
30+
pub config: Vc<Config>,
31+
pub export_usage: Vc<OptionExportUsageInfo>,
32+
}
33+
1734
#[turbo_tasks::function]
1835
pub async fn get_library_chunking_context(
19-
root_path: FileSystemPath,
20-
output_root: FileSystemPath,
21-
output_root_to_root_path: RcStr,
22-
environment: ResolvedVc<Environment>,
23-
mode: Vc<Mode>,
24-
module_id_strategy: ResolvedVc<Box<dyn ModuleIdStrategy>>,
25-
no_mangling: Vc<bool>,
26-
runtime_root: Vc<Option<RcStr>>,
27-
runtime_export: Vc<Vec<RcStr>>,
28-
config: ResolvedVc<Config>,
36+
options: LibraryChunkingContextOptions,
2937
) -> Result<Vc<Box<dyn ChunkingContext>>> {
38+
let LibraryChunkingContextOptions {
39+
mode,
40+
root_path,
41+
output_root,
42+
output_root_to_root_path,
43+
environment,
44+
module_id_strategy,
45+
no_mangling,
46+
runtime_root,
47+
runtime_export,
48+
config,
49+
export_usage,
50+
} = options;
3051
let minify = config.minify(mode);
3152
let concatenate_modules = config.concatenate_modules(mode);
3253
let mode = mode.await?;
@@ -47,11 +68,13 @@ pub async fn get_library_chunking_context(
4768
}
4869
};
4970

71+
let output = config.output().await?;
72+
5073
let mut builder = LibraryChunkingContext::builder(
5174
root_path,
5275
output_root,
5376
output_root_to_root_path,
54-
environment,
77+
environment.to_resolved().await?,
5578
runtime_type,
5679
(*runtime_root.await?).clone(),
5780
(*runtime_export.await?).clone(),
@@ -68,10 +91,12 @@ pub async fn get_library_chunking_context(
6891
} else {
6992
SourceMapsType::None
7093
})
71-
.module_id_strategy(module_id_strategy);
94+
.asset_base_path(output.asset_prefix.clone())
95+
.module_id_strategy(module_id_strategy.to_resolved().await?)
96+
.export_usage(*export_usage.await?);
7297

7398
if !mode.is_development()
74-
&& let Some(filename) = &config.output().await?.filename
99+
&& let Some(filename) = &output.filename
75100
{
76101
builder = builder.filename(filename.clone());
77102
}

0 commit comments

Comments
 (0)