diff --git a/crates/rspack_binding_values/src/compilation/mod.rs b/crates/rspack_binding_values/src/compilation/mod.rs index 1a230087a38..cf53a60c1b1 100644 --- a/crates/rspack_binding_values/src/compilation/mod.rs +++ b/crates/rspack_binding_values/src/compilation/mod.rs @@ -533,9 +533,10 @@ impl JsCompilation { ) -> napi::Result { let compilation = self.as_ref()?; - let path_and_asset_info = - compilation.get_path_with_info(&filename.into(), data.to_path_data())?; - Ok(path_and_asset_info.into()) + let mut asset_info = AssetInfo::default(); + let path = + compilation.get_path_with_info(&filename.into(), data.to_path_data(), &mut asset_info)?; + Ok((path, asset_info).into()) } #[napi] diff --git a/crates/rspack_core/src/chunk.rs b/crates/rspack_core/src/chunk.rs index 92b8c92a695..04c87f5dfc8 100644 --- a/crates/rspack_core/src/chunk.rs +++ b/crates/rspack_core/src/chunk.rs @@ -5,11 +5,13 @@ use std::{fmt::Debug, hash::Hash}; use indexmap::IndexMap; use itertools::Itertools; use rspack_collections::{DatabaseItem, UkeyIndexMap, UkeyIndexSet, UkeyMap, UkeySet}; +use rspack_error::Diagnostic; use rspack_hash::{RspackHash, RspackHashDigest}; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet, FxHasher}; use crate::{ compare_chunk_group, merge_runtime, sort_group_by_index, ChunkGraph, ChunkGroupOrderKey, + RenderManifestEntry, }; use crate::{ChunkGroupByUkey, ChunkGroupUkey, ChunkUkey, SourceType}; use crate::{Compilation, EntryOptions, Filename, ModuleGraph, RuntimeSpec}; @@ -42,6 +44,12 @@ impl ChunkHashesResult { } } +#[derive(Debug, Clone)] +pub struct ChunkRenderResult { + pub manifests: Vec, + pub diagnostics: Vec, +} + #[derive(Debug, Clone)] pub struct Chunk { ukey: ChunkUkey, diff --git a/crates/rspack_core/src/compiler/compilation.rs b/crates/rspack_core/src/compiler/compilation.rs index c611d4ef073..478e28d1852 100644 --- a/crates/rspack_core/src/compiler/compilation.rs +++ b/crates/rspack_core/src/compiler/compilation.rs @@ -39,12 +39,12 @@ use crate::{ old_cache::{use_code_splitting_cache, Cache as OldCache, CodeSplittingCache}, to_identifier, BoxDependency, BoxModule, CacheCount, CacheOptions, Chunk, ChunkByUkey, ChunkContentHash, ChunkGraph, ChunkGroupByUkey, ChunkGroupUkey, ChunkHashesResult, ChunkKind, - ChunkUkey, CodeGenerationJob, CodeGenerationResult, CodeGenerationResults, CompilationLogger, - CompilationLogging, CompilerOptions, DependencyId, DependencyType, Entry, EntryData, - EntryOptions, EntryRuntime, Entrypoint, ExecuteModuleId, Filename, ImportVarMap, LocalFilenameFn, - Logger, ModuleFactory, ModuleGraph, ModuleGraphPartial, ModuleIdentifier, PathData, - ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpecMap, SharedPluginDriver, SourceType, - Stats, + ChunkRenderResult, ChunkUkey, CodeGenerationJob, CodeGenerationResult, CodeGenerationResults, + CompilationLogger, CompilationLogging, CompilerOptions, DependencyId, DependencyType, Entry, + EntryData, EntryOptions, EntryRuntime, Entrypoint, ExecuteModuleId, Filename, ImportVarMap, + LocalFilenameFn, Logger, ModuleFactory, ModuleGraph, ModuleGraphPartial, ModuleIdentifier, + PathData, ResolverFactory, RuntimeGlobals, RuntimeModule, RuntimeSpecMap, SharedPluginDriver, + SourceType, Stats, }; pub type BuildDependency = ( @@ -175,7 +175,7 @@ pub struct Compilation { pub cgm_runtime_requirements_results: CgmRuntimeRequirementsResults, pub cgc_runtime_requirements_results: UkeyMap, pub chunk_hashes_results: UkeyMap, - pub chunk_render_results: UkeyMap, Vec)>, + pub chunk_render_results: UkeyMap, pub built_modules: IdentifierSet, pub code_generated_modules: IdentifierSet, pub build_time_executed_modules: IdentifierSet, @@ -972,15 +972,21 @@ impl Compilation { let chunk_render_results = chunks .iter() .map(|chunk| async { - let mut manifest = Vec::new(); + let mut manifests = Vec::new(); let mut diagnostics = Vec::new(); plugin_driver .compilation_hooks .render_manifest - .call(self, chunk, &mut manifest, &mut diagnostics) + .call(self, chunk, &mut manifests, &mut diagnostics) .await?; - Ok((*chunk, (manifest, diagnostics))) + Ok(( + *chunk, + ChunkRenderResult { + manifests, + diagnostics, + }, + )) }) .collect::>>(); let chunk_render_results = chunk_render_results @@ -995,11 +1001,18 @@ impl Compilation { chunk_render_results }; - for (chunk_ukey, (manifest, diagnostics)) in chunk_ukey_and_manifest { + for ( + chunk_ukey, + ChunkRenderResult { + manifests, + diagnostics, + }, + ) in chunk_ukey_and_manifest + { self.extend_diagnostics(diagnostics); - for file_manifest in manifest { - let filename = file_manifest.filename().to_string(); + for file_manifest in manifests { + let filename = file_manifest.filename; let current_chunk = self.chunk_by_ukey.expect_get_mut(&chunk_ukey); current_chunk.set_rendered(true); @@ -1996,13 +2009,13 @@ impl Compilation { &'a self, filename: &Filename, mut data: PathData<'b>, - ) -> Result<(String, AssetInfo), F::Error> { - let mut info = AssetInfo::default(); + info: &mut AssetInfo, + ) -> Result { if data.hash.is_none() { data.hash = self.get_hash(); } - let path = filename.render(data, Some(&mut info))?; - Ok((path, info)) + let path = filename.render(data, Some(info))?; + Ok(path) } pub fn get_asset_path( @@ -2324,42 +2337,10 @@ pub fn set_depth_if_lower( #[derive(Debug, Clone)] pub struct RenderManifestEntry { pub source: BoxSource, - filename: String, + pub filename: String, + pub has_filename: bool, /* webpack only asset has filename, js/css/wasm has filename template */ pub info: AssetInfo, - // pub identifier: String, - // hash?: string; - pub(crate) auxiliary: bool, - has_filename: bool, /* webpack only asset has filename, js/css/wasm has filename template */ -} - -impl RenderManifestEntry { - pub fn new( - source: BoxSource, - filename: String, - info: AssetInfo, - auxiliary: bool, - has_filename: bool, - ) -> Self { - Self { - source, - filename, - info, - auxiliary, - has_filename, - } - } - - pub fn source(&self) -> &BoxSource { - &self.source - } - - pub fn filename(&self) -> &str { - &self.filename - } - - pub fn has_filename(&self) -> bool { - self.has_filename - } + pub auxiliary: bool, } fn process_runtime_requirement_hook( diff --git a/crates/rspack_core/src/old_cache/mod.rs b/crates/rspack_core/src/old_cache/mod.rs index a9876f1be26..f617cc5ce0a 100644 --- a/crates/rspack_core/src/old_cache/mod.rs +++ b/crates/rspack_core/src/old_cache/mod.rs @@ -12,9 +12,7 @@ mod local; mod occasion; mod storage; pub use local::*; -use occasion::{ - CodeGenerateOccasion, CreateChunkAssetsOccasion, ProcessRuntimeRequirementsOccasion, -}; +use occasion::{ChunkRenderOccasion, CodeGenerateOccasion, ProcessRuntimeRequirementsOccasion}; use storage::new_storage; #[derive(Debug)] @@ -22,7 +20,7 @@ pub struct Cache { is_idle: AtomicBool, pub code_generate_occasion: CodeGenerateOccasion, pub process_runtime_requirements_occasion: ProcessRuntimeRequirementsOccasion, - pub create_chunk_assets_occasion: CreateChunkAssetsOccasion, + pub chunk_render_occasion: ChunkRenderOccasion, } impl Cache { @@ -33,7 +31,7 @@ impl Cache { process_runtime_requirements_occasion: ProcessRuntimeRequirementsOccasion::new(new_storage( &options.cache, )), - create_chunk_assets_occasion: CreateChunkAssetsOccasion::new(new_storage(&options.cache)), + chunk_render_occasion: ChunkRenderOccasion::new(new_storage(&options.cache)), } } diff --git a/crates/rspack_core/src/old_cache/occasion/chunk_render.rs b/crates/rspack_core/src/old_cache/occasion/chunk_render.rs new file mode 100644 index 00000000000..81803c7c96a --- /dev/null +++ b/crates/rspack_core/src/old_cache/occasion/chunk_render.rs @@ -0,0 +1,51 @@ +use futures::Future; +use rspack_collections::Identifier; +use rspack_error::{Diagnostic, Result}; +use rspack_sources::BoxSource; + +use crate::{old_cache::storage, Chunk, Compilation, SourceType}; + +type Storage = dyn storage::Storage<(BoxSource, Vec)>; + +#[derive(Debug)] +pub struct ChunkRenderOccasion { + storage: Option>, +} + +impl ChunkRenderOccasion { + pub fn new(storage: Option>) -> Self { + Self { storage } + } + + pub async fn use_cache( + &self, + compilation: &Compilation, + chunk: &Chunk, + source_type: &SourceType, + generator: G, + ) -> Result<(BoxSource, Vec)> + where + G: FnOnce() -> F, + F: Future)>>, + { + let storage = match &self.storage { + Some(s) => s, + // no cache return directly + None => return generator().await, + }; + + let Some(content_hash) = + chunk.content_hash_by_source_type(&compilation.chunk_hashes_results, source_type) + else { + return generator().await; + }; + let cache_key = Identifier::from(content_hash.encoded()); + if let Some(value) = storage.get(&cache_key) { + Ok(value) + } else { + let res = generator().await?; + storage.set(cache_key, res.clone()); + Ok(res) + } + } +} diff --git a/crates/rspack_core/src/old_cache/occasion/create_chunk_assets.rs b/crates/rspack_core/src/old_cache/occasion/create_chunk_assets.rs deleted file mode 100644 index 9c3bd4bf520..00000000000 --- a/crates/rspack_core/src/old_cache/occasion/create_chunk_assets.rs +++ /dev/null @@ -1,62 +0,0 @@ -use futures::Future; -use rspack_collections::{DatabaseItem, Identifier}; -use rspack_error::Result; - -use crate::{old_cache::storage, Chunk, Compilation, NormalModuleSource, RenderManifestEntry}; - -type Storage = dyn storage::Storage>; - -#[derive(Debug)] -pub struct CreateChunkAssetsOccasion { - storage: Option>, -} - -impl CreateChunkAssetsOccasion { - pub fn new(storage: Option>) -> Self { - Self { storage } - } - - pub async fn use_cache( - &self, - compilation: &Compilation, - chunk: &Chunk, - generator: G, - ) -> Result> - where - G: Fn() -> F, - F: Future>>, - { - let storage = match &self.storage { - Some(s) => s, - // no cache return directly - None => return generator().await, - }; - - let chunk_id = Identifier::from(chunk.expect_id()); - let modules = &compilation - .chunk_graph - .get_chunk_modules_identifier(&chunk.ukey()); - let is_cache_valid = modules.iter().all(|module_id| { - matches!( - compilation - .get_module_graph() - .module_by_identifier(module_id) - .and_then(|m| m.as_normal_module()) - .map(|m| matches!(m.source(), NormalModuleSource::Unbuild)), - Some(true) - ) - }); - - if is_cache_valid { - // read - if let Some(data) = storage.get(&chunk_id) { - return Ok(data); - } - } - // run generator and save to cache - let data = generator().await?; - // TODO sometime may not run save - storage.set(chunk_id, data.clone()); - Ok(data) - } -} diff --git a/crates/rspack_core/src/old_cache/occasion/mod.rs b/crates/rspack_core/src/old_cache/occasion/mod.rs index fa093479f95..8d8bf482d62 100644 --- a/crates/rspack_core/src/old_cache/occasion/mod.rs +++ b/crates/rspack_core/src/old_cache/occasion/mod.rs @@ -2,5 +2,5 @@ mod code_generate; pub use code_generate::*; mod process_runtime_requirements; pub use process_runtime_requirements::*; -mod create_chunk_assets; -pub use create_chunk_assets::*; +mod chunk_render; +pub use chunk_render::*; diff --git a/crates/rspack_core/src/options/output.rs b/crates/rspack_core/src/options/output.rs index 6a37b462fbf..4cedae6b2e3 100644 --- a/crates/rspack_core/src/options/output.rs +++ b/crates/rspack_core/src/options/output.rs @@ -379,7 +379,6 @@ impl From for PublicPath { } } -#[allow(clippy::if_same_then_else)] pub fn get_css_chunk_filename_template<'filename>( chunk: &'filename Chunk, output_options: &'filename OutputOptions, @@ -395,23 +394,20 @@ pub fn get_css_chunk_filename_template<'filename>( } } -#[allow(clippy::if_same_then_else)] -pub fn get_js_chunk_filename_template<'filename>( - chunk: &'filename Chunk, - output_options: &'filename OutputOptions, +pub fn get_js_chunk_filename_template( + chunk: &Chunk, + output_options: &OutputOptions, chunk_group_by_ukey: &ChunkGroupByUkey, -) -> &'filename Filename { +) -> Filename { // Align with https://github.com/webpack/webpack/blob/8241da7f1e75c5581ba535d127fa66aeb9eb2ac8/lib/javascript/JavascriptModulesPlugin.js#L480 if let Some(filename_template) = chunk.filename_template() { - filename_template - } else if chunk.can_be_initial(chunk_group_by_ukey) { - &output_options.filename + filename_template.clone() } else if matches!(chunk.kind(), ChunkKind::HotUpdate) { - // TODO: Should return output_options.hotUpdateChunkFilename - // See https://github.com/webpack/webpack/blob/8241da7f1e75c5581ba535d127fa66aeb9eb2ac8/lib/javascript/JavascriptModulesPlugin.js#L484 - &output_options.chunk_filename + output_options.hot_update_chunk_filename.clone().into() + } else if chunk.can_be_initial(chunk_group_by_ukey) { + output_options.filename.clone() } else { - &output_options.chunk_filename + output_options.chunk_filename.clone() } } diff --git a/crates/rspack_plugin_asset/src/lib.rs b/crates/rspack_plugin_asset/src/lib.rs index b5a56a3ca11..7a9174033cd 100644 --- a/crates/rspack_plugin_asset/src/lib.rs +++ b/crates/rspack_plugin_asset/src/lib.rs @@ -612,13 +612,13 @@ async fn render_manifest( .get::() .expect("should have asset_info") .inner(); - RenderManifestEntry::new( - source.clone(), - asset_filename.to_owned(), - asset_info.to_owned(), - true, - true, - ) + RenderManifestEntry { + source: source.clone(), + filename: asset_filename.to_owned(), + has_filename: true, + info: asset_info.to_owned(), + auxiliary: true, + } }); Ok(result) diff --git a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs index a0520d6c9a6..9e3fa9fde91 100644 --- a/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs +++ b/crates/rspack_plugin_css/src/plugin/impl_plugin_for_css_plugin.rs @@ -5,7 +5,8 @@ use std::sync::Arc; use async_trait::async_trait; use rayon::prelude::*; -use rspack_core::rspack_sources::ReplaceSource; +use rspack_collections::DatabaseItem; +use rspack_core::rspack_sources::{BoxSource, CachedSource, ReplaceSource}; use rspack_core::{ get_css_chunk_filename_template, rspack_sources::{ConcatSource, RawSource, Source, SourceExt}, @@ -13,9 +14,9 @@ use rspack_core::{ SourceType, }; use rspack_core::{ - ChunkLoading, ChunkLoadingType, ChunkUkey, Compilation, CompilationContentHash, + AssetInfo, ChunkLoading, ChunkLoadingType, ChunkUkey, Compilation, CompilationContentHash, CompilationParams, CompilationRenderManifest, CompilationRuntimeRequirementInTree, - CompilerCompilation, CompilerOptions, DependencyType, LibIdentOptions, PublicPath, + CompilerCompilation, CompilerOptions, DependencyType, LibIdentOptions, ModuleGraph, PublicPath, RuntimeGlobals, SelfModuleFactory, }; use rspack_error::{Diagnostic, Result}; @@ -39,9 +40,9 @@ impl CssPlugin { fn get_chunk_unused_local_idents( compilation: &Compilation, chunk: &Chunk, - ordered_css_modules: &[&dyn Module], + css_modules: &[&dyn Module], ) -> HashSet { - ordered_css_modules + css_modules .iter() .filter_map(|module| { let module_id = &module.identifier(); @@ -57,6 +58,65 @@ impl CssPlugin { .collect() } + fn render_chunk( + &self, + compilation: &Compilation, + mg: &ModuleGraph, + chunk: &Chunk, + output_path: &str, + css_import_modules: Vec<&dyn Module>, + css_modules: Vec<&dyn Module>, + ) -> Result<(BoxSource, Vec)> { + let (ordered_css_modules, conflicts) = + Self::get_ordered_chunk_css_modules(chunk, compilation, css_import_modules, css_modules); + let source = Self::render_chunk_to_source(compilation, chunk, &ordered_css_modules)?; + + let content = source.source(); + let len = AUTO_PUBLIC_PATH_PLACEHOLDER.len(); + let auto_public_path_matches: Vec<_> = content + .match_indices(AUTO_PUBLIC_PATH_PLACEHOLDER) + .map(|(index, _)| (index, index + len)) + .collect(); + let source = if !auto_public_path_matches.is_empty() { + let mut replace = ReplaceSource::new(source); + for (start, end) in auto_public_path_matches { + let relative = PublicPath::render_auto_public_path(compilation, output_path); + replace.replace(start as u32, end as u32, &relative, None); + } + replace.boxed() + } else { + source.boxed() + }; + let mut diagnostics = vec![]; + if let Some(conflicts) = conflicts { + diagnostics.extend(conflicts.into_iter().map(|conflict| { + let chunk = compilation.chunk_by_ukey.expect_get(&conflict.chunk); + + let failed_module = mg + .module_by_identifier(&conflict.failed_module) + .expect("should have module"); + let selected_module = mg + .module_by_identifier(&conflict.selected_module) + .expect("should have module"); + + Diagnostic::warn( + "Conflicting order".into(), + format!( + "chunk {}\nConflicting order between {} and {}", + chunk + .name() + .unwrap_or(chunk.id().expect("should have chunk id")), + failed_module.readable_identifier(&compilation.options.context), + selected_module.readable_identifier(&compilation.options.context) + ), + ) + .with_file(Some(output_path.to_owned().into())) + .with_chunk(Some(chunk.ukey().as_u32())) + })); + } + Ok((source, diagnostics)) + } + fn render_chunk_to_source( compilation: &Compilation, chunk: &Chunk, @@ -227,12 +287,16 @@ async fn content_hash( ) -> Result<()> { let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey); let module_graph = compilation.get_module_graph(); - let (ordered_modules, _) = Self::get_ordered_chunk_css_modules( - chunk, - &compilation.chunk_graph, - &module_graph, - compilation, - ); + let css_import_modules = compilation + .chunk_graph + .get_chunk_modules_iterable_by_source_type(chunk_ukey, SourceType::CssImport, &module_graph) + .collect::>(); + let css_modules = compilation + .chunk_graph + .get_chunk_modules_iterable_by_source_type(chunk_ukey, SourceType::Css, &module_graph) + .collect::>(); + let (ordered_modules, _) = + Self::get_ordered_chunk_css_modules(chunk, compilation, css_import_modules, css_modules); let mut hasher = hashes .entry(SourceType::Css) .or_insert_with(|| RspackHash::from(&compilation.options.output)); @@ -314,27 +378,27 @@ async fn render_manifest( return Ok(()); } let module_graph = compilation.get_module_graph(); - let (ordered_css_modules, conflicts) = Self::get_ordered_chunk_css_modules( - chunk, - &compilation.chunk_graph, - &module_graph, - compilation, - ); - - // Prevent generating css files for chunks which don't contain css modules. - if ordered_css_modules.is_empty() { + let css_import_modules = compilation + .chunk_graph + .get_chunk_modules_iterable_by_source_type(chunk_ukey, SourceType::CssImport, &module_graph) + .collect::>(); + let css_modules = compilation + .chunk_graph + .get_chunk_modules_iterable_by_source_type(chunk_ukey, SourceType::Css, &module_graph) + .collect::>(); + if css_import_modules.is_empty() && css_modules.is_empty() { return Ok(()); } - let source = Self::render_chunk_to_source(compilation, chunk, &ordered_css_modules)?; - let unused_idents = Self::get_chunk_unused_local_idents(compilation, chunk, &ordered_css_modules); - let filename_template = get_css_chunk_filename_template( chunk, &compilation.options.output, &compilation.chunk_group_by_ukey, ); - let (output_path, mut asset_info) = compilation.get_path_with_info( + let mut asset_info = AssetInfo::default(); + let unused_idents = Self::get_chunk_unused_local_idents(compilation, chunk, &css_modules); + asset_info.set_css_unused_idents(unused_idents); + let output_path = compilation.get_path_with_info( filename_template, PathData::default() .chunk_id_optional(chunk.id()) @@ -349,59 +413,33 @@ async fn render_manifest( compilation.options.output.hash_digest_length, )) .runtime(chunk.runtime().as_str()), + &mut asset_info, )?; - asset_info.set_css_unused_idents(unused_idents); - let content = source.source(); - let len = AUTO_PUBLIC_PATH_PLACEHOLDER.len(); - let auto_public_path_matches: Vec<_> = content - .match_indices(AUTO_PUBLIC_PATH_PLACEHOLDER) - .map(|(index, _)| (index, index + len)) - .collect(); - let source = if !auto_public_path_matches.is_empty() { - let mut replace = ReplaceSource::new(source); - for (start, end) in auto_public_path_matches { - let relative = PublicPath::render_auto_public_path(compilation, &output_path); - replace.replace(start as u32, end as u32, &relative, None); - } - replace.boxed() - } else { - source.boxed() - }; - if let Some(conflicts) = conflicts { - diagnostics.extend(conflicts.into_iter().map(|conflict| { - let chunk = compilation.chunk_by_ukey.expect_get(&conflict.chunk); - let mg = compilation.get_module_graph(); - - let failed_module = mg - .module_by_identifier(&conflict.failed_module) - .expect("should have module"); - let selected_module = mg - .module_by_identifier(&conflict.selected_module) - .expect("should have module"); - - Diagnostic::warn( - "Conflicting order".into(), - format!( - "chunk {}\nConflicting order between {} and {}", - chunk - .name() - .unwrap_or(chunk.id().expect("should have chunk id")), - failed_module.readable_identifier(&compilation.options.context), - selected_module.readable_identifier(&compilation.options.context) - ), - ) - .with_file(Some(output_path.to_owned().into())) - .with_chunk(Some(chunk_ukey.as_u32())) - })); - } - manifest.push(RenderManifestEntry::new( - source.boxed(), - output_path, - asset_info, - false, - false, - )); + let (source, more_diagnostics) = compilation + .old_cache + .chunk_render_occasion + .use_cache(compilation, chunk, &SourceType::Css, || async { + let (source, diagnostics) = self.render_chunk( + compilation, + &module_graph, + chunk, + &output_path, + css_import_modules, + css_modules, + )?; + Ok((CachedSource::new(source).boxed(), diagnostics)) + }) + .await?; + + diagnostics.extend(more_diagnostics); + manifest.push(RenderManifestEntry { + source: source.boxed(), + filename: output_path, + has_filename: false, + info: asset_info, + auxiliary: false, + }); Ok(()) } diff --git a/crates/rspack_plugin_css/src/plugin/mod.rs b/crates/rspack_plugin_css/src/plugin/mod.rs index 100c916c9d2..0f552f81db6 100644 --- a/crates/rspack_plugin_css/src/plugin/mod.rs +++ b/crates/rspack_plugin_css/src/plugin/mod.rs @@ -3,7 +3,7 @@ mod impl_plugin_for_css_plugin; use std::cmp::{self, Reverse}; use rspack_collections::{DatabaseItem, IdentifierSet}; -use rspack_core::{Chunk, ChunkGraph, Compilation, Module, ModuleGraph, SourceType}; +use rspack_core::{Chunk, Compilation, Module}; use rspack_core::{ChunkUkey, ModuleIdentifier}; use rspack_hook::plugin; @@ -19,31 +19,18 @@ pub struct CssOrderConflicts { } impl CssPlugin { - pub(crate) fn get_ordered_chunk_css_modules<'chunk_graph>( + pub(crate) fn get_ordered_chunk_css_modules<'a>( chunk: &Chunk, - chunk_graph: &'chunk_graph ChunkGraph, - module_graph: &'chunk_graph ModuleGraph, compilation: &Compilation, - ) -> ( - Vec<&'chunk_graph dyn Module>, - Option>, - ) { + mut css_import_modules: Vec<&'a dyn Module>, + mut css_modules: Vec<&'a dyn Module>, + ) -> (Vec<&'a dyn Module>, Option>) { + css_import_modules.sort_unstable_by_key(|module| module.identifier()); let (mut external_css_modules, conflicts_external) = - Self::get_ordered_chunk_css_modules_by_type( - chunk, - chunk_graph, - module_graph, - compilation, - SourceType::CssImport, - ); + Self::get_modules_in_order(chunk, css_import_modules, compilation); - let (mut css_modules, conflicts) = Self::get_ordered_chunk_css_modules_by_type( - chunk, - chunk_graph, - module_graph, - compilation, - SourceType::Css, - ); + css_modules.sort_unstable_by_key(|module| module.identifier()); + let (mut css_modules, conflicts) = Self::get_modules_in_order(chunk, css_modules, compilation); external_css_modules.append(&mut css_modules); @@ -60,27 +47,6 @@ impl CssPlugin { (external_css_modules, conflicts) } - fn get_ordered_chunk_css_modules_by_type<'chunk_graph>( - chunk: &Chunk, - chunk_graph: &'chunk_graph ChunkGraph, - module_graph: &'chunk_graph ModuleGraph, - compilation: &Compilation, - source_type: SourceType, - ) -> ( - Vec<&'chunk_graph dyn Module>, - Option>, - ) { - // Align with https://github.com/webpack/webpack/blob/8241da7f1e75c5581ba535d127fa66aeb9eb2ac8/lib/css/CssModulesPlugin.js#L368 - let mut css_modules = chunk_graph - .get_chunk_modules_iterable_by_source_type(&chunk.ukey(), source_type, module_graph) - .collect::>(); - css_modules.sort_unstable_by_key(|module| module.identifier()); - - let (css_modules, conflicts) = Self::get_modules_in_order(chunk, css_modules, compilation); - - (css_modules, conflicts) - } - pub fn get_modules_in_order<'module>( chunk: &Chunk, modules: Vec<&'module dyn Module>, diff --git a/crates/rspack_plugin_extract_css/src/plugin.rs b/crates/rspack_plugin_extract_css/src/plugin.rs index 7ab1b7ed3a3..21ecd7d6960 100644 --- a/crates/rspack_plugin_extract_css/src/plugin.rs +++ b/crates/rspack_plugin_extract_css/src/plugin.rs @@ -4,7 +4,7 @@ use std::{borrow::Cow, cmp::max, hash::Hash, sync::Arc}; use cow_utils::CowUtils; use regex::Regex; use rspack_collections::{DatabaseItem, IdentifierMap, IdentifierSet, UkeySet}; -use rspack_core::ChunkGraph; +use rspack_core::rspack_sources::{BoxSource, CachedSource, SourceExt}; use rspack_core::{ rspack_sources::{ConcatSource, RawSource, SourceMap, SourceMapSource, WithoutOriginalOptions}, ApplyContext, Chunk, ChunkGroupUkey, ChunkKind, ChunkUkey, Compilation, CompilationContentHash, @@ -13,6 +13,7 @@ use rspack_core::{ ModuleType, NormalModuleFactoryParser, ParserAndGenerator, ParserOptions, PathData, Plugin, PluginContext, RenderManifestEntry, RuntimeGlobals, SourceType, }; +use rspack_core::{AssetInfo, ChunkGraph}; use rspack_error::{Diagnostic, Result}; use rspack_hash::RspackHash; use rspack_hook::{plugin, plugin_hook}; @@ -116,7 +117,7 @@ impl PluginCssExtract { fn sort_modules<'comp>( &self, chunk: &Chunk, - modules: Vec<&dyn Module>, + modules: &[&dyn Module], compilation: &'comp Compilation, module_graph: &'comp ModuleGraph<'comp>, ) -> (Vec<&'comp dyn Module>, Option>) { @@ -297,17 +298,67 @@ impl PluginCssExtract { async fn render_content_asset<'comp>( &self, chunk: &Chunk, - rendered_modules: Vec<&dyn Module>, - filename_template: &Filename, + rendered_modules: &[&dyn Module], + filename: &str, compilation: &'comp Compilation, - path_data: PathData<'comp>, - ) -> Result<(RenderManifestEntry, Option>)> { + ) -> (BoxSource, Vec) { let module_graph = compilation.get_module_graph(); // mini-extract-plugin has different conflict order in some cases, // for compatibility, we cannot use experiments.css sorting algorithm let (used_modules, conflicts) = self.sort_modules(chunk, rendered_modules, compilation, &module_graph); + let mut diagnostics = Vec::new(); + if let Some(conflicts) = conflicts { + diagnostics.extend(conflicts.into_iter().map(|conflict| { + let chunk = compilation.chunk_by_ukey.expect_get(&conflict.chunk); + let fallback_module = module_graph + .module_by_identifier(&conflict.fallback_module) + .expect("should have module"); + + Diagnostic::warn( + "".into(), + format!( + r#"chunk {} [{PLUGIN_NAME}] +Conflicting order. Following module has been added: + * {} +despite it was not able to fulfill desired ordering with these modules: +{}"#, + chunk.name().unwrap_or(chunk.id().unwrap_or_default()), + fallback_module.readable_identifier(&compilation.options.context), + conflict + .reasons + .iter() + .map(|(m, failed_reasons, good_reasons)| { + let m = module_graph + .module_by_identifier(m) + .expect("should have module"); + + format!( + " * {}\n - couldn't fulfill desired order of chunk group(s) {}{}", + m.readable_identifier(&compilation.options.context), + failed_reasons + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_default(), + good_reasons + .as_ref() + .map(|s| format!( + "\n - while fulfilling desired order of chunk group(s) {}", + s.as_str() + )) + .unwrap_or_default(), + ) + }) + .collect::>() + .join("\n") + ), + ) + .with_file(Some(filename.to_owned().into())) + .with_chunk(Some(chunk.ukey().as_u32())) + })); + } + let used_modules = used_modules .into_iter() .filter_map(|module| module.downcast_ref::()); @@ -315,8 +366,6 @@ impl PluginCssExtract { let mut source = ConcatSource::default(); let mut external_source = ConcatSource::default(); - let (filename, asset_info) = compilation.get_path_with_info(filename_template, path_data)?; - for module in used_modules { let content = Cow::Borrowed(module.content.as_str()); let readable_identifier = module.readable_identifier(&compilation.options.context); @@ -368,7 +417,7 @@ impl PluginCssExtract { source.add(RawSource::from(format!("@layer {} {{\n", layer))); } - let undo_path = get_undo_path(&filename, compilation.options.output.path.as_str(), false); + let undo_path = get_undo_path(filename, compilation.options.output.path.as_str(), false); let content = content.cow_replace(ABSOLUTE_PUBLIC_PATH, ""); let content = content.cow_replace(SINGLE_DOT_PATH_SEGMENT, "."); @@ -408,16 +457,7 @@ impl PluginCssExtract { } external_source.add(source); - Ok(( - RenderManifestEntry::new( - Arc::new(external_source), - filename, - asset_info, - false, - false, - ), - conflicts, - )) + (external_source.boxed(), diagnostics) } } @@ -568,74 +608,43 @@ async fn render_manifest( &self.options.chunk_filename }; - let (render_result, conflicts) = self - .render_content_asset( - chunk, - rendered_modules, - filename_template, - compilation, - PathData::default() - .chunk_id_optional(chunk.id()) - .chunk_hash_optional(chunk.rendered_hash( - &compilation.chunk_hashes_results, - compilation.options.output.hash_digest_length, - )) - .chunk_name_optional(chunk.name_for_filename_template()) - .content_hash_optional(chunk.rendered_content_hash_by_source_type( - &compilation.chunk_hashes_results, - &SOURCE_TYPE[0], - compilation.options.output.hash_digest_length, - )), - ) + let mut asset_info = AssetInfo::default(); + let filename = compilation.get_path_with_info( + filename_template, + PathData::default() + .chunk_id_optional(chunk.id()) + .chunk_hash_optional(chunk.rendered_hash( + &compilation.chunk_hashes_results, + compilation.options.output.hash_digest_length, + )) + .chunk_name_optional(chunk.name_for_filename_template()) + .content_hash_optional(chunk.rendered_content_hash_by_source_type( + &compilation.chunk_hashes_results, + &SOURCE_TYPE[0], + compilation.options.output.hash_digest_length, + )), + &mut asset_info, + )?; + + let (source, more_diagnostics) = compilation + .old_cache + .chunk_render_occasion + .use_cache(compilation, chunk, &SOURCE_TYPE[0], || async { + let (source, diagnostics) = self + .render_content_asset(chunk, &rendered_modules, &filename, compilation) + .await; + Ok((CachedSource::new(source).boxed(), diagnostics)) + }) .await?; - if let Some(conflicts) = conflicts { - diagnostics.extend(conflicts.into_iter().map(|conflict| { - let chunk = compilation.chunk_by_ukey.expect_get(&conflict.chunk); - let fallback_module = module_graph - .module_by_identifier(&conflict.fallback_module) - .expect("should have module"); - - Diagnostic::warn( - "".into(), - format!( - "chunk {} [{PLUGIN_NAME}]\nConflicting order. Following module has been added:\n * {} -despite it was not able to fulfill desired ordering with these modules:\n{}", - chunk.name().unwrap_or(chunk.id().unwrap_or_default()), - fallback_module.readable_identifier(&compilation.options.context), - conflict - .reasons - .iter() - .map(|(m, failed_reasons, good_reasons)| { - let m = module_graph - .module_by_identifier(m) - .expect("should have module"); - - format!( - " * {}\n - couldn't fulfill desired order of chunk group(s) {}{}", - m.readable_identifier(&compilation.options.context), - failed_reasons - .as_ref() - .map(|s| s.as_str()) - .unwrap_or_default(), - good_reasons - .as_ref() - .map(|s| format!( - "\n - while fulfilling desired order of chunk group(s) {}", - s.as_str() - )) - .unwrap_or_default(), - ) - }) - .collect::>() - .join("\n") - ), - ) - .with_file(Some(render_result.filename().to_owned().into())) - .with_chunk(Some(chunk_ukey.as_u32())) - })); - } - manifest.push(render_result); + diagnostics.extend(more_diagnostics); + manifest.push(RenderManifestEntry { + source, + filename, + has_filename: false, + info: asset_info, + auxiliary: false, + }); Ok(()) } diff --git a/crates/rspack_plugin_hmr/src/lib.rs b/crates/rspack_plugin_hmr/src/lib.rs index 7fac55bc58c..49a4715417d 100644 --- a/crates/rspack_plugin_hmr/src/lib.rs +++ b/crates/rspack_plugin_hmr/src/lib.rs @@ -257,8 +257,8 @@ async fn process_assets(&self, compilation: &mut Compilation) -> Result<()> { compilation.extend_diagnostics(diagnostics); for entry in manifest { - let filename = if entry.has_filename() { - entry.filename().to_string() + let filename = if entry.has_filename { + entry.filename.to_string() } else { compilation .get_path( diff --git a/crates/rspack_plugin_html/src/asset.rs b/crates/rspack_plugin_html/src/asset.rs index 80462f3d053..525e01c2315 100644 --- a/crates/rspack_plugin_html/src/asset.rs +++ b/crates/rspack_plugin_html/src/asset.rs @@ -13,7 +13,7 @@ use rayon::prelude::*; use rspack_core::{ parse_to_url, rspack_sources::{RawSource, SourceExt}, - Compilation, CompilationAsset, Filename, NoFilenameFn, PathData, + AssetInfo, Compilation, CompilationAsset, Filename, NoFilenameFn, PathData, }; use rspack_error::{miette, AnyhowError}; use rspack_paths::Utf8PathBuf; @@ -343,12 +343,14 @@ pub fn create_html_asset( ) -> (String, CompilationAsset) { let hash = hash_for_source(html); - let (output_path, asset_info) = compilation + let mut asset_info = AssetInfo::default(); + let output_path = compilation .get_path_with_info( output_file_name, PathData::default() .filename(template_file_name) .content_hash(&hash), + &mut asset_info, ) .always_ok(); diff --git a/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs b/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs index ed4151cd7e9..f08c917d180 100644 --- a/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs +++ b/crates/rspack_plugin_javascript/src/plugin/impl_plugin_for_js_plugin.rs @@ -2,9 +2,9 @@ use std::hash::Hash; use std::sync::Arc; use async_trait::async_trait; -use rspack_core::rspack_sources::BoxSource; +use rspack_core::rspack_sources::{BoxSource, CachedSource, SourceExt}; use rspack_core::{ - get_js_chunk_filename_template, ChunkGraph, ChunkKind, ChunkUkey, Compilation, + get_js_chunk_filename_template, AssetInfo, ChunkGraph, ChunkKind, ChunkUkey, Compilation, CompilationAdditionalTreeRuntimeRequirements, CompilationChunkHash, CompilationContentHash, CompilationParams, CompilationRenderManifest, CompilerCompilation, CompilerOptions, DependencyType, IgnoreErrorModuleFactory, ModuleGraph, ModuleType, ParserAndGenerator, PathData, @@ -239,29 +239,42 @@ async fn render_manifest( _diagnostics: &mut Vec, ) -> Result<()> { let chunk = compilation.chunk_by_ukey.expect_get(chunk_ukey); - let source = if matches!(chunk.kind(), ChunkKind::HotUpdate) { - self.render_chunk(compilation, chunk_ukey).await? - } else if chunk.has_runtime(&compilation.chunk_group_by_ukey) { - self.render_main(compilation, chunk_ukey).await? - } else { - if !chunk_has_js( + let is_hot_update = matches!(chunk.kind(), ChunkKind::HotUpdate); + let is_main_chunk = chunk.has_runtime(&compilation.chunk_group_by_ukey); + if !is_hot_update + && !is_main_chunk + && !chunk_has_js( chunk_ukey, &compilation.chunk_graph, &compilation.get_module_graph(), - ) { - return Ok(()); - } - - self.render_chunk(compilation, chunk_ukey).await? - }; + ) + { + return Ok(()); + } + let (source, _) = compilation + .old_cache + .chunk_render_occasion + .use_cache(compilation, chunk, &SourceType::JavaScript, || async { + let source = if is_hot_update { + self.render_chunk(compilation, chunk_ukey).await? + } else if is_main_chunk { + self.render_main(compilation, chunk_ukey).await? + } else { + self.render_chunk(compilation, chunk_ukey).await? + }; + Ok((CachedSource::new(source).boxed(), Vec::new())) + }) + .await?; let filename_template = get_js_chunk_filename_template( chunk, &compilation.options.output, &compilation.chunk_group_by_ukey, ); - let (output_path, mut asset_info) = compilation.get_path_with_info( - filename_template, + let mut asset_info = AssetInfo::default(); + asset_info.set_javascript_module(compilation.options.output.module); + let output_path = compilation.get_path_with_info( + &filename_template, PathData::default() .chunk_hash_optional(chunk.rendered_hash( &compilation.chunk_hashes_results, @@ -275,15 +288,16 @@ async fn render_manifest( compilation.options.output.hash_digest_length, )) .runtime(chunk.runtime().as_str()), + &mut asset_info, )?; asset_info.set_javascript_module(compilation.options.output.module); - manifest.push(RenderManifestEntry::new( + manifest.push(RenderManifestEntry { source, - output_path, - asset_info, - false, - false, - )); + filename: output_path, + has_filename: false, + info: asset_info, + auxiliary: false, + }); Ok(()) } diff --git a/crates/rspack_plugin_runtime/src/helpers.rs b/crates/rspack_plugin_runtime/src/helpers.rs index 3df7ab66dc3..945ef49e9ce 100644 --- a/crates/rspack_plugin_runtime/src/helpers.rs +++ b/crates/rspack_plugin_runtime/src/helpers.rs @@ -260,7 +260,7 @@ pub fn get_chunk_output_name(chunk: &Chunk, compilation: &Compilation) -> Result &compilation.chunk_group_by_ukey, ); compilation.get_path( - filename, + &filename, PathData::default() .chunk_id_optional(chunk.id()) .chunk_hash_optional(chunk.rendered_hash( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs index 639bb378c72..224da4cd3c3 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/auto_public_path.rs @@ -43,7 +43,7 @@ impl RuntimeModule for AutoPublicPathRuntimeModule { &compilation.chunk_group_by_ukey, ); let filename = compilation.get_path( - filename, + &filename, PathData::default() .chunk_id_optional(chunk.id()) .chunk_hash_optional(chunk.rendered_hash( diff --git a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs index 72ab7cb6ada..a386e1a77a6 100644 --- a/crates/rspack_plugin_runtime/src/runtime_module/utils.rs +++ b/crates/rspack_plugin_runtime/src/runtime_module/utils.rs @@ -128,7 +128,7 @@ pub fn get_output_dir( &compilation.chunk_group_by_ukey, ); let output_dir = compilation.get_path( - filename, + &filename, PathData::default() .chunk_id_optional(chunk.id()) .chunk_hash_optional(chunk.rendered_hash( diff --git a/crates/rspack_plugin_wasm/src/wasm_plugin.rs b/crates/rspack_plugin_wasm/src/wasm_plugin.rs index 12264d4a8b7..9e13295d2da 100644 --- a/crates/rspack_plugin_wasm/src/wasm_plugin.rs +++ b/crates/rspack_plugin_wasm/src/wasm_plugin.rs @@ -66,7 +66,14 @@ async fn render_manifest( .get(&m.identifier()) .map(|s| s.clone()) .expect("should have wasm_filename"); - RenderManifestEntry::new(source.clone(), output_path, asset_info, false, false) + + RenderManifestEntry { + source: source.clone(), + filename: output_path, + has_filename: false, + info: asset_info, + auxiliary: false, + } }); Ok(result)