Skip to content

feat: support modify resource in after resolve hook #5924

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,20 @@ export function __chunk_inner_is_only_initial(jsChunkUkey: number, compilation:

export function __entrypoint_inner_get_runtime_chunk(ukey: number, compilation: JsCompilation): JsChunk

export interface AfterResolveCreateData {
request: string
userRequest: string
resource: string
}

export interface AfterResolveData {
request: string
context: string
fileDependencies: Array<string>
contextDependencies: Array<string>
missingDependencies: Array<string>
factoryMeta: FactoryMeta
createData?: AfterResolveCreateData
}

export interface BuiltinPlugin {
Expand Down Expand Up @@ -348,7 +355,7 @@ export interface JsHooks {
finishMake: (compilation: JsCompilation) => void
buildModule: (module: JsModule) => void
chunkAsset: (asset: JsChunkAssetArgs) => void
afterResolve: (data: AfterResolveData) => Promise<boolean | void>
afterResolve: (data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>
contextModuleFactoryBeforeResolve: (data: JsBeforeResolveArgs) => Promise<boolean | void>
contextModuleFactoryAfterResolve: (data: AfterResolveData) => Promise<boolean | void>
normalModuleFactoryCreateModule: (data: CreateModuleData) => void
Expand Down
33 changes: 31 additions & 2 deletions crates/node_binding/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use rspack_binding_values::{
use rspack_core::rspack_sources::Source;
use rspack_core::{
ApplyContext, BuildTimeExecutionOption, Chunk, ChunkAssetArgs, CompilerOptions, ModuleIdentifier,
NormalModuleAfterResolveArgs, PluginContext, RuntimeModule,
NormalModuleAfterResolveArgs, NormalModuleAfterResolveCreateData, PluginContext, RuntimeModule,
};
use rspack_core::{BeforeResolveArgs, PluginNormalModuleFactoryAfterResolveOutput};
use rspack_core::{
Expand Down Expand Up @@ -127,7 +127,36 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin {
if self.is_hook_disabled(&Hook::AfterResolve) {
return Ok(None);
}
self.hooks.after_resolve.call((&*args).into()).await

match self.hooks.after_resolve.call((&*args).into()).await {
Ok((ret, resolve_data)) => {
if let (Some(resolve_data), Some(create_data)) = (resolve_data, &args.create_data) {
fn override_resource(origin_data: &ResourceData, new_resource: String) -> ResourceData {
let mut resource_data = origin_data.clone();
let origin_resource_path = origin_data.resource_path.to_string_lossy().to_string();
resource_data.resource_path = new_resource.clone().into();
resource_data.resource = resource_data
.resource
.replace(&origin_resource_path, &new_resource);

resource_data
}

let request = resolve_data.request;
let user_request = resolve_data.user_request;
let resource = override_resource(&create_data.resource, resolve_data.resource);

args.create_data = Some(NormalModuleAfterResolveCreateData {
request,
user_request,
resource,
});
}

Ok(ret)
}
Err(err) => Err(err),
}
}

async fn context_module_before_resolve(
Expand Down
13 changes: 8 additions & 5 deletions crates/rspack_binding_values/src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ use napi_derive::napi;
use rspack_napi::threadsafe_function::ThreadsafeFunction;

use crate::{
AfterResolveData, CreateModuleData, JsAssetEmittedArgs, JsBeforeResolveArgs, JsChunkAssetArgs,
JsCompilation, JsExecuteModuleArg, JsModule, JsResolveForSchemeInput, JsResolveForSchemeResult,
JsRuntimeModule, JsRuntimeModuleArg,
AfterResolveCreateData, AfterResolveData, CreateModuleData, JsAssetEmittedArgs,
JsBeforeResolveArgs, JsChunkAssetArgs, JsCompilation, JsExecuteModuleArg, JsModule,
JsResolveForSchemeInput, JsResolveForSchemeResult, JsRuntimeModule, JsRuntimeModuleArg,
};

#[napi(object, object_to_js = false)]
Expand Down Expand Up @@ -41,8 +41,11 @@ pub struct JsHooks {
pub build_module: ThreadsafeFunction<JsModule, ()>, // TODO
#[napi(ts_type = "(asset: JsChunkAssetArgs) => void")]
pub chunk_asset: ThreadsafeFunction<JsChunkAssetArgs, ()>,
#[napi(ts_type = "(data: AfterResolveData) => Promise<boolean | void>")]
pub after_resolve: ThreadsafeFunction<AfterResolveData, Option<bool>>,
#[napi(
ts_type = "(data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>"
)]
pub after_resolve:
ThreadsafeFunction<AfterResolveData, (Option<bool>, Option<AfterResolveCreateData>)>,
#[napi(ts_type = "(data: JsBeforeResolveArgs) => Promise<boolean | void>")]
pub context_module_factory_before_resolve: ThreadsafeFunction<JsBeforeResolveArgs, Option<bool>>,
#[napi(ts_type = "(data: AfterResolveData) => Promise<boolean | void>")]
Expand Down
22 changes: 21 additions & 1 deletion crates/rspack_binding_values/src/normal_module_factory.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use napi_derive::napi;
use rspack_core::{
BeforeResolveArgs, NormalModuleAfterResolveArgs, NormalModuleCreateData, ResourceData,
BeforeResolveArgs, NormalModuleAfterResolveArgs, NormalModuleAfterResolveCreateData,
NormalModuleCreateData, ResourceData,
};

#[napi(object)]
Expand All @@ -23,6 +24,13 @@ pub struct JsBeforeResolveArgs {

pub type JsBeforeResolveOutput = (Option<bool>, JsBeforeResolveArgs);

#[napi(object)]
pub struct AfterResolveCreateData {
pub request: String,
pub user_request: String,
pub resource: String,
}

#[napi(object)]
pub struct AfterResolveData {
pub request: String,
Expand All @@ -31,6 +39,7 @@ pub struct AfterResolveData {
pub context_dependencies: Vec<String>,
pub missing_dependencies: Vec<String>,
pub factory_meta: FactoryMeta,
pub create_data: Option<AfterResolveCreateData>,
}

#[napi(object)]
Expand Down Expand Up @@ -124,6 +133,17 @@ impl From<&NormalModuleAfterResolveArgs<'_>> for AfterResolveData {
factory_meta: FactoryMeta {
side_effect_free: value.factory_meta.side_effect_free,
},
create_data: value.create_data.as_ref().map(AfterResolveCreateData::from),
}
}
}

impl From<&NormalModuleAfterResolveCreateData> for AfterResolveCreateData {
fn from(value: &NormalModuleAfterResolveCreateData) -> Self {
Self {
request: value.request.to_owned(),
user_request: value.user_request.to_owned(),
resource: value.resource.resource_path.to_string_lossy().to_string(),
}
}
}
1 change: 1 addition & 0 deletions crates/rspack_core/src/context_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ impl ContextModuleFactory {
missing_dependencies: &data.missing_dependencies,
diagnostics: &mut data.diagnostics,
factory_meta: &factory_result.factory_meta,
create_data: None,
})
.await
}
Expand Down
79 changes: 40 additions & 39 deletions crates/rspack_core/src/normal_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ use crate::{
FactorizeArgs, FactoryMeta, FuncUseCtx, GeneratorOptions, ModuleExt, ModuleFactory,
ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, ModuleRule, ModuleRuleEnforce,
ModuleRuleUse, ModuleRuleUseLoader, ModuleType, NormalModule, NormalModuleAfterResolveArgs,
NormalModuleCreateData, ParserOptions, RawModule, Resolve, ResolveArgs,
ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, ResourceData,
ResourceParsedData, SharedPluginDriver,
NormalModuleAfterResolveCreateData, NormalModuleCreateData, ParserOptions, RawModule, Resolve,
ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory,
ResourceData, ResourceParsedData, SharedPluginDriver,
};

pub type NormalModuleFactoryBeforeResolveHook = AsyncSeriesBailHook<BeforeResolveArgs, bool>;
Expand All @@ -45,9 +45,6 @@ impl ModuleFactory for NormalModuleFactory {
return Ok(before_resolve_data);
}
let factory_result = self.factorize(data).await?;
if let Ok(Some(after_resolve_data)) = self.after_resolve(data, &factory_result).await {
return Ok(after_resolve_data);
}

Ok(factory_result)
}
Expand Down Expand Up @@ -109,35 +106,6 @@ impl NormalModuleFactory {
Ok(None)
}

async fn after_resolve(
&self,
data: &mut ModuleFactoryCreateData,
factory_result: &ModuleFactoryResult,
) -> Result<Option<ModuleFactoryResult>> {
let dependency = data
.dependency
.as_module_dependency()
.expect("should be module dependency");
if let Ok(Some(false)) = self
.plugin_driver
.after_resolve(&mut NormalModuleAfterResolveArgs {
request: dependency.request(),
context: data.context.as_ref(),
file_dependencies: &data.file_dependencies,
context_dependencies: &data.context_dependencies,
missing_dependencies: &data.missing_dependencies,
factory_meta: &factory_result.factory_meta,
diagnostics: &mut data.diagnostics,
})
.await
{
// ignored
// See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/NormalModuleFactory.js#L301
return Ok(Some(ModuleFactoryResult::default()));
}
Ok(None)
}

fn get_loader_resolver(&self) -> Arc<Resolver> {
self
.loader_resolver_factory
Expand Down Expand Up @@ -579,10 +547,43 @@ impl NormalModuleFactory {
)
})?();

let after_resolve_create_data = {
let mut after_resolve_args = NormalModuleAfterResolveArgs {
request: dependency.request(),
context: data.context.as_ref(),
file_dependencies: &data.file_dependencies,
context_dependencies: &data.context_dependencies,
missing_dependencies: &data.missing_dependencies,
factory_meta: &factory_meta,
diagnostics: &mut data.diagnostics,
create_data: Some(NormalModuleAfterResolveCreateData {
request,
user_request,
resource: resource_data,
}),
};

if let Some(plugin_result) = self
.plugin_driver
.after_resolve(&mut after_resolve_args)
.await?
{
if !plugin_result {
// ignored
// See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/NormalModuleFactory.js#L301
return Ok(Some(ModuleFactoryResult::default()));
}
}

after_resolve_args
.create_data
.unwrap_or_else(|| unreachable!())
};

let mut create_data = NormalModuleCreateData {
dependency_type: data.dependency.dependency_type().clone(),
resolve_data_request: dependency.request(),
resource_resolve_data: resource_data.clone(),
resource_resolve_data: after_resolve_create_data.resource.clone(),
context: data.context.clone(),
diagnostics: &mut data.diagnostics,
};
Expand All @@ -594,15 +595,15 @@ impl NormalModuleFactory {
module
} else {
let normal_module = NormalModule::new(
request,
user_request,
after_resolve_create_data.request,
after_resolve_create_data.user_request,
dependency.request().to_owned(),
resolved_module_type,
resolved_parser_and_generator,
resolved_parser_options,
resolved_generator_options,
match_resource_data,
resource_data,
after_resolve_create_data.resource,
resolved_resolve_options,
loaders,
contains_inline,
Expand Down
13 changes: 13 additions & 0 deletions crates/rspack_core/src/plugin/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ pub struct NormalModuleCreateData<'a> {
pub diagnostics: &'a mut Vec<Diagnostic>,
}

#[derive(Debug)]
pub struct NormalModuleAfterResolveCreateData {
pub request: String,
pub user_request: String,
pub resource: ResourceData,
}

#[derive(Debug, Clone)]
pub struct NormalModuleBeforeResolveArgs {
pub request: String,
pub context: String,
}
#[derive(Debug)]
pub struct NormalModuleAfterResolveArgs<'a> {
pub request: &'a str,
Expand All @@ -91,6 +103,7 @@ pub struct NormalModuleAfterResolveArgs<'a> {
pub missing_dependencies: &'a HashSet<PathBuf>,
pub factory_meta: &'a FactoryMeta,
pub diagnostics: &'a mut Vec<Diagnostic>,
pub create_data: Option<NormalModuleAfterResolveCreateData>,
}

#[derive(Debug)]
Expand Down
2 changes: 1 addition & 1 deletion packages/rspack/src/Compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ class Compiler {
}
);
this.#updateDisabledHooks();
return res;
return [res, resolveData.createData];
}

