Skip to content

Commit

Permalink
feat: support dependencies in resolve hook callback (#7949)
Browse files Browse the repository at this point in the history
* feat: dependencies in context module factory hook

* feat: ContextModuleFactoryBeforeResolveData

* feat: update test case

* fix: cargo lint

* chore: update api docs

* feat: JsDependency and JsDependencyMut

* fix: remove lifecycle

* chore: update api

* fix: Dependency
  • Loading branch information
SyMind authored Oct 10, 2024
1 parent 45d529c commit 27188f2
Show file tree
Hide file tree
Showing 12 changed files with 415 additions and 205 deletions.
33 changes: 21 additions & 12 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class ExternalObject<T> {
}
}
export class DependenciesBlockDto {
get dependencies(): Array<DependencyDto>
get dependencies(): Array<JsDependency>
get blocks(): Array<DependenciesBlockDto>
}
export type DependenciesBlockDTO = DependenciesBlockDto
Expand All @@ -41,16 +41,9 @@ export class DependenciesDto {
}
export type DependenciesDTO = DependenciesDto

export class DependencyDto {
get type(): string
get category(): string
get request(): string | undefined
}
export type DependencyDTO = DependencyDto

export class EntryDataDto {
get dependencies(): Array<DependencyDTO>
get includeDependencies(): Array<DependencyDTO>
get dependencies(): Array<JsDependency>
get includeDependencies(): Array<JsDependency>
get options(): EntryOptionsDto
}
export type EntryDataDTO = EntryDataDto
Expand Down Expand Up @@ -132,6 +125,7 @@ export class JsContextModuleFactoryAfterResolveData {
set regExp(rawRegExp: RawRegex | undefined)
get recursive(): boolean
set recursive(recursive: boolean)
get dependencies(): Array<JsDependencyMut>
}

