Skip to content

Commit 1f35c13

Browse files
committed
feat: support modify resource in after resolve hook
1 parent bdf18cc commit 1f35c13

File tree

16 files changed

+240
-51
lines changed

16 files changed

+240
-51
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 BeforeResolveData {
@@ -348,7 +355,7 @@ export interface JsHooks {
348355
buildModule: (module: JsModule) => void
349356
chunkAsset: (asset: JsChunkAssetArgs) => void
350357
beforeResolve: (data: BeforeResolveData) => Promise<(boolean | void | BeforeResolveData)[]>
351-
afterResolve: (data: AfterResolveData) => Promise<boolean | void>
358+
afterResolve: (data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>
352359
contextModuleFactoryBeforeResolve: (data: BeforeResolveData) => 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::{NormalModuleBeforeResolveArgs, PluginNormalModuleFactoryAfterResolveOutput};
2222
use rspack_core::{
@@ -140,7 +140,36 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin {
140140
if self.is_hook_disabled(&Hook::AfterResolve) {
141141
return Ok(None);
142142
}
143-
self.hooks.after_resolve.call((&*args).into()).await
143+
144+
match self.hooks.after_resolve.call((&*args).into()).await {
145+
Ok((ret, resolve_data)) => {
146+
if let (Some(resolve_data), Some(create_data)) = (resolve_data, &args.create_data) {
147+
fn override_resource(origin_data: &ResourceData, new_resource: String) -> ResourceData {
148+
let mut resource_data = origin_data.clone();
149+
let origin_resource_path = origin_data.resource_path.to_string_lossy().to_string();
150+
resource_data.resource_path = new_resource.clone().into();
151+
resource_data.resource = resource_data
152+
.resource
153+
.replace(&origin_resource_path, &new_resource);
154+
155+
resource_data
156+
}
157+
158+
let request = resolve_data.request;
159+
let user_request = resolve_data.user_request;
160+
let resource = override_resource(&create_data.resource, resolve_data.resource);
161+
162+
args.create_data = Some(NormalModuleAfterResolveCreateData {
163+
request,
164+
user_request,
165+
resource,
166+
});
167+
}
168+
169+
Ok(ret)
170+
}
171+
Err(err) => Err(err),
172+
}
144173
}
145174

146175
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_shared::new_tsfn::ThreadsafeFunction;
33

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

1010
#[napi(object, object_to_js = false)]
@@ -43,8 +43,11 @@ pub struct JsHooks {
4343
pub chunk_asset: ThreadsafeFunction<JsChunkAssetArgs, ()>,
4444
#[napi(ts_type = "(data: BeforeResolveData) => Promise<(boolean | void | BeforeResolveData)[]>")]
4545
pub before_resolve: ThreadsafeFunction<BeforeResolveData, (Option<bool>, BeforeResolveData)>,
46-
#[napi(ts_type = "(data: AfterResolveData) => Promise<boolean | void>")]
47-
pub after_resolve: ThreadsafeFunction<AfterResolveData, Option<bool>>,
46+
#[napi(
47+
ts_type = "(data: AfterResolveData) => Promise<(boolean | void | AfterResolveCreateData)[]>"
48+
)]
49+
pub after_resolve:
50+
ThreadsafeFunction<AfterResolveData, (Option<bool>, Option<AfterResolveCreateData>)>,
4851
#[napi(ts_type = "(data: BeforeResolveData) => Promise<boolean | void>")]
4952
pub context_module_factory_before_resolve: ThreadsafeFunction<BeforeResolveData, Option<bool>>,
5053
#[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-
NormalModuleAfterResolveArgs, NormalModuleBeforeResolveArgs, NormalModuleCreateData, ResourceData,
3+
NormalModuleAfterResolveArgs, NormalModuleAfterResolveCreateData, NormalModuleBeforeResolveArgs,
4+
NormalModuleCreateData, ResourceData,
45
};
56

67
#[napi(object)]
@@ -21,6 +22,13 @@ pub struct BeforeResolveData {
2122
pub context: String,
2223
}
2324

25+
#[napi(object)]
26+
pub struct AfterResolveCreateData {
27+
pub request: String,
28+
pub user_request: String,
29+
pub resource: String,
30+
}
31+
2432
#[napi(object)]
2533
pub struct AfterResolveData {
2634
pub request: String,
@@ -29,6 +37,7 @@ pub struct AfterResolveData {
2937
pub context_dependencies: Vec<String>,
3038
pub missing_dependencies: Vec<String>,
3139
pub factory_meta: FactoryMeta,
40+
pub create_data: Option<AfterResolveCreateData>,
3241
}
3342

3443
#[napi(object)]
@@ -122,6 +131,17 @@ impl From<&NormalModuleAfterResolveArgs<'_>> for AfterResolveData {
122131
factory_meta: FactoryMeta {
123132
side_effect_free: value.factory_meta.side_effect_free,
124133
},
134+
create_data: value.create_data.as_ref().map(AfterResolveCreateData::from),
135+
}
136+
}
137+
}
138+
139+
impl From<&NormalModuleAfterResolveCreateData> for AfterResolveCreateData {
140+
fn from(value: &NormalModuleAfterResolveCreateData) -> Self {
141+
Self {
142+
request: value.request.to_owned(),
143+
user_request: value.user_request.to_owned(),
144+
resource: value.resource.resource_path.to_string_lossy().to_string(),
125145
}
126146
}
127147
}

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
FuncUseCtx, GeneratorOptions, ModuleExt, ModuleFactory, ModuleFactoryCreateData,
1818
ModuleFactoryResult, ModuleIdentifier, ModuleRule, ModuleRuleEnforce, ModuleRuleUse,
1919
ModuleRuleUseLoader, ModuleType, NormalModule, NormalModuleAfterResolveArgs,
20-
NormalModuleBeforeResolveArgs, NormalModuleCreateData, ParserOptions, RawModule, Resolve,
21-
ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult, Resolver, ResolverFactory,
22-
ResourceData, ResourceParsedData, SharedPluginDriver,
20+
NormalModuleAfterResolveCreateData, NormalModuleBeforeResolveArgs, NormalModuleCreateData,
21+
ParserOptions, RawModule, Resolve, ResolveArgs, ResolveOptionsWithDependencyType, ResolveResult,
22+
Resolver, ResolverFactory, ResourceData, ResourceParsedData, SharedPluginDriver,
2323
};
2424

