Skip to content

Commit 3c7a0d7

Browse files
authored
feat: support modify resource in after resolve hook (#5924)
* feat: support modify resource in after resolve hook * fix: update
1 parent 769ca8e commit 3c7a0d7

File tree

16 files changed

+243
-49
lines changed

16 files changed

+243
-49
lines changed

crates/node_binding/binding.d.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,20 @@ export function __chunk_inner_is_only_initial(jsChunkUkey: number, compilation:
123123

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

126+
export interface AfterResolveCreateData {
127+
request: string
128+
userRequest: string
129+
resource: string
130+
}
131+
126132
export interface AfterResolveData {
127133
request: string
128134
context: string
129135
fileDependencies: Array<string>
130136
contextDependencies: Array<string>
131137
missingDependencies: Array<string>
132138
factoryMeta: FactoryMeta
139+
createData?: AfterResolveCreateData
133140
}
134141

135142
export interface BuiltinPlugin {
@@ -348,7 +355,7 @@ export interface JsHooks {
348355
finishMake: (compilation: JsCompilation) => void
349356
buildModule: (module: JsModule) => void
350357
chunkAsset: (asset: JsChunkAssetArgs) => void
351-
afterResolve: (data: AfterResolveData) => Promise<boolean | void>
358+
afterResolve: (data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>
352359
contextModuleFactoryBeforeResolve: (data: JsBeforeResolveArgs) => Promise<boolean | void>
353360
contextModuleFactoryAfterResolve: (data: AfterResolveData) => Promise<boolean | void>
354361
normalModuleFactoryCreateModule: (data: CreateModuleData) => void

crates/node_binding/src/plugins/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rspack_binding_values::{
1616
use rspack_core::rspack_sources::Source;
1717
use rspack_core::{
1818
ApplyContext, BuildTimeExecutionOption, Chunk, ChunkAssetArgs, CompilerOptions, ModuleIdentifier,
19-
NormalModuleAfterResolveArgs, PluginContext, RuntimeModule,
19+
NormalModuleAfterResolveArgs, NormalModuleAfterResolveCreateData, PluginContext, RuntimeModule,
2020
};
2121
use rspack_core::{BeforeResolveArgs, PluginNormalModuleFactoryAfterResolveOutput};
2222
use rspack_core::{
@@ -127,7 +127,36 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin {
127127
if self.is_hook_disabled(&Hook::AfterResolve) {
128128
return Ok(None);
129129
}
130-
self.hooks.after_resolve.call((&*args).into()).await
130+
131+
match self.hooks.after_resolve.call((&*args).into()).await {
132+
Ok((ret, resolve_data)) => {
133+
if let (Some(resolve_data), Some(create_data)) = (resolve_data, &args.create_data) {
134+
fn override_resource(origin_data: &ResourceData, new_resource: String) -> ResourceData {
135+
let mut resource_data = origin_data.clone();
136+
let origin_resource_path = origin_data.resource_path.to_string_lossy().to_string();
137+
resource_data.resource_path = new_resource.clone().into();
138+
resource_data.resource = resource_data
139+
.resource
140+
.replace(&origin_resource_path, &new_resource);
141+
142+
resource_data
143+
}
144+
145+
let request = resolve_data.request;
146+
let user_request = resolve_data.user_request;
147+
let resource = override_resource(&create_data.resource, resolve_data.resource);
148+
149+
args.create_data = Some(NormalModuleAfterResolveCreateData {
150+
request,
151+
user_request,
152+
resource,
153+
});
154+
}
155+
156+
Ok(ret)
157+
}
158+
Err(err) => Err(err),
159+
}
131160
}
132161

133162
async fn context_module_before_resolve(

crates/rspack_binding_values/src/hooks.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use napi_derive::napi;
22
use rspack_napi::threadsafe_function::ThreadsafeFunction;
33

44
use crate::{
5-
AfterResolveData, CreateModuleData, JsAssetEmittedArgs, JsBeforeResolveArgs, JsChunkAssetArgs,
6-
JsCompilation, JsExecuteModuleArg, JsModule, JsResolveForSchemeInput, JsResolveForSchemeResult,
7-
JsRuntimeModule, JsRuntimeModuleArg,
5+
AfterResolveCreateData, AfterResolveData, CreateModuleData, JsAssetEmittedArgs,
6+
JsBeforeResolveArgs, JsChunkAssetArgs, JsCompilation, JsExecuteModuleArg, JsModule,
7+
JsResolveForSchemeInput, JsResolveForSchemeResult, JsRuntimeModule, JsRuntimeModuleArg,
88
};
99

1010
#[napi(object, object_to_js = false)]
@@ -41,8 +41,11 @@ pub struct JsHooks {
4141
pub build_module: ThreadsafeFunction<JsModule, ()>, // TODO
4242
#[napi(ts_type = "(asset: JsChunkAssetArgs) => void")]
4343
pub chunk_asset: ThreadsafeFunction<JsChunkAssetArgs, ()>,
44-
#[napi(ts_type = "(data: AfterResolveData) => Promise<boolean | void>")]
45-
pub after_resolve: ThreadsafeFunction<AfterResolveData, Option<bool>>,
44+
#[napi(
45+
ts_type = "(data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>"
46+
)]
47+
pub after_resolve:
48+
ThreadsafeFunction<AfterResolveData, (Option<bool>, Option<AfterResolveCreateData>)>,
4649
#[napi(ts_type = "(data: JsBeforeResolveArgs) => Promise<boolean | void>")]
4750
pub context_module_factory_before_resolve: ThreadsafeFunction<JsBeforeResolveArgs, Option<bool>>,
4851
#[napi(ts_type = "(data: AfterResolveData) => Promise<boolean | void>")]

crates/rspack_binding_values/src/normal_module_factory.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use napi_derive::napi;
22
use rspack_core::{
3-
BeforeResolveArgs, NormalModuleAfterResolveArgs, NormalModuleCreateData, ResourceData,
3+
BeforeResolveArgs, NormalModuleAfterResolveArgs, NormalModuleAfterResolveCreateData,
4+
NormalModuleCreateData, ResourceData,
45
};
56

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

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

27+
#[napi(object)]
28+
pub struct AfterResolveCreateData {
29+
pub request: String,
30+
pub user_request: String,
31+
pub resource: String,
32+
}
33+
2634
#[napi(object)]
2735
pub struct AfterResolveData {
2836
pub request: String,
@@ -31,6 +39,7 @@ pub struct AfterResolveData {
3139
pub context_dependencies: Vec<String>,
3240
pub missing_dependencies: Vec<String>,
3341
pub factory_meta: FactoryMeta,
42+
pub create_data: Option<AfterResolveCreateData>,
3443
}
3544

3645
#[napi(object)]
@@ -124,6 +133,17 @@ impl From<&NormalModuleAfterResolveArgs<'_>> for AfterResolveData {
124133
factory_meta: FactoryMeta {
125134
side_effect_free: value.factory_meta.side_effect_free,
126135
},
136+
create_data: value.create_data.as_ref().map(AfterResolveCreateData::from),
137+
}
138+
}
139+
}
140+
141+
impl From<&NormalModuleAfterResolveCreateData> for AfterResolveCreateData {
142+
fn from(value: &NormalModuleAfterResolveCreateData) -> Self {
143+
Self {
144+
request: value.request.to_owned(),
145+
user_request: value.user_request.to_owned(),
146+
resource: value.resource.resource_path.to_string_lossy().to_string(),
127147
}
128148
}
129149
}

crates/rspack_core/src/context_module_factory.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@ impl ContextModuleFactory {
234234
missing_dependencies: &data.missing_dependencies,
235235
diagnostics: &mut data.diagnostics,
236236
factory_meta: &factory_result.factory_meta,
237+
create_data: None,
237238
})
238239
.await
239240
}

crates/rspack_core/src/normal_module_factory.rs

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ use crate::{
1717
FactorizeArgs, FactoryMeta, FuncUseCtx, GeneratorOptions, ModuleExt, ModuleFactory,
1818
ModuleFactoryCreateData, ModuleFactoryResult, ModuleIdentifier, ModuleRule, ModuleRuleEnforce,
1919
ModuleRuleUse, ModuleRuleUseLoader, ModuleType, NormalModule, NormalModuleAfterResolveArgs,
20-
NormalModuleCreateData, ParserOptions, RawModule, Resolve, ResolveArgs,
21-
ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory, ResourceData,
22-
ResourceParsedData, SharedPluginDriver,
20+
NormalModuleAfterResolveCreateData, NormalModuleCreateData, ParserOptions, RawModule, Resolve,
21+
ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory,
22+
ResourceData, ResourceParsedData, SharedPluginDriver,
2323
};
2424

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

5249
Ok(factory_result)
5350
}
@@ -109,35 +106,6 @@ impl NormalModuleFactory {
109106
Ok(None)
110107
}
111108

112-
async fn after_resolve(
113-
&self,
114-
data: &mut ModuleFactoryCreateData,
115-
factory_result: &ModuleFactoryResult,
116-
) -> Result<Option<ModuleFactoryResult>> {
117-
let dependency = data
118-
.dependency
119-
.as_module_dependency()
120-
.expect("should be module dependency");
121-
if let Ok(Some(false)) = self
122-
.plugin_driver
123-
.after_resolve(&mut NormalModuleAfterResolveArgs {
124-
request: dependency.request(),
125-
context: data.context.as_ref(),
126-
file_dependencies: &data.file_dependencies,
127-
context_dependencies: &data.context_dependencies,
128-
missing_dependencies: &data.missing_dependencies,
129-
factory_meta: &factory_result.factory_meta,
130-
diagnostics: &mut data.diagnostics,
131-
})
132-
.await
133-
{
134-
// ignored
135-
// See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/NormalModuleFactory.js#L301
136-
return Ok(Some(ModuleFactoryResult::default()));
137-
}
138-
Ok(None)
139-
}
140-
141109
fn get_loader_resolver(&self) -> Arc<Resolver> {
142110
self
143111
.loader_resolver_factory
@@ -579,10 +547,43 @@ impl NormalModuleFactory {
579547
)
580548
})?();
581549

550+
let after_resolve_create_data = {
551+
let mut after_resolve_args = NormalModuleAfterResolveArgs {
552+
request: dependency.request(),
553+
context: data.context.as_ref(),
554+
file_dependencies: &data.file_dependencies,
555+
context_dependencies: &data.context_dependencies,
556+
missing_dependencies: &data.missing_dependencies,
557+
factory_meta: &factory_meta,
558+
diagnostics: &mut data.diagnostics,
559+
create_data: Some(NormalModuleAfterResolveCreateData {
560+
request,
561+
user_request,
562+
resource: resource_data,
563+
}),
564+
};
565+
566+
if let Some(plugin_result) = self
567+
.plugin_driver
568+
.after_resolve(&mut after_resolve_args)
569+
.await?
570+
{
571+
if !plugin_result {
572+
// ignored
573+
// See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/NormalModuleFactory.js#L301
574+
return Ok(Some(ModuleFactoryResult::default()));
575+
}
576+
}
577+
578+
after_resolve_args
579+
.create_data
580+
.unwrap_or_else(|| unreachable!())
581+
};
582+
582583
let mut create_data = NormalModuleCreateData {
583584
dependency_type: data.dependency.dependency_type().clone(),
584585
resolve_data_request: dependency.request(),
585-
resource_resolve_data: resource_data.clone(),
586+
resource_resolve_data: after_resolve_create_data.resource.clone(),
586587
context: data.context.clone(),
587588
diagnostics: &mut data.diagnostics,
588589
};
@@ -594,15 +595,15 @@ impl NormalModuleFactory {
594595
module
595596
} else {
596597
let normal_module = NormalModule::new(
597-
request,
598-
user_request,
598+
after_resolve_create_data.request,
599+
after_resolve_create_data.user_request,
599600
dependency.request().to_owned(),
600601
resolved_module_type,
601602
resolved_parser_and_generator,
602603
resolved_parser_options,
603604
resolved_generator_options,
604605
match_resource_data,
605-
resource_data,
606+
after_resolve_create_data.resource,
606607
resolved_resolve_options,
607608
loaders,
608609
contains_inline,

crates/rspack_core/src/plugin/args.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ pub struct NormalModuleCreateData<'a> {
8282
pub diagnostics: &'a mut Vec<Diagnostic>,
8383
}
8484

85+
#[derive(Debug)]
86+
pub struct NormalModuleAfterResolveCreateData {
87+
pub request: String,
88+
pub user_request: String,
89+
pub resource: ResourceData,
90+
}
91+
92+
#[derive(Debug, Clone)]
93+
pub struct NormalModuleBeforeResolveArgs {
94+
pub request: String,
95+
pub context: String,
96+
}
8597
#[derive(Debug)]
8698
pub struct NormalModuleAfterResolveArgs<'a> {
8799
pub request: &'a str,
@@ -91,6 +103,7 @@ pub struct NormalModuleAfterResolveArgs<'a> {
91103
pub missing_dependencies: &'a HashSet<PathBuf>,
92104
pub factory_meta: &'a FactoryMeta,
93105
pub diagnostics: &'a mut Vec<Diagnostic>,
106+
pub create_data: Option<NormalModuleAfterResolveCreateData>,
94107
}
95108

96109
#[derive(Debug)]

packages/rspack/src/Compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,7 @@ class Compiler {
621621
}
622622
);
623623
this.#updateDisabledHooks();
624-
return res;
624+
return [res, resolveData.createData];
625625
}
626626

