Skip to content
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

feat: support compilation.addRuntimeModule #7925

Merged
merged 7 commits into from
Sep 20, 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
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@
"ukey",
"Ukey"
]
}
}
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions crates/node_binding/binding.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export class JsCompilation {
rebuildModule(moduleIdentifiers: Array<string>, f: (...args: any[]) => any): void
importModule(request: string, layer: string | undefined | null, publicPath: JsFilename | undefined | null, baseUri: string | undefined | null, originalModule: string | undefined | null, originalModuleContext: string | undefined | null, callback: (...args: any[]) => any): void
get entries(): JsEntries
addRuntimeModule(chunkUkey: number, runtimeModule: JsAddingRuntimeModule): void
}

export class JsEntries {
Expand Down Expand Up @@ -284,6 +285,14 @@ export interface ContextInfo {

export function formatDiagnostic(diagnostic: JsDiagnostic): ExternalObject<'Diagnostic'>

export interface JsAddingRuntimeModule {
name: string
generator: () => String
cacheable: boolean
isolate: boolean
stage: number
}

export interface JsAdditionalTreeRuntimeRequirementsArg {
chunk: JsChunk
runtimeRequirements: JsRuntimeGlobals
Expand Down
31 changes: 16 additions & 15 deletions crates/rspack_binding_values/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,19 @@ name = "rspack_binding_values"
repository = "https://github.com/web-infra-dev/rspack"
version = "0.1.0"
[dependencies]
cow-utils = { workspace = true }
futures = { workspace = true }
heck = { workspace = true }
napi = { workspace = true, features = ["async", "tokio_rt", "serde-json", "anyhow"] }
napi-derive = { workspace = true }
rspack_collections = { version = "0.1.0", path = "../rspack_collections" }
rspack_core = { version = "0.1.0", path = "../rspack_core" }
rspack_error = { version = "0.1.0", path = "../rspack_error" }
rspack_napi = { version = "0.1.0", path = "../rspack_napi" }
rspack_plugin_html = { version = "0.1.0", path = "../rspack_plugin_html" }
rspack_regex = { version = "0.1.0", path = "../rspack_regex" }
rspack_util = { version = "0.1.0", path = "../rspack_util" }
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
cow-utils = { workspace = true }
futures = { workspace = true }
heck = { workspace = true }
napi = { workspace = true, features = ["async", "tokio_rt", "serde-json", "anyhow"] }
napi-derive = { workspace = true }
rspack_collections = { version = "0.1.0", path = "../rspack_collections" }
rspack_core = { version = "0.1.0", path = "../rspack_core" }
rspack_error = { version = "0.1.0", path = "../rspack_error" }
rspack_napi = { version = "0.1.0", path = "../rspack_napi" }
rspack_plugin_html = { version = "0.1.0", path = "../rspack_plugin_html" }
rspack_plugin_runtime = { version = "0.1.0", path = "../rspack_plugin_runtime" }
rspack_regex = { version = "0.1.0", path = "../rspack_regex" }
rspack_util = { version = "0.1.0", path = "../rspack_util" }
rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
18 changes: 18 additions & 0 deletions crates/rspack_binding_values/src/compilation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ use rspack_core::get_chunk_from_ukey;
use rspack_core::get_chunk_group_from_ukey;
use rspack_core::rspack_sources::BoxSource;
use rspack_core::AssetInfo;
use rspack_core::ChunkUkey;
use rspack_core::CompilationId;
use rspack_core::ModuleIdentifier;
use rspack_error::Diagnostic;
use rspack_napi::napi::bindgen_prelude::*;
use rspack_napi::NapiResultExt;
use rspack_napi::Ref;
use rspack_plugin_runtime::RuntimeModuleFromJs;
use sys::napi_env;

use super::module::ToJsModule;
use super::{JsFilename, PathWithInfo};
use crate::utils::callbackify;
use crate::JsAddingRuntimeModule;
use crate::JsStatsOptimizationBailout;
use crate::LocalJsFilename;
use crate::ModuleDTOWrapper;
Expand Down Expand Up @@ -532,6 +535,21 @@ impl JsCompilation {
pub fn entries(&'static mut self) -> JsEntries {
JsEntries::new(self.0)
}

#[napi]
pub fn add_runtime_module(
&'static mut self,
chunk_ukey: u32,
runtime_module: JsAddingRuntimeModule,
) -> napi::Result<()> {
self
.0
.add_runtime_module(
&ChunkUkey::from(chunk_ukey),
Box::new(RuntimeModuleFromJs::from(runtime_module)),
)
.map_err(|e| Error::new(napi::Status::GenericFailure, format!("{e}")))
}
}

#[derive(Default)]
Expand Down
36 changes: 33 additions & 3 deletions crates/rspack_binding_values/src/module.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use std::cell::RefCell;
use std::{cell::RefCell, sync::Arc};

use napi_derive::napi;
use rspack_collections::Identifier;
use rspack_core::{
AsyncDependenciesBlock, AsyncDependenciesBlockIdentifier, Compilation, CompilationId,
CompilerModuleContext, DependenciesBlock, Module, ModuleGraph, ModuleIdentifier, SourceType,
CompilerModuleContext, DependenciesBlock, Module, ModuleGraph, ModuleIdentifier,
RuntimeModuleStage, SourceType,
};
use rspack_napi::{napi::bindgen_prelude::*, Ref};
use rspack_napi::{napi::bindgen_prelude::*, threadsafe_function::ThreadsafeFunction, Ref};
use rspack_plugin_runtime::RuntimeModuleFromJs;
use rspack_util::source_map::SourceMapKind;
use rustc_hash::FxHashMap as HashMap;
use sys::napi_env;

Expand Down Expand Up @@ -464,3 +467,30 @@ pub struct JsRuntimeModuleArg {
pub module: JsRuntimeModule,
pub chunk: JsChunk,
}

type GenerateFn = ThreadsafeFunction<(), String>;

#[napi(object, object_to_js = false)]
pub struct JsAddingRuntimeModule {
pub name: String,
#[napi(ts_type = "() => String")]
pub generator: GenerateFn,
pub cacheable: bool,
pub isolate: bool,
pub stage: u32,
}

impl From<JsAddingRuntimeModule> for RuntimeModuleFromJs {
fn from(value: JsAddingRuntimeModule) -> Self {
Self {
name: value.name,
cacheable: value.cacheable,
isolate: value.isolate,
stage: RuntimeModuleStage::from(value.stage),
generator: Arc::new(move || value.generator.blocking_call_with_sync(())),
source_map_kind: SourceMapKind::empty(),
custom_source: None,
cached_generated_code: Default::default(),
}
}
}
20 changes: 19 additions & 1 deletion crates/rspack_core/src/runtime_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,32 @@ pub trait CustomSourceRuntimeModule {

pub type BoxRuntimeModule = Box<dyn RuntimeModule>;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RuntimeModuleStage {
Normal, // Runtime modules without any dependencies to other runtime modules
Basic, // Runtime modules with simple dependencies on other runtime modules
Attach, // Runtime modules which attach to handlers of other runtime modules
Trigger, // Runtime modules which trigger actions on bootstrap
}

impl Default for RuntimeModuleStage {
fn default() -> Self {
Self::Normal
}
}

impl From<u32> for RuntimeModuleStage {
fn from(stage: u32) -> Self {
match stage {
0 => RuntimeModuleStage::Normal,
5 => RuntimeModuleStage::Basic,
10 => RuntimeModuleStage::Attach,
20 => RuntimeModuleStage::Trigger,
_ => RuntimeModuleStage::Normal,
}
}
}

pub trait RuntimeModuleExt {
fn boxed(self) -> Box<dyn RuntimeModule>;
}
Expand Down
1 change: 1 addition & 0 deletions crates/rspack_plugin_runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ version = "0.1.0"
[dependencies]
async-trait = { workspace = true }
cow-utils = { workspace = true }
derivative = { workspace = true }
indexmap = { workspace = true }
itertools = { workspace = true }
rspack_collections = { version = "0.1.0", path = "../rspack_collections" }
Expand Down
2 changes: 2 additions & 0 deletions crates/rspack_plugin_runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ mod chunk_prefetch_preload;
pub use chunk_prefetch_preload::ChunkPrefetchPreloadPlugin;
mod bundler_info;
pub use bundler_info::{BundlerInfoForceMode, BundlerInfoPlugin};
mod runtime_module_from_js;
pub use runtime_module_from_js::RuntimeModuleFromJs;

pub fn enable_chunk_loading_plugin(loading_type: ChunkLoadingType, plugins: &mut Vec<BoxPlugin>) {
match loading_type {
Expand Down
46 changes: 46 additions & 0 deletions crates/rspack_plugin_runtime/src/runtime_module_from_js.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::sync::Arc;

use derivative::Derivative;
use rspack_collections::Identifier;
use rspack_core::{
impl_runtime_module,
rspack_sources::{BoxSource, RawSource, SourceExt},
Compilation, RuntimeModule, RuntimeModuleStage,
};

type GenerateFn = Arc<dyn Fn() -> rspack_error::Result<String> + Send + Sync>;

#[impl_runtime_module]
#[derive(Derivative)]
#[derivative(Debug)]
pub struct RuntimeModuleFromJs {
pub name: String,
#[derivative(Debug = "ignore")]
pub generator: GenerateFn,
pub cacheable: bool,
pub isolate: bool,
pub stage: RuntimeModuleStage,
}

impl RuntimeModule for RuntimeModuleFromJs {
fn name(&self) -> Identifier {
Identifier::from(format!("webpack/runtime/{}", self.name))
}

fn generate(&self, _: &Compilation) -> rspack_error::Result<BoxSource> {
let res = (self.generator)()?;
Ok(RawSource::from(res).boxed())
}

fn cacheable(&self) -> bool {
self.cacheable
}

fn should_isolate(&self) -> bool {
self.isolate
}

fn stage(&self) -> RuntimeModuleStage {
self.stage.clone()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
it("should inject runtime according to isolate scope", async function () {
expect(__webpack_require__.mock()).toBe("isolated");
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const { RuntimeModule, RuntimeGlobals } = require("@rspack/core");

class IsolateRuntimeModule extends RuntimeModule {
constructor(chunk) {
super("mock-isolate");
}

generate(compilation) {
return `
__webpack_require__.mock = function() {
return someGlobalValue;
};
`;
}
}

class NonIsolateRuntimeModule extends RuntimeModule {
constructor(chunk) {
super("mock-non-isolate");
}

shouldIsolate() {
return false;
}

generate(compilation) {
return `
var someGlobalValue = "isolated";
`;
}
}

/** @type {import("@rspack/core").Configuration} */
module.exports = {
entry: "./index.js",
mode: "development",
devtool: false,
optimization: {
minimize: false,
sideEffects: false,
concatenateModules: false,
usedExports: false,
innerGraph: false,
providedExports: false
},
plugins: [
compiler => {
compiler.hooks.thisCompilation.tap(
"MockRuntimePlugin",
(compilation) => {
compilation.hooks.runtimeRequirementInTree.tap("MockRuntimePlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new NonIsolateRuntimeModule()
);
compilation.addRuntimeModule(
chunk,
new IsolateRuntimeModule()
);
})
}
);
}
],
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
it("should inject trigger runtime module after normal runtime module", async function () {
expect(__webpack_require__.mockNormal).toBe("normal");
expect(__webpack_require__.mockTrigger).toBe("trigger");
const fs = require("fs");
const content = fs.readFileSync(__filename, 'utf-8');
const triggerIndex = content.indexOf(`__webpack_require__.mockTrigger = "trigger"`);
const normalIndex = content.indexOf(`__webpack_require__.mockNormal = "normal"`);
expect(normalIndex).toBeLessThan(triggerIndex);
});
Loading
Loading