Skip to content

Commit 288764e

Browse files
authored
feat: Sourcemap support (#16)
1 parent c06de15 commit 288764e

File tree

17 files changed

+1149
-182
lines changed

17 files changed

+1149
-182
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ jobs:
1919
with:
2020
node-version-file: 'package.json'
2121
- name: Build
22-
run: cargo build --all
22+
run: cargo build --all-features
2323
- name: Run tests
24-
run: cargo test --all
24+
run: cargo test --all-features
2525
- name: Run clippy
26-
run: cargo clippy --all -- -D warnings
26+
run: cargo clippy --all-features -- -D warnings
2727
- name: Run fmt
2828
run: cargo fmt --all -- --check
2929
- name: Build and Test wasm

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ swc = "21"
1818
swc_core = { version = "22", features = ["ecma_plugin_transform","ecma_quote"] }
1919
swc_ecma_parser = "11"
2020
swc_ecma_visit = { version = "8", features = ["path"] }
21+
sourcemap = "9"
2122

2223
# serde feature
2324
serde = { version = "1", features = ["derive"], optional = true }

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
"types": "./pkg/orchestrion_js.d.ts",
1818
"scripts": {
1919
"build": "wasm-pack build --target nodejs --release -- --features wasm",
20-
"test": "node ./tests/wasm/tests.mjs"
20+
"test": "vitest run",
21+
"test:watch": "vitest"
2122
},
2223
"devDependencies": {
24+
"source-map": "^0.7.6",
25+
"typescript": "^5.8.3",
26+
"vitest": "^3.2.4",
2327
"wasm-pack": "^0.13.1"
2428
},
2529
"volta": {
2630
"node": "22.15.0"
2731
}
28-
}
32+
}

src/lib.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,25 @@ use crate::error::OrchestrionError;
5252
#[cfg(feature = "wasm")]
5353
pub mod wasm;
5454

55+
/// Output of a transformation operation
56+
#[cfg_attr(
57+
feature = "serde",
58+
derive(serde::Serialize, serde::Deserialize),
59+
serde(rename_all = "lowercase")
60+
)]
61+
#[cfg_attr(
62+
feature = "wasm",
63+
derive(tsify::Tsify),
64+
tsify(into_wasm_abi, from_wasm_abi)
65+
)]
66+
#[derive(Debug, Clone)]
67+
pub struct TransformOutput {
68+
/// The transformed JavaScript code
69+
pub code: String,
70+
/// The sourcemap for the transformation (if generated)
71+
pub map: Option<String>,
72+
}
73+
5574
/// This struct is responsible for managing all instrumentations. It's created from a YAML string
5675
/// via the [`FromStr`] trait. See tests for examples, but by-and-large this just means you can
5776
/// call `.parse()` on a YAML string to get an `Instrumentor` instance, if it's valid.
@@ -141,18 +160,25 @@ impl InstrumentationVisitor {
141160
}
142161
}
143162

