diff --git a/.vscode/settings.json b/.vscode/settings.json index a08a0c167a93..4866c63edf39 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,5 +43,5 @@ "ukey", "Ukey" ], - "rust-analyzer.checkOnSave": true + "rust-analyzer.checkOnSave": false } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c740cddfb52e..d1c69ccef95f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4162,6 +4162,7 @@ dependencies = [ "rspack_paths", "rspack_plugin_html", "rspack_plugin_javascript", + "rspack_plugin_rsdoctor", "rspack_tracing", "rspack_util", "tracing", @@ -4671,6 +4672,7 @@ version = "0.2.0" dependencies = [ "async-trait", "cow-utils", + "dashmap 6.1.0", "futures", "indexmap 2.7.0", "rayon", @@ -4678,7 +4680,9 @@ dependencies = [ "rspack_core", "rspack_error", "rspack_hook", + "rspack_util", "rustc-hash 2.1.0", + "tokio", "tracing", ] diff --git a/crates/node_binding/Cargo.toml b/crates/node_binding/Cargo.toml index 90e99cf6e954..4672640bc0af 100644 --- a/crates/node_binding/Cargo.toml +++ b/crates/node_binding/Cargo.toml @@ -27,6 +27,7 @@ rspack_napi = { workspace = true } rspack_paths = { workspace = true } rspack_plugin_html = { workspace = true } rspack_plugin_javascript = { workspace = true } +rspack_plugin_rsdoctor = { workspace = true } rspack_util = { workspace = true } rspack_tracing = { workspace = true } diff --git a/crates/node_binding/src/plugins/interceptor.rs b/crates/node_binding/src/plugins/interceptor.rs index 38484881901e..4cde0b3e217b 100644 --- a/crates/node_binding/src/plugins/interceptor.rs +++ b/crates/node_binding/src/plugins/interceptor.rs @@ -19,8 +19,10 @@ use rspack_binding_values::{ JsContextModuleFactoryBeforeResolveDataWrapper, JsContextModuleFactoryBeforeResolveResult, JsCreateData, JsExecuteModuleArg, JsFactorizeArgs, JsFactorizeOutput, JsModuleWrapper, JsNormalModuleFactoryCreateModuleArgs, JsResolveArgs, JsResolveForSchemeArgs, - JsResolveForSchemeOutput, JsResolveOutput, JsRuntimeGlobals, JsRuntimeModule, JsRuntimeModuleArg, - JsRuntimeRequirementInTreeArg, JsRuntimeRequirementInTreeResult, ToJsCompatSourceOwned, + JsResolveForSchemeOutput, JsResolveOutput, JsRsdoctorAsset, JsRsdoctorChunkGraph, + JsRsdoctorModuleGraph, JsRsdoctorModuleSource, JsRuntimeGlobals, JsRuntimeModule, + JsRuntimeModuleArg, JsRuntimeRequirementInTreeArg, JsRuntimeRequirementInTreeResult, + ToJsCompatSourceOwned, }; use rspack_collections::IdentifierSet; use rspack_core::{ @@ -67,6 +69,12 @@ use rspack_plugin_html::{ HtmlPluginBeforeAssetTagGenerationHook, HtmlPluginBeforeEmit, HtmlPluginBeforeEmitHook, }; use rspack_plugin_javascript::{JavascriptModulesChunkHash, JavascriptModulesChunkHashHook}; +use rspack_plugin_rsdoctor::{ + RsdoctorAsset, RsdoctorChunkGraph, RsdoctorModuleGraph, RsdoctorModuleSource, + RsdoctorPluginAssets, RsdoctorPluginAssetsHook, RsdoctorPluginChunkGraph, + RsdoctorPluginChunkGraphHook, RsdoctorPluginModuleGraph, RsdoctorPluginModuleGraphHook, + RsdoctorPluginModuleSources, RsdoctorPluginModuleSourcesHook, +}; #[napi(object)] pub struct JsTap<'f> { @@ -386,6 +394,10 @@ pub enum RegisterJsTapKind { HtmlPluginAfterTemplateExecution, HtmlPluginBeforeEmit, HtmlPluginAfterEmit, + RsdoctorPluginModuleGraph, + RsdoctorPluginChunkGraph, + RsdoctorPluginModuleSources, + RsdoctorPluginAssets, } #[derive(Default, Clone)] @@ -559,6 +571,7 @@ pub struct RegisterJsTaps { ts_type = "(stages: Array) => Array<{ function: ((arg: JsChunk) => Buffer); stage: number; }>" )] pub register_javascript_modules_chunk_hash_taps: RegisterFunction, + // html plugin #[napi( ts_type = "(stages: Array) => Array<{ function: ((arg: JsBeforeAssetTagGenerationData) => JsBeforeAssetTagGenerationData); stage: number; }>" )] @@ -589,6 +602,27 @@ pub struct RegisterJsTaps { )] pub register_html_plugin_after_emit_taps: RegisterFunction>, + // rsdoctor plugin + #[napi( + ts_type = "(stages: Array) => Array<{ function: ((arg: JsRsdoctorModuleGraph) => Promise); stage: number; }>" + )] + pub register_rsdoctor_plugin_module_graph_taps: + RegisterFunction>>, + #[napi( + ts_type = "(stages: Array) => Array<{ function: ((arg: JsRsdoctorChunkGraph) => Promise); stage: number; }>" + )] + pub register_rsdoctor_plugin_chunk_graph_taps: + RegisterFunction>>, + #[napi( + ts_type = "(stages: Array) => Array<{ function: ((arg: Vec) => Promise); stage: number; }>" + )] + pub register_rsdoctor_plugin_module_sources_taps: + RegisterFunction, Promise>>, + #[napi( + ts_type = "(stages: Array) => Array<{ function: ((arg: Vec) => Promise); stage: number; }>" + )] + pub register_rsdoctor_plugin_assets_taps: + RegisterFunction, Promise>>, } /* Compiler Hooks */ @@ -936,6 +970,43 @@ define_register!( skip = true, ); +/* Rsdoctor Plugin Hooks */ +define_register!( + RegisterRsdoctorPluginModuleGraphTaps, + tap = RsdoctorPluginModuleGraphTap>> @ RsdoctorPluginModuleGraphHook, + cache = true, + sync = false, + kind = RegisterJsTapKind::RsdoctorPluginModuleGraph, + skip = true, +); + +define_register!( + RegisterRsdoctorPluginChunkGraphTaps, + tap = RsdoctorPluginChunkGraphTap>> @ RsdoctorPluginChunkGraphHook, + cache = true, + sync = false, + kind = RegisterJsTapKind::RsdoctorPluginChunkGraph, + skip = true, +); + +define_register!( + RegisterRsdoctorPluginAssetsTaps, + tap = RsdoctorPluginAssetsTap, Promise>> @ RsdoctorPluginAssetsHook, + cache = true, + sync = false, + kind = RegisterJsTapKind::RsdoctorPluginAssets, + skip = true, +); + +define_register!( + RegisterRsdoctorPluginModuleSourcesTaps, + tap = RsdoctorPluginModuleSourcesTap, Promise>> @ RsdoctorPluginModuleSourcesHook, + cache = true, + sync = false, + kind = RegisterJsTapKind::RsdoctorPluginModuleSources, + skip = true, +); + #[async_trait] impl CompilerThisCompilation for CompilerThisCompilationTap { async fn run( @@ -1768,3 +1839,77 @@ impl HtmlPluginAfterEmit for HtmlPluginAfterEmitTap { self.stage } } + +#[async_trait] +impl RsdoctorPluginModuleGraph for RsdoctorPluginModuleGraphTap { + async fn run(&self, data: &mut RsdoctorModuleGraph) -> rspack_error::Result> { + let data = std::mem::take(data); + let bail = self + .function + .call_with_promise(JsRsdoctorModuleGraph::from(data)) + .await?; + Ok(bail) + } + + fn stage(&self) -> i32 { + self.stage + } +} + +#[async_trait] +impl RsdoctorPluginChunkGraph for RsdoctorPluginChunkGraphTap { + async fn run(&self, data: &mut RsdoctorChunkGraph) -> rspack_error::Result> { + let data = std::mem::take(data); + let bail = self + .function + .call_with_promise(JsRsdoctorChunkGraph::from(data)) + .await?; + Ok(bail) + } + + fn stage(&self) -> i32 { + self.stage + } +} + +#[async_trait] +impl RsdoctorPluginModuleSources for RsdoctorPluginModuleSourcesTap { + async fn run(&self, data: &mut Vec) -> rspack_error::Result> { + let data = std::mem::take(data); + let bail = self + .function + .call_with_promise( + data + .into_iter() + .map(JsRsdoctorModuleSource::from) + .collect::>(), + ) + .await?; + Ok(bail) + } + + fn stage(&self) -> i32 { + self.stage + } +} + +#[async_trait] +impl RsdoctorPluginAssets for RsdoctorPluginAssetsTap { + async fn run(&self, data: &mut Vec) -> rspack_error::Result> { + let data = std::mem::take(data); + let bail = self + .function + .call_with_promise( + data + .into_iter() + .map(JsRsdoctorAsset::from) + .collect::>(), + ) + .await?; + Ok(bail) + } + + fn stage(&self) -> i32 { + self.stage + } +} diff --git a/crates/node_binding/src/plugins/mod.rs b/crates/node_binding/src/plugins/mod.rs index 3d5a5b9a18f1..90b92044783c 100644 --- a/crates/node_binding/src/plugins/mod.rs +++ b/crates/node_binding/src/plugins/mod.rs @@ -14,6 +14,7 @@ use rspack_hook::plugin_hook; use rspack_hook::Hook as _; use rspack_plugin_html::HtmlRspackPlugin; use rspack_plugin_javascript::JsPlugin; +use rspack_plugin_rsdoctor::RsdoctorPlugin; use self::interceptor::*; @@ -66,6 +67,10 @@ pub struct JsHooksAdapterPlugin { register_html_plugin_after_template_execution_taps: RegisterHtmlPluginAfterTemplateExecutionTaps, register_html_plugin_before_emit_taps: RegisterHtmlPluginBeforeEmitTaps, register_html_plugin_after_emit_taps: RegisterHtmlPluginAfterEmitTaps, + register_rsdoctor_plugin_module_graph_taps: RegisterRsdoctorPluginModuleGraphTaps, + register_rsdoctor_plugin_chunk_graph_taps: RegisterRsdoctorPluginChunkGraphTaps, + register_rsdoctor_plugin_assets_taps: RegisterRsdoctorPluginAssetsTaps, + register_rsdoctor_plugin_module_sources_taps: RegisterRsdoctorPluginModuleSourcesTaps, } impl fmt::Debug for JsHooksAdapterPlugin { @@ -310,6 +315,12 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin { .compilation .tap(html_hooks_adapter_compilation::new(self)); + ctx + .context + .compiler_hooks + .compilation + .tap(rsdoctor_hooks_adapter_compilation::new(self)); + Ok(()) } @@ -395,6 +406,14 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin { .clear_cache(); self.register_html_plugin_before_emit_taps.clear_cache(); self.register_html_plugin_after_emit_taps.clear_cache(); + self + .register_rsdoctor_plugin_module_graph_taps + .clear_cache(); + self.register_rsdoctor_plugin_chunk_graph_taps.clear_cache(); + self.register_rsdoctor_plugin_assets_taps.clear_cache(); + self + .register_rsdoctor_plugin_module_sources_taps + .clear_cache(); } } @@ -447,6 +466,29 @@ async fn html_hooks_adapter_compilation( Ok(()) } +#[plugin_hook(CompilerCompilation for JsHooksAdapterPlugin)] +async fn rsdoctor_hooks_adapter_compilation( + &self, + compilation: &mut Compilation, + _params: &mut CompilationParams, +) -> rspack_error::Result<()> { + let mut hooks = RsdoctorPlugin::get_compilation_hooks_mut(compilation); + hooks + .module_graph + .intercept(self.register_rsdoctor_plugin_module_graph_taps.clone()); + hooks + .chunk_graph + .intercept(self.register_rsdoctor_plugin_chunk_graph_taps.clone()); + hooks + .assets + .intercept(self.register_rsdoctor_plugin_assets_taps.clone()); + hooks + .module_sources + .intercept(self.register_rsdoctor_plugin_module_sources_taps.clone()); + + Ok(()) +} + impl JsHooksAdapterPlugin { pub fn from_js_hooks(_env: Env, register_js_taps: RegisterJsTaps) -> Result { let non_skippable_registers = NonSkippableRegisters::default(); @@ -631,6 +673,22 @@ impl JsHooksAdapterPlugin { register_js_taps.register_html_plugin_after_emit_taps, non_skippable_registers.clone(), ), + register_rsdoctor_plugin_module_graph_taps: RegisterRsdoctorPluginModuleGraphTaps::new( + register_js_taps.register_rsdoctor_plugin_module_graph_taps, + non_skippable_registers.clone(), + ), + register_rsdoctor_plugin_chunk_graph_taps: RegisterRsdoctorPluginChunkGraphTaps::new( + register_js_taps.register_rsdoctor_plugin_chunk_graph_taps, + non_skippable_registers.clone(), + ), + register_rsdoctor_plugin_assets_taps: RegisterRsdoctorPluginAssetsTaps::new( + register_js_taps.register_rsdoctor_plugin_assets_taps, + non_skippable_registers.clone(), + ), + register_rsdoctor_plugin_module_sources_taps: RegisterRsdoctorPluginModuleSourcesTaps::new( + register_js_taps.register_rsdoctor_plugin_module_sources_taps, + non_skippable_registers.clone(), + ), non_skippable_registers, } .into(), diff --git a/crates/rspack_binding_values/src/lib.rs b/crates/rspack_binding_values/src/lib.rs index 35201ffc78b0..dd09e8cc25ca 100644 --- a/crates/rspack_binding_values/src/lib.rs +++ b/crates/rspack_binding_values/src/lib.rs @@ -26,6 +26,7 @@ mod plugins; mod raw_options; mod resolver; mod resource_data; +mod rsdoctor; mod rspack_error; mod runtime; mod source; @@ -57,6 +58,7 @@ pub(crate) use plugins::*; pub use raw_options::*; pub use resolver::*; pub use resource_data::*; +pub use rsdoctor::*; pub use rspack_error::*; pub use runtime::*; pub use source::*; diff --git a/crates/rspack_binding_values/src/plugins/mod.rs b/crates/rspack_binding_values/src/plugins/mod.rs index 392a1d770573..b8015e2e9c41 100644 --- a/crates/rspack_binding_values/src/plugins/mod.rs +++ b/crates/rspack_binding_values/src/plugins/mod.rs @@ -1,8 +1,6 @@ mod context_replacement; mod js_loader; -mod rsdoctor; pub use context_replacement::*; pub(super) use js_loader::{JsLoaderRspackPlugin, JsLoaderRunner}; pub mod buildtime_plugins; -pub use rsdoctor::*; diff --git a/crates/rspack_binding_values/src/plugins/rsdoctor/mod.rs b/crates/rspack_binding_values/src/plugins/rsdoctor/mod.rs deleted file mode 100644 index 737443b927f3..000000000000 --- a/crates/rspack_binding_values/src/plugins/rsdoctor/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod data; -mod options; - -pub use data::*; -pub use options::*; diff --git a/crates/rspack_binding_values/src/plugins/rsdoctor/options.rs b/crates/rspack_binding_values/src/plugins/rsdoctor/options.rs deleted file mode 100644 index b352d7dc7e49..000000000000 --- a/crates/rspack_binding_values/src/plugins/rsdoctor/options.rs +++ /dev/null @@ -1,51 +0,0 @@ -use std::sync::Arc; - -use derive_more::Debug; -use futures::future::BoxFuture; -use napi::bindgen_prelude::*; -use napi_derive::napi; -use rspack_error::Result; -use rspack_napi::threadsafe_function::ThreadsafeFunction; -use rspack_plugin_rsdoctor::{ - RsdoctorChunkGraph, RsdoctorModuleGraph, RsdoctorPluginOptions, SendChunkGraph, SendModuleGraph, -}; - -use super::{JsRsdoctorChunkGraph, JsRsdoctorModuleGraph}; - -pub type RawSendModuleGraphFn = ThreadsafeFunction; -pub type RawSendChunkGraphFn = ThreadsafeFunction; - -#[napi(object, object_to_js = false)] -pub struct RawRsdoctorPluginOptions { - #[napi(ts_type = "(moduleGraph: JsRsdoctorModuleGraph) => Promise")] - pub on_module_graph: Option, - #[napi(ts_type = "(chunkGraph: JsRsdoctorChunkGraph) => Promise")] - pub on_chunk_graph: Option, -} - -impl From for RsdoctorPluginOptions { - fn from(value: RawRsdoctorPluginOptions) -> Self { - let on_module_graph = value.on_module_graph.map(|func| -> SendModuleGraph { - Arc::new( - move |module_graph: RsdoctorModuleGraph| -> BoxFuture<'static, Result<()>> { - let f = func.clone(); - Box::pin(async move { f.call(module_graph.into()).await }) - }, - ) as _ - }); - - let on_chunk_graph = value.on_chunk_graph.map(|func| -> SendChunkGraph { - Arc::new( - move |chunk_graph: RsdoctorChunkGraph| -> BoxFuture<'static, Result<()>> { - let f = func.clone(); - Box::pin(async move { f.call(chunk_graph.into()).await }) - }, - ) as _ - }); - - Self { - on_module_graph, - on_chunk_graph, - } - } -} diff --git a/crates/rspack_binding_values/src/raw_options/raw_builtins/mod.rs b/crates/rspack_binding_values/src/raw_options/raw_builtins/mod.rs index f9cac94c2bae..92a8b588da02 100644 --- a/crates/rspack_binding_values/src/raw_options/raw_builtins/mod.rs +++ b/crates/rspack_binding_values/src/raw_options/raw_builtins/mod.rs @@ -68,6 +68,7 @@ use rspack_plugin_progress::ProgressPlugin; use rspack_plugin_real_content_hash::RealContentHashPlugin; use rspack_plugin_remove_duplicate_modules::RemoveDuplicateModulesPlugin; use rspack_plugin_remove_empty_chunks::RemoveEmptyChunksPlugin; +use rspack_plugin_rsdoctor::RsdoctorPlugin; use rspack_plugin_runtime::{ enable_chunk_loading_plugin, ArrayPushCallbackChunkFormatPlugin, BundlerInfoPlugin, ChunkPrefetchPreloadPlugin, CommonJsChunkFormatPlugin, ModuleChunkFormatPlugin, RuntimePlugin, @@ -102,7 +103,7 @@ use self::{ raw_runtime_chunk::RawRuntimeChunkOptions, raw_size_limits::RawSizeLimitsPluginOptions, }; -use crate::entry::JsEntryPluginOptions; +use crate::{entry::JsEntryPluginOptions, RawRsdoctorPluginOptions}; use crate::{ plugins::JsLoaderRspackPlugin, JsLoaderRunner, RawContextReplacementPluginOptions, RawDynamicEntryPluginOptions, RawEvalDevToolModulePluginOptions, RawExternalItemWrapper, @@ -192,6 +193,7 @@ pub enum BuiltinPluginName { LightningCssMinimizerRspackPlugin, BundlerInfoRspackPlugin, CssExtractRspackPlugin, + RsdoctorRspackPlugin, // rspack js adapter plugins // naming format follow XxxRspackPlugin @@ -558,6 +560,11 @@ impl BuiltinPlugin { let options = raw_options.into(); plugins.push(DllReferenceAgencyPlugin::new(options).boxed()); } + BuiltinPluginName::RsdoctorRspackPlugin => { + let raw_options = downcast_into::(self.options)?; + let options = raw_options.into(); + plugins.push(RsdoctorPlugin::new(options).boxed()); + } } Ok(()) } diff --git a/crates/rspack_binding_values/src/plugins/rsdoctor/data.rs b/crates/rspack_binding_values/src/rsdoctor.rs similarity index 95% rename from crates/rspack_binding_values/src/plugins/rsdoctor/data.rs rename to crates/rspack_binding_values/src/rsdoctor.rs index bb281b87d672..24cb8e682f72 100644 --- a/crates/rspack_binding_values/src/plugins/rsdoctor/data.rs +++ b/crates/rspack_binding_values/src/rsdoctor.rs @@ -2,8 +2,8 @@ use napi_derive::napi; use rspack_plugin_rsdoctor::{ RsdoctorAsset, RsdoctorChunk, RsdoctorChunkGraph, RsdoctorDependency, RsdoctorEntrypoint, RsdoctorExportInfo, RsdoctorModule, RsdoctorModuleGraph, RsdoctorModuleGraphModule, - RsdoctorModuleSource, RsdoctorSideEffect, RsdoctorSourcePosition, RsdoctorSourceRange, - RsdoctorStatement, RsdoctorVariable, + RsdoctorModuleSource, RsdoctorPluginOptions, RsdoctorSideEffect, RsdoctorSourcePosition, + RsdoctorSourceRange, RsdoctorStatement, RsdoctorVariable, }; #[napi(object)] @@ -312,3 +312,14 @@ impl From for JsRsdoctorChunkGraph { } } } + +#[napi(object, object_to_js = false)] +pub struct RawRsdoctorPluginOptions {} + +impl From for RsdoctorPluginOptions { + fn from(_value: RawRsdoctorPluginOptions) -> Self { + Self { + // TODO: rsdoctor plugin options + } + } +} diff --git a/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs b/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs index 008d0f0ef139..7f7463e8fab8 100644 --- a/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs +++ b/crates/rspack_core/src/chunk_graph/chunk_graph_module.rs @@ -261,7 +261,7 @@ impl ChunkGraph { ) -> Option<&UkeySet> { self .chunk_graph_module_by_module_identifier - .get(&module_identifier) + .get(module_identifier) .map(|cgm| &cgm.chunks) } diff --git a/crates/rspack_plugin_rsdoctor/Cargo.toml b/crates/rspack_plugin_rsdoctor/Cargo.toml index 9eee00670df1..507741443a03 100644 --- a/crates/rspack_plugin_rsdoctor/Cargo.toml +++ b/crates/rspack_plugin_rsdoctor/Cargo.toml @@ -10,6 +10,7 @@ version = "0.2.0" [dependencies] async-trait = { workspace = true } cow-utils = { workspace = true } +dashmap = { workspace = true } futures = { workspace = true } indexmap = { workspace = true } rayon = { workspace = true } @@ -17,7 +18,9 @@ rspack_collections = { workspace = true } rspack_core = { workspace = true } rspack_error = { workspace = true } rspack_hook = { workspace = true } +rspack_util = { workspace = true } rustc-hash = { workspace = true } +tokio = { workspace = true } tracing = { workspace = true } [package.metadata.cargo-shear] diff --git a/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs b/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs index b9f8dd0d4926..0522879d5ffa 100644 --- a/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs +++ b/crates/rspack_plugin_rsdoctor/src/chunk_graph.rs @@ -7,8 +7,8 @@ use rspack_core::{ChunkByUkey, ChunkGroupUkey}; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ - AssetUkey as RsdoctorAssetUkey, ChunkUkey as RsdoctorChunkUkey, - EntrypointUkey as RsdoctorEntrypointUkey, RsdoctorAsset, RsdoctorChunk, RsdoctorEntrypoint, + ChunkUkey as RsdoctorChunkUkey, EntrypointUkey as RsdoctorEntrypointUkey, RsdoctorAsset, + RsdoctorChunk, RsdoctorEntrypoint, }; pub fn collect_chunks( @@ -68,7 +68,7 @@ pub fn collect_chunk_dependencies( if let Some(pg) = chunk_group_by_ukey.get(p) { for c in &pg.chunks { if chunk_by_ukey.get(c).is_some() { - parents.insert(c.clone()); + parents.insert(*c); } } } @@ -78,7 +78,7 @@ pub fn collect_chunk_dependencies( if let Some(pg) = chunk_group_by_ukey.get(p) { for c in &pg.chunks { if chunk_by_ukey.get(c).is_some() { - children.insert(c.clone()); + children.insert(*c); } } } @@ -170,27 +170,3 @@ pub fn collect_assets( }) .collect::>() } - -pub fn collect_chunk_assets( - rsd_assets: &HashMap, - chunk_by_ukey: &ChunkByUkey, -) -> HashMap> { - chunk_by_ukey - .keys() - .par_bridge() - .map(|chunk_ukey| { - let rsd_chunk_ukey = chunk_ukey.as_u32() as RsdoctorChunkUkey; - let assets = rsd_assets - .iter() - .filter_map(|(_, asset)| { - if asset.chunks.contains(&rsd_chunk_ukey) { - Some(asset.ukey) - } else { - None - } - }) - .collect::>(); - (rsd_chunk_ukey, assets) - }) - .collect::>() -} diff --git a/crates/rspack_plugin_rsdoctor/src/data.rs b/crates/rspack_plugin_rsdoctor/src/data.rs index 02209db90eb7..d33366cc75c2 100644 --- a/crates/rspack_plugin_rsdoctor/src/data.rs +++ b/crates/rspack_plugin_rsdoctor/src/data.rs @@ -2,7 +2,9 @@ use rspack_collections::Identifier; use rspack_core::DependencyType; use rustc_hash::FxHashSet as HashSet; +#[derive(Debug, Default)] pub enum ModuleKind { + #[default] Normal, Concatenated, } @@ -26,6 +28,7 @@ pub type ExportInfoUkey = usize; pub type VariableUkey = usize; pub type SideEffectUkey = usize; +#[derive(Debug, Default)] pub struct RsdoctorModule { pub ukey: ModuleUkey, pub identifier: Identifier, @@ -39,6 +42,7 @@ pub struct RsdoctorModule { pub chunks: HashSet, } +#[derive(Debug, Default)] pub struct RsdoctorDependency { pub ukey: DependencyUkey, pub kind: DependencyType, @@ -47,6 +51,7 @@ pub struct RsdoctorDependency { pub dependency: ModuleUkey, } +#[derive(Debug, Default)] pub struct RsdoctorChunk { pub ukey: ChunkUkey, pub name: String, @@ -57,25 +62,30 @@ pub struct RsdoctorChunk { pub imported: HashSet, } +#[derive(Debug, Default)] pub struct RsdoctorEntrypoint { pub ukey: EntrypointUkey, pub name: String, pub chunks: HashSet, } +#[derive(Debug, Default)] pub struct RsdoctorAsset { pub ukey: AssetUkey, pub path: String, pub chunks: HashSet, } +#[derive(Debug, Default)] pub struct RsdoctorModuleSource { + pub module: ModuleUkey, pub source_size: usize, pub transform_size: usize, pub source: Option, pub source_map: Option, } +#[derive(Debug, Default)] pub struct RsdoctorModuleGraphModule { pub ukey: ModuleGraphModuleUkey, pub module: ModuleUkey, @@ -85,6 +95,7 @@ pub struct RsdoctorModuleGraphModule { pub dynamic: bool, } +#[derive(Debug, Default)] pub struct RsdoctorSideEffect { pub ukey: SideEffectUkey, pub name: String, @@ -97,6 +108,7 @@ pub struct RsdoctorSideEffect { pub variable: Option, } +#[derive(Debug, Default)] pub struct RsdoctorVariable { pub ukey: VariableUkey, pub name: String, @@ -106,6 +118,7 @@ pub struct RsdoctorVariable { pub exported: Option, } +#[derive(Debug, Default)] pub struct RsdoctorExportInfo { pub ukey: ExportInfoUkey, pub name: String, @@ -115,28 +128,33 @@ pub struct RsdoctorExportInfo { pub side_effects: Vec, } +#[derive(Debug, Default)] pub struct RsdoctorStatement { pub module: ModuleUkey, pub source_position: Option, pub transformed_position: RsdoctorSourceRange, } +#[derive(Debug, Default)] pub struct RsdoctorSourceRange { pub start: RsdoctorSourcePosition, pub end: Option, } +#[derive(Debug, Default)] pub struct RsdoctorSourcePosition { pub line: Option, pub column: Option, pub index: Option, } +#[derive(Debug, Default)] pub struct RsdoctorModuleGraph { pub modules: Vec, pub dependencies: Vec, } +#[derive(Debug, Default)] pub struct RsdoctorChunkGraph { pub chunks: Vec, pub entrypoints: Vec, diff --git a/crates/rspack_plugin_rsdoctor/src/drive.rs b/crates/rspack_plugin_rsdoctor/src/drive.rs new file mode 100644 index 000000000000..f563d6fc0bfa --- /dev/null +++ b/crates/rspack_plugin_rsdoctor/src/drive.rs @@ -0,0 +1,16 @@ +use rspack_hook::define_hook; + +use crate::{RsdoctorAsset, RsdoctorChunkGraph, RsdoctorModuleGraph, RsdoctorModuleSource}; + +define_hook!(RsdoctorPluginModuleGraph: AsyncSeriesBail(data: &mut RsdoctorModuleGraph) -> bool); +define_hook!(RsdoctorPluginChunkGraph: AsyncSeriesBail(data: &mut RsdoctorChunkGraph) -> bool); +define_hook!(RsdoctorPluginModuleSources: AsyncSeriesBail(data: &mut Vec) -> bool); +define_hook!(RsdoctorPluginAssets: AsyncSeriesBail(data: &mut Vec) -> bool); + +#[derive(Debug, Default)] +pub struct RsdoctorPluginHooks { + pub module_graph: RsdoctorPluginModuleGraphHook, + pub chunk_graph: RsdoctorPluginChunkGraphHook, + pub module_sources: RsdoctorPluginModuleSourcesHook, + pub assets: RsdoctorPluginAssetsHook, +} diff --git a/crates/rspack_plugin_rsdoctor/src/lib.rs b/crates/rspack_plugin_rsdoctor/src/lib.rs index 1b5e05b3ffd9..1674e54dec92 100644 --- a/crates/rspack_plugin_rsdoctor/src/lib.rs +++ b/crates/rspack_plugin_rsdoctor/src/lib.rs @@ -1,7 +1,12 @@ mod chunk_graph; mod data; +mod drive; mod module_graph; mod plugin; pub use data::*; -pub use plugin::{RsdoctorPlugin, RsdoctorPluginOptions, SendChunkGraph, SendModuleGraph}; +pub use drive::*; +pub use plugin::{ + RsdoctorPlugin, RsdoctorPluginOptions, SendAssets, SendChunkGraph, SendModuleGraph, + SendModuleSources, +}; diff --git a/crates/rspack_plugin_rsdoctor/src/module_graph.rs b/crates/rspack_plugin_rsdoctor/src/module_graph.rs index e288497ed7d7..87847800ee21 100644 --- a/crates/rspack_plugin_rsdoctor/src/module_graph.rs +++ b/crates/rspack_plugin_rsdoctor/src/module_graph.rs @@ -6,6 +6,7 @@ use rspack_core::{ rspack_sources::MapOptions, BoxModule, ChunkGraph, Compilation, Context, DependencyId, ModuleGraph, }; +use rspack_util::fx_hash::FxDashMap; use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet}; use crate::{ @@ -28,7 +29,7 @@ pub fn collect_modules( let path = if let Some(nfc) = module.name_for_condition() { nfc.to_string() } else { - module.readable_identifier(&context).to_string() + module.readable_identifier(context).to_string() }; let is_concatenated = module.as_concatenated_module().is_some(); let chunks = chunk_graph @@ -89,7 +90,7 @@ pub fn collect_concatenated_modules( pub fn collect_module_dependencies( modules: &IdentifierMap<&BoxModule>, - rsd_modules: &HashMap, + module_ukeys: &FxDashMap, module_graph: &ModuleGraph, ) -> HashMap> { let dependency_ukey_counter = Arc::new(AtomicUsize::new(0)); @@ -97,33 +98,29 @@ pub fn collect_module_dependencies( modules .par_iter() .filter_map(|(module_id, _)| { - let Some(rsd_module_ukey) = rsd_modules.get(module_id).map(|m| m.ukey) else { - return None; - }; + let rsd_module_ukey = module_ukeys.get(module_id)?; let dependencies = module_graph .get_outgoing_connections(module_id) .filter_map(|conn| { - let Some(dep) = module_graph.dependency_by_id(&conn.dependency_id) else { - return None; - }; + let dep = module_graph.dependency_by_id(&conn.dependency_id)?; if let (Some(dep), Some(dep_module)) = ( dep.as_module_dependency(), module_graph .module_identifier_by_dependency_id(&conn.dependency_id) - .and_then(|mid| rsd_modules.get(mid)), + .and_then(|mid| module_ukeys.get(mid).map(|ukey| (*mid, *ukey))), ) { let dep_ukey = dependency_ukey_counter.fetch_add(1, std::sync::atomic::Ordering::Relaxed); return Some(( - dep_module.identifier, + dep_module.0, ( conn.dependency_id, RsdoctorDependency { ukey: dep_ukey, kind: *dep.dependency_type(), request: dep.user_request().into(), - module: rsd_module_ukey, - dependency: dep_module.ukey, + module: *rsd_module_ukey, + dependency: dep_module.1, }, ), )); @@ -139,24 +136,24 @@ pub fn collect_module_dependencies( pub fn collect_module_sources( modules: &IdentifierMap<&BoxModule>, + module_ukeys: &FxDashMap, compilation: &Compilation, -) -> HashMap { +) -> Vec { modules .par_iter() .filter_map(|(module_id, module)| { let source = module.original_source(); let size = module.size(None, Some(compilation)) as usize; - Some(( - module_id.to_owned(), - RsdoctorModuleSource { - source_size: size, - transform_size: size, - source: source.map(|s| s.source().to_string()), - source_map: source - .and_then(|s| s.map(&MapOptions::default())) - .and_then(|m| m.to_json().ok()), - }, - )) + let ukey = module_ukeys.get(module_id)?; + Some(RsdoctorModuleSource { + module: *ukey, + source_size: size, + transform_size: size, + source: source.map(|s| s.source().to_string()), + source_map: source + .and_then(|s| s.map(&MapOptions::default())) + .and_then(|m| m.to_json().ok()), + }) }) - .collect::>() + .collect::>() } diff --git a/crates/rspack_plugin_rsdoctor/src/plugin.rs b/crates/rspack_plugin_rsdoctor/src/plugin.rs index 197a9aa180a4..08a68a4c0a07 100644 --- a/crates/rspack_plugin_rsdoctor/src/plugin.rs +++ b/crates/rspack_plugin_rsdoctor/src/plugin.rs @@ -1,47 +1,47 @@ -use std::future::Future; -use std::pin::Pin; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use async_trait::async_trait; use futures::future::BoxFuture; +use rspack_collections::Identifier; use rspack_core::{ ApplyContext, Compilation, CompilationAfterCodeGeneration, CompilationAfterProcessAssets, - CompilationOptimizeChunkModules, CompilationOptimizeChunks, CompilerOptions, Plugin, - PluginContext, + CompilationId, CompilationOptimizeChunkModules, CompilationOptimizeChunks, CompilationParams, + CompilerCompilation, CompilerOptions, Plugin, PluginContext, }; use rspack_error::Result; use rspack_hook::{plugin, plugin_hook}; +use rspack_util::fx_hash::FxDashMap; use rustc_hash::FxHashMap as HashMap; use crate::chunk_graph::{ - collect_assets, collect_chunk_assets, collect_chunk_dependencies, collect_chunks, - collect_entrypoints, + collect_assets, collect_chunk_dependencies, collect_chunks, collect_entrypoints, }; use crate::module_graph::{ collect_concatenated_modules, collect_module_dependencies, collect_module_sources, collect_modules, }; -use crate::{RsdoctorChunkGraph, RsdoctorModuleGraph}; +use crate::{ + ModuleUkey, RsdoctorAsset, RsdoctorChunkGraph, RsdoctorModuleGraph, RsdoctorModuleSource, + RsdoctorPluginHooks, +}; pub type SendModuleGraph = Arc BoxFuture<'static, Result<()>> + Send + Sync>; pub type SendChunkGraph = Arc BoxFuture<'static, Result<()>> + Send + Sync>; +pub type SendAssets = + Arc) -> BoxFuture<'static, Result<()>> + Send + Sync>; +pub type SendModuleSources = + Arc) -> BoxFuture<'static, Result<()>> + Send + Sync>; -#[derive(Default)] -pub struct RsdoctorPluginOptions { - pub on_module_graph: Option, - pub on_chunk_graph: Option, -} +static COMPILATION_HOOKS_MAP: LazyLock>> = + LazyLock::new(Default::default); -impl std::fmt::Debug for RsdoctorPluginOptions { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("RsdoctorPluginOptions") - .field("on_module_graph", &self.on_module_graph.is_some()) - .field("on_chunk_graph", &self.on_chunk_graph.is_some()) - .finish() - } -} +static MODULE_UKEY_MAP: LazyLock> = + LazyLock::new(FxDashMap::default); + +#[derive(Default, Debug)] +pub struct RsdoctorPluginOptions {} #[plugin] #[derive(Debug)] @@ -49,8 +49,43 @@ pub struct RsdoctorPlugin { pub options: RsdoctorPluginOptions, } +impl RsdoctorPlugin { + pub fn new(config: RsdoctorPluginOptions) -> Self { + Self::new_inner(config) + } + + pub fn get_compilation_hooks( + id: CompilationId, + ) -> dashmap::mapref::one::Ref<'static, CompilationId, Box> { + if !COMPILATION_HOOKS_MAP.contains_key(&id) { + COMPILATION_HOOKS_MAP.insert(id, Default::default()); + } + COMPILATION_HOOKS_MAP + .get(&id) + .expect("should have js plugin drive") + } + + pub fn get_compilation_hooks_mut( + compilation: &Compilation, + ) -> dashmap::mapref::one::RefMut<'_, CompilationId, Box> { + COMPILATION_HOOKS_MAP.entry(compilation.id()).or_default() + } +} + +#[plugin_hook(CompilerCompilation for RsdoctorPlugin)] +async fn compilation( + &self, + _compilation: &mut Compilation, + _params: &mut CompilationParams, +) -> Result<()> { + MODULE_UKEY_MAP.clear(); + Ok(()) +} + #[plugin_hook(CompilationOptimizeChunks for RsdoctorPlugin)] fn optimize_chunks(&self, compilation: &mut Compilation) -> Result> { + let hooks = RsdoctorPlugin::get_compilation_hooks(compilation.id()); + let chunk_by_ukey = &compilation.chunk_by_ukey; let chunk_group_by_ukey = &compilation.chunk_group_by_ukey; let chunk_graph = &compilation.chunk_graph; @@ -79,13 +114,27 @@ fn optimize_chunks(&self, compilation: &mut Compilation) -> Result> chunk_group_by_ukey, )); - // TODO: send rsd_chunks and rsd_entrypoints to the js + tokio::spawn(async move { + match hooks + .chunk_graph + .call(&mut RsdoctorChunkGraph { + chunks: rsd_chunks.into_values().collect::>(), + entrypoints: rsd_entrypoints.into_values().collect::>(), + }) + .await + { + Ok(_) => {} + Err(e) => panic!("rsdoctor send chunk graph failed: {}", e), + }; + }); Ok(None) } #[plugin_hook(CompilationOptimizeChunkModules for RsdoctorPlugin)] async fn optimize_chunk_modules(&self, compilation: &mut Compilation) -> Result> { + let hooks = RsdoctorPlugin::get_compilation_hooks(compilation.id()); + let mut rsd_modules = HashMap::default(); let mut rsd_dependencies = HashMap::default(); @@ -97,7 +146,7 @@ async fn optimize_chunk_modules(&self, compilation: &mut Compilation) -> Result< rsd_modules.extend(collect_modules( &modules, &module_graph, - &chunk_graph, + chunk_graph, &compilation.options.context, )); @@ -109,8 +158,12 @@ async fn optimize_chunk_modules(&self, compilation: &mut Compilation) -> Result< } } + for (module_id, module) in rsd_modules.iter() { + MODULE_UKEY_MAP.insert(*module_id, module.ukey); + } + // 3. collect module dependencies - let dependency_infos = collect_module_dependencies(&modules, &rsd_modules, &module_graph); + let dependency_infos = collect_module_dependencies(&modules, &MODULE_UKEY_MAP, &module_graph); for (origin_module_id, dependencies) in dependency_infos { for (dep_module_id, (dep_id, dependency)) in dependencies { if let Some(rsd_module) = rsd_modules.get_mut(&dep_module_id) { @@ -123,28 +176,59 @@ async fn optimize_chunk_modules(&self, compilation: &mut Compilation) -> Result< } } - // TODO: send rsd_modules and rsd_dependencies to the js + tokio::spawn(async move { + match hooks + .module_graph + .call(&mut RsdoctorModuleGraph { + modules: rsd_modules.into_values().collect::>(), + dependencies: rsd_dependencies.into_values().collect::>(), + }) + .await + { + Ok(_) => {} + Err(e) => panic!("rsdoctor send module graph failed: {}", e), + }; + }); Ok(None) } #[plugin_hook(CompilationAfterCodeGeneration for RsdoctorPlugin)] fn after_code_generation(&self, compilation: &mut Compilation) -> Result<()> { + let hooks = RsdoctorPlugin::get_compilation_hooks(compilation.id()); + let module_graph = compilation.get_module_graph(); let modules = module_graph.modules(); - let rsd_module_sources = collect_module_sources(&modules, compilation); + let mut rsd_module_sources = collect_module_sources(&modules, &MODULE_UKEY_MAP, compilation); - // TODO: send rsd_module_sources to the js + tokio::spawn(async move { + match hooks.module_sources.call(&mut rsd_module_sources).await { + Ok(_) => {} + Err(e) => panic!("rsdoctor send module sources failed: {}", e), + }; + }); Ok(()) } #[plugin_hook(CompilationAfterProcessAssets for RsdoctorPlugin)] async fn after_process_asssets(&self, compilation: &mut Compilation) -> Result<()> { + let hooks = RsdoctorPlugin::get_compilation_hooks(compilation.id()); + let chunk_by_ukey = &compilation.chunk_by_ukey; - let rsd_assets = collect_assets(&compilation.assets(), chunk_by_ukey); - let rsd_chunk_assets = collect_chunk_assets(&rsd_assets, chunk_by_ukey); - // TODO: send rsd_chunk_assets and rsd_assets to the js + let rsd_assets = collect_assets(compilation.assets(), chunk_by_ukey); + + tokio::spawn(async move { + match hooks + .assets + .call(&mut rsd_assets.into_values().collect::>()) + .await + { + Ok(_) => {} + Err(e) => panic!("rsdoctor send assets failed: {}", e), + }; + }); + Ok(()) } @@ -155,6 +239,11 @@ impl Plugin for RsdoctorPlugin { } fn apply(&self, ctx: PluginContext<&mut ApplyContext>, _options: &CompilerOptions) -> Result<()> { + ctx + .context + .compiler_hooks + .compilation + .tap(compilation::new(self)); ctx .context .compilation_hooks