export class JsContextModuleFactoryBeforeResolveData {
Expand All @@ -145,6 +139,21 @@ export class JsContextModuleFactoryBeforeResolveData {
set recursive(recursive: boolean)
}

export class JsDependency {
get type(): string
get category(): string
get request(): string | undefined
get critical(): boolean
}

export class JsDependencyMut {
get type(): string
get category(): string
get request(): string | undefined
get critical(): boolean
set critical(val: boolean)
}

export class JsEntries {
clear(): void
get size(): number
Expand Down Expand Up @@ -526,8 +535,8 @@ export interface JsDiagnosticLocation {
}

export interface JsEntryData {
dependencies: Array<DependencyDTO>
includeDependencies: Array<DependencyDTO>
dependencies: Array<JsDependency>
includeDependencies: Array<JsDependency>
options: JsEntryOptions
}

Expand Down
45 changes: 21 additions & 24 deletions crates/rspack_binding_values/src/compilation/entries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use napi_derive::napi;
use rspack_core::{ChunkLoading, Compilation, EntryData, EntryOptions, EntryRuntime};
use rspack_napi::napi::bindgen_prelude::*;

use crate::{dependency::DependencyDTO, entry::JsEntryOptions, library::JsLibraryOptions};
use crate::{dependency::JsDependency, entry::JsEntryOptions, library::JsLibraryOptions};

#[napi]
pub struct EntryOptionsDTO(EntryOptions);
Expand Down Expand Up @@ -152,8 +152,8 @@ impl EntryOptionsDTO {

#[napi(object, object_to_js = false)]
pub struct JsEntryData {
pub dependencies: Vec<ClassInstance<DependencyDTO>>,
pub include_dependencies: Vec<ClassInstance<DependencyDTO>>,
pub dependencies: Vec<ClassInstance<JsDependency>>,
pub include_dependencies: Vec<ClassInstance<JsDependency>>,
pub options: JsEntryOptions,
}

Expand All @@ -163,12 +163,12 @@ impl From<JsEntryData> for EntryData {
dependencies: value
.dependencies
.into_iter()
.map(|dep| dep.dependency_id)
.map(|dep| *dep.id())
.collect::<Vec<_>>(),
include_dependencies: value
.include_dependencies
.into_iter()
.map(|dep| dep.dependency_id)
.map(|dep| *dep.id())
.collect::<Vec<_>>(),
options: value.options.into(),
}
Expand All @@ -184,36 +184,33 @@ pub struct EntryDataDTO {
#[napi]
impl EntryDataDTO {
#[napi(getter)]
pub fn dependencies(&'static self, env: Env) -> Result<Vec<ClassInstance<DependencyDTO>>> {
pub fn dependencies(&'static self) -> Vec<JsDependency> {
let module_graph = self.compilation.get_module_graph();
self
.entry_data
.dependencies
.clone()
.into_iter()
.map(|id| {
let js_dep = DependencyDTO::new(id, self.compilation);
let instance = js_dep.into_instance(env)?;
Ok(instance)
.iter()
.map(|dependency_id| {
#[allow(clippy::unwrap_used)]
let dep = module_graph.dependency_by_id(dependency_id).unwrap();
JsDependency::new(dep)
})
.collect::<Result<Vec<ClassInstance<DependencyDTO>>>>()
.collect::<Vec<_>>()
}

#[napi(getter)]
pub fn include_dependencies(
&'static self,
env: Env,
) -> Result<Vec<ClassInstance<DependencyDTO>>> {
pub fn include_dependencies(&'static self) -> Vec<JsDependency> {
let module_graph = self.compilation.get_module_graph();
self
.entry_data
.include_dependencies
.clone()
.into_iter()
.map(|id| {
let js_dep = DependencyDTO::new(id, self.compilation);
let instance = js_dep.into_instance(env)?;
Ok(instance)
.iter()
.map(|dependency_id| {
#[allow(clippy::unwrap_used)]
let dep = module_graph.dependency_by_id(dependency_id).unwrap();
JsDependency::new(dep)
})
.collect::<Result<Vec<ClassInstance<DependencyDTO>>>>()
.collect::<Vec<_>>()
}

#[napi(getter)]
Expand Down
12 changes: 11 additions & 1 deletion crates/rspack_binding_values/src/context_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use napi::bindgen_prelude::{
use napi_derive::napi;
use rspack_core::{AfterResolveData, BeforeResolveData};

use crate::RawRegex;
use crate::{JsDependencyMut, RawRegex};

#[napi]
pub struct JsContextModuleFactoryBeforeResolveData(Box<BeforeResolveData>);
Expand Down Expand Up @@ -173,6 +173,16 @@ impl JsContextModuleFactoryAfterResolveData {
pub fn set_recursive(&mut self, recursive: bool) {
self.0.recursive = recursive;
}

#[napi(getter)]
pub fn dependencies(&mut self) -> Vec<JsDependencyMut> {
self
.0
.dependencies
.iter_mut()
.map(JsDependencyMut::new)
.collect::<Vec<_>>()
}
}

pub struct JsContextModuleFactoryAfterResolveDataWrapper(Box<AfterResolveData>);
Expand Down
111 changes: 81 additions & 30 deletions crates/rspack_binding_values/src/dependency.rs
Original file line number Diff line number Diff line change
@@ -1,57 +1,108 @@
use napi_derive::napi;
use rspack_core::{Compilation, Dependency, DependencyId, ModuleDependency, ModuleGraph};
use rspack_core::{BoxDependency, DependencyId};

// JsDependency allows JS-side access to a Dependency instance that has already
// been processed and stored in the Compilation.
#[napi]
pub struct DependencyDTO {
pub(crate) dependency_id: DependencyId,
pub(crate) compilation: &'static Compilation,
pub struct JsDependency(&'static BoxDependency);

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)
}

pub(crate) fn id(&self) -> &DependencyId {
self.0.id()
}
}

impl DependencyDTO {
pub(crate) fn new(dependency_id: DependencyId, compilation: &'static Compilation) -> Self {
Self {
dependency_id,
compilation,
#[napi]
impl JsDependency {
#[napi(getter)]
pub fn get_type(&self) -> &str {
self.0.dependency_type().as_str()
}

#[napi(getter)]
pub fn category(&self) -> &str {
self.0.category().as_str()
}

#[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(()),
}
}

fn dependency<'a>(&self, module_graph: &'a ModuleGraph) -> &'a dyn Dependency {
module_graph
.dependency_by_id(&self.dependency_id)
.unwrap_or_else(|| panic!("Failed to get dependency by id = {:?}", &self.dependency_id))
.as_ref()
#[napi(getter)]
pub fn critical(&self) -> bool {
match self.0.as_context_dependency() {
Some(dep) => dep.critical().is_some(),
None => false,
}
}
}

fn module_dependency<'a>(
&self,
module_graph: &'a ModuleGraph,
) -> Option<&'a dyn ModuleDependency> {
self.dependency(module_graph).as_module_dependency()
// 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);

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)
}
}

#[napi]
impl DependencyDTO {
impl JsDependencyMut {
#[napi(getter)]
pub fn get_type(&self) -> &str {
let module_graph = self.compilation.get_module_graph();
let dep = self.dependency(&module_graph);
dep.dependency_type().as_str()
self.0.dependency_type().as_str()
}

#[napi(getter)]
pub fn category(&self) -> &str {
let module_graph = self.compilation.get_module_graph();
let dep = self.dependency(&module_graph);
dep.category().as_str()
self.0.category().as_str()
}

#[napi(getter)]
pub fn request(&self) -> napi::Either<String, ()> {
let module_graph = self.compilation.get_module_graph();
match self.module_dependency(&module_graph) {
Some(dep) => napi::Either::A(dep.request().to_string()),
pub fn request(&self) -> napi::Either<&str, ()> {
match self.0.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() {
Some(dep) => dep.critical().is_some(),
None => false,
}
}

#[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;
}
}
}
}
2 changes: 1 addition & 1 deletion crates/rspack_binding_values/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub use chunk_group::*;
pub use codegen_result::*;
pub use compilation::*;
pub use context_module_factory::*;
pub use dependency::DependencyDTO;
pub use dependency::*;
pub use filename::*;
pub use html::*;
pub use module::*;
Expand Down
11 changes: 7 additions & 4 deletions crates/rspack_binding_values/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_hash::FxHashMap as HashMap;
use sys::napi_env;

use super::{JsCompatSource, ToJsCompatSource};
use crate::{DependencyDTO, JsChunk, JsCodegenerationResults};
use crate::{JsChunk, JsCodegenerationResults, JsDependency};

#[derive(Default)]
#[napi(object)]
Expand Down Expand Up @@ -52,14 +52,17 @@ impl DependenciesBlockDTO {
#[napi]
impl DependenciesBlockDTO {
#[napi(getter)]
pub fn dependencies(&self) -> Vec<DependencyDTO> {
pub fn dependencies(&self) -> Vec<JsDependency> {
let module_graph = self.compilation.get_module_graph();
let block = self.block(&module_graph);
block
.get_dependencies()
.iter()
.cloned()
.map(|dep_id| DependencyDTO::new(dep_id, self.compilation))
.map(|dependency_id| {
#[allow(clippy::unwrap_used)]
let dep = module_graph.dependency_by_id(dependency_id).unwrap();
JsDependency::new(dep)
})
.collect::<Vec<_>>()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ class Plugin {
resolveData.regExp = /[/\\](en(\.js)?|zh(\.js)?)$/;
return resolveData;
}
for (const d of resolveData.dependencies) {
if (d.critical) d.critical = false;
}
});
}
);
}
}

/**@type {import("@rspack/core").Configuration}*/
module.exports = {
context: __dirname,
Expand Down
Loading

2 comments on commit 27188f2

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-10-10 5416306) Current Change
10000_development-mode + exec 2.23 s ± 55 ms 2.17 s ± 32 ms -2.77 %
10000_development-mode_hmr + exec 684 ms ± 14 ms 690 ms ± 27 ms +0.96 %
10000_production-mode + exec 2.78 s ± 44 ms 2.74 s ± 45 ms -1.65 %
arco-pro_development-mode + exec 1.84 s ± 85 ms 1.82 s ± 82 ms -1.18 %
arco-pro_development-mode_hmr + exec 436 ms ± 2.8 ms 436 ms ± 2.6 ms +0.09 %
arco-pro_production-mode + exec 3.13 s ± 94 ms 3.12 s ± 83 ms -0.54 %
arco-pro_production-mode_generate-package-json-webpack-plugin + exec 3.19 s ± 68 ms 3.2 s ± 81 ms +0.54 %
threejs_development-mode_10x + exec 1.66 s ± 20 ms 1.69 s ± 8.5 ms +1.81 %
threejs_development-mode_10x_hmr + exec 783 ms ± 17 ms 808 ms ± 15 ms +3.10 %
threejs_production-mode_10x + exec 5.09 s ± 42 ms 5.05 s ± 33 ms -0.77 %

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ✅ success
_selftest ✅ success
rspress ✅ success
rslib ✅ success
rsbuild ❌ failure
examples ✅ success
devserver ✅ success

Please sign in to comment.