144-
/// Transform the given JavaScript code.
163+
/// Transform the given JavaScript code with sourcemap support.
145164
/// # Errors
146165
/// Returns an error if the transformation fails.
147166
pub fn transform(
148167
&mut self,
149168
contents: &str,
150169
is_module: IsModule,
151-
) -> Result<String, Box<dyn Error>> {
170+
sourcemap: Option<&str>,
171+
) -> Result<TransformOutput, Box<dyn Error>> {
152172
let compiler = Compiler::new(Arc::new(swc_core::common::SourceMap::new(
153173
FilePathMapping::empty(),
154174
)));
155175

176+
// Parse input sourcemap if provided
177+
let sourcemap = sourcemap
178+
.and_then(|input_map| sourcemap::SourceMap::from_slice(input_map.as_bytes()).ok());
179+
180+
let filename = sourcemap.as_ref().and_then(|map| map.get_file());
181+
156182
#[allow(clippy::redundant_closure_for_method_calls)]
157183
let result = try_with_handler(
158184
compiler.cm.clone(),
@@ -161,11 +187,15 @@ impl InstrumentationVisitor {
161187
skip_filename: false,
162188
},
163189
|handler| {
164-
let source_file = compiler.cm.new_source_file(
165-
Arc::new(FileName::Real(PathBuf::from("index.mjs"))),
166-
contents.to_string(),
190+
let source_filename = filename.map_or_else(
191+
|| Arc::new(FileName::Real(PathBuf::from("index.js"))),
192+
|f| Arc::new(FileName::Real(PathBuf::from(f))),
167193
);
168194

195+
let source_file = compiler
196+
.cm
197+
.new_source_file(source_filename, contents.to_string());
198+
169199
let program = compiler
170200
.parse_js(
171201
source_file.clone(),
@@ -184,18 +214,24 @@ impl InstrumentationVisitor {
184214
program.visit_mut_with(self);
185215
program
186216
})?;
217+
218+
let enable_sourcemap = sourcemap.is_some();
187219
let result = compiler.print(
188220
&program,
189221
PrintArgs {
190-
source_file_name: None,
191-
source_map: SourceMapsConfig::Bool(false),
222+
source_file_name: filename,
223+
source_map: SourceMapsConfig::Bool(enable_sourcemap),
192224
comments: None,
193-
emit_source_map_columns: false,
225+
emit_source_map_columns: true,
226+
orig: sourcemap.as_ref(),
194227
..Default::default()
195228
},
196229
)?;
197230

198-
Ok(result.code)
231+
Ok(TransformOutput {
232+
code: result.code,
233+
map: result.map,
234+
})
199235
},
200236
)
201237
.map_err(|e| e.to_pretty_error())?;

src/wasm.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{Config, InstrumentationConfig, InstrumentationVisitor, Instrumentor};
1+
use crate::{Config, InstrumentationConfig, InstrumentationVisitor, Instrumentor, TransformOutput};
22
use std::path::PathBuf;
33
use swc::config::IsModule;
44
use wasm_bindgen::prelude::*;
@@ -58,15 +58,25 @@ pub struct Transformer(InstrumentationVisitor);
5858

5959
#[wasm_bindgen]
6060
impl Transformer {
61+
/// Transform the given JavaScript code with optional sourcemap support.
62+
/// # Errors
63+
/// Returns an error if the transformation fails to find injection points.
6164
#[wasm_bindgen]
62-
pub fn transform(&mut self, contents: &str, is_module: ModuleType) -> Result<String, JsError> {
65+
#[allow(clippy::needless_pass_by_value)]
66+
pub fn transform(
67+
&mut self,
68+
code: String,
69+
module_type: ModuleType,
70+
sourcemap: Option<String>,
71+
) -> Result<TransformOutput, JsError> {
6372
self.0
64-
.transform(contents, is_module.into())
73+
.transform(&code, module_type.into(), sourcemap.as_deref())
6574
.map_err(|e| JsError::new(&e.to_string()))
6675
}
6776
}
6877

6978
#[wasm_bindgen]
79+
#[must_use]
7080
pub fn create(
7181
configs: Vec<InstrumentationConfig>,
7282
dc_module: Option<String>,

tests/common/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ pub fn transpile_and_test(test_file: &str, mjs: bool, config: Config) {
2727
let mut contents = String::new();
2828
file.read_to_string(&mut contents).unwrap();
2929

30-
if let Ok(result) = instrumentations.transform(&contents, IsModule::Bool(mjs)) {
30+
if let Ok(result) = instrumentations.transform(&contents, IsModule::Bool(mjs), None) {
3131
let instrumented_file = test_dir.join(format!("instrumented.{}", extension));
3232
let mut file = std::fs::File::create(&instrumented_file).unwrap();
33-
file.write_all(result.as_bytes()).unwrap();
33+
file.write_all(result.code.as_bytes()).unwrap();
3434
}
3535

3636
let test_file = format!("test.{}", extension);
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Orchestrion JS Transformer > should transform CommonJS module correctly 1`] = `
4+
{
5+
"code": "const { tracingChannel: tr_ch_apm_tracingChannel } = require("diagnostics_channel");
6+
const tr_ch_apm$up_fetch = tr_ch_apm_tracingChannel("orchestrion:one:up:fetch");
7+
const tr_ch_apm$up_constructor = tr_ch_apm_tracingChannel("orchestrion:one:up:constructor");
8+
module.exports = class Up {
9+
constructor(){
10+
const tr_ch_apm_ctx$up_constructor = {
11+
arguments
12+
};
13+
try {
14+
if (tr_ch_apm$up_constructor.hasSubscribers) {
15+
tr_ch_apm$up_constructor.start.publish(tr_ch_apm_ctx$up_constructor);
16+
}
17+
console.log('constructor');
18+
} catch (tr_ch_err) {
19+
if (tr_ch_apm$up_constructor.hasSubscribers) {
20+
tr_ch_apm_ctx$up_constructor.error = tr_ch_err;
21+
try {
22+
tr_ch_apm_ctx$up_constructor.self = this;
23+
} catch (refErr) {}
24+
tr_ch_apm$up_constructor.error.publish(tr_ch_apm_ctx$up_constructor);
25+
}
26+
throw tr_ch_err;
27+
} finally{
28+
if (tr_ch_apm$up_constructor.hasSubscribers) {
29+
tr_ch_apm_ctx$up_constructor.self = this;
30+
tr_ch_apm$up_constructor.end.publish(tr_ch_apm_ctx$up_constructor);
31+
}
32+
}
33+
}
34+
fetch() {
35+
const __apm$original_args = arguments;
36+
const __apm$traced = ()=>{
37+
const __apm$wrapped = ()=>{
38+
console.log('fetch');
39+
};
40+
return __apm$wrapped.apply(null, __apm$original_args);
41+
};
42+
if (!tr_ch_apm$up_fetch.hasSubscribers) return __apm$traced();
43+
return tr_ch_apm$up_fetch.traceSync(__apm$traced, {
44+
arguments,
45+
self: this
46+
});
47+
}
48+
};
49+
",
50+
"map": undefined,
51+
}
52+
`;
53+
54+
exports[`Orchestrion JS Transformer > should transform ESM module correctly 1`] = `
55+
{
56+
"code": "import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel";
57+
const tr_ch_apm$up_fetch = tr_ch_apm_tracingChannel("orchestrion:one:up:fetch");
58+
const tr_ch_apm$up_constructor = tr_ch_apm_tracingChannel("orchestrion:one:up:constructor");
59+
export class Up {
60+
constructor(){
61+
const tr_ch_apm_ctx$up_constructor = {
62+
arguments
63+
};
64+
try {
65+
if (tr_ch_apm$up_constructor.hasSubscribers) {
66+
tr_ch_apm$up_constructor.start.publish(tr_ch_apm_ctx$up_constructor);
67+
}
68+
console.log('constructor');
69+
} catch (tr_ch_err) {
70+
if (tr_ch_apm$up_constructor.hasSubscribers) {
71+
tr_ch_apm_ctx$up_constructor.error = tr_ch_err;
72+
try {
73+
tr_ch_apm_ctx$up_constructor.self = this;
74+
} catch (refErr) {}
75+
tr_ch_apm$up_constructor.error.publish(tr_ch_apm_ctx$up_constructor);
76+
}
77+
throw tr_ch_err;
78+
} finally{
79+
if (tr_ch_apm$up_constructor.hasSubscribers) {
80+
tr_ch_apm_ctx$up_constructor.self = this;
81+
tr_ch_apm$up_constructor.end.publish(tr_ch_apm_ctx$up_constructor);
82+
}
83+
}
84+
}
85+
fetch() {
86+
const __apm$original_args = arguments;
87+
const __apm$traced = ()=>{
88+
const __apm$wrapped = ()=>{
89+
console.log('fetch');
90+
};
91+
return __apm$wrapped.apply(null, __apm$original_args);
92+
};
93+
if (!tr_ch_apm$up_fetch.hasSubscribers) return __apm$traced();
94+
return tr_ch_apm$up_fetch.traceSync(__apm$traced, {
95+
arguments,
96+
self: this
97+
});
98+
}
99+
}
100+
",
101+
"map": undefined,
102+
}
103+
`;
104+
105+
exports[`Orchestrion JS Transformer > should transform TypeScript with source map correctly 1`] = `
106+
{
107+
"code": "import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel";
108+
const tr_ch_apm$up_fetch = tr_ch_apm_tracingChannel("orchestrion:one:up:fetch");
109+
const tr_ch_apm$up_constructor = tr_ch_apm_tracingChannel("orchestrion:one:up:constructor");
110+
export class Up {
111+
constructor(){
112+
const tr_ch_apm_ctx$up_constructor = {
113+
arguments
114+
};
115+
try {
116+
if (tr_ch_apm$up_constructor.hasSubscribers) {
117+
tr_ch_apm$up_constructor.start.publish(tr_ch_apm_ctx$up_constructor);
118+
}
119+
console.log('constructor');
120+
} catch (tr_ch_err) {
121+
if (tr_ch_apm$up_constructor.hasSubscribers) {
122+
tr_ch_apm_ctx$up_constructor.error = tr_ch_err;
123+
try {
124+
tr_ch_apm_ctx$up_constructor.self = this;
125+
} catch (refErr) {}
126+
tr_ch_apm$up_constructor.error.publish(tr_ch_apm_ctx$up_constructor);
127+
}
128+
throw tr_ch_err;
129+
} finally{
130+
if (tr_ch_apm$up_constructor.hasSubscribers) {
131+
tr_ch_apm_ctx$up_constructor.self = this;
132+
tr_ch_apm$up_constructor.end.publish(tr_ch_apm_ctx$up_constructor);
133+
}
134+
}
135+
}
136+
fetch(url) {
137+
const __apm$original_args = arguments;
138+
const __apm$traced = ()=>{
139+
const __apm$wrapped = (url)=>{
140+
console.log('fetch');
141+
};
142+
return __apm$wrapped.apply(null, __apm$original_args);
143+
};
144+
if (!tr_ch_apm$up_fetch.hasSubscribers) return __apm$traced();
145+
return tr_ch_apm$up_fetch.traceSync(__apm$traced, {
146+
arguments,
147+
self: this
148+
});
149+
}
150+
}
151+
",
152+
"map": "{"version":3,"file":"module.js","sources":["module.ts"],"sourceRoot":"","names":[],"mappings":";;;AAEA,MAAM,CAAA,MAAO,EAAE;IACX,aAAA;;;;;;;;YACI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;;;;;;;;;;;;;;;;IAC/B,CAAC;IACD,KAAK,IAAS,EAAA;;;mCAAR;gBACF,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;;;;;;;;;IACzB,CAAC;CACJ"}",
153+
}
154+
`;

tests/wasm/testdata/expected-cjs.js

Lines changed: 0 additions & 44 deletions
This file was deleted.

0 commit comments

Comments
 (0)