use async_trait::async_trait;
use rustc_hash::FxHashSet;
use super::*;
use crate::{cache::Cache, compilation::pass::PassExt, logger::Logger};
pub struct CreateChunkAssetsPass;
#[async_trait]
impl PassExt for CreateChunkAssetsPass {
fn name(&self) -> &'static str {
"create chunk assets"
}
async fn before_pass(&self, compilation: &mut Compilation, cache: &mut dyn Cache) {
cache.before_chunk_asset(compilation).await;
}
async fn run_pass(&self, compilation: &mut Compilation) -> Result<()> {
let plugin_driver = compilation.plugin_driver.clone();
create_chunk_assets(compilation, plugin_driver).await?;
Ok(())
}
async fn after_pass(&self, compilation: &mut Compilation, cache: &mut dyn Cache) {
cache.after_chunk_asset(compilation).await;
}
}
#[instrument("Compilation::create_chunk_assets",target=TRACING_BENCH_TARGET, skip_all)]
pub async fn create_chunk_assets(
compilation: &mut Compilation,
plugin_driver: SharedPluginDriver,
) -> Result<()> {
if (compilation.options.output.filename.has_hash_placeholder()
|| compilation
.options
.output
.chunk_filename
.has_hash_placeholder()
|| compilation
.options
.output
.css_filename
.has_hash_placeholder()
|| compilation
.options
.output
.css_chunk_filename
.has_hash_placeholder())
&& let Some(diagnostic) = compilation.incremental.disable_passes(
IncrementalPasses::CHUNK_ASSET,
"Chunk filename that dependent on full hash",
"chunk filename that dependent on full hash is not supported in incremental compilation",
)
&& let Some(diagnostic) = diagnostic
{
compilation.push_diagnostic(diagnostic);
}
if !compilation
.incremental
.passes_enabled(IncrementalPasses::CHUNK_ASSET)
{
compilation.chunk_render_artifact.clear();
}
let chunks = if let Some(mutations) = compilation
.incremental
.mutations_read(IncrementalPasses::CHUNK_ASSET)
&& !compilation.chunk_render_artifact.is_empty()
{
let removed_chunks = mutations.iter().filter_map(|mutation| match mutation {
Mutation::ChunkRemove { chunk } => Some(*chunk),
_ => None,
});
for removed_chunk in removed_chunks {
compilation.chunk_render_artifact.remove(&removed_chunk);
}
compilation.chunk_render_artifact.retain(|chunk, _| {
compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.contains(chunk)
});
let chunks: FxHashSet<ChunkUkey> = mutations
.iter()
.filter_map(|mutation| match mutation {
Mutation::ChunkSetHashes { chunk } => Some(*chunk),
_ => None,
})
.collect();
tracing::debug!(target: incremental::TRACING_TARGET, passes = %IncrementalPasses::CHUNK_ASSET, %mutations);
let logger = compilation.get_logger("rspack.incremental.chunkAsset");
logger.log(format!(
"{} chunks are affected, {} in total",
chunks.len(),
compilation.build_chunk_graph_artifact.chunk_by_ukey.len()
));
chunks
} else {
compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.keys()
.copied()
.collect()
};
let compilation_ref = &*compilation;
let results = rspack_parallel::scope::<_, Result<_>>(|token| {
chunks.iter().for_each(|chunk| {
let s = unsafe { token.used((compilation_ref, &plugin_driver, chunk)) };
s.spawn(|(this, plugin_driver, chunk)| async {
let mut manifests = Vec::new();
let mut diagnostics = Vec::new();
plugin_driver
.compilation_hooks
.render_manifest
.call(this, chunk, &mut manifests, &mut diagnostics)
.await?;
rspack_error::Result::Ok((
*chunk,
ChunkRenderResult {
manifests,
diagnostics,
},
))
});
})
})
.await;
let mut chunk_render_results = ChunkRenderArtifact::default();
for result in results {
let item = result.to_rspack_result()?;
let (key, value) = item?;
chunk_render_results.insert(key, value);
}
let chunk_ukey_and_manifest = if compilation
.incremental
.passes_enabled(IncrementalPasses::CHUNK_ASSET)
{
compilation
.chunk_render_artifact
.extend(chunk_render_results);
compilation.chunk_render_artifact.clone()
} else {
chunk_render_results
};
for (
chunk_ukey,
ChunkRenderResult {
manifests,
diagnostics,
},
) in chunk_ukey_and_manifest
{
compilation.extend_diagnostics(diagnostics);
for file_manifest in manifests {
let filename = file_manifest.filename;
let current_chunk = compilation
.build_chunk_graph_artifact
.chunk_by_ukey
.expect_get_mut(&chunk_ukey);
current_chunk.set_rendered(true);
if file_manifest.auxiliary {
current_chunk.add_auxiliary_file(filename.clone());
} else {
current_chunk.add_file(filename.clone());
}
compilation.emit_asset(
filename.clone(),
CompilationAsset::new(Some(file_manifest.source), file_manifest.info),
);
_ = chunk_asset(compilation, chunk_ukey, &filename, plugin_driver.clone()).await;
}
}
Ok(())
}
async fn chunk_asset(
compilation: &Compilation,
chunk_ukey: ChunkUkey,
filename: &str,
plugin_driver: SharedPluginDriver,
) -> Result<()> {
plugin_driver
.compilation_hooks
.chunk_asset
.call(compilation, &chunk_ukey, filename)
.await?;
Ok(())
}