From 78aa2e0f17e4a29bdfb3cf2a280eb3058aaa4cc7 Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Wed, 13 Nov 2024 16:26:22 +0800 Subject: [PATCH] feat: unify js dependency (#8394) --- crates/node_binding/binding.d.ts | 21 +- .../src/compilation/entries.rs | 20 +- .../src/context_module_factory.rs | 10 +- .../rspack_binding_values/src/dependency.rs | 198 ++++++++++++------ .../src/dependency_block.rs | 125 +++++++++++ crates/rspack_binding_values/src/lib.rs | 2 + crates/rspack_binding_values/src/module.rs | 138 ++++-------- .../src/compiler/make/repair/mod.rs | 1 + .../rspack_core/src/context_module_factory.rs | 12 +- packages/rspack/etc/core.api.md | 5 +- packages/rspack/src/Dependency.ts | 10 +- 11 files changed, 334 insertions(+), 208 deletions(-) create mode 100644 crates/rspack_binding_values/src/dependency_block.rs diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 01bfd372837..79455c23db6 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -20,8 +20,8 @@ export declare class ExternalObject { } } export declare class EntryDataDto { - get dependencies(): Array - get includeDependencies(): Array + get dependencies(): JsDependency[] + get includeDependencies(): JsDependency[] get options(): EntryOptionsDto } export type EntryDataDTO = EntryDataDto @@ -108,7 +108,7 @@ export declare class JsContextModuleFactoryAfterResolveData { set regExp(rawRegExp: RegExp | undefined) get recursive(): boolean set recursive(recursive: boolean) - get dependencies(): Array + get dependencies(): JsDependency[] } export declare class JsContextModuleFactoryBeforeResolveData { @@ -138,8 +138,8 @@ export declare class JsDependencies { } export declare class JsDependenciesBlock { - get dependencies(): Array - get blocks(): Array + get dependencies(): JsDependency[] + get blocks(): JsDependenciesBlock[] } export declare class JsDependency { @@ -147,13 +147,6 @@ export declare class JsDependency { get category(): string get request(): string | undefined get critical(): boolean -} - -export declare class JsDependencyMut { - get type(): string - get category(): string - get request(): string | undefined - get critical(): boolean set critical(val: boolean) } @@ -181,8 +174,8 @@ export declare class JsModule { get factoryMeta(): JsFactoryMeta | undefined get type(): string get layer(): string | undefined - get blocks(): Array - get dependencies(): Array + get blocks(): JsDependenciesBlock[] + get dependencies(): JsDependency[] size(ty?: string | undefined | null): number get modules(): JsModule[] | undefined get useSourceMap(): boolean diff --git a/crates/rspack_binding_values/src/compilation/entries.rs b/crates/rspack_binding_values/src/compilation/entries.rs index 31900c6227a..5a13301c090 100644 --- a/crates/rspack_binding_values/src/compilation/entries.rs +++ b/crates/rspack_binding_values/src/compilation/entries.rs @@ -2,7 +2,9 @@ use napi_derive::napi; use rspack_core::{ChunkLoading, Compilation, EntryData, EntryOptions, EntryRuntime}; use rspack_napi::napi::bindgen_prelude::*; -use crate::{dependency::JsDependency, entry::JsEntryOptions, library::JsLibraryOptions}; +use crate::{ + dependency::JsDependency, entry::JsEntryOptions, library::JsLibraryOptions, JsDependencyWrapper, +}; #[napi] pub struct EntryOptionsDTO(EntryOptions); @@ -163,12 +165,12 @@ impl From for EntryData { dependencies: value .dependencies .into_iter() - .map(|dep| *dep.id()) + .map(|dep| dep.dependency_id) .collect::>(), include_dependencies: value .include_dependencies .into_iter() - .map(|dep| *dep.id()) + .map(|dep| dep.dependency_id) .collect::>(), options: value.options.into(), } @@ -183,8 +185,8 @@ pub struct EntryDataDTO { #[napi] impl EntryDataDTO { - #[napi(getter)] - pub fn dependencies(&'static self) -> Vec { + #[napi(getter, ts_return_type = "JsDependency[]")] + pub fn dependencies(&'static self) -> Vec { let module_graph = self.compilation.get_module_graph(); self .entry_data @@ -193,13 +195,13 @@ impl EntryDataDTO { .map(|dependency_id| { #[allow(clippy::unwrap_used)] let dep = module_graph.dependency_by_id(dependency_id).unwrap(); - JsDependency::new(dep) + JsDependencyWrapper::new(dep.as_ref(), self.compilation.id(), Some(self.compilation)) }) .collect::>() } - #[napi(getter)] - pub fn include_dependencies(&'static self) -> Vec { + #[napi(getter, ts_return_type = "JsDependency[]")] + pub fn include_dependencies(&'static self) -> Vec { let module_graph = self.compilation.get_module_graph(); self .entry_data @@ -208,7 +210,7 @@ impl EntryDataDTO { .map(|dependency_id| { #[allow(clippy::unwrap_used)] let dep = module_graph.dependency_by_id(dependency_id).unwrap(); - JsDependency::new(dep) + JsDependencyWrapper::new(dep.as_ref(), self.compilation.id(), Some(self.compilation)) }) .collect::>() } diff --git a/crates/rspack_binding_values/src/context_module_factory.rs b/crates/rspack_binding_values/src/context_module_factory.rs index 46512dda267..28f4ecb8660 100644 --- a/crates/rspack_binding_values/src/context_module_factory.rs +++ b/crates/rspack_binding_values/src/context_module_factory.rs @@ -5,7 +5,7 @@ use napi_derive::napi; use rspack_core::{AfterResolveData, BeforeResolveData}; use rspack_regex::RspackRegex; -use crate::JsDependencyMut; +use crate::JsDependencyWrapper; #[napi] pub struct JsContextModuleFactoryBeforeResolveData(Box); @@ -169,13 +169,13 @@ impl JsContextModuleFactoryAfterResolveData { self.0.recursive = recursive; } - #[napi(getter)] - pub fn dependencies(&mut self) -> Vec { + #[napi(getter, ts_return_type = "JsDependency[]")] + pub fn dependencies(&self) -> Vec { self .0 .dependencies - .iter_mut() - .map(JsDependencyMut::new) + .iter() + .map(|dep| JsDependencyWrapper::new(dep.as_ref(), self.0.compilation_id, None)) .collect::>() } } diff --git a/crates/rspack_binding_values/src/dependency.rs b/crates/rspack_binding_values/src/dependency.rs index 1d85b7e7218..dfb297d4718 100644 --- a/crates/rspack_binding_values/src/dependency.rs +++ b/crates/rspack_binding_values/src/dependency.rs @@ -1,108 +1,180 @@ +use std::{cell::RefCell, ptr::NonNull}; + +use napi::bindgen_prelude::ToNapiValue; use napi_derive::napi; -use rspack_core::{BoxDependency, DependencyId}; +use rspack_core::{Compilation, CompilationId, Dependency, DependencyId}; +use rspack_napi::OneShotRef; +use rustc_hash::FxHashMap as HashMap; // JsDependency allows JS-side access to a Dependency instance that has already // been processed and stored in the Compilation. #[napi] -pub struct JsDependency(&'static BoxDependency); +pub struct JsDependency { + pub(crate) compilation: Option>, + pub(crate) dependency_id: DependencyId, + pub(crate) dependency: NonNull, +} impl JsDependency { - pub(crate) fn new(dependency: &BoxDependency) -> Self { - // SAFETY: - // The lifetime of the &mut BoxDependency reference is extended to 'static. - // Accessing fields and methods on the Rust object from the JS side after the Rust object's - // lifetime has ended is undefined behavior, which we currently disregard. - let dependency = - unsafe { std::mem::transmute::<&BoxDependency, &'static BoxDependency>(dependency) }; - Self(dependency) + fn as_ref(&mut self) -> napi::Result<&dyn Dependency> { + if let Some(compilation) = self.compilation { + let compilation = unsafe { compilation.as_ref() }; + let module_graph = compilation.get_module_graph(); + if let Some(dependency) = module_graph.dependency_by_id(&self.dependency_id) { + self.dependency = { + #[allow(clippy::unwrap_used)] + NonNull::new(dependency.as_ref() as *const dyn Dependency as *mut dyn Dependency).unwrap() + }; + Ok(unsafe { self.dependency.as_ref() }) + } else { + Err(napi::Error::from_reason(format!( + "Unable to access dependency with id = {:?} now. The dependency have been removed on the Rust side.", + self.dependency_id + ))) + } + } else { + // SAFETY: + // We need to make users aware in the documentation that values obtained within the JS hook callback should not be used outside the scope of the callback. + // We do not guarantee that the memory pointed to by the pointer remains valid when used outside the scope. + Ok(unsafe { self.dependency.as_ref() }) + } } - pub(crate) fn id(&self) -> &DependencyId { - self.0.id() + fn as_mut(&mut self) -> napi::Result<&mut dyn Dependency> { + // SAFETY: + // We need to make users aware in the documentation that values obtained within the JS hook callback should not be used outside the scope of the callback. + // We do not guarantee that the memory pointed to by the pointer remains valid when used outside the scope. + Ok(unsafe { self.dependency.as_mut() }) } } #[napi] impl JsDependency { #[napi(getter)] - pub fn get_type(&self) -> &str { - self.0.dependency_type().as_str() + pub fn get_type(&mut self) -> napi::Result<&str> { + let dependency = self.as_ref()?; + + Ok(dependency.dependency_type().as_str()) } #[napi(getter)] - pub fn category(&self) -> &str { - self.0.category().as_str() + pub fn category(&mut self) -> napi::Result<&str> { + let dependency = self.as_ref()?; + + Ok(dependency.category().as_str()) } #[napi(getter)] - pub fn request(&self) -> napi::Either<&str, ()> { - match self.0.as_module_dependency() { + pub fn request(&mut self) -> napi::Result> { + let dependency = self.as_ref()?; + + Ok(match dependency.as_module_dependency() { Some(dep) => napi::Either::A(dep.request()), None => napi::Either::B(()), - } + }) } #[napi(getter)] - pub fn critical(&self) -> bool { - match self.0.as_context_dependency() { + pub fn critical(&mut self) -> napi::Result { + let dependency = self.as_ref()?; + + Ok(match dependency.as_context_dependency() { Some(dep) => dep.critical().is_some(), None => false, + }) + } + + #[napi(setter)] + pub fn set_critical(&mut self, val: bool) -> napi::Result<()> { + let dependency = self.as_mut()?; + + if let Some(dep) = dependency.as_context_dependency_mut() { + let critical = dep.critical_mut(); + if !val { + *critical = None; + } } + Ok(()) } } -// JsDependency represents a Dependency instance that is currently being processed. -// It is in the make stage and has not yet been added to the Compilation. -#[napi] -pub struct JsDependencyMut(&'static mut BoxDependency); +type DependencyInstanceRefs = HashMap>; -impl JsDependencyMut { - pub(crate) fn new(dependency: &mut BoxDependency) -> Self { - // SAFETY: - // The lifetime of the &mut BoxDependency reference is extended to 'static. - // Accessing fields and methods on the Rust object from the JS side after the Rust object's - // lifetime has ended is undefined behavior, which we currently disregard. - let dependency = - unsafe { std::mem::transmute::<&mut BoxDependency, &'static mut BoxDependency>(dependency) }; - Self(dependency) - } +type DependencyInstanceRefsByCompilationId = + RefCell>; + +thread_local! { + static DEPENDENCY_INSTANCE_REFS: DependencyInstanceRefsByCompilationId = Default::default(); } -#[napi] -impl JsDependencyMut { - #[napi(getter)] - pub fn get_type(&self) -> &str { - self.0.dependency_type().as_str() - } +pub struct JsDependencyWrapper { + dependency_id: DependencyId, + dependency: NonNull, + compilation_id: CompilationId, + compilation: Option>, +} - #[napi(getter)] - pub fn category(&self) -> &str { - self.0.category().as_str() - } +impl JsDependencyWrapper { + pub fn new( + dependency: &dyn Dependency, + compilation_id: CompilationId, + compilation: Option<&Compilation>, + ) -> Self { + let dependency_id = *dependency.id(); - #[napi(getter)] - pub fn request(&self) -> napi::Either<&str, ()> { - match self.0.as_module_dependency() { - Some(dep) => napi::Either::A(dep.request()), - None => napi::Either::B(()), + #[allow(clippy::unwrap_used)] + Self { + dependency_id, + dependency: NonNull::new(dependency as *const dyn Dependency as *mut dyn Dependency).unwrap(), + compilation_id, + compilation: compilation + .map(|c| NonNull::new(c as *const Compilation as *mut Compilation).unwrap()), } } - #[napi(getter)] - pub fn critical(&self) -> bool { - match self.0.as_context_dependency() { - Some(dep) => dep.critical().is_some(), - None => false, - } + pub fn cleanup_last_compilation(compilation_id: CompilationId) { + DEPENDENCY_INSTANCE_REFS.with(|refs| { + let mut refs_by_compilation_id = refs.borrow_mut(); + refs_by_compilation_id.remove(&compilation_id) + }); } +} - #[napi(setter)] - pub fn set_critical(&mut self, val: bool) { - if let Some(dep) = self.0.as_context_dependency_mut() { - let critical = dep.critical_mut(); - if !val { - *critical = None; +impl ToNapiValue for JsDependencyWrapper { + unsafe fn to_napi_value( + env: napi::sys::napi_env, + val: Self, + ) -> napi::Result { + DEPENDENCY_INSTANCE_REFS.with(|refs| { + let mut refs_by_compilation_id = refs.borrow_mut(); + let entry = refs_by_compilation_id.entry(val.compilation_id); + let refs = match entry { + std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), + std::collections::hash_map::Entry::Vacant(entry) => { + let refs = HashMap::default(); + entry.insert(refs) + } + }; + + match refs.entry(val.dependency_id) { + std::collections::hash_map::Entry::Occupied(occupied_entry) => { + let r = occupied_entry.get(); + let instance = r.from_napi_mut_ref()?; + instance.compilation = val.compilation; + instance.dependency = val.dependency; + + ToNapiValue::to_napi_value(env, r) + } + std::collections::hash_map::Entry::Vacant(vacant_entry) => { + let js_dependency = JsDependency { + compilation: val.compilation, + dependency_id: val.dependency_id, + dependency: val.dependency, + }; + let r = vacant_entry.insert(OneShotRef::new(env, js_dependency)?); + ToNapiValue::to_napi_value(env, r) + } } - } + }) } } diff --git a/crates/rspack_binding_values/src/dependency_block.rs b/crates/rspack_binding_values/src/dependency_block.rs new file mode 100644 index 00000000000..3103ef75026 --- /dev/null +++ b/crates/rspack_binding_values/src/dependency_block.rs @@ -0,0 +1,125 @@ +use std::{cell::RefCell, ptr::NonNull}; + +use napi_derive::napi; +use rspack_core::{ + AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, CompilationId, + DependenciesBlock, +}; +use rspack_napi::{napi::bindgen_prelude::*, OneShotRef}; +use rustc_hash::FxHashMap as HashMap; + +use crate::JsDependencyWrapper; + +#[napi] +pub struct JsDependenciesBlock { + block_id: AsyncDependenciesBlockIdentifier, + compilation: NonNull, +} + +#[napi] +impl JsDependenciesBlock { + #[napi(getter, ts_return_type = "JsDependency[]")] + pub fn dependencies(&mut self) -> Vec { + let compilation = unsafe { self.compilation.as_ref() }; + let module_graph = compilation.get_module_graph(); + if let Some(block) = module_graph.block_by_id(&self.block_id) { + block + .get_dependencies() + .iter() + .filter_map(|dependency_id| { + module_graph + .dependency_by_id(dependency_id) + .map(|dep| JsDependencyWrapper::new(dep.as_ref(), compilation.id(), Some(compilation))) + }) + .collect::>() + } else { + vec![] + } + } + + #[napi(getter, ts_return_type = "JsDependenciesBlock[]")] + pub fn blocks(&mut self) -> Vec { + let compilation = unsafe { self.compilation.as_ref() }; + let module_graph = compilation.get_module_graph(); + if let Some(block) = module_graph.block_by_id(&self.block_id) { + block + .get_blocks() + .iter() + .filter_map(|block_id| { + module_graph + .block_by_id(block_id) + .map(|block| JsDependenciesBlockWrapper::new(block, compilation)) + }) + .collect::>() + } else { + vec![] + } + } +} + +type BlockInstanceRefs = HashMap>; + +type BlockInstanceRefsByCompilationId = RefCell>; + +thread_local! { + static BLOCK_INSTANCE_REFS: BlockInstanceRefsByCompilationId = Default::default(); +} + +pub struct JsDependenciesBlockWrapper { + block_id: AsyncDependenciesBlockIdentifier, + compilation: NonNull, +} + +impl JsDependenciesBlockWrapper { + pub fn new(block: &AsyncDependenciesBlock, compilation: &Compilation) -> Self { + let block_id = block.identifier(); + + #[allow(clippy::unwrap_used)] + Self { + block_id, + compilation: NonNull::new(compilation as *const Compilation as *mut Compilation).unwrap(), + } + } + + pub fn cleanup_last_compilation(compilation_id: CompilationId) { + BLOCK_INSTANCE_REFS.with(|refs| { + let mut refs_by_compilation_id = refs.borrow_mut(); + refs_by_compilation_id.remove(&compilation_id) + }); + } +} + +impl ToNapiValue for JsDependenciesBlockWrapper { + unsafe fn to_napi_value( + env: napi::sys::napi_env, + val: Self, + ) -> napi::Result { + BLOCK_INSTANCE_REFS.with(|refs| { + let compilation = unsafe { val.compilation.as_ref() }; + let mut refs_by_compilation_id = refs.borrow_mut(); + let entry = refs_by_compilation_id.entry(compilation.id()); + let refs = match entry { + std::collections::hash_map::Entry::Occupied(entry) => entry.into_mut(), + std::collections::hash_map::Entry::Vacant(entry) => { + let refs = HashMap::default(); + entry.insert(refs) + } + }; + + match refs.entry(val.block_id) { + std::collections::hash_map::Entry::Occupied(occupied_entry) => { + let r = occupied_entry.get(); + ToNapiValue::to_napi_value(env, r) + } + std::collections::hash_map::Entry::Vacant(vacant_entry) => { + let js_block = JsDependenciesBlock { + block_id: val.block_id, + compilation: val.compilation, + }; + let r = vacant_entry.insert(OneShotRef::new(env, js_block)?); + ToNapiValue::to_napi_value(env, r) + } + } + }) + } +} diff --git a/crates/rspack_binding_values/src/lib.rs b/crates/rspack_binding_values/src/lib.rs index 5bdf243f1ee..e6dd15665de 100644 --- a/crates/rspack_binding_values/src/lib.rs +++ b/crates/rspack_binding_values/src/lib.rs @@ -8,6 +8,7 @@ mod codegen_result; mod compilation; mod context_module_factory; mod dependency; +mod dependency_block; mod filename; mod html; mod identifier; @@ -32,6 +33,7 @@ pub use codegen_result::*; pub use compilation::*; pub use context_module_factory::*; pub use dependency::*; +pub use dependency_block::*; pub use filename::*; pub use html::*; pub use module::*; diff --git a/crates/rspack_binding_values/src/module.rs b/crates/rspack_binding_values/src/module.rs index 7a09b29259a..33c9c199010 100644 --- a/crates/rspack_binding_values/src/module.rs +++ b/crates/rspack_binding_values/src/module.rs @@ -3,9 +3,8 @@ use std::{cell::RefCell, ptr::NonNull, sync::Arc}; use napi_derive::napi; use rspack_collections::IdentifierMap; use rspack_core::{ - AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, BuildMeta, BuildMetaDefaultObject, - BuildMetaExportsType, Compilation, CompilationId, DependenciesBlock, ExportsArgument, Module, - ModuleArgument, ModuleGraph, ModuleIdentifier, RuntimeModuleStage, SourceType, + BuildMeta, BuildMetaDefaultObject, BuildMetaExportsType, Compilation, CompilationId, + ExportsArgument, Module, ModuleArgument, ModuleIdentifier, RuntimeModuleStage, SourceType, }; use rspack_napi::{napi::bindgen_prelude::*, threadsafe_function::ThreadsafeFunction, OneShotRef}; use rspack_plugin_runtime::RuntimeModuleFromJs; @@ -13,7 +12,7 @@ use rspack_util::source_map::SourceMapKind; use rustc_hash::FxHashMap as HashMap; use super::{JsCompatSource, ToJsCompatSource}; -use crate::{JsChunk, JsCodegenerationResults, JsDependency}; +use crate::{JsChunk, JsCodegenerationResults, JsDependenciesBlockWrapper, JsDependencyWrapper}; #[derive(Default)] #[napi(object)] @@ -21,65 +20,6 @@ pub struct JsFactoryMeta { pub side_effect_free: Option, } -#[napi] -pub struct JsDependenciesBlock { - block_id: AsyncDependenciesBlockIdentifier, - compilation: NonNull, -} - -impl JsDependenciesBlock { - pub fn new(block_id: AsyncDependenciesBlockIdentifier, compilation: *const Compilation) -> Self { - #[allow(clippy::unwrap_used)] - Self { - block_id, - compilation: NonNull::new(compilation as *mut Compilation).unwrap(), - } - } - - fn block<'a>(&self, module_graph: &'a ModuleGraph) -> &'a AsyncDependenciesBlock { - module_graph.block_by_id(&self.block_id).unwrap_or_else(|| { - panic!( - "Cannot find block with id = {:?}. It might have been removed on the Rust side.", - self.block_id - ) - }) - } -} - -#[napi] -impl JsDependenciesBlock { - #[napi(getter)] - pub fn dependencies(&self) -> Vec { - let compilation = unsafe { self.compilation.as_ref() }; - - let module_graph = compilation.get_module_graph(); - let block = self.block(&module_graph); - block - .get_dependencies() - .iter() - .filter_map(|dependency_id| { - module_graph - .dependency_by_id(dependency_id) - .map(JsDependency::new) - }) - .collect::>() - } - - #[napi(getter)] - pub fn blocks(&self) -> Vec { - let compilation = unsafe { self.compilation.as_ref() }; - - let module_graph = compilation.get_module_graph(); - let block = self.block(&module_graph); - let blocks = block.get_blocks(); - blocks - .iter() - .cloned() - .map(|block_id| JsDependenciesBlock::new(block_id, self.compilation.as_ptr())) - .collect::>() - } -} - #[napi] pub struct JsModule { identifier: ModuleIdentifier, @@ -89,18 +29,7 @@ pub struct JsModule { } impl JsModule { - fn attach(&mut self, compilation: NonNull) { - if self.compilation.is_none() { - self.compilation = Some(compilation); - } - } - fn as_ref(&mut self) -> napi::Result<&'static dyn Module> { - let module = unsafe { self.module.as_ref() }; - if module.identifier() == self.identifier { - return Ok(module); - } - if let Some(compilation) = self.compilation { let compilation = unsafe { compilation.as_ref() }; if let Some(module) = compilation.module_by_identifier(&self.identifier) { @@ -109,26 +38,26 @@ impl JsModule { #[allow(clippy::unwrap_used)] NonNull::new(module as *const dyn Module as *mut dyn Module).unwrap() }; - return Ok(module); + Ok(module) + } else { + Err(napi::Error::from_reason(format!( + "Unable to access module with id = {} now. The module have been removed on the Rust side.", + self.identifier + ))) } + } else { + // SAFETY: + // We need to make users aware in the documentation that values obtained within the JS hook callback should not be used outside the scope of the callback. + // We do not guarantee that the memory pointed to by the pointer remains valid when used outside the scope. + Ok(unsafe { self.module.as_ref() }) } - - Err(napi::Error::from_reason(format!( - "Unable to access module with id = {} now. The module have been removed on the Rust side.", - self.identifier - ))) } fn as_mut(&mut self) -> napi::Result<&'static mut dyn Module> { - let module = unsafe { self.module.as_mut() }; - if module.identifier() == self.identifier { - return Ok(module); - } - - Err(napi::Error::from_reason(format!( - "Unable to access module with id = {} now. The module have been removed on the Rust side.", - self.identifier - ))) + // SAFETY: + // We need to make users aware in the documentation that values obtained within the JS hook callback should not be used outside the scope of the callback. + // We do not guarantee that the memory pointed to by the pointer remains valid when used outside the scope. + Ok(unsafe { self.module.as_mut() }) } } @@ -256,17 +185,22 @@ impl JsModule { }) } - #[napi(getter)] - pub fn blocks(&mut self) -> napi::Result> { + #[napi(getter, ts_return_type = "JsDependenciesBlock[]")] + pub fn blocks(&mut self) -> napi::Result> { Ok(match self.compilation { Some(compilation) => { + let compilation = unsafe { compilation.as_ref() }; + let module_graph = compilation.get_module_graph(); let module = self.as_ref()?; let blocks = module.get_blocks(); blocks .iter() - .cloned() - .map(|block_id| JsDependenciesBlock::new(block_id, compilation.as_ptr())) + .filter_map(|block_id| { + module_graph + .block_by_id(block_id) + .map(|block| JsDependenciesBlockWrapper::new(block, compilation)) + }) .collect::>() } None => { @@ -275,8 +209,8 @@ impl JsModule { }) } - #[napi(getter)] - pub fn dependencies(&mut self) -> napi::Result> { + #[napi(getter, ts_return_type = "JsDependency[]")] + pub fn dependencies(&mut self) -> napi::Result> { Ok(match self.compilation { Some(compilation) => { let compilation = unsafe { compilation.as_ref() }; @@ -286,9 +220,10 @@ impl JsModule { dependencies .iter() .filter_map(|dependency_id| { - module_graph - .dependency_by_id(dependency_id) - .map(JsDependency::new) + module_graph.dependency_by_id(dependency_id).map(|dep| { + let compilation = unsafe { self.compilation.map(|c| c.as_ref()) }; + JsDependencyWrapper::new(dep.as_ref(), self.compilation_id, compilation) + }) }) .collect::>() } @@ -418,11 +353,8 @@ impl ToNapiValue for JsModuleWrapper { std::collections::hash_map::Entry::Occupied(entry) => { let r = entry.get(); let instance = r.from_napi_mut_ref()?; - if let Some(compilation) = val.compilation { - instance.attach(compilation); - } else { - instance.module = val.module; - } + instance.compilation = val.compilation; + instance.module = val.module; ToNapiValue::to_napi_value(env, r) } std::collections::hash_map::Entry::Vacant(entry) => { diff --git a/crates/rspack_core/src/compiler/make/repair/mod.rs b/crates/rspack_core/src/compiler/make/repair/mod.rs index 2c4fc28c052..f4f205da377 100644 --- a/crates/rspack_core/src/compiler/make/repair/mod.rs +++ b/crates/rspack_core/src/compiler/make/repair/mod.rs @@ -19,6 +19,7 @@ use crate::{ }; pub struct MakeTaskContext { + // compilation info pub compilation_id: CompilationId, pub plugin_driver: SharedPluginDriver, pub buildtime_plugin_driver: SharedPluginDriver, diff --git a/crates/rspack_core/src/context_module_factory.rs b/crates/rspack_core/src/context_module_factory.rs index a17ffe9f18b..f2e2ae777cf 100644 --- a/crates/rspack_core/src/context_module_factory.rs +++ b/crates/rspack_core/src/context_module_factory.rs @@ -10,11 +10,11 @@ use swc_core::common::util::take::Take; use tracing::instrument; use crate::{ - resolve, BoxDependency, ContextElementDependency, ContextModule, ContextModuleOptions, - DependencyCategory, DependencyId, DependencyType, ErrorSpan, ModuleExt, ModuleFactory, - ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, RawModule, ResolveArgs, - ResolveContextModuleDependencies, ResolveInnerOptions, ResolveOptionsWithDependencyType, - ResolveResult, Resolver, ResolverFactory, SharedPluginDriver, + resolve, BoxDependency, CompilationId, ContextElementDependency, ContextModule, + ContextModuleOptions, DependencyCategory, DependencyId, DependencyType, ErrorSpan, ModuleExt, + ModuleFactory, ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, RawModule, + ResolveArgs, ResolveContextModuleDependencies, ResolveInnerOptions, + ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, SharedPluginDriver, }; #[derive(Debug)] @@ -50,6 +50,7 @@ pub enum AfterResolveResult { #[derive(Derivative)] #[derivative(Debug, Clone)] pub struct AfterResolveData { + pub compilation_id: CompilationId, pub resource: Utf8PathBuf, pub context: String, pub dependencies: Vec, @@ -339,6 +340,7 @@ impl ContextModuleFactory { ) -> Result> { let context_options = &context_module_options.context_options; let after_resolve_data = AfterResolveData { + compilation_id: data.compilation_id, resource: context_module_options.resource.clone(), context: context_options.context.clone(), dependencies: data.dependencies.clone(), diff --git a/packages/rspack/etc/core.api.md b/packages/rspack/etc/core.api.md index 1f0bf116c31..2405fdee82f 100644 --- a/packages/rspack/etc/core.api.md +++ b/packages/rspack/etc/core.api.md @@ -42,8 +42,7 @@ import type { JsContextModuleFactoryAfterResolveData } from '@rspack/binding'; import type { JsContextModuleFactoryBeforeResolveData } from '@rspack/binding'; import type { JsCreateData } from '@rspack/binding'; import type { JsDependenciesBlock } from '@rspack/binding'; -import { JsDependency } from '@rspack/binding'; -import { JsDependencyMut } from '@rspack/binding'; +import type { JsDependency } from '@rspack/binding'; import type { JsFactoryMeta } from '@rspack/binding'; import { JsHtmlPluginTag } from '@rspack/binding'; import { JsLibraryOptions } from '@rspack/binding'; @@ -1315,7 +1314,7 @@ class DependenciesBlock { // @public (undocumented) class Dependency { // (undocumented) - static __from_binding(binding: JsDependencyMut | JsDependency): Dependency; + static __from_binding(binding: JsDependency): Dependency; // (undocumented) readonly category: string; // (undocumented) diff --git a/packages/rspack/src/Dependency.ts b/packages/rspack/src/Dependency.ts index 49e3eb5b3ef..8030b7cd203 100644 --- a/packages/rspack/src/Dependency.ts +++ b/packages/rspack/src/Dependency.ts @@ -1,4 +1,4 @@ -import { type JsDependency, JsDependencyMut } from "@rspack/binding"; +import type { JsDependency } from "@rspack/binding"; export class Dependency { declare readonly type: string; @@ -6,11 +6,11 @@ export class Dependency { declare readonly request: string | undefined; declare critical: boolean; - static __from_binding(binding: JsDependencyMut | JsDependency): Dependency { + static __from_binding(binding: JsDependency): Dependency { return new Dependency(binding); } - private constructor(binding: JsDependencyMut | JsDependency) { + private constructor(binding: JsDependency) { Object.defineProperties(this, { type: { enumerable: true, @@ -36,9 +36,7 @@ export class Dependency { return binding.critical; }, set(val: boolean) { - if (binding instanceof JsDependencyMut) { - binding.critical = val; - } + binding.critical = val; } } });