From ccd6ee8ad9836c89164290e8bb76a97cf2075fdd Mon Sep 17 00:00:00 2001 From: Cong-Cong Pan Date: Tue, 6 Aug 2024 20:58:46 +0800 Subject: [PATCH] feat: support compilation.modules[i].blocks (#7460) * feat: module dto * DependenciesBlockDTO * feat: DependenciesBlockDTO * feat: DependenciesBlock and Dependency in js side * chore: update api md * fix: js lint * fix: cargo lint wrong * fix: test case --- crates/node_binding/binding.d.ts | 25 ++- .../src/compilation/dependency.rs | 33 ++- .../src/compilation/mod.rs | 14 +- crates/rspack_binding_values/src/module.rs | 204 +++++++++++++++++- .../configCases/compilation/modules/a.js | 0 .../configCases/compilation/modules/index.js | 1 + .../compilation/modules/rspack.config.js | 30 +++ packages/rspack/etc/api.md | 33 ++- packages/rspack/src/Compilation.ts | 18 +- packages/rspack/src/DependenciesBlock.ts | 18 ++ packages/rspack/src/Dependency.ts | 21 ++ packages/rspack/src/Module.ts | 20 +- 12 files changed, 376 insertions(+), 41 deletions(-) create mode 100644 packages/rspack-test-tools/tests/configCases/compilation/modules/a.js create mode 100644 packages/rspack-test-tools/tests/configCases/compilation/modules/index.js create mode 100644 packages/rspack-test-tools/tests/configCases/compilation/modules/rspack.config.js create mode 100644 packages/rspack/src/DependenciesBlock.ts create mode 100644 packages/rspack/src/Dependency.ts diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 9ed82e013d5c..79858673a7c2 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -19,9 +19,16 @@ export class ExternalObject { [K: symbol]: T } } +export class DependenciesBlockDto { + get dependencies(): Array + get blocks(): Array +} +export type DependenciesBlockDTO = DependenciesBlockDto + export class DependencyDto { get type(): string get category(): string + get request(): string | undefined } export type DependencyDTO = DependencyDto @@ -57,7 +64,7 @@ export class JsCompilation { getAssets(): Readonly[] getAsset(name: string): JsAsset | null getAssetSource(name: string): JsCompatSource | null - getModules(): Array + get modules(): Array getOptimizationBailout(): Array getChunks(): Array getNamedChunkKeys(): Array @@ -126,6 +133,22 @@ export class JsStats { getLogging(acceptedTypes: number): Array } +export class ModuleDto { + get context(): string | undefined + get originalSource(): JsCompatSource | undefined + get resource(): string | undefined + get moduleIdentifier(): string + get nameForCondition(): string | undefined + get request(): string | undefined + get userRequest(): string | undefined + get rawRequest(): string | undefined + get factoryMeta(): JsFactoryMeta | undefined + get type(): string + get layer(): string | undefined + get blocks(): Array +} +export type ModuleDTO = ModuleDto + export class Rspack { constructor(options: RawOptions, builtinPlugins: Array, registerJsTaps: RegisterJsTaps, outputFilesystem: ThreadsafeNodeFS, resolverFactoryReference: JsResolverFactory) setNonSkippableRegisters(kinds: Array): void diff --git a/crates/rspack_binding_values/src/compilation/dependency.rs b/crates/rspack_binding_values/src/compilation/dependency.rs index 06fe2fd7e9e1..6427449f2af9 100644 --- a/crates/rspack_binding_values/src/compilation/dependency.rs +++ b/crates/rspack_binding_values/src/compilation/dependency.rs @@ -1,5 +1,5 @@ use napi_derive::napi; -use rspack_core::{Compilation, DependencyId}; +use rspack_core::{Compilation, Dependency, DependencyId, ModuleDependency, ModuleGraph}; #[napi] pub struct DependencyDTO { @@ -14,6 +14,20 @@ impl DependencyDTO { compilation, } } + + 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() + } + + fn module_dependency<'a>( + &self, + module_graph: &'a ModuleGraph, + ) -> Option<&'a dyn ModuleDependency> { + self.dependency(module_graph).as_module_dependency() + } } #[napi] @@ -21,18 +35,23 @@ impl DependencyDTO { #[napi(getter)] pub fn get_type(&self) -> &str { let module_graph = self.compilation.get_module_graph(); - let dep = module_graph - .dependency_by_id(&self.dependency_id) - .unwrap_or_else(|| panic!("Failed to get dependency by id = {:?}", &self.dependency_id)); + let dep = self.dependency(&module_graph); dep.dependency_type().as_str() } #[napi(getter)] pub fn category(&self) -> &str { let module_graph = self.compilation.get_module_graph(); - let dep = module_graph - .dependency_by_id(&self.dependency_id) - .unwrap_or_else(|| panic!("Failed to get dependency by id = {:?}", &self.dependency_id)); + let dep = self.dependency(&module_graph); dep.category().as_str() } + + #[napi(getter)] + pub fn request(&self) -> napi::Either { + let module_graph = self.compilation.get_module_graph(); + match self.module_dependency(&module_graph) { + Some(dep) => napi::Either::A(dep.request().to_string()), + None => napi::Either::B(()), + } + } } diff --git a/crates/rspack_binding_values/src/compilation/mod.rs b/crates/rspack_binding_values/src/compilation/mod.rs index 3186b153560b..6cd95156adf0 100644 --- a/crates/rspack_binding_values/src/compilation/mod.rs +++ b/crates/rspack_binding_values/src/compilation/mod.rs @@ -6,6 +6,7 @@ use std::ops::Deref; use std::ops::DerefMut; use std::path::PathBuf; +pub use dependency::*; use entries::JsEntries; use napi_derive::napi; use rspack_collections::IdentifierSet; @@ -25,8 +26,8 @@ use crate::utils::callbackify; use crate::JsStatsOptimizationBailout; use crate::LocalJsFilename; use crate::{ - chunk::JsChunk, module::JsModule, CompatSource, JsAsset, JsAssetInfo, JsChunkGroup, - JsCompatSource, JsPathData, JsStats, ToJsCompatSource, + chunk::JsChunk, CompatSource, JsAsset, JsAssetInfo, JsChunkGroup, JsCompatSource, JsPathData, + JsStats, ModuleDTO, ToJsCompatSource, }; use crate::{JsDiagnostic, JsRspackError}; @@ -148,14 +149,15 @@ impl JsCompilation { .transpose() } - #[napi] - pub fn get_modules(&self) -> Vec { + #[napi(getter)] + pub fn modules(&'static self) -> Vec { self .0 .get_module_graph() .modules() - .values() - .filter_map(|module| module.to_js_module().ok()) + .keys() + .cloned() + .map(|module_id| ModuleDTO::new(module_id, self.0)) .collect::>() } diff --git a/crates/rspack_binding_values/src/module.rs b/crates/rspack_binding_values/src/module.rs index 80344b3eacf2..969420162336 100644 --- a/crates/rspack_binding_values/src/module.rs +++ b/crates/rspack_binding_values/src/module.rs @@ -1,9 +1,12 @@ use napi_derive::napi; -use rspack_core::{CompilerModuleContext, Module}; +use rspack_core::{ + AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, CompilerModuleContext, + DependenciesBlock, Module, ModuleGraph, ModuleIdentifier, +}; use rspack_napi::napi::bindgen_prelude::*; use super::{JsCompatSource, ToJsCompatSource}; -use crate::{JsChunk, JsCodegenerationResults}; +use crate::{DependencyDTO, JsChunk, JsCodegenerationResults}; #[derive(Default)] #[napi(object)] @@ -11,6 +14,203 @@ pub struct JsFactoryMeta { pub side_effect_free: Option, } +#[napi] +pub struct DependenciesBlockDTO { + block_id: AsyncDependenciesBlockIdentifier, + compilation: &'static Compilation, +} + +impl DependenciesBlockDTO { + pub fn new( + block_id: AsyncDependenciesBlockIdentifier, + compilation: &'static Compilation, + ) -> Self { + Self { + block_id, + compilation, + } + } + + 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 DependenciesBlockDTO { + #[napi(getter)] + pub fn dependencies(&self) -> Vec { + 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)) + .collect::>() + } + + #[napi(getter)] + pub fn blocks(&self) -> Vec { + let module_graph = self.compilation.get_module_graph(); + let block = self.block(&module_graph); + let blocks = block.get_blocks(); + blocks + .iter() + .cloned() + .map(|block_id| DependenciesBlockDTO::new(block_id, self.compilation)) + .collect::>() + } +} + +#[napi] +pub struct ModuleDTO { + pub(crate) module_id: ModuleIdentifier, + pub(crate) compilation: &'static Compilation, +} + +impl ModuleDTO { + pub fn new(module_id: ModuleIdentifier, compilation: &'static Compilation) -> Self { + Self { + module_id, + compilation, + } + } + + fn module(&self) -> &dyn Module { + self + .compilation + .module_by_identifier(&self.module_id) + .unwrap_or_else(|| { + panic!( + "Cannot find module with id = {}. It might have been removed on the Rust side.", + self.module_id + ) + }) + .as_ref() + } +} + +#[napi] +impl ModuleDTO { + #[napi(getter)] + pub fn context(&self) -> Either { + let module = self.module(); + match module.get_context() { + Some(ctx) => Either::A(ctx.to_string()), + None => Either::B(()), + } + } + + #[napi(getter)] + pub fn original_source(&self) -> Either { + let module = self.module(); + match module.original_source() { + Some(source) => match source.to_js_compat_source().ok() { + Some(s) => Either::A(s), + None => Either::B(()), + }, + None => Either::B(()), + } + } + + #[napi(getter)] + pub fn resource(&self) -> Either { + let module = self.module(); + match module.try_as_normal_module() { + Ok(normal_module) => Either::A(normal_module.resource_resolved_data().resource.to_string()), + Err(_) => Either::B(()), + } + } + + #[napi(getter)] + pub fn module_identifier(&self) -> &str { + let module = self.module(); + module.identifier().as_str() + } + + #[napi(getter)] + pub fn name_for_condition(&self) -> Either { + let module = self.module(); + match module.name_for_condition() { + Some(s) => Either::A(s.to_string()), + None => Either::B(()), + } + } + + #[napi(getter)] + pub fn request(&self) -> Either<&str, ()> { + let module = self.module(); + match module.try_as_normal_module() { + Ok(normal_module) => Either::A(normal_module.request()), + Err(_) => Either::B(()), + } + } + + #[napi(getter)] + pub fn user_request(&self) -> Either<&str, ()> { + let module = self.module(); + match module.try_as_normal_module() { + Ok(normal_module) => Either::A(normal_module.user_request()), + Err(_) => Either::B(()), + } + } + + #[napi(getter)] + pub fn raw_request(&self) -> Either<&str, ()> { + let module = self.module(); + match module.try_as_normal_module() { + Ok(normal_module) => Either::A(normal_module.raw_request()), + Err(_) => Either::B(()), + } + } + + #[napi(getter)] + pub fn factory_meta(&self) -> Either { + let module = self.module(); + match module.try_as_normal_module() { + Ok(normal_module) => match normal_module.factory_meta() { + Some(meta) => Either::A(JsFactoryMeta { + side_effect_free: meta.side_effect_free, + }), + None => Either::B(()), + }, + Err(_) => Either::B(()), + } + } + + #[napi(getter)] + pub fn get_type(&self) -> &str { + let module = self.module(); + module.module_type().as_str() + } + + #[napi(getter)] + pub fn layer(&self) -> Either<&String, ()> { + let module = self.module(); + match module.get_layer() { + Some(layer) => Either::A(layer), + None => Either::B(()), + } + } + + #[napi(getter)] + pub fn blocks(&self) -> Vec { + let module = self.module(); + let blocks = module.get_blocks(); + blocks + .iter() + .cloned() + .map(|block_id| DependenciesBlockDTO::new(block_id, self.compilation)) + .collect::>() + } +} + #[derive(Default)] #[napi(object)] pub struct JsModule { diff --git a/packages/rspack-test-tools/tests/configCases/compilation/modules/a.js b/packages/rspack-test-tools/tests/configCases/compilation/modules/a.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rspack-test-tools/tests/configCases/compilation/modules/index.js b/packages/rspack-test-tools/tests/configCases/compilation/modules/index.js new file mode 100644 index 000000000000..7db157f0fe5f --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/compilation/modules/index.js @@ -0,0 +1 @@ +import("./a"); diff --git a/packages/rspack-test-tools/tests/configCases/compilation/modules/rspack.config.js b/packages/rspack-test-tools/tests/configCases/compilation/modules/rspack.config.js new file mode 100644 index 000000000000..916acb361d52 --- /dev/null +++ b/packages/rspack-test-tools/tests/configCases/compilation/modules/rspack.config.js @@ -0,0 +1,30 @@ +const rspack = require("@rspack/core"); + +const PLUGIN_NAME = "plugin"; + +class Plugin { + /** + * @param {import("@rspack/core").Compiler} compiler + */ + apply(compiler) { + compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => { + compilation.hooks.processAssets.tap( + { + name: PLUGIN_NAME, + stage: rspack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, + }, + () => { + const module = Array.from(compilation.modules).find(module => module.rawRequest === "./index.js"); + const block = module.blocks[0]; + expect(block.dependencies[0].request).toBe("./a"); + } + ) + }); + } +} + +/**@type {import("@rspack/core").Configuration}*/ +module.exports = { + entry: "./index.js", + plugins: [new Plugin()] +}; diff --git a/packages/rspack/etc/api.md b/packages/rspack/etc/api.md index c1e840650c32..eb03ceade5ce 100644 --- a/packages/rspack/etc/api.md +++ b/packages/rspack/etc/api.md @@ -16,6 +16,8 @@ import type { Callback } from '@rspack/lite-tapable'; import { cleanupGlobalTrace } from '@rspack/binding'; import { Compiler as Compiler_2 } from '../Compiler'; import { default as default_2 } from './util/hash'; +import type { DependenciesBlockDTO } from '@rspack/binding'; +import type { DependencyDTO } from '@rspack/binding'; import { RawEvalDevToolModulePluginOptions as EvalDevToolModulePluginOptions } from '@rspack/binding'; import { EventEmitter } from 'events'; import { ExternalObject } from '@rspack/binding'; @@ -32,7 +34,7 @@ import type { JsCreateData } from '@rspack/binding'; import type { JsFactoryMeta } from '@rspack/binding'; import { JsLibraryOptions } from '@rspack/binding'; import { JsLoaderItem } from '@rspack/binding'; -import { JsModule } from '@rspack/binding'; +import type { JsModule } from '@rspack/binding'; import { JsPathData } from '@rspack/binding'; import { JsRuntimeModule } from '@rspack/binding'; import type { JsStats } from '@rspack/binding'; @@ -41,6 +43,7 @@ import type { JsStatsError } from '@rspack/binding'; import type { JsStatsWarning } from '@rspack/binding'; import * as liteTapable from '@rspack/lite-tapable'; import { Logger as Logger_2 } from './logging/Logger'; +import type { ModuleDTO } from '@rspack/binding'; import { RawCopyPattern } from '@rspack/binding'; import type { RawCssExtractPluginOption } from '@rspack/binding'; import type { RawFuncUseCtx } from '@rspack/binding'; @@ -1006,8 +1009,6 @@ export class Compilation { buildMeta: Record; }; // @internal - __internal__getModules(): JsModule[]; - // @internal __internal__hasAsset(name: string): boolean; // @internal __internal__pushDiagnostic(diagnostic: binding.JsDiagnostic): void; @@ -1785,6 +1786,26 @@ export type Dependencies = z.infer; // @public (undocumented) const dependencies: z.ZodArray; +// @public (undocumented) +class DependenciesBlock { + constructor(binding: DependenciesBlockDTO); + // (undocumented) + get blocks(): DependenciesBlock[]; + // (undocumented) + get dependencies(): Dependency[]; +} + +// @public (undocumented) +class Dependency { + constructor(binding: DependencyDTO); + // (undocumented) + get category(): string; + // (undocumented) + get request(): string | undefined; + // (undocumented) + get type(): string; +} + // @public (undocumented) type DependencyLocation = any; @@ -6028,9 +6049,11 @@ const mode: z.ZodEnum<["development", "production", "none"]>; // @public (undocumented) export class Module { - constructor(module: JsModule, compilation?: Compilation); + constructor(module: JsModule | ModuleDTO, compilation?: Compilation); + // (undocumented) + static __from_binding(module: JsModule | ModuleDTO, compilation?: Compilation): Module; // (undocumented) - static __from_binding(module: JsModule, compilation?: Compilation): Module; + get blocks(): DependenciesBlock[]; buildInfo: Record; buildMeta: Record; // (undocumented) diff --git a/packages/rspack/src/Compilation.ts b/packages/rspack/src/Compilation.ts index 9ed31dda5312..c7aea54b0d3c 100644 --- a/packages/rspack/src/Compilation.ts +++ b/packages/rspack/src/Compilation.ts @@ -420,13 +420,8 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si } get modules(): ReadonlySet { - return memoizeValue( - () => - new Set( - this.__internal__getModules().map(item => - Module.__from_binding(item, this) - ) - ) + return new Set( + this.#inner.modules.map(module => Module.__from_binding(module, this)) ); } @@ -1118,15 +1113,6 @@ BREAKING CHANGE: Asset processing hooks in Compilation has been merged into a si return this.#inner.hasAsset(name); } - /** - * Note: This is not a webpack public API, maybe removed in future. - * - * @internal - */ - __internal__getModules(): JsModule[] { - return this.#inner.getModules(); - } - /** * Note: This is not a webpack public API, maybe removed in future. * diff --git a/packages/rspack/src/DependenciesBlock.ts b/packages/rspack/src/DependenciesBlock.ts new file mode 100644 index 000000000000..452d63bd62c4 --- /dev/null +++ b/packages/rspack/src/DependenciesBlock.ts @@ -0,0 +1,18 @@ +import type { DependenciesBlockDTO } from "@rspack/binding"; +import { Dependency } from "./Dependency"; + +export class DependenciesBlock { + #binding: DependenciesBlockDTO; + + constructor(binding: DependenciesBlockDTO) { + this.#binding = binding; + } + + get dependencies(): Dependency[] { + return this.#binding.dependencies.map(d => new Dependency(d)); + } + + get blocks(): DependenciesBlock[] { + return this.#binding.blocks.map(b => new DependenciesBlock(b)); + } +} diff --git a/packages/rspack/src/Dependency.ts b/packages/rspack/src/Dependency.ts new file mode 100644 index 000000000000..d0aacf047018 --- /dev/null +++ b/packages/rspack/src/Dependency.ts @@ -0,0 +1,21 @@ +import type { DependencyDTO } from "@rspack/binding"; + +export class Dependency { + #binding: DependencyDTO; + + constructor(binding: DependencyDTO) { + this.#binding = binding; + } + + get type(): string { + return this.#binding.type; + } + + get category(): string { + return this.#binding.category; + } + + get request(): string | undefined { + return this.#binding.request; + } +} diff --git a/packages/rspack/src/Module.ts b/packages/rspack/src/Module.ts index 184d7dbff6db..c987ea25a617 100644 --- a/packages/rspack/src/Module.ts +++ b/packages/rspack/src/Module.ts @@ -2,11 +2,13 @@ import type { JsCodegenerationResult, JsCreateData, JsFactoryMeta, - JsModule + JsModule, + ModuleDTO } from "@rspack/binding"; import type { Source } from "webpack-sources"; import type { Compilation } from "./Compilation"; +import { DependenciesBlock } from "./DependenciesBlock"; import { JsSource } from "./util/source"; export type ResourceData = { @@ -50,7 +52,7 @@ export type ContextModuleFactoryAfterResolveResult = }; export class Module { - #inner: JsModule; + #inner: JsModule | ModuleDTO; #originalSource?: Source; context?: Readonly; @@ -76,11 +78,14 @@ export class Module { */ buildMeta: Record; - static __from_binding(module: JsModule, compilation?: Compilation) { + static __from_binding( + module: JsModule | ModuleDTO, + compilation?: Compilation + ) { return new Module(module, compilation); } - constructor(module: JsModule, compilation?: Compilation) { + constructor(module: JsModule | ModuleDTO, compilation?: Compilation) { this.#inner = module; this.type = module.type; this.layer = module.layer ?? null; @@ -119,6 +124,13 @@ export class Module { } return null; } + + get blocks(): DependenciesBlock[] { + if ("blocks" in this.#inner) { + return this.#inner.blocks.map(b => new DependenciesBlock(b)); + } + return []; + } } export class CodeGenerationResult {