async #contextModuleFactoryBeforeResolve(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "a"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "b"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "c"
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import a from "./a";
import b from "./b"; // b.js will be transform to c.js
import c from "./c";
import fs from "fs";

it("should remove duplicate request modules generate by after resolve hook", () => {
expect(a).toBe("a");
expect(b).toBe("c");
expect(c).toBe("c");
const ext = ".js";
expect(fs.readFileSync(__filename, "utf-8")).not.toContain("./b" + ext);
expect(fs.readFileSync(__filename, "utf-8")).toContain("./c" + ext);
expect(fs.readFileSync(__filename, "utf-8").split("./c" + ext).length - 1).toBe(2);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import a from "./a";
import b from "./b";
import fs from "fs";

it("should modify request by after resolve hook", () => {
expect(a).toBe("a");
expect(b).toBe("b");
const ext = ".js";
expect(fs.readFileSync(__filename, "utf-8")).toContain("./c" + ext);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import a from "./a";
import b from "./b";
import fs from "fs";

it("should modify resource by after resolve hook", () => {
expect(a).toBe("a")
expect(b).toBe("c")
const ext = ".js";
expect(fs.readFileSync(__filename, "utf-8")).toContain("./b" + ext);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
findBundle(index) {
switch (index) {
case 0: return ['resource.js'];
case 1: return ['request.js'];
case 2: return ['duplicate.js'];
}
}
};
Loading