Skip to content

Commit

Permalink
feat: support nmf resolve hook (#6998)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahabhgk authored Jul 2, 2024
1 parent 983b67b commit 6fa3fd7
Show file tree
Hide file tree
Showing 17 changed files with 351 additions and 19 deletions.
20 changes: 14 additions & 6 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,12 @@ export interface JsPathData {
chunk?: JsChunkPathData
}

export interface JsResolveArgs {
request: string
context: string
issuer: string
}

export interface JsResolveForSchemeArgs {
resourceData: JsResourceData
scheme: string
Expand Down Expand Up @@ -1514,12 +1520,13 @@ export enum RegisterJsTapKind {
CompilationAfterSeal = 23,
NormalModuleFactoryBeforeResolve = 24,
NormalModuleFactoryFactorize = 25,
NormalModuleFactoryAfterResolve = 26,
NormalModuleFactoryCreateModule = 27,
NormalModuleFactoryResolveForScheme = 28,
ContextModuleFactoryBeforeResolve = 29,
ContextModuleFactoryAfterResolve = 30,
JavascriptModulesChunkHash = 31
NormalModuleFactoryResolve = 26,
NormalModuleFactoryAfterResolve = 27,
NormalModuleFactoryCreateModule = 28,
NormalModuleFactoryResolveForScheme = 29,
ContextModuleFactoryBeforeResolve = 30,
ContextModuleFactoryAfterResolve = 31,
JavascriptModulesChunkHash = 32
}

export interface RegisterJsTaps {
Expand Down Expand Up @@ -1549,6 +1556,7 @@ export interface RegisterJsTaps {
registerCompilationAfterSealTaps: (stages: Array<number>) => Array<{ function: (() => Promise<void>); stage: number; }>
registerNormalModuleFactoryBeforeResolveTaps: (stages: Array<number>) => Array<{ function: ((arg: JsBeforeResolveArgs) => Promise<[boolean | undefined, JsBeforeResolveArgs]>); stage: number; }>
registerNormalModuleFactoryFactorizeTaps: (stages: Array<number>) => Array<{ function: ((arg: JsFactorizeArgs) => Promise<JsFactorizeArgs>); stage: number; }>
registerNormalModuleFactoryResolveTaps: (stages: Array<number>) => Array<{ function: ((arg: JsResolveArgs) => Promise<JsResolveArgs>); stage: number; }>
registerNormalModuleFactoryResolveForSchemeTaps: (stages: Array<number>) => Array<{ function: ((arg: JsResolveForSchemeArgs) => Promise<[boolean | undefined, JsResolveForSchemeArgs]>); stage: number; }>
registerNormalModuleFactoryAfterResolveTaps: (stages: Array<number>) => Array<{ function: ((arg: JsAfterResolveData) => Promise<[boolean | undefined, JsCreateData | undefined]>); stage: number; }>
registerNormalModuleFactoryCreateModuleTaps: (stages: Array<number>) => Array<{ function: ((arg: JsNormalModuleFactoryCreateModuleArgs) => Promise<void>); stage: number; }>
Expand Down
62 changes: 58 additions & 4 deletions crates/node_binding/src/plugins/interceptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use rspack_binding_values::{
JsContextModuleFactoryAfterResolveData, JsContextModuleFactoryAfterResolveResult,
JsContextModuleFactoryBeforeResolveData, JsContextModuleFactoryBeforeResolveResult, JsCreateData,
JsExecuteModuleArg, JsFactorizeArgs, JsFactorizeOutput, JsModule,
JsNormalModuleFactoryCreateModuleArgs, JsResolveForSchemeArgs, JsResolveForSchemeOutput,
JsRuntimeGlobals, JsRuntimeModule, JsRuntimeModuleArg, ToJsCompatSource, ToJsModule,
JsNormalModuleFactoryCreateModuleArgs, JsResolveArgs, JsResolveForSchemeArgs,
JsResolveForSchemeOutput, JsResolveOutput, JsRuntimeGlobals, JsRuntimeModule, JsRuntimeModuleArg,
ToJsCompatSource, ToJsModule,
};
use rspack_core::{
rspack_sources::SourceExt, AfterResolveData, AfterResolveResult, AssetEmittedInfo,
Expand Down Expand Up @@ -46,8 +47,9 @@ use rspack_core::{
NormalModuleFactoryAfterResolveHook, NormalModuleFactoryBeforeResolve,
NormalModuleFactoryBeforeResolveHook, NormalModuleFactoryCreateModule,
NormalModuleFactoryCreateModuleHook, NormalModuleFactoryFactorize,
NormalModuleFactoryFactorizeHook, NormalModuleFactoryResolveForScheme,
NormalModuleFactoryResolveForSchemeHook, ResourceData, RuntimeGlobals,
NormalModuleFactoryFactorizeHook, NormalModuleFactoryResolve,
NormalModuleFactoryResolveForScheme, NormalModuleFactoryResolveForSchemeHook,
NormalModuleFactoryResolveHook, NormalModuleFactoryResolveResult, ResourceData, RuntimeGlobals,
};
use rspack_hash::RspackHash;
use rspack_hook::{Hook, Interceptor};
Expand Down Expand Up @@ -317,6 +319,7 @@ pub enum RegisterJsTapKind {
CompilationAfterSeal,
NormalModuleFactoryBeforeResolve,
NormalModuleFactoryFactorize,
NormalModuleFactoryResolve,
NormalModuleFactoryAfterResolve,
NormalModuleFactoryCreateModule,
NormalModuleFactoryResolveForScheme,
Expand Down Expand Up @@ -450,6 +453,11 @@ pub struct RegisterJsTaps {
)]
pub register_normal_module_factory_factorize_taps:
RegisterFunction<JsFactorizeArgs, Promise<JsFactorizeOutput>>,
#[napi(
ts_type = "(stages: Array<number>) => Array<{ function: ((arg: JsResolveArgs) => Promise<JsResolveArgs>); stage: number; }>"
)]
pub register_normal_module_factory_resolve_taps:
RegisterFunction<JsResolveArgs, Promise<JsResolveOutput>>,
#[napi(
ts_type = "(stages: Array<number>) => Array<{ function: ((arg: JsResolveForSchemeArgs) => Promise<[boolean | undefined, JsResolveForSchemeArgs]>); stage: number; }>"
)]
Expand Down Expand Up @@ -698,6 +706,14 @@ define_register!(
kind = RegisterJsTapKind::NormalModuleFactoryFactorize,
skip = true,
);
define_register!(
RegisterNormalModuleFactoryResolveTaps,
tap = NormalModuleFactoryResolveTap<JsResolveArgs, Promise<JsResolveOutput>> @ NormalModuleFactoryResolveHook,
cache = true,
sync = false,
kind = RegisterJsTapKind::NormalModuleFactoryResolve,
skip = true,
);
define_register!(
RegisterNormalModuleFactoryResolveForSchemeTaps,
tap = NormalModuleFactoryResolveForSchemeTap<JsResolveForSchemeArgs, Promise<JsResolveForSchemeOutput>> @ NormalModuleFactoryResolveForSchemeHook,
Expand Down Expand Up @@ -1235,6 +1251,44 @@ impl NormalModuleFactoryFactorize for NormalModuleFactoryFactorizeTap {
}
}

#[async_trait]
impl NormalModuleFactoryResolve for NormalModuleFactoryResolveTap {
async fn run(
&self,
data: &mut ModuleFactoryCreateData,
) -> rspack_error::Result<Option<NormalModuleFactoryResolveResult>> {
let dependency = data
.dependency
.as_module_dependency_mut()
.expect("should be module dependency");
match self
.function
.call_with_promise(JsResolveArgs {
request: dependency.request().to_string(),
context: data.context.to_string(),
issuer: data
.issuer
.as_ref()
.map(|issuer| issuer.to_string())
.unwrap_or_default(),
})
.await
{
Ok(resolve_data) => {
dependency.set_request(resolve_data.request);
data.context = resolve_data.context.into();
// only supports update resolve request for now
Ok(None)
}
Err(err) => Err(err),
}
}

fn stage(&self) -> i32 {
self.stage
}
}

#[async_trait]
impl NormalModuleFactoryResolveForScheme for NormalModuleFactoryResolveForSchemeTap {
async fn run(
Expand Down
10 changes: 10 additions & 0 deletions crates/node_binding/src/plugins/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct JsHooksAdapterPlugin {
register_compilation_after_seal_taps: RegisterCompilationAfterSealTaps,
register_normal_module_factory_before_resolve_taps: RegisterNormalModuleFactoryBeforeResolveTaps,
register_normal_module_factory_factorize_taps: RegisterNormalModuleFactoryFactorizeTaps,
register_normal_module_factory_resolve_taps: RegisterNormalModuleFactoryResolveTaps,
register_normal_module_factory_resolve_for_scheme_taps:
RegisterNormalModuleFactoryResolveForSchemeTaps,
register_normal_module_factory_after_resolve_taps: RegisterNormalModuleFactoryAfterResolveTaps,
Expand Down Expand Up @@ -222,6 +223,11 @@ impl rspack_core::Plugin for JsHooksAdapterPlugin {
.normal_module_factory_hooks
.factorize
.intercept(self.register_normal_module_factory_factorize_taps.clone());
ctx
.context
.normal_module_factory_hooks
.resolve
.intercept(self.register_normal_module_factory_resolve_taps.clone());
ctx
.context
.normal_module_factory_hooks
Expand Down Expand Up @@ -407,6 +413,10 @@ impl JsHooksAdapterPlugin {
register_js_taps.register_normal_module_factory_factorize_taps,
non_skippable_registers.clone(),
),
register_normal_module_factory_resolve_taps: RegisterNormalModuleFactoryResolveTaps::new(
register_js_taps.register_normal_module_factory_resolve_taps,
non_skippable_registers.clone(),
),
register_normal_module_factory_resolve_for_scheme_taps:
RegisterNormalModuleFactoryResolveForSchemeTaps::new(
register_js_taps.register_normal_module_factory_resolve_for_scheme_taps,
Expand Down
9 changes: 9 additions & 0 deletions crates/rspack_binding_values/src/normal_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ pub struct JsFactorizeArgs {

pub type JsFactorizeOutput = JsFactorizeArgs;

#[napi(object)]
pub struct JsResolveArgs {
pub request: String,
pub context: String,
pub issuer: String,
}

pub type JsResolveOutput = JsResolveArgs;

#[napi(object)]
pub struct JsCreateData {
pub request: String,
Expand Down
47 changes: 41 additions & 6 deletions crates/rspack_core/src/normal_module_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@ use crate::{

define_hook!(NormalModuleFactoryBeforeResolve: AsyncSeriesBail(data: &mut ModuleFactoryCreateData) -> bool);
define_hook!(NormalModuleFactoryFactorize: AsyncSeriesBail(data: &mut ModuleFactoryCreateData) -> BoxModule);
define_hook!(NormalModuleFactoryResolve: AsyncSeriesBail(data: &mut ModuleFactoryCreateData) -> NormalModuleFactoryResolveResult);
define_hook!(NormalModuleFactoryResolveForScheme: AsyncSeriesBail(data: &mut ModuleFactoryCreateData, resource_data: &mut ResourceData) -> bool);
define_hook!(NormalModuleFactoryAfterResolve: AsyncSeriesBail(data: &mut ModuleFactoryCreateData, create_data: &mut NormalModuleCreateData) -> bool);
define_hook!(NormalModuleFactoryCreateModule: AsyncSeriesBail(data: &mut ModuleFactoryCreateData, create_data: &mut NormalModuleCreateData) -> BoxModule);
define_hook!(NormalModuleFactoryModule: AsyncSeries(data: &mut ModuleFactoryCreateData, create_data: &mut NormalModuleCreateData, module: &mut BoxModule));
define_hook!(NormalModuleFactoryResolveLoader: AsyncSeriesBail(context: &Context, resolver: &Resolver, l: &ModuleRuleUseLoader) -> BoxLoader);

pub enum NormalModuleFactoryResolveResult {
Module(BoxModule),
Ignored,
}

#[derive(Debug, Default)]
pub struct NormalModuleFactoryHooks {
pub before_resolve: NormalModuleFactoryBeforeResolveHook,
pub factorize: NormalModuleFactoryFactorizeHook,
pub resolve: NormalModuleFactoryResolveHook,
pub resolve_for_scheme: NormalModuleFactoryResolveForSchemeHook,
pub after_resolve: NormalModuleFactoryAfterResolveHook,
pub create_module: NormalModuleFactoryCreateModuleHook,
Expand Down Expand Up @@ -119,7 +126,7 @@ impl NormalModuleFactory {
})
}

pub async fn factorize_normal_module(
async fn resolve_normal_module(
&self,
data: &mut ModuleFactoryCreateData,
) -> Result<Option<ModuleFactoryResult>> {
Expand Down Expand Up @@ -693,18 +700,46 @@ impl NormalModuleFactory {
}

async fn factorize(&self, data: &mut ModuleFactoryCreateData) -> Result<ModuleFactoryResult> {
let result = self
if let Some(result) = self
.plugin_driver
.normal_module_factory_hooks
.factorize
.call(data)
.await?;

if let Some(result) = result {
.await?
{
return Ok(ModuleFactoryResult::new_with_module(result));
}

if let Some(result) = self.factorize_normal_module(data).await? {
if let Some(result) = self
.plugin_driver
.normal_module_factory_hooks
.resolve
.call(data)
.await?
{
if let NormalModuleFactoryResolveResult::Module(result) = result {
return Ok(ModuleFactoryResult::new_with_module(result));
} else {
let ident = format!(
"{}/{}",
&data.context,
data.request().expect("normal module should have request")
);
let module_identifier = ModuleIdentifier::from(format!("ignored|{ident}"));

let raw_module = RawModule::new(
"/* (ignored) */".to_owned(),
module_identifier,
format!("{ident} (ignored)"),
Default::default(),
)
.boxed();

return Ok(ModuleFactoryResult::new_with_module(raw_module));
}
}

if let Some(result) = self.resolve_normal_module(data).await? {
return Ok(result);
}

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,104 @@
```js title=main.js
(() => { // webpackBootstrap
var __webpack_modules__ = ({
"./a.js": (function (module) {
module.exports = "a";


}),
"./b.js": (function (module) {
module.exports = "c";


}),

});
/************************************************************************/
// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {

// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
exports: {}
});
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);

// Return the exports of the module
return module.exports;

}

/************************************************************************/
// webpack/runtime/compat_get_default_export
(() => {
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter = module && module.__esModule ?
function () { return module['default']; } :
function () { return module; };
__webpack_require__.d(getter, { a: getter });
return getter;
};




})();
// webpack/runtime/define_property_getters
(() => {
__webpack_require__.d = function(exports, definition) {
for(var key in definition) {
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};
})();
// webpack/runtime/has_own_property
(() => {
__webpack_require__.o = function (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
};

})();
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be in strict mode.
(() => {
"use strict";

// EXTERNAL MODULE: ./a.js
let a_ = __webpack_require__("./a.js");
let a_default = /*#__PURE__*/__webpack_require__.n(a_);
// EXTERNAL MODULE: ./b.js
let b_ = __webpack_require__("./b.js");
let b_default = /*#__PURE__*/__webpack_require__.n(b_);
;// CONCATENATED MODULE: external "fs"
var external_fs_namespaceObject = require("fs");
let external_fs_default = /*#__PURE__*/__webpack_require__.n(external_fs_namespaceObject);
;// CONCATENATED MODULE: ./resource.js




it("should modify resource by resolve hook", () => {
expect((a_default())).toBe("a");
expect((b_default())).toBe("c");
const ext = ".js";
expect(external_fs_default().readFileSync(__filename, "utf-8")).toContain("./b" + 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 resolve hook", () => {
expect(a).toBe("a");
expect(b).toBe("c");
const ext = ".js";
expect(fs.readFileSync(__filename, "utf-8")).toContain("./b" + ext);
});
Loading

2 comments on commit 6fa3fd7

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Ran ecosystem CI: Open

suite result
modernjs ❌ failure
_selftest ✅ success
nx ✅ success
rspress ❌ failure
rsbuild ✅ success
compat ✅ success
examples ❌ failure

@rspack-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Benchmark detail: Open

Name Base (2024-07-02 a22f039) Current Change
10000_development-mode + exec 2.16 s ± 22 ms 2.15 s ± 18 ms -0.60 %
10000_development-mode_hmr + exec 685 ms ± 7.3 ms 687 ms ± 5.1 ms +0.22 %
10000_production-mode + exec 2.82 s ± 23 ms 2.77 s ± 35 ms -1.80 %
arco-pro_development-mode + exec 1.9 s ± 47 ms 1.87 s ± 57 ms -1.79 %
arco-pro_development-mode_hmr + exec 433 ms ± 1.5 ms 433 ms ± 1.2 ms -0.14 %
arco-pro_production-mode + exec 3.46 s ± 67 ms 3.41 s ± 74 ms -1.44 %
threejs_development-mode_10x + exec 1.57 s ± 16 ms 1.57 s ± 13 ms -0.24 %
threejs_development-mode_10x_hmr + exec 797 ms ± 13 ms 792 ms ± 11 ms -0.60 %
threejs_production-mode_10x + exec 5.57 s ± 50 ms 5.6 s ± 22 ms +0.40 %

Please sign in to comment.