Skip to content

Commit 9101e5d

Browse files
authored
perf: napi source map serialize and deserialize (#10989)
1 parent 0ddb7b2 commit 9101e5d

File tree

5 files changed

+97
-47
lines changed

5 files changed

+97
-47
lines changed

crates/node_binding/binding.d.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ export interface JsLoaderContext {
919919
content: null | Buffer
920920
additionalData?: any
921921
__internal__parseMeta: Record<string, string>
922-
sourceMap?: Buffer
922+
sourceMap?: SourceMap
923923
cacheable: boolean
924924
fileDependencies: Array<string>
925925
contextDependencies: Array<string>
@@ -2723,6 +2723,16 @@ export interface RegisterJsTaps {
27232723
registerRsdoctorPluginAssetsTaps: (stages: Array<number>) => Array<{ function: ((arg: JsRsdoctorAssetPatch) => Promise<boolean | undefined>); stage: number; }>
27242724
}
27252725

2726+
export interface SourceMap {
2727+
file?: string
2728+
sources?: Array<string | undefined | null>
2729+
sourceRoot?: string
2730+
sourcesContent?: Array<string | undefined | null>
2731+
names?: Array<string | undefined | null>
2732+
mappings?: string
2733+
debugId?: string
2734+
}
2735+
27262736
export interface SourceMapDevToolPluginOptions {
27272737
append?: (false | null) | string | Function
27282738
columns?: boolean

crates/rspack_binding_api/src/plugins/js_loader/context.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ use std::{collections::HashMap, ptr::NonNull, sync::Arc};
33
use napi::bindgen_prelude::*;
44
use napi_derive::napi;
55
use rspack_core::{LoaderContext, Module, RunnerContext};
6-
use rspack_error::ToStringResultToRspackResultExt;
76
use rspack_loader_runner::State as LoaderState;
87
use rspack_napi::threadsafe_js_value_ref::ThreadsafeJsValueRef;
98

10-
use crate::{ModuleObject, RspackError};
9+
use crate::{ModuleObject, RspackError, SourceMap};
1110

1211
#[napi(object)]
1312
#[derive(Hash)]
@@ -100,7 +99,7 @@ pub struct JsLoaderContext {
10099
pub additional_data: Option<ThreadsafeJsValueRef<Unknown<'static>>>,
101100
#[napi(js_name = "__internal__parseMeta")]
102101
pub parse_meta: HashMap<String, String>,
103-
pub source_map: Option<Buffer>,
102+
pub source_map: Option<SourceMap>,
104103
pub cacheable: bool,
105104
pub file_dependencies: Vec<String>,
106105
pub context_dependencies: Vec<String>,
@@ -145,13 +144,7 @@ impl TryFrom<&mut LoaderContext<RunnerContext>> for JsLoaderContext {
145144
.additional_data()
146145
.and_then(|data| data.get::<ThreadsafeJsValueRef<Unknown>>())
147146
.cloned(),
148-
source_map: cx
149-
.source_map()
150-
.cloned()
151-
.map(|v| v.to_json())
152-
.transpose()
153-
.to_rspack_result()?
154-
.map(|v| v.into_bytes().into()),
147+
source_map: cx.source_map().map(Into::into),
155148
cacheable: cx.cacheable,
156149
file_dependencies: cx
157150
.file_dependencies

crates/rspack_binding_api/src/plugins/js_loader/scheduler.rs

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rspack_core::{
33
diagnostics::CapturedLoaderError, AdditionalData, LoaderContext, NormalModuleLoaderShouldYield,
44
NormalModuleLoaderStartYielding, RunnerContext, BUILTIN_LOADER_PREFIX,
55
};
6-
use rspack_error::{miette::IntoDiagnostic, Result, ToStringResultToRspackResultExt};
6+
use rspack_error::{miette::IntoDiagnostic, Result};
77
use rspack_hook::plugin_hook;
88
use rspack_loader_runner::State as LoaderState;
99

@@ -143,17 +143,7 @@ pub(crate) fn merge_loader_context(
143143
Some(content)
144144
}
145145
};
146-
let source_map = from
147-
.source_map
148-
.as_ref()
149-
.map(|s| {
150-
rspack_core::rspack_sources::SourceMap::from_json(
151-
// SAFETY: `sourceMap` is serialized by JavaScript from a JSON object. This is an invariant should be followed on the JavaScript side.
152-
unsafe { str::from_utf8_unchecked(s) },
153-
)
154-
})
155-
.transpose()
156-
.to_rspack_result()?;
146+
let source_map = from.source_map.map(Into::into);
157147
let additional_data = from.additional_data.take().map(|data| {
158148
let mut additional = AdditionalData::default();
159149
additional.insert(data);

crates/rspack_binding_api/src/source.rs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::{hash::Hash, sync::Arc};
33
use napi_derive::napi;
44
use rspack_core::rspack_sources::{
55
BoxSource, CachedSource, ConcatSource, MapOptions, OriginalSource, RawBufferSource, RawSource,
6-
RawStringSource, ReplaceSource, Source, SourceExt, SourceMap, SourceMapSource,
7-
WithoutOriginalOptions,
6+
RawStringSource, ReplaceSource, Source, SourceExt, SourceMapSource, WithoutOriginalOptions,
87
};
98
use rspack_napi::napi::bindgen_prelude::*;
109

@@ -26,7 +25,7 @@ impl<'s> From<JsCompatSource<'s>> for BoxSource {
2625
match value.source {
2726
Either::A(string) => {
2827
if let Some(map) = value.map {
29-
match SourceMap::from_slice(map.as_ref()).ok() {
28+
match rspack_core::rspack_sources::SourceMap::from_slice(map.as_ref()).ok() {
3029
Some(source_map) => SourceMapSource::new(WithoutOriginalOptions {
3130
value: string,
3231
name: "inmemory://from js",
@@ -55,7 +54,7 @@ impl From<JsCompatSourceOwned> for BoxSource {
5554
match value.source {
5655
Either::A(string) => {
5756
if let Some(map) = value.map {
58-
match SourceMap::from_slice(map.as_ref()).ok() {
57+
match rspack_core::rspack_sources::SourceMap::from_slice(map.as_ref()).ok() {
5958
Some(source_map) => SourceMapSource::new(WithoutOriginalOptions {
6059
value: string,
6160
name: "inmemory://from js",
@@ -324,3 +323,77 @@ fn to_webpack_map(source: &dyn Source) -> Result<Option<String>> {
324323

325324
map.map(|m| m.to_json()).transpose().to_napi_result()
326325
}
326+
327+
#[napi(object)]
328+
pub struct SourceMap {
329+
pub file: Option<String>,
330+
pub sources: Option<Vec<Option<String>>>,
331+
pub source_root: Option<String>,
332+
pub sources_content: Option<Vec<Option<String>>>,
333+
pub names: Option<Vec<Option<String>>>,
334+
pub mappings: Option<String>,
335+
pub debug_id: Option<String>,
336+
}
337+
338+
impl From<&rspack_core::rspack_sources::SourceMap> for SourceMap {
339+
fn from(value: &rspack_core::rspack_sources::SourceMap) -> Self {
340+
SourceMap {
341+
file: value.file().map(|file| file.to_string()),
342+
sources: Some(
343+
value
344+
.sources()
345+
.iter()
346+
.map(|source| Some(source.to_string()))
347+
.collect::<Vec<_>>(),
348+
),
349+
source_root: value
350+
.source_root()
351+
.map(|source_root| source_root.to_string()),
352+
sources_content: Some(
353+
value
354+
.sources_content()
355+
.iter()
356+
.map(|content| Some(content.to_string()))
357+
.collect::<Vec<_>>(),
358+
),
359+
names: Some(
360+
value
361+
.names()
362+
.iter()
363+
.map(|name| Some(name.to_string()))
364+
.collect::<Vec<_>>(),
365+
),
366+
mappings: Some(value.mappings().to_string()),
367+
debug_id: value.get_debug_id().map(|id| id.to_string()),
368+
}
369+
}
370+
}
371+
372+
impl From<SourceMap> for rspack_core::rspack_sources::SourceMap {
373+
fn from(value: SourceMap) -> Self {
374+
let mappings = value.mappings.unwrap_or_default();
375+
let sources = value
376+
.sources
377+
.unwrap_or_default()
378+
.into_iter()
379+
.map(|source| source.unwrap_or_default())
380+
.collect::<Vec<_>>();
381+
let sources_content = value
382+
.sources_content
383+
.unwrap_or_default()
384+
.into_iter()
385+
.map(|source| source.unwrap_or_default())
386+
.collect::<Vec<_>>();
387+
let names = value
388+
.names
389+
.unwrap_or_default()
390+
.into_iter()
391+
.map(|name| name.unwrap_or_default())
392+
.collect::<Vec<_>>();
393+
let mut map =
394+
rspack_core::rspack_sources::SourceMap::new(mappings, sources, sources_content, names);
395+
map.set_source_root(value.source_root);
396+
map.set_debug_id(value.debug_id);
397+
map
398+
}
399+
}

packages/rspack/src/loader-runner/index.ts

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,7 @@ import {
3838
isUseSourceMap
3939
} from "../config/adapterRuleUse";
4040
import { JavaScriptTracer } from "../trace";
41-
import {
42-
isNil,
43-
serializeObject,
44-
stringifyLoaderObject,
45-
toBuffer,
46-
toObject
47-
} from "../util";
41+
import { isNil, stringifyLoaderObject, toBuffer } from "../util";
4842
import { createHash } from "../util/createHash";
4943
import {
5044
absolutify,
@@ -220,16 +214,6 @@ export class LoaderObject {
220214
}
221215
}
222216

223-
class JsSourceMap {
224-
static __from_binding(map?: Buffer) {
225-
return isNil(map) ? undefined : toObject(map);
226-
}
227-
228-
static __to_binding(map?: object) {
229-
return serializeObject(map);
230-
}
231-
}
232-
233217
function dirname(path: string) {
234218
if (path === "/") return "/";
235219
const i = path.lastIndexOf("/");
@@ -1045,7 +1029,7 @@ export async function runLoaders(
10451029
if (hasArg) {
10461030
const [content, sourceMap, additionalData] = args;
10471031
context.content = isNil(content) ? null : toBuffer(content);
1048-
context.sourceMap = serializeObject(sourceMap);
1032+
context.sourceMap = sourceMap || undefined;
10491033
context.additionalData = additionalData || undefined;
10501034
break;
10511035
}
@@ -1055,7 +1039,7 @@ export async function runLoaders(
10551039
}
10561040
case JsLoaderState.Normal: {
10571041
let content = context.content;
1058-
let sourceMap = JsSourceMap.__from_binding(context.sourceMap);
1042+
let sourceMap = context.sourceMap;
10591043
let additionalData = context.additionalData;
10601044

10611045
while (loaderContext.loaderIndex >= 0) {
@@ -1085,7 +1069,7 @@ export async function runLoaders(
10851069
}
10861070

10871071
context.content = isNil(content) ? null : toBuffer(content);
1088-
context.sourceMap = JsSourceMap.__to_binding(sourceMap);
1072+
context.sourceMap = sourceMap || undefined;
10891073
context.additionalData = additionalData || undefined;
10901074
context.__internal__utf8Hint = typeof content === "string";
10911075

0 commit comments

Comments
 (0)