From 5c355ba962f7b3f3b847ad8cc12d245d285667c0 Mon Sep 17 00:00:00 2001 From: Bob Evans Date: Tue, 3 Mar 2026 21:56:42 -0500 Subject: [PATCH] feat: Convert windows path to unix path before comparing against ModuleMatcher file_path value. --- README.md | 4 +- src/config.rs | 10 +++-- src/instrumentation.rs | 4 +- src/lib.rs | 4 +- tests/common/mod.rs | 16 +++++++- tests/instrumentor_test.rs | 1 + tests/wasm/__snapshots__/tests.test.mjs.snap | 29 +++++++++++++++ tests/wasm/tests.test.mjs | 39 ++++++++++++++++++++ tests/windows_path/mod.js | 9 +++++ tests/windows_path/mod.rs | 17 +++++++++ tests/windows_path/test.js | 17 +++++++++ 11 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 tests/windows_path/mod.js create mode 100644 tests/windows_path/mod.rs create mode 100644 tests/windows_path/test.js diff --git a/README.md b/README.md index c3a0d27..be05c1f 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ type FunctionQuery = type ModuleMatcher = { name: string; // Module name versionRange: string; // Matching semver range - filePath: string; // Path to the file from the module root + filePath: string; // Relative Unix-style path to the file from the module root (e.g. "lib/index.js") }; ``` @@ -177,7 +177,7 @@ matching instrumentation configurations. - `module_name` - Name of the module. - `version` - Version of the module. -- `file_path` - Path to the file from the module root. +- `file_path` - Relative Unix-style path to the file from the module root (e.g. `"lib/index.js"`). Windows-style backslash paths are also accepted and will be normalized automatically. ```ts free(): void; diff --git a/src/config.rs b/src/config.rs index dd6d6aa..3fb91b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ **/ use crate::function_query::FunctionQuery; use nodejs_semver::{Range, SemverError, Version}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; #[cfg_attr(feature = "wasm", derive(tsify::Tsify))] #[cfg_attr( @@ -37,7 +37,7 @@ impl ModuleMatcher { } #[must_use] - pub fn matches(&self, module_name: &str, version: &str, file_path: &PathBuf) -> bool { + pub fn matches(&self, module_name: &str, version: &str, file_path: &Path) -> bool { let version: Version = match version.parse() { Ok(v) => v, Err(e) => { @@ -46,9 +46,11 @@ impl ModuleMatcher { } }; + // convert windows paths to unix before comparing against `self.file_path` + let normalized_path = PathBuf::from(file_path.to_string_lossy().replace('\\', "/")); self.name == module_name && version.satisfies(&self.version_range) - && self.file_path == *file_path + && self.file_path == normalized_path } } @@ -120,7 +122,7 @@ impl Config { impl InstrumentationConfig { #[must_use] - pub fn matches(&self, module_name: &str, version: &str, file_path: &PathBuf) -> bool { + pub fn matches(&self, module_name: &str, version: &str, file_path: &Path) -> bool { self.module.matches(module_name, version, file_path) } } diff --git a/src/instrumentation.rs b/src/instrumentation.rs index 2748a86..a0fdf65 100644 --- a/src/instrumentation.rs +++ b/src/instrumentation.rs @@ -4,7 +4,7 @@ **/ use crate::config::InstrumentationConfig; use std::collections::HashMap; -use std::path::PathBuf; +use std::path::Path; use swc_core::common::{Span, SyntaxContext}; use swc_core::ecma::{ ast::{ @@ -244,7 +244,7 @@ impl Instrumentation { } #[must_use] - pub fn matches(&self, module_name: &str, version: &str, file_path: &PathBuf) -> bool { + pub fn matches(&self, module_name: &str, version: &str, file_path: &Path) -> bool { self.config.matches(module_name, version, file_path) } diff --git a/src/lib.rs b/src/lib.rs index 02fcbd8..36d07e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,7 @@ * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. * This product includes software developed at Datadog (/). Copyright 2025 Datadog, Inc. **/ -use std::{collections::HashMap, error::Error, path::PathBuf, sync::Arc}; +use std::{collections::HashMap, error::Error, path::Path, path::PathBuf, sync::Arc}; use swc::{ config::{IsModule, SourceMapsConfig}, sourcemap::SourceMap, @@ -134,7 +134,7 @@ impl Instrumentor { &self, module_name: &str, version: &str, - file_path: &PathBuf, + file_path: &Path, ) -> InstrumentationVisitor { let instrumentations = self .instrumentations diff --git a/tests/common/mod.rs b/tests/common/mod.rs index f3dc816..5760721 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -13,10 +13,18 @@ static TEST_MODULE_NAME: &str = "undici"; static TEST_MODULE_PATH: &str = "index.mjs"; pub fn transpile_and_test(test_file: &str, mjs: bool, config: Config) { + transpile_and_test_with_path(test_file, mjs, config, PathBuf::from(TEST_MODULE_PATH)); +} + +pub fn transpile_and_test_with_path( + test_file: &str, + mjs: bool, + config: Config, + file_path: PathBuf, +) { let test_file = PathBuf::from(test_file); let test_dir = test_file.parent().expect("Couldn't find test directory"); - let file_path = PathBuf::from("index.mjs"); let instrumentor = Instrumentor::new(config); let mut instrumentations = instrumentor.get_matching_instrumentations(TEST_MODULE_NAME, "0.0.1", &file_path); @@ -46,3 +54,9 @@ pub fn transpile_and_test(test_file: &str, mjs: bool, config: Config) { pub fn test_module_matcher() -> ModuleMatcher { ModuleMatcher::new(TEST_MODULE_NAME, ">=0.0.1", TEST_MODULE_PATH).unwrap() } + +static WINDOWS_TEST_MODULE_PATH: &str = "lib/index.mjs"; + +pub fn windows_module_matcher() -> ModuleMatcher { + ModuleMatcher::new(TEST_MODULE_NAME, ">=0.0.1", WINDOWS_TEST_MODULE_PATH).unwrap() +} diff --git a/tests/instrumentor_test.rs b/tests/instrumentor_test.rs index eaaba3b..0323b02 100644 --- a/tests/instrumentor_test.rs +++ b/tests/instrumentor_test.rs @@ -29,3 +29,4 @@ mod polyfill_mjs; mod private_method_cjs; mod var_class_export_alias_mjs; mod var_named_class_export_alias_mjs; +mod windows_path; diff --git a/tests/wasm/__snapshots__/tests.test.mjs.snap b/tests/wasm/__snapshots__/tests.test.mjs.snap index c81f3d6..28f366f 100644 --- a/tests/wasm/__snapshots__/tests.test.mjs.snap +++ b/tests/wasm/__snapshots__/tests.test.mjs.snap @@ -287,3 +287,32 @@ export class Up { "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;YACzB,CAAC;;;;;;;;;;IACD,KAAK,CAAC,UAAU,GAAA;;;;;;;;;;;;IAAU,CAAC;IAC3B,GAAG,GAAA;;;;;;;;;;;;IAAU,CAAC;IACd,KAAK,EAAC,IAAK;;;;;;;;;;;;IAAU,CAAC;IACtB,KAAK,CAAC,IAAI,GAAA,CAAU,CAAC;CACxB"}", } `; + +exports[`windows style paths > should transform ESM module correctly 1`] = ` +{ + "code": "import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel"; +const tr_ch_apm$up_fetch = tr_ch_apm_tracingChannel("orchestrion:one:up:fetch"); +export class Up { + constructor(){ + console.log('constructor'); + } + fetch() { + const __apm$original_args = arguments; + const __apm$traced = ()=>{ + const __apm$wrapped = ()=>{ + console.log('fetch'); + }; + return __apm$wrapped.apply(null, __apm$original_args); + }; + if (!tr_ch_apm$up_fetch.hasSubscribers) return __apm$traced(); + return tr_ch_apm$up_fetch.traceSync(__apm$traced, { + arguments, + self: this, + moduleVersion: "1.0.0" + }); + } +} +", + "map": undefined, +} +`; diff --git a/tests/wasm/tests.test.mjs b/tests/wasm/tests.test.mjs index 1d94867..ac81067 100644 --- a/tests/wasm/tests.test.mjs +++ b/tests/wasm/tests.test.mjs @@ -166,3 +166,42 @@ export class Up { }).toThrow('Failed to find injection points for: ["constructor", "fetch", "asyncFetch", "get", "send"]'); }); }); + +describe('windows style paths', () => { + const instrumentor = create([ + { + channelName: "up:fetch", + module: { name: "one", versionRange: ">=1", filePath: "path/to/file.js" }, + functionQuery: { + className: "Up", + methodName: "fetch", + kind: "Sync", + }, + }, + ]); + + const matchedTransforms = instrumentor.getTransformer( + "one", + "1.0.0", + "path\\to\\file.js", + ); + + test('should get transformer for matching module', () => { + expect(matchedTransforms).toBeTruthy(); + }); + + test('should transform ESM module correctly', () => { + const originalEsm = `export class Up { + constructor() { + console.log('constructor') + } + + fetch() { + console.log('fetch') + } +}`; + + const output = matchedTransforms.transform(originalEsm, 'esm'); + expect(output).toMatchSnapshot(); + }); +}) diff --git a/tests/windows_path/mod.js b/tests/windows_path/mod.js new file mode 100644 index 0000000..c0cbfa6 --- /dev/null +++ b/tests/windows_path/mod.js @@ -0,0 +1,9 @@ +/** + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2025 Datadog, Inc. + **/ +async function fetch (url) { + return 42; +} + +module.exports = { fetch }; diff --git a/tests/windows_path/mod.rs b/tests/windows_path/mod.rs new file mode 100644 index 0000000..7a6fc9c --- /dev/null +++ b/tests/windows_path/mod.rs @@ -0,0 +1,17 @@ +use crate::common::*; +use orchestrion_js::*; +use std::path::PathBuf; + +#[test] +fn windows_path() { + transpile_and_test_with_path( + file!(), + false, + Config::new_single(InstrumentationConfig::new( + "fetch_decl", + windows_module_matcher(), + FunctionQuery::function_declaration("fetch", FunctionKind::Async), + )), + PathBuf::from("lib\\index.mjs"), + ); +} diff --git a/tests/windows_path/test.js b/tests/windows_path/test.js new file mode 100644 index 0000000..602a66a --- /dev/null +++ b/tests/windows_path/test.js @@ -0,0 +1,17 @@ +/** + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. + * This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2025 Datadog, Inc. + **/ +const { fetch } = require('./instrumented.js'); +const { assert, getContext } = require('../common/preamble.js'); +const context = getContext('orchestrion:undici:fetch_decl'); +(async () => { + const result = await fetch('https://example.com'); + assert.strictEqual(result, 42); + assert.deepStrictEqual(context, { + start: true, + end: true, + asyncStart: 42, + asyncEnd: 42 + }); +})();