2525
#[derive(Debug, Default)]
@@ -43,9 +43,6 @@ impl ModuleFactory for NormalModuleFactory {
4343
return Ok(before_resolve_data);
4444
}
4545
let factory_result = self.factorize(data).await?;
46-
if let Ok(Some(after_resolve_data)) = self.after_resolve(data, &factory_result).await {
47-
return Ok(after_resolve_data);
48-
}
4946

5047
Ok(factory_result)
5148
}
@@ -105,35 +102,6 @@ impl NormalModuleFactory {
105102
Ok(None)
106103
}
107104

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

546+
let after_resolve_create_data = {
547+
let mut after_resolve_args = NormalModuleAfterResolveArgs {
548+
request: dependency.request(),
549+
context: data.context.as_ref(),
550+
file_dependencies: &data.file_dependencies,
551+
context_dependencies: &data.context_dependencies,
552+
missing_dependencies: &data.missing_dependencies,
553+
factory_meta: &factory_meta,
554+
diagnostics: &mut data.diagnostics,
555+
create_data: Some(NormalModuleAfterResolveCreateData {
556+
request,
557+
user_request,
558+
resource: resource_data,
559+
}),
560+
};
561+
562+
if let Some(plugin_result) = self
563+
.plugin_driver
564+
.after_resolve(&mut after_resolve_args)
565+
.await?
566+
{
567+
if !plugin_result {
568+
// ignored
569+
// See https://github.com/webpack/webpack/blob/6be4065ade1e252c1d8dcba4af0f43e32af1bdc1/lib/NormalModuleFactory.js#L301
570+
return Ok(Some(ModuleFactoryResult::default()));
571+
}
572+
}
573+
574+
after_resolve_args
575+
.create_data
576+
.unwrap_or_else(|| unreachable!())
577+
};
578+
578579
let mut create_data = NormalModuleCreateData {
579580
dependency_type: data.dependency.dependency_type().clone(),
580581
resolve_data_request: dependency.request(),
581-
resource_resolve_data: resource_data.clone(),
582+
resource_resolve_data: after_resolve_create_data.resource.clone(),
582583
context: data.context.clone(),
583584
diagnostics: &mut data.diagnostics,
584585
};
@@ -590,15 +591,15 @@ impl NormalModuleFactory {
590591
module
591592
} else {
592593
let normal_module = NormalModule::new(
593-
request,
594-
user_request,
594+
after_resolve_create_data.request,
595+
after_resolve_create_data.user_request,
595596
dependency.request().to_owned(),
596597
resolved_module_type,
597598
resolved_parser_and_generator,
598599
resolved_parser_options,
599600
resolved_generator_options,
600601
match_resource_data,
601-
resource_data,
602+
after_resolve_create_data.resource,
602603
resolved_resolve_options,
603604
loaders,
604605
contains_inline,

crates/rspack_core/src/plugin/args.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ 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+
8592
#[derive(Debug, Clone)]
8693
pub struct NormalModuleBeforeResolveArgs {
8794
pub request: String,
@@ -96,6 +103,7 @@ pub struct NormalModuleAfterResolveArgs<'a> {
96103
pub missing_dependencies: &'a HashSet<PathBuf>,
97104
pub factory_meta: &'a FactoryMeta,
98105
pub diagnostics: &'a mut Vec<Diagnostic>,
106+
pub create_data: Option<NormalModuleAfterResolveCreateData>,
99107
}
100108

101109
#[derive(Debug)]

packages/rspack/src/Compiler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,8 @@ class Compiler {
532532
(hook.taps
533533
? !hook.isUsed()
534534
: hook._map
535-
? /* hook map */ hook._map.size === 0
536-
: false)
535+
? /* hook map */ hook._map.size === 0
536+
: false)
537537
) {
538538
disabledHooks.push(name);
539539
}
@@ -616,7 +616,7 @@ class Compiler {
616616
}
617617
);
618618
this.#updateDisabledHooks();
619-
return res;
619+
return [res, resolveData.createData];
620620
}
621621

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