Skip to content

Commit 92e2975

Browse files
committed
Add script for retrieving original source location
This adds a script that can be used for retrieving the original source location when given a location in the released extension. It will download the source map from the Actions workflow run of the release and use the `source-map` library to extract the original location.
1 parent 7a9b8bb commit 92e2975

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/**
2+
* This scripts helps finding the original source file and line number for a
3+
* given file and line number in the compiled extension. It currently only
4+
* works with released extensions.
5+
*
6+
* Usage: npx ts-node scripts/source-map.ts <version-number> <filename>:<line>:<column>
7+
*/
8+
9+
import { spawnSync } from "child_process";
10+
import { basename, resolve } from "path";
11+
import { pathExists, readJSON } from "fs-extra";
12+
import { SourceMapConsumer } from "source-map";
13+
14+
if (process.argv.length !== 4) {
15+
console.error(
16+
"Expected 2 arguments - the version number and the filename:line number",
17+
);
18+
}
19+
20+
const versionNumber = process.argv[2].startsWith("v")
21+
? process.argv[2]
22+
: `v${process.argv[2]}`;
23+
const filenameAndLine = process.argv[3];
24+
25+
async function extractSourceMap() {
26+
const sourceMapsDirectory = resolve(
27+
__dirname,
28+
"..",
29+
"artifacts",
30+
"source-maps",
31+
versionNumber,
32+
);
33+
34+
if (!(await pathExists(sourceMapsDirectory))) {
35+
console.log("Downloading source maps...");
36+
37+
const workflowRuns = runGhJSON<WorkflowRunListItem[]>([
38+
"run",
39+
"list",
40+
"--workflow",
41+
"release.yml",
42+
"--branch",
43+
versionNumber,
44+
"--json",
45+
"databaseId,number",
46+
]);
47+
48+
if (workflowRuns.length !== 1) {
49+
throw new Error(
50+
`Expected exactly one workflow run for ${versionNumber}, got ${workflowRuns.length}`,
51+
);
52+
}
53+
54+
const workflowRun = workflowRuns[0];
55+
56+
runGh([
57+
"run",
58+
"download",
59+
workflowRun.databaseId.toString(),
60+
"--name",
61+
"vscode-codeql-sourcemaps",
62+
"--dir",
63+
sourceMapsDirectory,
64+
]);
65+
}
66+
67+
const [filename, line, column] = filenameAndLine.split(":", 3);
68+
69+
const fileBasename = basename(filename);
70+
71+
const sourcemapName = `${fileBasename}.map`;
72+
const sourcemapPath = resolve(sourceMapsDirectory, sourcemapName);
73+
74+
if (!(await pathExists(sourcemapPath))) {
75+
throw new Error(`No source map found for ${fileBasename}`);
76+
}
77+
78+
const rawSourceMap = await readJSON(sourcemapPath);
79+
80+
const originalPosition = await SourceMapConsumer.with(
81+
rawSourceMap,
82+
null,
83+
async function (consumer) {
84+
return consumer.originalPositionFor({
85+
line: parseInt(line),
86+
column: parseInt(column),
87+
});
88+
},
89+
);
90+
91+
if (!originalPosition.source) {
92+
throw new Error(`No source found for ${filenameAndLine}`);
93+
}
94+
95+
const originalFilename = resolve(filename, "..", originalPosition.source);
96+
97+
console.log(
98+
`${originalFilename}:${originalPosition.line}:${originalPosition.column}`,
99+
);
100+
}
101+
102+
extractSourceMap().catch((e: unknown) => {
103+
console.error(e);
104+
process.exit(2);
105+
});
106+
107+
function runGh(args: readonly string[]): string {
108+
const gh = spawnSync("gh", args);
109+
if (gh.status !== 0) {
110+
throw new Error(
111+
`Failed to get the source map for ${versionNumber}: ${gh.stderr}`,
112+
);
113+
}
114+
return gh.stdout.toString("utf-8");
115+
}
116+
117+
function runGhJSON<T>(args: readonly string[]): T {
118+
return JSON.parse(runGh(args));
119+
}
120+
121+
type WorkflowRunListItem = {
122+
databaseId: number;
123+
number: number;
124+
};

0 commit comments

Comments
 (0)