Skip to content

Commit 039dce4

Browse files
authored
Lazy DevHtmlAsset chunk generation (vercel/turborepo#4679)
### Description This fixes a perf regression introduced in the chunking refactor, where we would be eagerly generating all assets from the web entry and fallback sources. ### Testing Instructions Benchmarks [Next.js PR](https://github.com/vercel/next.js/compare/alexkirsz/web-944-perf-regression?expand=1) link WEB-944
1 parent 42255cb commit 039dce4

File tree

2 files changed

+72
-29
lines changed

2 files changed

+72
-29
lines changed

crates/turbopack-cli/src/dev/web_entry_source.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,20 +254,20 @@ pub async fn create_web_entry_source(
254254
.try_join()
255255
.await?;
256256

257-
let chunk_groups: Vec<_> = entries
257+
let entries: Vec<_> = entries
258258
.into_iter()
259259
.flatten()
260260
.map(|module| async move {
261261
if let Some(ecmascript) = EcmascriptModuleAssetVc::resolve_from(module).await? {
262-
let chunk_group = chunking_context.evaluated_chunk_group(
263-
ecmascript.as_root_chunk(chunking_context),
264-
runtime_entries.with_entry(ecmascript.into()),
265-
);
266-
Ok(chunk_group)
262+
Ok((
263+
ecmascript.into(),
264+
chunking_context,
265+
Some(runtime_entries.with_entry(ecmascript.into())),
266+
))
267267
} else if let Some(chunkable) = ChunkableAssetVc::resolve_from(module).await? {
268268
// TODO this is missing runtime code, so it's probably broken and we should also
269269
// add an ecmascript chunk with the runtime code
270-
Ok(chunking_context.chunk_group(chunkable.as_root_chunk(chunking_context)))
270+
Ok((chunkable.into(), chunking_context, None))
271271
} else {
272272
// TODO convert into a serve-able asset
273273
Err(anyhow!(
@@ -279,7 +279,7 @@ pub async fn create_web_entry_source(
279279
.try_join()
280280
.await?;
281281

282-
let entry_asset = DevHtmlAssetVc::new(server_root.join("index.html"), chunk_groups).into();
282+
let entry_asset = DevHtmlAssetVc::new(server_root.join("index.html"), entries).into();
283283

284284
let graph = if eager_compile {
285285
AssetGraphContentSourceVc::new_eager(server_root, entry_asset)

crates/turbopack-dev-server/src/html.rs

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use anyhow::{anyhow, Result};
22
use mime_guess::mime::TEXT_HTML_UTF_8;
3-
use turbo_tasks::primitives::StringVc;
3+
use turbo_tasks::{primitives::StringVc, TryJoinIterExt};
44
use turbo_tasks_fs::{File, FileSystemPathVc};
55
use turbo_tasks_hash::{encode_hex, Xxh3Hash64Hasher};
66
use turbopack_core::{
77
asset::{Asset, AssetContentVc, AssetVc, AssetsVc},
8+
chunk::{
9+
ChunkableAsset, ChunkableAssetVc, ChunkingContext, ChunkingContextVc, EvaluatableAssetsVc,
10+
},
811
ident::AssetIdentVc,
912
reference::{AssetReferencesVc, SingleAssetReferenceVc},
1013
version::{Version, VersionVc, VersionedContent, VersionedContentVc},
@@ -17,7 +20,13 @@ use turbopack_core::{
1720
#[derive(Clone)]
1821
pub struct DevHtmlAsset {
1922
path: FileSystemPathVc,
20-
chunk_groups: Vec<AssetsVc>,
23+
// TODO(WEB-945) This should become a `Vec<DevHtmlEntry>` once we have a
24+
// `turbo_tasks::input` attribute macro/`Input` derive macro.
25+
entries: Vec<(
26+
ChunkableAssetVc,
27+
ChunkingContextVc,
28+
Option<EvaluatableAssetsVc>,
29+
)>,
2130
body: Option<String>,
2231
}
2332

@@ -39,16 +48,12 @@ impl Asset for DevHtmlAsset {
3948
}
4049

4150
#[turbo_tasks::function]
42-
async fn references(&self) -> Result<AssetReferencesVc> {
51+
async fn references(self_vc: DevHtmlAssetVc) -> Result<AssetReferencesVc> {
4352
let mut references = Vec::new();
44-
for chunk_group in &self.chunk_groups {
45-
let chunks = chunk_group.await?;
46-
for chunk in chunks.iter() {
47-
references.push(
48-
SingleAssetReferenceVc::new(*chunk, dev_html_chunk_reference_description())
49-
.into(),
50-
);
51-
}
53+
for chunk in &*self_vc.chunks().await? {
54+
references.push(
55+
SingleAssetReferenceVc::new(*chunk, dev_html_chunk_reference_description()).into(),
56+
);
5257
}
5358
Ok(AssetReferencesVc::cell(references))
5459
}
@@ -61,10 +66,17 @@ impl Asset for DevHtmlAsset {
6166

6267
impl DevHtmlAssetVc {
6368
/// Create a new dev HTML asset.
64-
pub fn new(path: FileSystemPathVc, chunk_groups: Vec<AssetsVc>) -> Self {
69+
pub fn new(
70+
path: FileSystemPathVc,
71+
entries: Vec<(
72+
ChunkableAssetVc,
73+
ChunkingContextVc,
74+
Option<EvaluatableAssetsVc>,
75+
)>,
76+
) -> Self {
6577
DevHtmlAsset {
6678
path,
67-
chunk_groups,
79+
entries,
6880
body: None,
6981
}
7082
.cell()
@@ -73,12 +85,16 @@ impl DevHtmlAssetVc {
7385
/// Create a new dev HTML asset.
7486
pub fn new_with_body(
7587
path: FileSystemPathVc,
76-
chunk_groups: Vec<AssetsVc>,
88+
entries: Vec<(
89+
ChunkableAssetVc,
90+
ChunkingContextVc,
91+
Option<EvaluatableAssetsVc>,
92+
)>,
7793
body: String,
7894
) -> Self {
7995
DevHtmlAsset {
8096
path,
81-
chunk_groups,
97+
entries,
8298
body: Some(body),
8399
}
84100
.cell()
@@ -110,17 +126,44 @@ impl DevHtmlAssetVc {
110126
let context_path = this.path.parent().await?;
111127

112128
let mut chunk_paths = vec![];
113-
for chunk_group in &this.chunk_groups {
114-
for chunk in chunk_group.await?.iter() {
115-
let chunk_path = &*chunk.ident().path().await?;
116-
if let Some(relative_path) = context_path.get_path_to(chunk_path) {
117-
chunk_paths.push(format!("/{relative_path}"));
118-
}
129+
for chunk in &*self.chunks().await? {
130+
let chunk_path = &*chunk.ident().path().await?;
131+
if let Some(relative_path) = context_path.get_path_to(chunk_path) {
132+
chunk_paths.push(format!("/{relative_path}"));
119133
}
120134
}
121135

122136
Ok(DevHtmlAssetContentVc::new(chunk_paths, this.body.clone()))
123137
}
138+
139+
#[turbo_tasks::function]
140+
async fn chunks(self) -> Result<AssetsVc> {
141+
let this = self.await?;
142+
143+
let all_assets = this
144+
.entries
145+
.iter()
146+
.map(|entry| async move {
147+
let (chunkable_asset, chunking_context, runtime_entries) = entry;
148+
149+
let chunk = chunkable_asset.as_root_chunk(*chunking_context);
150+
let assets = if let Some(runtime_entries) = runtime_entries {
151+
chunking_context.evaluated_chunk_group(chunk, *runtime_entries)
152+
} else {
153+
chunking_context.chunk_group(chunk)
154+
};
155+
156+
Ok(assets.await?)
157+
})
158+
.try_join()
159+
.await?
160+
.iter()
161+
.flatten()
162+
.copied()
163+
.collect();
164+
165+
Ok(AssetsVc::cell(all_assets))
166+
}
124167
}
125168

126169
#[turbo_tasks::value]

0 commit comments

Comments
 (0)