From 81c1a2ef088b6deeb6152a09eaaed1f58ffc4005 Mon Sep 17 00:00:00 2001 From: Cong-Cong Date: Wed, 8 Jan 2025 14:41:57 +0800 Subject: [PATCH] perf: js source map --- crates/node_binding/binding.d.ts | 16 ++- .../src/plugins/js_loader/context.rs | 126 +++++++++++++++--- .../src/plugins/js_loader/scheduler.rs | 16 +-- packages/rspack/src/loader-runner/index.ts | 22 +-- 4 files changed, 131 insertions(+), 49 deletions(-) diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts index 58e87ff92fd9..0f69cfa97a07 100644 --- a/crates/node_binding/binding.d.ts +++ b/crates/node_binding/binding.d.ts @@ -733,15 +733,13 @@ export interface JsLibraryOptions { export interface JsLoaderContext { resourceData: Readonly - /** Will be deprecated. Use module.module_identifier instead */ - _moduleIdentifier: Readonly _module: JsModule hot: Readonly /** Content maybe empty in pitching stage */ - content: null | Buffer + content: null | Buffer | string additionalData?: any __internal__parseMeta: Record - sourceMap?: Buffer + sourceMap?: JsSourceMap cacheable: boolean fileDependencies: Array contextDependencies: Array @@ -870,6 +868,16 @@ export interface JsRuntimeRequirementInTreeResult { runtimeRequirements: JsRuntimeGlobals } +export interface JsSourceMap { + version: number + file?: string + sources: Array + sourcesContent?: Array + names: Array + mappings: string + sourceRoot?: string +} + export interface JsStatsAsset { type: string name: string diff --git a/crates/rspack_binding_values/src/plugins/js_loader/context.rs b/crates/rspack_binding_values/src/plugins/js_loader/context.rs index 44c629fcadbc..a8e2c3aa05fe 100644 --- a/crates/rspack_binding_values/src/plugins/js_loader/context.rs +++ b/crates/rspack_binding_values/src/plugins/js_loader/context.rs @@ -2,13 +2,109 @@ use std::collections::HashMap; use napi::bindgen_prelude::*; use napi_derive::napi; -use rspack_core::{LoaderContext, RunnerContext}; -use rspack_error::error; +use rspack_core::{rspack_sources::SourceMap, LoaderContext, RunnerContext}; use rspack_loader_runner::{LoaderItem, State as LoaderState}; -use rspack_napi::threadsafe_js_value_ref::ThreadsafeJsValueRef; +use rspack_napi::{ + napi::JsString, string::JsStringExt, threadsafe_js_value_ref::ThreadsafeJsValueRef, +}; use crate::{JsModuleWrapper, JsResourceData, JsRspackError}; +#[napi(object)] +pub struct JsSourceMap { + pub version: u8, + pub file: Option, + pub sources: Vec, + pub sources_content: Option>, + pub names: Vec, + pub mappings: JsString, + pub source_root: Option, +} + +pub struct JsSourceMapWrapper(SourceMap); + +impl JsSourceMapWrapper { + pub fn new(source_map: SourceMap) -> Self { + Self(source_map) + } + + pub fn take(self) -> SourceMap { + self.0 + } +} + +impl ToNapiValue for JsSourceMapWrapper { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + let env_wrapper = Env::from_raw(env); + + let file = match val.0.file() { + Some(s) => Some(env_wrapper.create_string(s)?), + None => None, + }; + let mut sources = Vec::with_capacity(val.0.sources().len()); + for source in val.0.sources() { + sources.push(env_wrapper.create_string(source)?); + } + let mut sources_content = Vec::with_capacity(val.0.sources_content().len()); + for source_content in val.0.sources_content() { + sources_content.push(env_wrapper.create_string(source_content)?); + } + let mut names = Vec::with_capacity(val.0.sources_content().len()); + for name in val.0.names() { + names.push(env_wrapper.create_string(name)?); + } + let mappings = env_wrapper.create_string(val.0.mappings())?; + let source_root = match val.0.source_root() { + Some(s) => Some(env_wrapper.create_string(s)?), + None => None, + }; + + let js_source_map = JsSourceMap { + version: 3, + file, + sources, + sources_content: if sources_content.is_empty() { + None + } else { + Some(sources_content) + }, + names, + mappings, + source_root, + }; + ToNapiValue::to_napi_value(env, js_source_map) + } +} + +impl FromNapiValue for JsSourceMapWrapper { + unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { + let js_source_map: JsSourceMap = FromNapiValue::from_napi_value(env, napi_val)?; + + let sources_content = match js_source_map.sources_content { + Some(sources_content) => sources_content + .into_iter() + .map(|source| source.into_string()) + .collect::>(), + None => vec![], + }; + + Ok(JsSourceMapWrapper(SourceMap::new( + js_source_map.mappings.into_string(), + js_source_map + .sources + .into_iter() + .map(|source| source.into_string()) + .collect::>(), + sources_content, + js_source_map + .names + .into_iter() + .map(|source| source.into_string()) + .collect::>(), + ))) + } +} + #[napi(object)] pub struct JsLoaderItem { pub request: String, @@ -57,21 +153,19 @@ impl From for JsLoaderState { pub struct JsLoaderContext { #[napi(ts_type = "Readonly")] pub resource_data: JsResourceData, - /// Will be deprecated. Use module.module_identifier instead - #[napi(js_name = "_moduleIdentifier", ts_type = "Readonly")] - pub module_identifier: String, #[napi(js_name = "_module", ts_type = "JsModule")] pub module: JsModuleWrapper, #[napi(ts_type = "Readonly")] pub hot: bool, /// Content maybe empty in pitching stage - pub content: Either, + pub content: Either3, #[napi(ts_type = "any")] pub additional_data: Option>, #[napi(js_name = "__internal__parseMeta")] pub parse_meta: HashMap, - pub source_map: Option, + #[napi(ts_type = "JsSourceMap")] + pub source_map: Option, pub cacheable: bool, pub file_dependencies: Vec, pub context_dependencies: Vec, @@ -96,25 +190,21 @@ impl TryFrom<&mut LoaderContext> for JsLoaderContext { Ok(JsLoaderContext { resource_data: cx.resource_data.as_ref().into(), - module_identifier: module.identifier().to_string(), module: JsModuleWrapper::new(module, cx.context.compilation_id, None), hot: cx.hot, content: match cx.content() { - Some(c) => Either::B(c.to_owned().into_bytes().into()), - None => Either::A(Null), + Some(c) => match c { + rspack_core::Content::String(s) => Either3::C(s.to_string()), + rspack_core::Content::Buffer(vec) => Either3::B(vec.clone().into()), + }, + None => Either3::A(Null), }, parse_meta: cx.parse_meta.clone().into_iter().collect(), additional_data: cx .additional_data() .and_then(|data| data.get::>()) .cloned(), - source_map: cx - .source_map() - .cloned() - .map(|v| v.to_json()) - .transpose() - .map_err(|e| error!(e.to_string()))? - .map(|v| v.into_bytes().into()), + source_map: cx.source_map().cloned().map(JsSourceMapWrapper::new), cacheable: cx.cacheable, file_dependencies: cx .file_dependencies diff --git a/crates/rspack_binding_values/src/plugins/js_loader/scheduler.rs b/crates/rspack_binding_values/src/plugins/js_loader/scheduler.rs index 49f72259d72f..f128ba29d8e6 100644 --- a/crates/rspack_binding_values/src/plugins/js_loader/scheduler.rs +++ b/crates/rspack_binding_values/src/plugins/js_loader/scheduler.rs @@ -1,9 +1,9 @@ -use napi::Either; +use napi::bindgen_prelude::Either3; use rspack_core::{ diagnostics::CapturedLoaderError, AdditionalData, LoaderContext, NormalModuleLoaderShouldYield, NormalModuleLoaderStartYielding, RunnerContext, BUILTIN_LOADER_PREFIX, }; -use rspack_error::{error, Result}; +use rspack_error::Result; use rspack_hook::plugin_hook; use rspack_loader_runner::State as LoaderState; @@ -78,15 +78,11 @@ pub(crate) fn merge_loader_context( .collect(); let content = match from.content { - Either::A(_) => None, - Either::B(c) => Some(rspack_core::Content::from(Into::>::into(c))), + Either3::A(_) => None, + Either3::B(c) => Some(rspack_core::Content::from(Into::>::into(c))), + Either3::C(s) => Some(rspack_core::Content::from(s)), }; - let source_map = from - .source_map - .as_ref() - .map(|s| rspack_core::rspack_sources::SourceMap::from_slice(s)) - .transpose() - .map_err(|e| error!(e.to_string()))?; + let source_map = from.source_map.map(|s| s.take()); let additional_data = from.additional_data.take().map(|data| { let mut additional = AdditionalData::default(); additional.insert(data); diff --git a/packages/rspack/src/loader-runner/index.ts b/packages/rspack/src/loader-runner/index.ts index a90e51167210..b81a834532e4 100644 --- a/packages/rspack/src/loader-runner/index.ts +++ b/packages/rspack/src/loader-runner/index.ts @@ -42,10 +42,8 @@ import { import { concatErrorMsgAndStack, isNil, - serializeObject, stringifyLoaderObject, - toBuffer, - toObject + toBuffer } from "../util"; import { createHash } from "../util/createHash"; import { @@ -222,16 +220,6 @@ export class LoaderObject { } } -class JsSourceMap { - static __from_binding(map?: Buffer) { - return isNil(map) ? undefined : toObject(map); - } - - static __to_binding(map?: object) { - return serializeObject(map); - } -} - const loadLoaderAsync: (loaderObject: LoaderObject) => Promise = promisify(loadLoader); @@ -679,7 +667,7 @@ export async function runLoaders( name, source!, assetInfo!, - context._moduleIdentifier + context._module.moduleIdentifier ); }; loaderContext.fs = compiler.inputFileSystem; @@ -823,7 +811,7 @@ export async function runLoaders( if (hasArg) { const [content, sourceMap, additionalData] = args; context.content = isNil(content) ? null : toBuffer(content); - context.sourceMap = serializeObject(sourceMap); + context.sourceMap = sourceMap; context.additionalData = additionalData; break; } @@ -833,7 +821,7 @@ export async function runLoaders( } case JsLoaderState.Normal: { let content = context.content; - let sourceMap = JsSourceMap.__from_binding(context.sourceMap); + let sourceMap = context.sourceMap; let additionalData = context.additionalData; while (loaderContext.loaderIndex >= 0) { @@ -857,7 +845,7 @@ export async function runLoaders( } context.content = isNil(content) ? null : toBuffer(content); - context.sourceMap = JsSourceMap.__to_binding(sourceMap); + context.sourceMap = sourceMap; context.additionalData = additionalData; break;