Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7313d34

Browse files
committedApr 4, 2025
enable content hashing in production
1 parent c3ece21 commit 7313d34

File tree

4 files changed

+71
-10
lines changed

4 files changed

+71
-10
lines changed
 

‎crates/next-core/src/next_client/context.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ use turbopack::{
1414
resolve_options_context::ResolveOptionsContext,
1515
};
1616
use turbopack_browser::{
17-
react_refresh::assert_can_resolve_react_refresh, BrowserChunkingContext, CurrentChunkMethod,
17+
react_refresh::assert_can_resolve_react_refresh, BrowserChunkingContext, ContentHashing,
18+
CurrentChunkMethod,
1819
};
1920
use turbopack_core::{
2021
chunk::{
@@ -486,6 +487,7 @@ pub async fn get_client_chunking_context(
486487
..Default::default()
487488
},
488489
);
490+
builder = builder.use_content_hashing(ContentHashing::Direct { length: 16 })
489491
}
490492

491493
Ok(Vc::upcast(builder.build()))

‎turbopack/crates/turbopack-browser/src/chunking_context.rs

+65-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
use anyhow::{bail, Context, Result};
2+
use serde::{Deserialize, Serialize};
23
use tracing::Instrument;
34
use turbo_rcstr::RcStr;
4-
use turbo_tasks::{ResolvedVc, TryJoinIterExt, Upcast, Value, ValueToString, Vc};
5+
use turbo_tasks::{
6+
trace::TraceRawVcs, NonLocalValue, ResolvedVc, TaskInput, TryJoinIterExt, Upcast, Value,
7+
ValueToString, Vc,
8+
};
59
use turbo_tasks_fs::FileSystemPath;
10+
use turbo_tasks_hash::{hash_xxh3_hash64, DeterministicHash};
611
use turbopack_core::{
7-
asset::Asset,
12+
asset::{Asset, AssetContent},
813
chunk::{
914
availability_info::AvailabilityInfo,
1015
chunk_group::{make_chunk_group, MakeChunkGroupResult},
@@ -42,6 +47,31 @@ pub enum CurrentChunkMethod {
4247
pub const CURRENT_CHUNK_METHOD_DOCUMENT_CURRENT_SCRIPT_EXPR: &str =
4348
"typeof document === \"object\" ? document.currentScript : undefined";
4449

50+
#[derive(
51+
Debug,
52+
TaskInput,
53+
Clone,
54+
Copy,
55+
PartialEq,
56+
Eq,
57+
Hash,
58+
Serialize,
59+
Deserialize,
60+
TraceRawVcs,
61+
DeterministicHash,
62+
NonLocalValue,
63+
)]
64+
pub enum ContentHashing {
65+
/// Direct content hashing: Embeds the chunk content hash directly into the referencing chunk.
66+
/// Benefit: No hash manifest needed.
67+
/// Downside: Causes cascading hash invalidation.
68+
Direct {
69+
/// The length of the content hash in hex chars. Anything lower than 8 is not recommended
70+
/// due to the high risk of collisions.
71+
length: u8,
72+
},
73+
}
74+
4575
pub struct BrowserChunkingContextBuilder {
4676
chunking_context: BrowserChunkingContext,
4777
}
@@ -125,6 +155,11 @@ impl BrowserChunkingContextBuilder {
125155
self
126156
}
127157

158+
pub fn use_content_hashing(mut self, content_hashing: ContentHashing) -> Self {
159+
self.chunking_context.content_hashing = Some(content_hashing);
160+
self
161+
}
162+
128163
pub fn build(self) -> Vc<BrowserChunkingContext> {
129164
BrowserChunkingContext::new(Value::new(self.chunking_context))
130165
}
@@ -173,6 +208,8 @@ pub struct BrowserChunkingContext {
173208
runtime_type: RuntimeType,
174209
/// Whether to minify resulting chunks
175210
minify_type: MinifyType,
211+
/// Whether content hashing is enabled.
212+
content_hashing: Option<ContentHashing>,
176213
/// Whether to generate source maps
177214
source_maps_type: SourceMapsType,
178215
/// Method to use when figuring out the current chunk src
@@ -214,6 +251,7 @@ impl BrowserChunkingContext {
214251
environment,
215252
runtime_type,
216253
minify_type: MinifyType::NoMinify,
254+
content_hashing: None,
217255
source_maps_type: SourceMapsType::Full,
218256
current_chunk_method: CurrentChunkMethod::StringLiteral,
219257
manifest_chunks: false,
@@ -361,15 +399,35 @@ impl ChunkingContext for BrowserChunkingContext {
361399
#[turbo_tasks::function]
362400
async fn chunk_path(
363401
&self,
364-
_asset: Option<Vc<Box<dyn Asset>>>,
402+
asset: Option<Vc<Box<dyn Asset>>>,
365403
ident: Vc<AssetIdent>,
366404
extension: RcStr,
367405
) -> Result<Vc<FileSystemPath>> {
368406
let root_path = self.chunk_root_path;
369-
let name = ident
370-
.output_name(*self.root_path, extension)
371-
.owned()
372-
.await?;
407+
let name = match self.content_hashing {
408+
None => {
409+
ident
410+
.output_name(*self.root_path, extension)
411+
.owned()
412+
.await?
413+
}
414+
Some(ContentHashing::Direct { length }) => {
415+
let Some(asset) = asset else {
416+
bail!("chunk_path requires an asset when content hashing is enabled");
417+
};
418+
let content = asset.content().await?;
419+
if let AssetContent::File(file) = &*content {
420+
let hash = hash_xxh3_hash64(&file.await?);
421+
let length = length as usize;
422+
format!("{hash:0length$x}{extension}").into()
423+
} else {
424+
bail!(
425+
"chunk_path requires an asset with file content when content hashing is \
426+
enabled"
427+
);
428+
}
429+
}
430+
};
373431
Ok(root_path.join(name))
374432
}
375433

‎turbopack/crates/turbopack-browser/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub mod ecmascript;
88
pub mod react_refresh;
99

1010
pub use chunking_context::{
11-
BrowserChunkingContext, BrowserChunkingContextBuilder, CurrentChunkMethod,
11+
BrowserChunkingContext, BrowserChunkingContextBuilder, ContentHashing, CurrentChunkMethod,
1212
};
1313

1414
pub fn register() {

‎turbopack/crates/turbopack-cli/src/build/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use turbopack::{
2121
css::chunk::CssChunkType, ecmascript::chunk::EcmascriptChunkType,
2222
global_module_ids::get_global_module_id_strategy,
2323
};
24-
use turbopack_browser::BrowserChunkingContext;
24+
use turbopack_browser::{BrowserChunkingContext, ContentHashing};
2525
use turbopack_cli_utils::issue::{ConsoleUi, LogOptions};
2626
use turbopack_core::{
2727
asset::Asset,
@@ -354,6 +354,7 @@ async fn build_internal(
354354
..Default::default()
355355
},
356356
);
357+
builder = builder.use_content_hashing(ContentHashing::Direct { length: 16 })
357358
}
358359
}
359360

0 commit comments

Comments
 (0)
Please sign in to comment.