627627
async #contextModuleFactoryBeforeResolve(
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = "a"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = "b"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = "c"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import a from "./a";
2+
import b from "./b"; // b.js will be transform to c.js
3+
import c from "./c";
4+
import fs from "fs";
5+
6+
it("should remove duplicate request modules generate by after resolve hook", () => {
7+
expect(a).toBe("a");
8+
expect(b).toBe("c");
9+
expect(c).toBe("c");
10+
const ext = ".js";
11+
expect(fs.readFileSync(__filename, "utf-8")).not.toContain("./b" + ext);
12+
expect(fs.readFileSync(__filename, "utf-8")).toContain("./c" + ext);
13+
expect(fs.readFileSync(__filename, "utf-8").split("./c" + ext).length - 1).toBe(2);
14+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import a from "./a";
2+
import b from "./b";
3+
import fs from "fs";
4+
5+
it("should modify request by after resolve hook", () => {
6+
expect(a).toBe("a");
7+
expect(b).toBe("b");
8+
const ext = ".js";
9+
expect(fs.readFileSync(__filename, "utf-8")).toContain("./c" + ext);
10+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import a from "./a";
2+
import b from "./b";
3+
import fs from "fs";
4+
5+
it("should modify resource by after resolve hook", () => {
6+
expect(a).toBe("a")
7+
expect(b).toBe("c")
8+
const ext = ".js";
9+
expect(fs.readFileSync(__filename, "utf-8")).toContain("./b" + ext);
10+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module.exports = {
2+
findBundle(index) {
3+
switch (index) {
4+
case 0: return ['resource.js'];
5+
case 1: return ['request.js'];
6+
case 2: return ['duplicate.js'];
7+
}
8+
}
9+
};

0 commit comments

Comments
 (0)