Skip to content

Commit bed290d

Browse files
veksenclaude
andcommitted
feat(typeorm): normalize source-map-resolved file paths
When compiled JS is relocated (e.g., postbuild copies dist/ to a deployment directory), source map relative paths resolve to wrong absolute paths. This extracts the src/-relative portion from the stack trace path and reconstructs it using the real project root found via tsconfig.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3176e37 commit bed290d

3 files changed

Lines changed: 77 additions & 2 deletions

File tree

nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { alreadyHasTrailingComment, serializeTags, type Tag } from "./sqlcommenter.js";
22
import { als } from "./als.js";
33
import { pushW3CTraceContext } from "./tracing.js";
4+
import { resolveFilePath } from "./path.js";
45

56
const LIBRARY_NAME = "sqlcommenter-typeorm";
67

@@ -47,7 +48,7 @@ export function traceCaller(): string | undefined {
4748
}
4849
const match = methodCaller.match(filepathRegex);
4950
if (match) {
50-
return match[1];
51+
return resolveFilePath(match[1]);
5152
}
5253
}
5354

nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/src/path.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,35 @@ export function findProjectRoot(): string {
2828
return projectRoot;
2929
}
3030

31+
/**
32+
* Resolves a file path from a stack trace to a correct absolute path.
33+
*
34+
* When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory),
35+
* source-map-resolved paths become incorrect because the relative `sources` entries in
36+
* `.map` files resolve against the new location instead of the original project.
37+
*
38+
* This extracts the `src/`-relative portion and reconstructs the path using the real
39+
* project root.
40+
*
41+
* @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15"
42+
* @returns The resolved path like "/project/root/src/routes/admin.ts:12:15"
43+
*/
44+
export function resolveFilePath(raw: string): string {
45+
// Split off :line:column suffix
46+
const match = raw.match(/^(.*?):(\d+:\d+)$/);
47+
if (!match) {
48+
return raw;
49+
}
50+
const [, filePath, lineCol] = match;
51+
const srcIdx = filePath.indexOf("src/");
52+
if (srcIdx < 0) {
53+
return raw;
54+
}
55+
const projectRoot = findProjectRoot();
56+
const relativePath = filePath.substring(srcIdx);
57+
return `${projectRoot}/${relativePath}:${lineCol}`;
58+
}
59+
3160
/** @internal Exposed for testing only */
3261
export function _resetProjectRootCache() {
3362
cachedProjectRoot = undefined;

nodejs/sqlcommenter-nodejs/packages/sqlcommenter-typeorm/test/path.spec.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { test } from "node:test";
22
import assert from "node:assert";
33
import { existsSync } from "node:fs";
44
import { join } from "node:path";
5-
import { findProjectRoot, _resetProjectRootCache } from "../src/path.js";
5+
import {
6+
findProjectRoot,
7+
resolveFilePath,
8+
_resetProjectRootCache,
9+
} from "../src/path.js";
610

711
test("findProjectRoot", async (t) => {
812
t.afterEach(() => {
@@ -23,3 +27,44 @@ test("findProjectRoot", async (t) => {
2327
assert.strictEqual(first, second);
2428
});
2529
});
30+
31+
test("resolveFilePath", async (t) => {
32+
t.afterEach(() => {
33+
_resetProjectRootCache();
34+
});
35+
36+
await t.test("resolves path with src/ to project root", () => {
37+
const projectRoot = findProjectRoot();
38+
const result = resolveFilePath(
39+
"/wrong/deploy/dir/src/routes/admin.ts:12:15",
40+
);
41+
assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`);
42+
});
43+
44+
await t.test("leaves path without src/ unchanged", () => {
45+
const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10");
46+
assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10");
47+
});
48+
49+
await t.test("preserves line:column suffix", () => {
50+
const projectRoot = findProjectRoot();
51+
const result = resolveFilePath("/bad/path/src/index.ts:99:3");
52+
assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`);
53+
});
54+
55+
await t.test("uses first src/ occurrence", () => {
56+
const projectRoot = findProjectRoot();
57+
const result = resolveFilePath(
58+
"/deploy/src/nested/src/routes/admin.ts:1:1",
59+
);
60+
assert.strictEqual(
61+
result,
62+
`${projectRoot}/src/nested/src/routes/admin.ts:1:1`,
63+
);
64+
});
65+
66+
await t.test("returns raw string if no line:column suffix", () => {
67+
const result = resolveFilePath("/some/path/src/file.ts");
68+
assert.strictEqual(result, "/some/path/src/file.ts");
69+
});
70+
});

0 commit comments

Comments
 (0)