Skip to content

Commit

Permalink
fix: ContextModuleFactoryAfterResolve in js
Browse files Browse the repository at this point in the history
  • Loading branch information
SyMind committed Sep 20, 2024
1 parent 7662ee8 commit 75a1ad7
Show file tree
Hide file tree
Showing 7 changed files with 246 additions and 138 deletions.
18 changes: 11 additions & 7 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ export class JsContextModuleFactoryAfterResolveData {
set recursive(recursive: boolean)
}

export class JsContextModuleFactoryBeforeResolveData {
get context(): string
set context(context: string)
get request(): string
set request(request: string)
get regExp(): RawRegex | undefined
set regExp(rawRegExp: RawRegex | undefined)
get recursive(): boolean
set recursive(recursive: boolean)
}

export class JsEntries {
clear(): void
get size(): number
Expand Down Expand Up @@ -475,13 +486,6 @@ export interface JsCompatSource {
map?: string
}

export interface JsContextModuleFactoryBeforeResolveData {
context: string
request: string
regExp?: RawRegex
recursive: boolean
}

export interface JsCreateData {
request: string
userRequest: string
Expand Down
44 changes: 12 additions & 32 deletions crates/node_binding/src/plugins/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,21 @@ use rspack_binding_values::{
JsAlterAssetTagGroupsData, JsAlterAssetTagsData, JsAssetEmittedArgs,
JsBeforeAssetTagGenerationData, JsBeforeEmitData, JsBeforeResolveArgs, JsBeforeResolveOutput,
JsChunk, JsChunkAssetArgs, JsCompilationWrapper, JsContextModuleFactoryAfterResolveDataWrapper,
JsContextModuleFactoryAfterResolveResult, JsContextModuleFactoryBeforeResolveData,
JsContextModuleFactoryAfterResolveResult, JsContextModuleFactoryBeforeResolveDataWrapper,
JsContextModuleFactoryBeforeResolveResult, JsCreateData, JsExecuteModuleArg, JsFactorizeArgs,
JsFactorizeOutput, JsModule, JsNormalModuleFactoryCreateModuleArgs, JsResolveArgs,
JsResolveForSchemeArgs, JsResolveForSchemeOutput, JsResolveOutput, JsRuntimeGlobals,
JsRuntimeModule, JsRuntimeModuleArg, ToJsCompatSource, ToJsModule,
};
use rspack_collections::IdentifierSet;
use rspack_core::{
parse_resource, AfterResolveResult, AssetEmittedInfo, BeforeResolveData, BeforeResolveResult,
BoxModule, Chunk, ChunkUkey, CodeGenerationResults, Compilation,
CompilationAdditionalTreeRuntimeRequirements, CompilationAdditionalTreeRuntimeRequirementsHook,
CompilationAfterOptimizeModules, CompilationAfterOptimizeModulesHook,
CompilationAfterProcessAssets, CompilationAfterProcessAssetsHook, CompilationAfterSeal,
CompilationAfterSealHook, CompilationBuildModule, CompilationBuildModuleHook,
CompilationChunkAsset, CompilationChunkAssetHook, CompilationChunkHash, CompilationChunkHashHook,
parse_resource, AfterResolveResult, AssetEmittedInfo, BeforeResolveResult, BoxModule, Chunk,
ChunkUkey, CodeGenerationResults, Compilation, CompilationAdditionalTreeRuntimeRequirements,
CompilationAdditionalTreeRuntimeRequirementsHook, CompilationAfterOptimizeModules,
CompilationAfterOptimizeModulesHook, CompilationAfterProcessAssets,
CompilationAfterProcessAssetsHook, CompilationAfterSeal, CompilationAfterSealHook,
CompilationBuildModule, CompilationBuildModuleHook, CompilationChunkAsset,
CompilationChunkAssetHook, CompilationChunkHash, CompilationChunkHashHook,
CompilationExecuteModule, CompilationExecuteModuleHook, CompilationFinishModules,
CompilationFinishModulesHook, CompilationOptimizeChunkModules,
CompilationOptimizeChunkModulesHook, CompilationOptimizeModules, CompilationOptimizeModulesHook,
Expand Down Expand Up @@ -1507,34 +1507,14 @@ impl ContextModuleFactoryBeforeResolve for ContextModuleFactoryBeforeResolveTap
async fn run(&self, result: BeforeResolveResult) -> rspack_error::Result<BeforeResolveResult> {
let js_result = match result {
BeforeResolveResult::Ignored => JsContextModuleFactoryBeforeResolveResult::A(false),
BeforeResolveResult::Data(d) => {
let reg_exp = d.reg_exp.map(|js_regex| js_regex.into());
JsContextModuleFactoryBeforeResolveResult::B(JsContextModuleFactoryBeforeResolveData {
context: d.context,
request: d.request,
reg_exp,
recursive: d.recursive,
})
}
BeforeResolveResult::Data(data) => JsContextModuleFactoryBeforeResolveResult::B(
JsContextModuleFactoryBeforeResolveDataWrapper::new(data),
),
};
match self.function.call_with_promise(js_result).await {
Ok(js_result) => match js_result {
napi::bindgen_prelude::Either::A(_) => Ok(BeforeResolveResult::Ignored),
napi::bindgen_prelude::Either::B(d) => {
let reg_exp = match d.reg_exp {
Some(js_regex) => Some(js_regex.try_into()?),
None => None,
};
let data = BeforeResolveData {
context: d.context,
request: d.request,
reg_exp,
recursive: d.recursive,
// TODO: fix
critical: true,
};
Ok(BeforeResolveResult::Data(Box::new(data)))
}
napi::bindgen_prelude::Either::B(js_data) => Ok(BeforeResolveResult::Data(js_data.take())),
},
Err(err) => Err(err),
}
Expand Down
113 changes: 102 additions & 11 deletions crates/rspack_binding_values/src/context_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,113 @@ use napi::bindgen_prelude::{
ClassInstance, Either, FromNapiValue, ToNapiValue, TypeName, ValidateNapiValue,
};
use napi_derive::napi;
use rspack_core::AfterResolveData;
use rspack_core::{AfterResolveData, BeforeResolveData};

use crate::RawRegex;

#[napi(object)]
pub struct JsContextModuleFactoryBeforeResolveData {
pub context: String,
pub request: String,
pub reg_exp: Option<RawRegex>,
pub recursive: bool,
#[napi]
pub struct JsContextModuleFactoryBeforeResolveData(Box<BeforeResolveData>);

#[napi]
impl JsContextModuleFactoryBeforeResolveData {
#[napi(getter)]
pub fn context(&self) -> &str {
&self.0.context
}

#[napi(setter)]
pub fn set_context(&mut self, context: String) {
self.0.context = context;
}

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

#[napi(setter)]
pub fn set_request(&mut self, request: String) {
self.0.request = request;
}

#[napi(getter)]
pub fn reg_exp(&self) -> Either<RawRegex, ()> {
match &self.0.reg_exp {
Some(r) => Either::A(r.clone().into()),
None => Either::B(()),
}
}

#[napi(setter)]
pub fn set_reg_exp(&mut self, raw_reg_exp: Either<RawRegex, ()>) {
self.0.reg_exp = match raw_reg_exp {
Either::A(raw_reg_exp) => match raw_reg_exp.try_into() {
Ok(reg_exp) => Some(reg_exp),
Err(_) => None,
},
Either::B(_) => None,
};
}

#[napi(getter)]
pub fn recursive(&self) -> bool {
self.0.recursive
}

#[napi(setter)]
pub fn set_recursive(&mut self, recursive: bool) {
self.0.recursive = recursive;
}
}

pub struct JsContextModuleFactoryBeforeResolveDataWrapper(Box<BeforeResolveData>);

impl JsContextModuleFactoryBeforeResolveDataWrapper {
pub fn new(data: Box<BeforeResolveData>) -> Self {
Self(data)
}

pub fn take(self) -> Box<BeforeResolveData> {
self.0
}
}

impl FromNapiValue for JsContextModuleFactoryBeforeResolveDataWrapper {
unsafe fn from_napi_value(
env: napi::sys::napi_env,
napi_val: napi::sys::napi_value,
) -> napi::Result<Self> {
let instance =
<ClassInstance<JsContextModuleFactoryBeforeResolveData> as FromNapiValue>::from_napi_value(
env, napi_val,
)?;
Ok(Self(instance.0.clone()))
}
}

impl ToNapiValue for JsContextModuleFactoryBeforeResolveDataWrapper {
unsafe fn to_napi_value(
env: napi::sys::napi_env,
val: Self,
) -> napi::Result<napi::sys::napi_value> {
let js_val = JsContextModuleFactoryBeforeResolveData(val.0);
ToNapiValue::to_napi_value(env, js_val)
}
}

impl TypeName for JsContextModuleFactoryBeforeResolveDataWrapper {
fn type_name() -> &'static str {
"JsContextModuleFactoryBeforeResolveData"
}
fn value_type() -> napi::ValueType {
napi::ValueType::Object
}
}

impl ValidateNapiValue for JsContextModuleFactoryBeforeResolveDataWrapper {}

pub type JsContextModuleFactoryBeforeResolveResult =
Either<bool, JsContextModuleFactoryBeforeResolveData>;
Either<bool, JsContextModuleFactoryBeforeResolveDataWrapper>;

#[napi]
pub struct JsContextModuleFactoryAfterResolveData(Box<AfterResolveData>);
Expand Down Expand Up @@ -103,9 +196,7 @@ impl FromNapiValue for JsContextModuleFactoryAfterResolveDataWrapper {
<ClassInstance<JsContextModuleFactoryAfterResolveData> as FromNapiValue>::from_napi_value(
env, napi_val,
)?;
Ok(JsContextModuleFactoryAfterResolveDataWrapper(
instance.0.clone(),
))
Ok(Self(instance.0.clone()))
}
}

Expand Down
63 changes: 24 additions & 39 deletions crates/rspack_core/src/context_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use rspack_error::{error, miette::IntoDiagnostic, Result};
use rspack_hook::define_hook;
use rspack_paths::{AssertUtf8, Utf8Path, Utf8PathBuf};
use rspack_regex::RspackRegex;
use swc_core::common::util::take::Take;
use tracing::instrument;

use crate::{
Expand All @@ -22,14 +23,14 @@ pub enum BeforeResolveResult {
Data(Box<BeforeResolveData>),
}

#[derive(Debug)]
#[derive(Debug, Clone)]
pub struct BeforeResolveData {
// context_info
// resolve_options
pub context: String,
pub request: String,
// assertions
// dependencies
pub dependencies: Vec<BoxDependency>,
// dependency_type
// file_dependencies
// missing_dependencies
Expand All @@ -38,10 +39,6 @@ pub struct BeforeResolveData {
// cacheable
pub recursive: bool,
pub reg_exp: Option<RspackRegex>,
// FIX: This field is used by ContextReplacementPlugin to ignore errors collected during the build phase of Context modules.
// In Webpack, the ContextModuleFactory's beforeResolve hook directly traverses dependencies in context and modifies the ContextDependency's critical field.
// Since Rspack currently has difficulty passing the dependencies field, an additional field is used to indicate whether to ignore the collected errors.
pub critical: bool,
}

#[derive(Clone)]
Expand All @@ -55,7 +52,7 @@ pub enum AfterResolveResult {
pub struct AfterResolveData {
pub resource: Utf8PathBuf,
pub context: String,
// dependencies
pub dependencies: Vec<BoxDependency>,
// layer
// resolve_options
// file_dependencies: HashSet<String>,
Expand All @@ -74,11 +71,6 @@ pub struct AfterResolveData {
// type_prefix: String,
// category: String,
// referenced_exports

// FIX: This field is used by ContextReplacementPlugin to ignore errors collected during the build phase of Context modules.
// In Webpack, the ContextModuleFactory's beforeResolve hook directly traverses dependencies in context and modifies the ContextDependency's critical field.
// Since Rspack currently has difficulty passing the dependencies field, an additional field is used to indicate whether to ignore the collected errors.
pub critical: bool,
#[derivative(Debug = "ignore")]
pub resolve_dependencies: ResolveContextModuleDependencies,
}
Expand Down Expand Up @@ -110,12 +102,8 @@ impl ModuleFactory for ContextModuleFactory {
BeforeResolveResult::Data(before_resolve_result) => {
let (factorize_result, context_module_options) =
self.resolve(data, before_resolve_result).await?;

if let Some(context_module_options) = context_module_options {
if let Some(factorize_result) = self
.after_resolve(context_module_options, &mut data.dependencies)
.await?
{
if let Some(factorize_result) = self.after_resolve(data, context_module_options).await? {
return Ok(factorize_result);
}
}
Expand Down Expand Up @@ -180,7 +168,7 @@ impl ContextModuleFactory {
request: dependency.request().to_string(),
recursive: dependency_options.recursive,
reg_exp: dependency_options.reg_exp.clone(),
critical: true,
dependencies: data.dependencies.clone(),
};

match self
Expand All @@ -191,10 +179,9 @@ impl ContextModuleFactory {
.await?
{
BeforeResolveResult::Ignored => Ok(BeforeResolveResult::Ignored),
BeforeResolveResult::Data(result) => {
if !result.critical {
*dependency.critical_mut() = None;
}
BeforeResolveResult::Data(mut result) => {
// The dependencies can be modified in the before resolve hook
data.dependencies = result.dependencies.take();
Ok(BeforeResolveResult::Data(result))
}
}
Expand Down Expand Up @@ -347,17 +334,17 @@ impl ContextModuleFactory {

async fn after_resolve(
&self,
data: &mut ModuleFactoryCreateData,
mut context_module_options: ContextModuleOptions,
dependencies: &mut [BoxDependency],
) -> Result<Option<ModuleFactoryResult>> {
let context_options = &context_module_options.context_options;
let after_resolve_data = AfterResolveData {
resource: context_module_options.resource.clone(),
context: context_options.context.clone(),
dependencies: data.dependencies.clone(),
request: context_options.request.clone(),
reg_exp: context_options.reg_exp.clone(),
recursive: context_options.recursive,
critical: true,
resolve_dependencies: self.resolve_dependencies.clone(),
};

Expand All @@ -369,21 +356,19 @@ impl ContextModuleFactory {
.await?
{
AfterResolveResult::Ignored => Ok(Some(ModuleFactoryResult::default())),
AfterResolveResult::Data(result) => {
context_module_options.resource = result.resource;
context_module_options.context_options.context = result.context;
context_module_options.context_options.reg_exp = result.reg_exp;
context_module_options.context_options.recursive = result.recursive;

let dependency = dependencies[0]
.as_context_dependency_mut()
.expect("should be context dependency");
if !result.critical {
*dependency.critical_mut() = None;
}

let module =
ContextModule::new(result.resolve_dependencies, context_module_options.clone());
AfterResolveResult::Data(mut after_resolve_data) => {
// The dependencies can be modified in the after resolve hook
data.dependencies = after_resolve_data.dependencies.take();

context_module_options.resource = after_resolve_data.resource;
context_module_options.context_options.context = after_resolve_data.context;
context_module_options.context_options.reg_exp = after_resolve_data.reg_exp;
context_module_options.context_options.recursive = after_resolve_data.recursive;

let module = ContextModule::new(
after_resolve_data.resolve_dependencies,
context_module_options.clone(),
);

Ok(Some(ModuleFactoryResult::new_with_module(Box::new(module))))
}
Expand Down
Loading

0 comments on commit 75a1ad7

Please sign in to comment.