diff --git a/Cargo.lock b/Cargo.lock index 79a717f02f3..10529b17b79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3265,8 +3265,6 @@ dependencies = [ "rspack_plugin_runtime", "rspack_util", "rustc-hash", - "serde", - "serde_json", "ustr-fxhash", ] diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 2ae2a02b85a..652a1ac3300 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -305,7 +305,7 @@ export interface JsExecuteModuleResult { export interface JsLoaderContext { /** Content maybe empty in pitching stage */ content: null | Buffer - additionalData?: Buffer + additionalData?: any sourceMap?: Buffer resource: string resourcePath: string diff --git a/crates/node_binding/src/lib.rs b/crates/node_binding/src/lib.rs index db61aedc186..3d4694eaeff 100644 --- a/crates/node_binding/src/lib.rs +++ b/crates/node_binding/src/lib.rs @@ -49,7 +49,7 @@ impl Rspack { let js_plugin = JsHooksAdapterPlugin::from_js_hooks(env, register_js_taps)?; plugins.push(js_plugin.clone().boxed()); for bp in builtin_plugins { - bp.append_to(&mut plugins) + bp.append_to(env, &mut plugins) .map_err(|e| Error::from_reason(format!("{e}")))?; } diff --git a/crates/rspack_binding_options/src/lib.rs b/crates/rspack_binding_options/src/lib.rs index ea07181904d..fc8290e51ee 100644 --- a/crates/rspack_binding_options/src/lib.rs +++ b/crates/rspack_binding_options/src/lib.rs @@ -1,4 +1,5 @@ #![feature(try_blocks)] +#![feature(let_chains)] mod options; mod plugins; pub use options::*; diff --git a/crates/rspack_binding_options/src/options/raw_builtins/mod.rs b/crates/rspack_binding_options/src/options/raw_builtins/mod.rs index 05fdaa23bb2..4d7464a4cf9 100644 --- a/crates/rspack_binding_options/src/options/raw_builtins/mod.rs +++ b/crates/rspack_binding_options/src/options/raw_builtins/mod.rs @@ -10,7 +10,7 @@ mod raw_progress; mod raw_swc_js_minimizer; mod raw_to_be_deprecated; -use napi::{bindgen_prelude::FromNapiValue, JsUnknown}; +use napi::{bindgen_prelude::FromNapiValue, Env, JsUnknown}; use napi_derive::napi; use rspack_core::{BoxPlugin, Define, DefinePlugin, PluginExt, Provide, ProvidePlugin}; use rspack_error::Result; @@ -77,9 +77,10 @@ use self::{ raw_mf::{RawConsumeSharedPluginOptions, RawContainerReferencePluginOptions, RawProvideOptions}, }; use crate::{ - plugins::JsLoaderResolverPlugin, JsLoaderRunner, RawEntryPluginOptions, - RawEvalDevToolModulePluginOptions, RawExternalItemWrapper, RawExternalsPluginOptions, - RawHttpExternalsRspackPluginOptions, RawSourceMapDevToolPluginOptions, RawSplitChunksOptions, + plugins::{CssExtractRspackAdditionalDataPlugin, JsLoaderResolverPlugin}, + JsLoaderRunner, RawEntryPluginOptions, RawEvalDevToolModulePluginOptions, RawExternalItemWrapper, + RawExternalsPluginOptions, RawHttpExternalsRspackPluginOptions, RawSourceMapDevToolPluginOptions, + RawSplitChunksOptions, }; #[napi(string_enum)] @@ -163,7 +164,7 @@ pub struct BuiltinPlugin { } impl BuiltinPlugin { - pub fn append_to(self, plugins: &mut Vec) -> rspack_error::Result<()> { + pub fn append_to(self, env: Env, plugins: &mut Vec) -> rspack_error::Result<()> { match self.name { // webpack also have these plugins BuiltinPluginName::DefinePlugin => { @@ -420,6 +421,8 @@ impl BuiltinPlugin { ) } BuiltinPluginName::CssExtractRspackPlugin => { + let additional_data_plugin = CssExtractRspackAdditionalDataPlugin::new(env)?.boxed(); + plugins.push(additional_data_plugin); let plugin = rspack_plugin_extract_css::plugin::PluginCssExtract::new( downcast_into::(self.options)?.into(), ) diff --git a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs index aa118fd3761..2618f679d55 100644 --- a/crates/rspack_binding_options/src/options/raw_module/js_loader.rs +++ b/crates/rspack_binding_options/src/options/raw_module/js_loader.rs @@ -11,6 +11,7 @@ use { rspack_error::error, rspack_identifier::{Identifiable, Identifier}, rspack_napi::threadsafe_function::ThreadsafeFunction, + rspack_napi::threadsafe_js_value_ref::ThreadsafeJsValueRef, }; use crate::get_builtin_loader; @@ -138,11 +139,11 @@ fn sync_loader_context( .map_err(|e| error!(e.to_string()))?; loader_context.additional_data = loader_result.additional_data_external.clone(); if let Some(data) = loader_result.additional_data { + loader_context.additional_data.insert(data); + } else { loader_context .additional_data - .insert(String::from_utf8_lossy(&data).to_string()); - } else { - loader_context.additional_data.remove::(); + .remove::>(); } loader_context.asset_filenames = loader_result.asset_filenames.into_iter().collect(); @@ -153,7 +154,8 @@ fn sync_loader_context( pub struct JsLoaderContext { /// Content maybe empty in pitching stage pub content: Either, - pub additional_data: Option, + #[napi(ts_type = "any")] + pub additional_data: Option>, pub source_map: Option, pub resource: String, pub resource_path: String, @@ -208,8 +210,8 @@ impl TryFrom<&mut rspack_core::LoaderContext<'_, rspack_core::LoaderRunnerContex }, additional_data: cx .additional_data - .get::() - .map(|v| v.clone().into_bytes().into()), + .get::>() + .cloned(), source_map: cx .source_map .clone() @@ -269,10 +271,7 @@ pub async fn run_builtin_loader( let list = &[loader_item]; let additional_data = { let mut additional_data = loader_context.additional_data_external.clone(); - if let Some(data) = loader_context - .additional_data - .map(|b| String::from_utf8_lossy(b.as_ref()).to_string()) - { + if let Some(data) = loader_context.additional_data { additional_data.insert(data); } additional_data @@ -358,7 +357,7 @@ pub struct JsLoaderResult { pub build_dependencies: Vec, pub asset_filenames: Vec, pub source_map: Option, - pub additional_data: Option, + pub additional_data: Option>, pub additional_data_external: External, pub cacheable: bool, /// Used to instruct how rust loaders should execute @@ -432,7 +431,9 @@ impl napi::bindgen_prelude::FromNapiValue for JsLoaderResult { ) })?; let source_map_: Option = obj.get("sourceMap")?; - let additional_data_: Option = obj.get("additionalData")?; + let additional_data_: Option> = + obj.get::<_, ThreadsafeJsValueRef>("additionalData")?; + // change: eagerly clone this field since `External` might be dropped. let additional_data_external_: External = obj .get("additionalDataExternal")? diff --git a/crates/rspack_binding_options/src/plugins/css_extract_additional_data.rs b/crates/rspack_binding_options/src/plugins/css_extract_additional_data.rs new file mode 100644 index 00000000000..ed3bcb02995 --- /dev/null +++ b/crates/rspack_binding_options/src/plugins/css_extract_additional_data.rs @@ -0,0 +1,108 @@ +use std::fmt::Debug; + +use napi::{bindgen_prelude::Unknown, Env}; +use rspack_core::{ + ApplyContext, CompilerOptions, NormalModuleAdditionalData, Plugin, PluginContext, +}; +use rspack_error::Result; +use rspack_hook::{plugin, plugin_hook}; +use rspack_loader_runner::AdditionalData; +use rspack_napi::{threadsafe_js_value_ref::ThreadsafeJsValueRef, JsCallback, NapiResultExt}; +use rspack_plugin_extract_css::{CssExtractJsonData, CssExtractJsonDataList}; +use tokio::sync::oneshot; + +#[plugin] +pub(crate) struct CssExtractRspackAdditionalDataPlugin { + js_callback: JsCallback>, +} + +impl CssExtractRspackAdditionalDataPlugin { + pub fn new(env: Env) -> Result { + Ok(Self::new_inner( + unsafe { JsCallback::new(env.raw()) }.into_rspack_result()?, + )) + } +} + +impl Debug for CssExtractRspackAdditionalDataPlugin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "CssExtractRspackAdditionalDataPlugin(..)") + } +} + +#[plugin_hook(NormalModuleAdditionalData for CssExtractRspackAdditionalDataPlugin)] +async fn additional_data(&self, additional_data: &mut AdditionalData) -> Result<()> { + if !additional_data.contains::>() { + return Ok(()); + } + let (tx, rx) = oneshot::channel::(); + let mut old_data = std::mem::take(additional_data); + self.js_callback.call(Box::new(move |env| { + if let Some(data) = old_data.get::>() + && let Ok(data) = data.get(env) + && let Ok(data) = data.coerce_to_object() + && let Ok(Some(data)) = data.get::<_, String>("css-extract-rspack-plugin") + { + let mut list = data.split("__RSPACK_CSS_EXTRACT_SEP__"); + let mut data_list = vec![]; + while let Some(identifier) = list.next() { + #[allow(clippy::unwrap_used)] + { + // parse the css data from js loader + // data: + // [identifier]__RSPACK_CSS_EXTRACT_SEP__ + // [content]__RSPACK_CSS_EXTRACT_SEP__ + // [context]__RSPACK_CSS_EXTRACT_SEP__ + // [media]__RSPACK_CSS_EXTRACT_SEP__ + // [supports]__RSPACK_CSS_EXTRACT_SEP__ + // [sourceMap]__RSPACK_CSS_EXTRACT_SEP__ + // [identifier]__RSPACK_CSS_EXTRACT_SEP__ ... repeated + // [content]__RSPACK_CSS_EXTRACT_SEP__ + data_list.push(CssExtractJsonData { + identifier: identifier.into(), + content: list.next().unwrap().into(), + context: list.next().unwrap().into(), + media: list.next().unwrap().into(), + supports: list.next().unwrap().into(), + source_map: list.next().unwrap().into(), + identifier_index: list + .next() + .unwrap() + .parse() + .expect("Cannot parse identifier_index, this should never happen"), + filepath: list.next().unwrap().into(), + }); + } + } + old_data.insert(CssExtractJsonDataList(data_list)); + }; + tx.send(old_data) + .expect("should send `additional_data` for `CssExtractRspackAdditionalDataPlugin`"); + })); + let new_data = rx + .await + .expect("should receive `additional_data` for `CssExtractRspackAdditionalDataPlugin`"); + // ignore the default value here + let _ = std::mem::replace(additional_data, new_data); + Ok(()) +} + +#[async_trait::async_trait] +impl Plugin for CssExtractRspackAdditionalDataPlugin { + fn name(&self) -> &'static str { + "CssExtractRspackAdditionalDataPlugin" + } + + fn apply( + &self, + ctx: PluginContext<&mut ApplyContext>, + _options: &mut CompilerOptions, + ) -> Result<()> { + ctx + .context + .normal_module_hooks + .additional_data + .tap(additional_data::new(self)); + Ok(()) + } +} diff --git a/crates/rspack_binding_options/src/plugins/mod.rs b/crates/rspack_binding_options/src/plugins/mod.rs index d427f366943..32de5b82363 100644 --- a/crates/rspack_binding_options/src/plugins/mod.rs +++ b/crates/rspack_binding_options/src/plugins/mod.rs @@ -1,2 +1,4 @@ +mod css_extract_additional_data; mod js_loader_resolver; +pub(super) use css_extract_additional_data::CssExtractRspackAdditionalDataPlugin; pub(super) use js_loader_resolver::JsLoaderResolverPlugin; diff --git a/crates/rspack_core/src/normal_module.rs b/crates/rspack_core/src/normal_module.rs index 669a4d7a412..c2eccfe1ab8 100644 --- a/crates/rspack_core/src/normal_module.rs +++ b/crates/rspack_core/src/normal_module.rs @@ -81,12 +81,14 @@ impl ModuleIssuer { define_hook!(NormalModuleReadResource: AsyncSeriesBail(resource_data: &mut ResourceData) -> Content); define_hook!(NormalModuleLoader: SyncSeries(loader_context: &mut LoaderContext)); define_hook!(NormalModuleBeforeLoaders: SyncSeries(module: &mut NormalModule)); +define_hook!(NormalModuleAdditionalData: AsyncSeries(additional_data: &mut AdditionalData)); #[derive(Debug, Default)] pub struct NormalModuleHooks { pub read_resource: NormalModuleReadResourceHook, pub loader: NormalModuleLoaderHook, pub before_loaders: NormalModuleBeforeLoadersHook, + pub additional_data: NormalModuleAdditionalDataHook, } #[impl_source_map_config] @@ -411,7 +413,7 @@ impl Module for NormalModule { additional_data, ) .await; - let (loader_result, ds) = match loader_result { + let (mut loader_result, ds) = match loader_result { Ok(r) => r.split_into_parts(), Err(e) => { let mut e = ModuleBuildError(e).boxed(); @@ -441,6 +443,12 @@ impl Module for NormalModule { }); } }; + build_context + .plugin_driver + .normal_module_hooks + .additional_data + .call(&mut loader_result.additional_data) + .await?; self.add_diagnostics(ds); let content = if self.module_type().is_binary() { diff --git a/crates/rspack_loader_runner/src/runner.rs b/crates/rspack_loader_runner/src/runner.rs index f29684a140a..71aa95775dd 100644 --- a/crates/rspack_loader_runner/src/runner.rs +++ b/crates/rspack_loader_runner/src/runner.rs @@ -464,7 +464,7 @@ impl TryFrom> for TWithDiagnosticArray { } } -pub async fn run_loaders( +pub async fn run_loaders( loaders: &[Arc>], resource_data: &mut ResourceData, plugins: &[&dyn LoaderRunnerPlugin], diff --git a/crates/rspack_napi/src/callback.rs b/crates/rspack_napi/src/callback.rs index ab22af662bf..7f114c4ddfc 100644 --- a/crates/rspack_napi/src/callback.rs +++ b/crates/rspack_napi/src/callback.rs @@ -42,7 +42,9 @@ pub struct JsCallback { unsafe impl Send for JsCallback {} impl JsCallback { - pub(crate) fn new(env: sys::napi_env) -> Result { + /// # Safety + /// The provided `env` must be a valid `napi_env`. + pub unsafe fn new(env: sys::napi_env) -> Result { let mut async_resource_name = ptr::null_mut(); let s = unsafe { CStr::from_bytes_with_nul_unchecked(b"napi_js_callback\0") }; check_status!( diff --git a/crates/rspack_napi/src/js_values/js_value_ref.rs b/crates/rspack_napi/src/js_values/js_value_ref.rs new file mode 100644 index 00000000000..38423b4c01e --- /dev/null +++ b/crates/rspack_napi/src/js_values/js_value_ref.rs @@ -0,0 +1,44 @@ +use std::marker::PhantomData; + +use napi::bindgen_prelude::*; +use napi::{Env, NapiValue, Ref}; + +pub struct JsValueRef { + ref_: Ref<()>, + _phantom: PhantomData, +} + +impl JsValueRef { + pub fn new(env: Env, value: T) -> Result { + let ref_ = env.create_reference(value)?; + + Ok(Self { + ref_, + _phantom: PhantomData, + }) + } + + pub fn get(&self, env: Env) -> Result { + env.get_reference_value(&self.ref_) + } + + pub fn unref(&mut self, env: Env) -> Result { + self.ref_.unref(env) + } +} + +impl ToNapiValue for JsValueRef { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + val + .get(Env::from(env)) + .and_then(|v| unsafe { T::to_napi_value(env, v) }) + } +} + +impl FromNapiValue for JsValueRef { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + JsValueRef::::new(Env::from(env), unsafe { + T::from_napi_value(env, napi_val) + }?) + } +} diff --git a/crates/rspack_napi/src/js_values/mod.rs b/crates/rspack_napi/src/js_values/mod.rs index efb6ca18ab0..514aa9842ed 100644 --- a/crates/rspack_napi/src/js_values/mod.rs +++ b/crates/rspack_napi/src/js_values/mod.rs @@ -1 +1,2 @@ pub mod js_reg_exp; +pub mod js_value_ref; diff --git a/crates/rspack_napi/src/lib.rs b/crates/rspack_napi/src/lib.rs index 9276d891b31..e3cabd35487 100644 --- a/crates/rspack_napi/src/lib.rs +++ b/crates/rspack_napi/src/lib.rs @@ -9,9 +9,10 @@ mod errors; pub use errors::{NapiErrorExt, NapiResultExt}; mod callback; -pub(crate) use callback::JsCallback; +pub use callback::JsCallback; pub mod threadsafe_function; +pub mod threadsafe_js_value_ref; pub mod regexp { pub use crate::ext::js_reg_exp_ext::JsRegExpExt; diff --git a/crates/rspack_napi/src/threadsafe_function.rs b/crates/rspack_napi/src/threadsafe_function.rs index 7f9a35896a3..6ed52896b51 100644 --- a/crates/rspack_napi/src/threadsafe_function.rs +++ b/crates/rspack_napi/src/threadsafe_function.rs @@ -56,7 +56,7 @@ impl FromNapiValue for ThreadsafeFunction< Ok(Self { inner, env, - resolver: JsCallback::new(env)?, + resolver: unsafe { JsCallback::new(env) }?, _data: PhantomData, }) } diff --git a/crates/rspack_napi/src/threadsafe_js_value_ref.rs b/crates/rspack_napi/src/threadsafe_js_value_ref.rs new file mode 100644 index 00000000000..6f19c4a2912 --- /dev/null +++ b/crates/rspack_napi/src/threadsafe_js_value_ref.rs @@ -0,0 +1,83 @@ +use std::sync::{Arc, Mutex}; + +use napi::bindgen_prelude::*; +use napi::NapiValue; + +use crate::js_values::js_value_ref::JsValueRef; +use crate::JsCallback; + +struct ThreadsafeJsValueRefHandle { + value_ref: Arc>>, + drop_handle: JsCallback>, +} + +impl ThreadsafeJsValueRefHandle { + fn new(env: Env, js_ref: JsValueRef) -> Result { + Ok(Self { + value_ref: Arc::new(Mutex::new(js_ref)), + drop_handle: unsafe { JsCallback::new(env.raw()) }?, + }) + } +} + +impl Drop for ThreadsafeJsValueRefHandle { + fn drop(&mut self) { + let value_ref = self.value_ref.clone(); + self.drop_handle.call(Box::new(move |env| { + let _ = value_ref + .lock() + .expect("should lock `value_ref`") + .unref(env); + })) + } +} + +pub struct ThreadsafeJsValueRef { + inner: Arc>, +} + +unsafe impl Send for ThreadsafeJsValueRef {} +unsafe impl Sync for ThreadsafeJsValueRef {} + +impl Clone for ThreadsafeJsValueRef { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + } + } +} + +impl FromNapiValue for ThreadsafeJsValueRef { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + Self::new(Env::from(env), unsafe { + T::from_napi_value(env, napi_val) + }?) + } +} + +impl ToNapiValue for ThreadsafeJsValueRef { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + val + .get(Env::from(env)) + .and_then(|v| unsafe { T::to_napi_value(env, v) }) + } +} + +impl ThreadsafeJsValueRef { + pub fn new(env: Env, value: T) -> Result { + let js_ref = JsValueRef::new(env, value)?; + + Ok(Self { + inner: Arc::new(ThreadsafeJsValueRefHandle::new(env, js_ref)?), + }) + } + + pub fn get(&self, env: Env) -> Result { + self + .inner + .value_ref + .lock() + .expect("should lock `value_ref`") + .get(env) + } +} diff --git a/crates/rspack_plugin_extract_css/Cargo.toml b/crates/rspack_plugin_extract_css/Cargo.toml index 1146ccc3fac..7c4d74bdeab 100644 --- a/crates/rspack_plugin_extract_css/Cargo.toml +++ b/crates/rspack_plugin_extract_css/Cargo.toml @@ -18,6 +18,4 @@ rspack_plugin_css = { path = "../rspack_plugin_css" } rspack_plugin_runtime = { path = "../rspack_plugin_runtime" } rspack_util = { path = "../rspack_util" } rustc-hash = { workspace = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } ustr = { workspace = true } diff --git a/crates/rspack_plugin_extract_css/src/lib.rs b/crates/rspack_plugin_extract_css/src/lib.rs index ffab4474ce4..32b87987daa 100644 --- a/crates/rspack_plugin_extract_css/src/lib.rs +++ b/crates/rspack_plugin_extract_css/src/lib.rs @@ -2,5 +2,6 @@ pub mod css_dependency; mod css_module; mod parser_and_generator; +pub use parser_and_generator::{CssExtractJsonData, CssExtractJsonDataList}; pub mod plugin; mod runtime; diff --git a/crates/rspack_plugin_extract_css/src/parser_and_generator.rs b/crates/rspack_plugin_extract_css/src/parser_and_generator.rs index 52ad46b151a..bfa0086a476 100644 --- a/crates/rspack_plugin_extract_css/src/parser_and_generator.rs +++ b/crates/rspack_plugin_extract_css/src/parser_and_generator.rs @@ -1,21 +1,31 @@ +use std::path::PathBuf; + use rspack_core::{ChunkGraph, Dependency, Module, ModuleGraph, ParserAndGenerator}; use rspack_error::TWithDiagnosticArray; use rustc_hash::FxHashMap; -use serde::Deserialize; use crate::css_dependency::CssDependency; -#[derive(Deserialize)] -struct CssExtractJsonData { - #[serde(rename = "css-extract-rspack-plugin")] - value: String, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CssExtractJsonData { + pub identifier: String, + pub content: String, + pub context: String, + pub media: String, + pub supports: String, + pub source_map: String, + pub identifier_index: u32, + pub filepath: PathBuf, } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct CssExtractJsonDataList(pub Vec); + #[derive(Debug)] pub(crate) struct CssExtractParserAndGenerator { orig_parser_generator: Box, #[allow(clippy::vec_box)] - cache: FxHashMap>>, + cache: FxHashMap>>, } impl CssExtractParserAndGenerator { @@ -46,51 +56,46 @@ impl ParserAndGenerator for CssExtractParserAndGenerator { &mut self, parse_context: rspack_core::ParseContext, ) -> rspack_error::Result> { - let deps = if let Some(additional_data) = parse_context.additional_data.get::() { + let deps = if let Some(additional_data) = parse_context + .additional_data + .get::() + { if let Some(deps) = self.cache.get(additional_data) { deps.clone() - } else if let Ok(data) = serde_json::from_str::(additional_data) { - // parse the css data from js loader - // data: - // [identifier]__RSPACK_CSS_EXTRACT_SEP__ - // [content]__RSPACK_CSS_EXTRACT_SEP__ - // [context]__RSPACK_CSS_EXTRACT_SEP__ - // [media]__RSPACK_CSS_EXTRACT_SEP__ - // [supports]__RSPACK_CSS_EXTRACT_SEP__ - // [sourceMap]__RSPACK_CSS_EXTRACT_SEP__ - // [identifier]__RSPACK_CSS_EXTRACT_SEP__ ... repeated - // [content]__RSPACK_CSS_EXTRACT_SEP__ - let mut list = data.value.split("__RSPACK_CSS_EXTRACT_SEP__"); - - let mut deps = vec![]; + } else { let mut idx = 0; - while let Some(identifier) = list.next() { - #[allow(clippy::unwrap_in_result)] - { - deps.push(Box::new(CssDependency::new( - identifier.into(), - list.next().unwrap().into(), - list.next().unwrap().into(), - list.next().unwrap().into(), - list.next().unwrap().into(), - list.next().unwrap().into(), - list - .next() - .unwrap() - .parse() - .expect("Cannot parse identifier_index, this should never happen"), - idx, - list.next().unwrap().into(), - ))); - } - idx += 1; - } - - self.cache.insert(data.value.clone(), deps.clone()); - + let deps = additional_data + .0 + .iter() + .map( + |CssExtractJsonData { + identifier, + content, + context, + media, + supports, + source_map, + identifier_index, + filepath, + }| { + let dep = Box::new(CssDependency::new( + identifier.into(), + content.clone(), + context.clone(), + media.clone(), + supports.clone(), + source_map.clone(), + *identifier_index, + idx, + filepath.clone(), + )); + idx += 1; + dep + }, + ) + .collect::>(); + self.cache.insert(additional_data.clone(), deps.clone()); deps - } else { - vec![] } } else { vec![] diff --git a/packages/rspack/src/loader-runner/index.ts b/packages/rspack/src/loader-runner/index.ts index 2806b063734..69bf0f64d10 100644 --- a/packages/rspack/src/loader-runner/index.ts +++ b/packages/rspack/src/loader-runner/index.ts @@ -638,7 +638,7 @@ export async function runLoaders( resolve({ content: isNil(content) ? undefined : toBuffer(content), sourceMap: serializeObject(sourceMap), - additionalData: serializeObject(additionalData), + additionalData, buildDependencies, cacheable, fileDependencies, @@ -662,7 +662,7 @@ export async function runLoaders( : toObject(rawContext.sourceMap), isNil(rawContext.additionalData) ? undefined - : toObject(rawContext.additionalData) + : rawContext.additionalData ], (err: Error, result: any[]) => { if (err) { @@ -672,7 +672,7 @@ export async function runLoaders( resolve({ content: isNil(content) ? undefined : toBuffer(content), sourceMap: serializeObject(sourceMap), - additionalData: serializeObject(additionalData), + additionalData, buildDependencies, cacheable, fileDependencies, diff --git a/packages/rspack/src/loader-runner/loadLoader.js b/packages/rspack/src/loader-runner/loadLoader.js index 3babed86786..b5872171056 100644 --- a/packages/rspack/src/loader-runner/loadLoader.js +++ b/packages/rspack/src/loader-runner/loadLoader.js @@ -62,7 +62,7 @@ module.exports = function loadLoader(loader, callback) { Object.assign({}, this.__internal__context, { content: isNil(content) ? undefined : toBuffer(content), sourceMap: serializeObject(sourceMap), - additionalData: serializeObject(additionalData) + additionalData }) ); @@ -83,9 +83,7 @@ module.exports = function loadLoader(loader, callback) { isNil(context.sourceMap) ? undefined : toObject(context.sourceMap), - isNil(context.additionalData) - ? undefined - : toObject(context.additionalData) + isNil(context.additionalData) ? undefined : context.additionalData ); // @ts-expect-error this._compilation.__internal__pushNativeDiagnostics( diff --git a/packages/rspack/tests/configCases/loader/rspack-issue-5600/a.js b/packages/rspack/tests/configCases/loader/rspack-issue-5600/a.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/rspack/tests/configCases/loader/rspack-issue-5600/index.js b/packages/rspack/tests/configCases/loader/rspack-issue-5600/index.js new file mode 100644 index 00000000000..988b714bfb5 --- /dev/null +++ b/packages/rspack/tests/configCases/loader/rspack-issue-5600/index.js @@ -0,0 +1,6 @@ +it('should pass additional data to next loader without execute toJSON', () => { + expect(require('./a.js')).toStrictEqual({ + str: 'str', + num: 1 + }) +}) diff --git a/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-a.js b/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-a.js new file mode 100644 index 00000000000..2c3965c93c8 --- /dev/null +++ b/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-a.js @@ -0,0 +1,13 @@ +/** + * @type {import("@rspack/core").LoaderDefinition} + */ +module.exports = function(content, sourceMap, additionalData) { + const callback = this.async() + callback(null, content, sourceMap, { + str: 'str', + num: 1, + toJSON() { + throw new Error('unreachable') + } + }) +} diff --git a/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-b.js b/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-b.js new file mode 100644 index 00000000000..08915a7140f --- /dev/null +++ b/packages/rspack/tests/configCases/loader/rspack-issue-5600/loader-b.js @@ -0,0 +1,13 @@ +/** + * @type {import("@rspack/core").LoaderDefinition} + */ +module.exports = function(content, sourceMap, additionalData) { + const callback = this.async() + callback(null, `module.exports = ${ + JSON.stringify( + Object.fromEntries( + Object.entries(additionalData).filter(([,v]) => typeof v !== 'function') + ) + ) + }`, sourceMap, additionalData) +} diff --git a/packages/rspack/tests/configCases/loader/rspack-issue-5600/webpack.config.js b/packages/rspack/tests/configCases/loader/rspack-issue-5600/webpack.config.js new file mode 100644 index 00000000000..281ef6f750b --- /dev/null +++ b/packages/rspack/tests/configCases/loader/rspack-issue-5600/webpack.config.js @@ -0,0 +1,15 @@ +/** @type {import("@rspack/core").Configuration} */ +module.exports = { + mode: "none", + module: { + rules: [ + { + test: /a\.js$/, + use: [ + { loader: './loader-b.js' }, + { loader: './loader-a.js' } + ] + }, + ] + } +}; diff --git a/packages/rspack/tests/diagnostics/module-build-failed/asset-module-build-failed/stats.err b/packages/rspack/tests/diagnostics/module-build-failed/asset-module-build-failed/stats.err index 430f61c76b3..e1748c91ca7 100644 --- a/packages/rspack/tests/diagnostics/module-build-failed/asset-module-build-failed/stats.err +++ b/packages/rspack/tests/diagnostics/module-build-failed/asset-module-build-failed/stats.err @@ -1 +1 @@ -ERROR in ./logo.svg× Module build failed:╰─▶ × Error: Failed to loadat Object..module.exports (/tests/diagnostics/module-build-failed/asset-module-build-failed/my-loader.js:2:8)at LOADER_EXECUTION (/dist/loader-runner/index.js:617:23)at runSyncOrAsync (/dist/loader-runner/index.js:618:11)at /dist/loader-runner/index.js:722:9at handleResult (/dist/loader-runner/loadLoader.js:128:5)at loadLoader (/dist/loader-runner/loadLoader.js:109:16)at iterateNormalLoaders (/dist/loader-runner/index.js:711:5)at /dist/loader-runner/index.js:538:13at new Promise ()at runLoaders (/dist/loader-runner/index.js:513:12)help: File was processed with this loader: '/tests/diagnostics/module-build-failed/asset-module-build-failed/my-loader.js' \ No newline at end of file +ERROR in ./logo.svg× Module build failed:╰─▶ × Error: Failed to loadat Object..module.exports (/tests/diagnostics/module-build-failed/asset-module-build-failed/my-loader.js:2:8)at LOADER_EXECUTION (/dist/loader-runner/index.js:617:23)at runSyncOrAsync (/dist/loader-runner/index.js:618:11)at /dist/loader-runner/index.js:722:9at handleResult (/dist/loader-runner/loadLoader.js:126:5)at loadLoader (/dist/loader-runner/loadLoader.js:107:16)at iterateNormalLoaders (/dist/loader-runner/index.js:711:5)at /dist/loader-runner/index.js:538:13at new Promise ()at runLoaders (/dist/loader-runner/index.js:513:12)help: File was processed with this loader: '/tests/diagnostics/module-build-failed/asset-module-build-failed/my-loader.js' \ No newline at end of file diff --git a/packages/rspack/tests/diagnostics/module-build-failed/loader-throw-error/stats.err b/packages/rspack/tests/diagnostics/module-build-failed/loader-throw-error/stats.err index 184477e5879..a93d4b9d137 100644 --- a/packages/rspack/tests/diagnostics/module-build-failed/loader-throw-error/stats.err +++ b/packages/rspack/tests/diagnostics/module-build-failed/loader-throw-error/stats.err @@ -1 +1 @@ -ERROR in ./lib.js× Module build failed:╰─▶ × Error: Failed to loadat Object..module.exports (/tests/diagnostics/module-build-failed/loader-throw-error/my-loader.js:2:9)at LOADER_EXECUTION (/dist/loader-runner/index.js:617:23)at runSyncOrAsync (/dist/loader-runner/index.js:618:11)at /dist/loader-runner/index.js:722:9at handleResult (/dist/loader-runner/loadLoader.js:128:5)at loadLoader (/dist/loader-runner/loadLoader.js:109:16)at iterateNormalLoaders (/dist/loader-runner/index.js:711:5)at /dist/loader-runner/index.js:538:13at new Promise ()at runLoaders (/dist/loader-runner/index.js:513:12)help: File was processed with this loader: '/tests/diagnostics/module-build-failed/loader-throw-error/my-loader.js' \ No newline at end of file +ERROR in ./lib.js× Module build failed:╰─▶ × Error: Failed to loadat Object..module.exports (/tests/diagnostics/module-build-failed/loader-throw-error/my-loader.js:2:9)at LOADER_EXECUTION (/dist/loader-runner/index.js:617:23)at runSyncOrAsync (/dist/loader-runner/index.js:618:11)at /dist/loader-runner/index.js:722:9at handleResult (/dist/loader-runner/loadLoader.js:126:5)at loadLoader (/dist/loader-runner/loadLoader.js:107:16)at iterateNormalLoaders (/dist/loader-runner/index.js:711:5)at /dist/loader-runner/index.js:538:13at new Promise ()at runLoaders (/dist/loader-runner/index.js:513:12)help: File was processed with this loader: '/tests/diagnostics/module-build-failed/loader-throw-error/my-loader.js' \ No newline at end of file