Skip to content
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 3 additions & 47 deletions crates/rspack_core/src/chunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,42 +24,15 @@ pub enum ChunkKind {

pub type ChunkContentHash = HashMap<SourceType, RspackHashDigest>;

/// The CSS parts of `content_hash` before the chunk-hash salt is applied, so
/// they only change when the emitted stylesheet actually changes. `None` when
/// the chunk has no CSS of that kind. Used by HMR to compute the manifest
/// `css` (native css runtime) and `miniCss` (CssExtractRspackPlugin) fields,
/// which are kept apart so an update of one runtime's CSS never makes the
/// other runtime fetch a stylesheet it does not own.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ChunkCssHashes {
pub css: Option<RspackHashDigest>,
pub mini_css: Option<RspackHashDigest>,
}

impl ChunkCssHashes {
pub fn is_empty(&self) -> bool {
self.css.is_none() && self.mini_css.is_none()
}
}

#[derive(Debug, PartialEq, Eq)]
pub struct ChunkHashesResult {
hash: RspackHashDigest,
content_hash: ChunkContentHash,
css_hashes: ChunkCssHashes,
}

impl ChunkHashesResult {
pub fn new(
hash: RspackHashDigest,
content_hash: ChunkContentHash,
css_hashes: ChunkCssHashes,
) -> Self {
Self {
hash,
content_hash,
css_hashes,
}
pub fn new(hash: RspackHashDigest, content_hash: ChunkContentHash) -> Self {
Self { hash, content_hash }
}

pub fn hash(&self) -> &RspackHashDigest {
Expand All @@ -69,10 +42,6 @@ impl ChunkHashesResult {
pub fn content_hash(&self) -> &ChunkContentHash {
&self.content_hash
}

pub fn css_hashes(&self) -> &ChunkCssHashes {
&self.css_hashes
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -320,26 +289,13 @@ impl Chunk {
.map(|hash| hash.rendered(len))
}

pub fn css_hashes<'a>(
&self,
chunk_hashes_results: &'a ChunkHashesArtifact,
) -> Option<&'a ChunkCssHashes> {
chunk_hashes_results
.get(&self.ukey)
.map(|result| result.css_hashes())
}

pub fn set_hashes(
&self,
chunk_hashes_results: &mut ChunkHashesArtifact,
chunk_hash: RspackHashDigest,
content_hash: ChunkContentHash,
css_hashes: ChunkCssHashes,
) -> bool {
chunk_hashes_results.set_hashes(
self.ukey,
ChunkHashesResult::new(chunk_hash, content_hash, css_hashes),
)
chunk_hashes_results.set_hashes(self.ukey, ChunkHashesResult::new(chunk_hash, content_hash))
}

pub fn rendered(&self) -> bool {
Expand Down
80 changes: 8 additions & 72 deletions crates/rspack_core/src/compilation/create_hash/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
use std::sync::LazyLock;

use async_trait::async_trait;
use rspack_hash::RspackHasher;
use rustc_hash::FxHashSet;

use super::*;
use crate::{
ChunkCssHashes, ModuleCodeGenerationContext, cache::Cache, compilation::pass::PassExt,
logger::Logger,
ModuleCodeGenerationContext, cache::Cache, compilation::pass::PassExt, logger::Logger,
};

pub struct ChunkHashResult {
pub hash: RspackHashDigest,
pub content_hash: ChunkContentHash,
pub css_hashes: ChunkCssHashes,
}

pub struct CreateHashPass;
Expand Down Expand Up @@ -149,7 +145,6 @@ pub async fn create_hash(
&mut compilation.chunk_hashes_artifact,
chunk_hash_result.hash,
chunk_hash_result.content_hash,
chunk_hash_result.css_hashes,
);
if chunk_hashes_changed && let Some(mut mutations) = compilation.incremental.mutations_write()
{
Expand Down Expand Up @@ -200,26 +195,14 @@ pub async fn create_hash(
}

// create hash for other chunks
let empty_css_digest =
RspackHasher::from(&compilation.options.output).digest(&compilation.options.output.hash_digest);
let compilation_ref = &*compilation;
let other_chunks_hash_results = rspack_parallel::scope::<_, Result<_>>(|token| {
for chunk in other_chunks {
let s = unsafe {
token.used((
compilation_ref,
chunk,
plugin_driver.clone(),
&empty_css_digest,
))
};
s.spawn(
|(compilation, chunk, plugin_driver, empty_css_digest)| async move {
let hash_result =
process_chunk_hash(compilation, *chunk, &plugin_driver, empty_css_digest).await?;
Ok((*chunk, hash_result))
},
);
let s = unsafe { token.used((compilation_ref, chunk, plugin_driver.clone())) };
s.spawn(|(compilation, chunk, plugin_driver)| async move {
let hash_result = process_chunk_hash(compilation, *chunk, &plugin_driver).await?;
Ok((*chunk, hash_result))
});
}
})
.await
Expand Down Expand Up @@ -384,13 +367,8 @@ pub async fn create_hash(
compilation.runtime_modules_hash.insert(mid, digest);
}

let chunk_hash_result = process_chunk_hash(
compilation,
runtime_chunk_ukey,
&plugin_driver,
&empty_css_digest,
)
.await?;
let chunk_hash_result =
process_chunk_hash(compilation, runtime_chunk_ukey, &plugin_driver).await?;
let chunk = compilation
.build_chunk_graph_artifact
.chunk_by_ukey
Expand All @@ -399,7 +377,6 @@ pub async fn create_hash(
&mut compilation.chunk_hashes_artifact,
chunk_hash_result.hash,
chunk_hash_result.content_hash,
chunk_hash_result.css_hashes,
);
if chunk_hashes_changed && let Some(mut mutations) = compilation.incremental.mutations_write() {
mutations.add(Mutation::ChunkSetHashes {
Expand Down Expand Up @@ -488,17 +465,10 @@ pub async fn create_hash(
})
.collect()
};
// The css content hashes are intentionally not re-salted with the full
// hash: they must stay stable when only the full hash changes.
let css_hashes = chunk
.css_hashes(&compilation.chunk_hashes_artifact)
.cloned()
.unwrap_or_default();
let chunk_hashes_changed = chunk.set_hashes(
&mut compilation.chunk_hashes_artifact,
new_chunk_hash,
new_content_hash,
css_hashes,
);
if chunk_hashes_changed && let Some(mut mutations) = compilation.incremental.mutations_write() {
mutations.add(Mutation::ChunkSetHashes { chunk: chunk_ukey });
Expand Down Expand Up @@ -559,7 +529,6 @@ async fn process_chunk_hash(
compilation: &Compilation,
chunk_ukey: ChunkUkey,
plugin_driver: &SharedPluginDriver,
empty_css_digest: &RspackHashDigest,
) -> Result<ChunkHashResult> {
let mut hasher = RspackHasher::from(&compilation.options.output);
if let Some(chunk) = compilation
Expand All @@ -583,8 +552,6 @@ async fn process_chunk_hash(
.call(compilation, &chunk_ukey, &mut content_hashes)
.await?;

let css_hashes = chunk_css_hashes(compilation, &content_hashes, empty_css_digest);

let content_hashes = content_hashes
.into_iter()
.map(|(t, mut hasher)| {
Expand All @@ -596,36 +563,5 @@ async fn process_chunk_hash(
Ok(ChunkHashResult {
hash: chunk_hash,
content_hash: content_hashes,
css_hashes,
})
}

/// The chunk's CSS content hashes, digested from the css-related
/// `content_hash` hook entries before the chunk-hash salt is applied, i.e.
/// derived from exactly the ordered module lists the CSS assets are rendered
/// and hashed from. The native css and CssExtractRspackPlugin hashes are kept
/// apart because each feeds its own HMR runtime. Deliberately O(1) per chunk:
/// emptiness is detected from the hook entries themselves — the
/// CssExtractRspackPlugin entry is only inserted when the chunk has extracted
/// css, while the native css plugin inserts a `SourceType::Css` entry for
/// every chunk, so an entry whose digest equals the empty digest means "no
/// css" (the hook writes nothing for css-less chunks).
fn chunk_css_hashes(
compilation: &Compilation,
content_hashes: &HashMap<SourceType, RspackHasher>,
empty_css_digest: &RspackHashDigest,
) -> ChunkCssHashes {
// The content-hash key CssExtractRspackPlugin emits under.
static MINI_EXTRACT_CSS: LazyLock<SourceType> =
LazyLock::new(|| SourceType::Custom("css/mini-extract".into()));
let output = &compilation.options.output;
let digest_of = |source_type: SourceType| {
content_hashes
.get(&source_type)
.map(|hasher| hasher.clone().digest(&output.hash_digest))
};
ChunkCssHashes {
css: digest_of(SourceType::Css).filter(|digest| digest != empty_css_digest),
mini_css: digest_of(*MINI_EXTRACT_CSS),
}
}
20 changes: 1 addition & 19 deletions crates/rspack_core/src/compiler/rebuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rspack_tasks::within_compiler_context;
use rustc_hash::FxHashSet;

use crate::{
ChunkCssHashes, ChunkGraph, ChunkKind, Compilation, Compiler, RuntimeSpec,
ChunkGraph, ChunkKind, Compilation, Compiler, RuntimeSpec,
chunk_graph_chunk::ChunkIdMap,
chunk_graph_module::{ModuleIdMap, ModuleIdSet},
compilation::build_module_graph::ModuleExecutor,
Expand Down Expand Up @@ -131,7 +131,6 @@ pub struct CompilationRecords {
pub runtime_modules: IdentifierMap<RspackHashDigest>,
pub chunks: ChunkIdMap<(RuntimeSpec, ModuleIdSet)>,
pub modules: ModuleIdMap<ChunkIdMap<RspackHashDigest>>,
pub chunk_css_hashes: ChunkIdMap<ChunkCssHashes>,
pub hash: Option<RspackHashDigest>,
}

Expand All @@ -142,27 +141,10 @@ impl CompilationRecords {
runtime_modules: Self::record_runtime_modules(compilation),
chunks: Self::record_chunks(compilation),
modules: Self::record_modules(compilation),
chunk_css_hashes: Self::record_chunk_css_hashes(compilation),
hash: Self::record_hash(compilation),
}
}

fn record_chunk_css_hashes(compilation: &Compilation) -> ChunkIdMap<ChunkCssHashes> {
compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.values()
.filter(|chunk| chunk.kind() != ChunkKind::HotUpdate)
.filter_map(|chunk| {
let css_hashes = chunk.css_hashes(&compilation.chunk_hashes_artifact)?;
if css_hashes.is_empty() {
return None;
}
Some((chunk.expect_id().clone(), css_hashes.clone()))
})
.collect()
}

fn record_hash(compilation: &Compilation) -> Option<RspackHashDigest> {
compilation.hash.clone()
}
Expand Down
12 changes: 7 additions & 5 deletions crates/rspack_plugin_hmr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
rspack_plugin_runtime = { workspace = true }
rspack_util = { workspace = true }

async-trait = { workspace = true }
rustc-hash = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
async-trait = { workspace = true }
atomic_refcell = { workspace = true }
rustc-hash = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
ustr = { workspace = true }

[package.metadata.cargo-shear]
ignored = ["tracing", "rspack_cacheable", "rspack_hash", "tokio"]

Check warning on line 30 in crates/rspack_plugin_hmr/Cargo.toml

View workflow job for this annotation

GitHub Actions / Run Rust Tests / Check Rust Dependencies

shear/redundant_ignore

redundant ignore `rspack_hash` (remove from ignored list)

[lints]
workspace = true
Expand Down
Loading
Loading