Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@query-doctor/sqlcommenter-mikroorm",
"version": "0.0.2",
"version": "0.1.0",
"description": "SQLCommenter patch for MikroORM",
"main": "dist/cjs/index.js",
"type": "module",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { alreadyHasTrailingComment, serializeTags, type Tag } from "./sqlcommenter.js";
import { als } from "./als.js";
import { pushW3CTraceContext } from "./tracing.js";
import { resolveFilePath } from "./path.js";

const LIBRARY_NAME = "sqlcommenter-mikroorm";

Expand Down Expand Up @@ -42,7 +43,7 @@ export function traceCaller(): string | undefined {
}
const match = methodCaller.match(filepathRegex);
if (match) {
return match[1];
return resolveFilePath(match[1]);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { existsSync } from "node:fs";
import { dirname, join } from "node:path";

let cachedProjectRoot: string | undefined;

/**
* Finds the project root by walking up from `process.cwd()` looking for `tsconfig.json`.
*
* In deployed environments, `process.cwd()` may not be the project root
* (e.g., `cd .amplify-hosting/compute/default/ && node app.js`).
* Walking up to find `tsconfig.json` — which is never copied to deployment directories —
* gives us the real project root.
*
* The result is cached since the project root doesn't change during a process's lifetime.
*/
export function findProjectRoot(): string {
if (cachedProjectRoot !== undefined) {
return cachedProjectRoot;
}
let projectRoot = process.cwd();
for (let d = projectRoot; d !== dirname(d); d = dirname(d)) {
if (existsSync(join(d, "tsconfig.json"))) {
projectRoot = d;
break;
}
}
cachedProjectRoot = projectRoot;
return projectRoot;
}

/**
* Resolves a file path from a stack trace to a correct absolute path.
*
* When compiled JS is relocated (e.g., postbuild copies `dist/` to a deployment directory),
* source-map-resolved paths become incorrect because the relative `sources` entries in
* `.map` files resolve against the new location instead of the original project.
*
* This extracts the `src/`-relative portion and reconstructs the path using the real
* project root.
*
* @param raw - A stack trace entry like "/wrong/path/src/routes/admin.ts:12:15"
* @returns The resolved path like "/project/root/src/routes/admin.ts:12:15"
*/
export function resolveFilePath(raw: string): string {
// Split off :line:column suffix
const match = raw.match(/^(.*?):(\d+:\d+)$/);
if (!match) {
return raw;
}
const [, filePath, lineCol] = match;
const srcIdx = filePath.indexOf("src/");
if (srcIdx < 0) {
return raw;
}
const projectRoot = findProjectRoot();
const relativePath = filePath.substring(srcIdx);
const resolved = `${projectRoot}/${relativePath}`;
return `${applyWslPrefix(resolved)}:${lineCol}`;
}

/**
* Prefixes an absolute path with the WSL network path when running inside WSL.
*
* Inside WSL, absolute paths like `/home/user/project/...` can't be resolved
* from Windows-side tooling (e.g., clickable links in dashboards or VS Code).
* The `WSL_DISTRO_NAME` env var is always set inside WSL, and the path format
* `//wsl.localhost/<distro>/...` makes paths accessible from Windows.
*/
export function applyWslPrefix(filePath: string): string {
const distro = process.env.WSL_DISTRO_NAME;
if (distro) {
return `//wsl.localhost/${distro}${filePath}`;
}
return filePath;
}

/** @internal Exposed for testing only */
export function _resetProjectRootCache() {
cachedProjectRoot = undefined;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import { test } from "node:test";
import assert from "node:assert";
import { existsSync } from "node:fs";
import { join } from "node:path";
import {
findProjectRoot,
resolveFilePath,
applyWslPrefix,
_resetProjectRootCache,
} from "../src/path.js";

test("findProjectRoot", async (t) => {
t.afterEach(() => {
_resetProjectRootCache();
});

await t.test("returns a directory containing tsconfig.json", () => {
const root = findProjectRoot();
assert.ok(
existsSync(join(root, "tsconfig.json")),
`Expected ${root} to contain tsconfig.json`,
);
});

await t.test("caches the result across calls", () => {
const first = findProjectRoot();
const second = findProjectRoot();
assert.strictEqual(first, second);
});
});

test("resolveFilePath", async (t) => {
t.afterEach(() => {
_resetProjectRootCache();
});

await t.test("resolves path with src/ to project root", () => {
const projectRoot = findProjectRoot();
const result = resolveFilePath(
"/wrong/deploy/dir/src/routes/admin.ts:12:15",
);
assert.strictEqual(result, `${projectRoot}/src/routes/admin.ts:12:15`);
});

await t.test("leaves path without src/ unchanged", () => {
const result = resolveFilePath("/some/other/path/routes/admin.ts:5:10");
assert.strictEqual(result, "/some/other/path/routes/admin.ts:5:10");
});

await t.test("preserves line:column suffix", () => {
const projectRoot = findProjectRoot();
const result = resolveFilePath("/bad/path/src/index.ts:99:3");
assert.strictEqual(result, `${projectRoot}/src/index.ts:99:3`);
});

await t.test("uses first src/ occurrence", () => {
const projectRoot = findProjectRoot();
const result = resolveFilePath(
"/deploy/src/nested/src/routes/admin.ts:1:1",
);
assert.strictEqual(
result,
`${projectRoot}/src/nested/src/routes/admin.ts:1:1`,
);
});

await t.test("returns raw string if no line:column suffix", () => {
const result = resolveFilePath("/some/path/src/file.ts");
assert.strictEqual(result, "/some/path/src/file.ts");
});
});

test("applyWslPrefix", async (t) => {
const originalWslDistroName = process.env.WSL_DISTRO_NAME;

t.afterEach(() => {
if (originalWslDistroName === undefined) {
delete process.env.WSL_DISTRO_NAME;
} else {
process.env.WSL_DISTRO_NAME = originalWslDistroName;
}
});

await t.test("prefixes path when WSL_DISTRO_NAME is set", () => {
process.env.WSL_DISTRO_NAME = "Ubuntu-22.04";
const path = "/home/user/project/src/file.ts:1:1";
const result = applyWslPrefix(path);
assert.strictEqual(result, "//wsl.localhost/Ubuntu-22.04/home/user/project/src/file.ts:1:1");
});

await t.test("returns path unchanged when WSL_DISTRO_NAME is not set", () => {
delete process.env.WSL_DISTRO_NAME;
const path = "/home/user/project/src/file.ts:1:1";
const result = applyWslPrefix(path);
assert.strictEqual(result, path);
});
});

test("resolveFilePath with WSL", async (t) => {
const originalWslDistroName = process.env.WSL_DISTRO_NAME;

t.afterEach(() => {
_resetProjectRootCache();
if (originalWslDistroName === undefined) {
delete process.env.WSL_DISTRO_NAME;
} else {
process.env.WSL_DISTRO_NAME = originalWslDistroName;
}
});

await t.test("applies WSL prefix to resolved src/ paths", () => {
process.env.WSL_DISTRO_NAME = "Ubuntu-22.04";
const projectRoot = findProjectRoot();
const rawPath = "/wrong/deploy/dir/src/routes/admin.ts:12:15";
const result = resolveFilePath(rawPath);
assert.strictEqual(result, `//wsl.localhost/Ubuntu-22.04${projectRoot}/src/routes/admin.ts:12:15`);
});
});