Skip to content

Commit f3f8de0

Browse files
committed
Merge remote-tracking branch 'origin/main' into koesie10/source-map-script-from-release-asset
2 parents ac127b3 + 81502fe commit f3f8de0

1 file changed

Lines changed: 96 additions & 28 deletions

File tree

extensions/ql-vscode/scripts/source-map.ts

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,21 @@
55
*
66
* Usage: npx ts-node scripts/source-map.ts <version-number> <filename>:<line>:<column>
77
* For example: npx ts-node scripts/source-map.ts v1.7.8 "/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131164:13"
8+
*
9+
* Alternative usage: npx ts-node scripts/source-map.ts <version-number> <multi-line-stacktrace>
10+
* For example: npx ts-node scripts/source-map.ts v1.7.8 'Error: Failed to find CodeQL distribution.
11+
* at CodeQLCliServer.getCodeQlPath (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131164:13)
12+
* at CodeQLCliServer.launchProcess (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131169:24)
13+
* at CodeQLCliServer.runCodeQlCliInternal (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131194:24)
14+
* at CodeQLCliServer.runJsonCodeQlCliCommand (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131330:20)
15+
* at CodeQLCliServer.resolveRam (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:131455:12)
16+
* at QueryServerClient2.startQueryServerImpl (/Users/user/.vscode/extensions/github.vscode-codeql-1.7.8/out/extension.js:138618:21)'
817
*/
918

1019
import { spawnSync } from "child_process";
1120
import { basename, resolve } from "path";
1221
import { pathExists, readJSON } from "fs-extra";
13-
import { SourceMapConsumer } from "source-map";
22+
import { RawSourceMap, SourceMapConsumer } from "source-map";
1423
import { Open } from "unzipper";
1524

1625
if (process.argv.length !== 4) {
@@ -19,10 +28,13 @@ if (process.argv.length !== 4) {
1928
);
2029
}
2130

31+
const stackLineRegex =
32+
/at (?<name>.*)? \((?<file>.*):(?<line>\d+):(?<column>\d+)\)/gm;
33+
2234
const versionNumber = process.argv[2].startsWith("v")
2335
? process.argv[2]
2436
: `v${process.argv[2]}`;
25-
const filenameAndLine = process.argv[3];
37+
const stacktrace = process.argv[3];
2638

2739
async function extractSourceMap() {
2840
const releaseAssetsDirectory = resolve(
@@ -102,39 +114,80 @@ async function extractSourceMap() {
102114
}
103115
}
104116

105-
const [filename, line, column] = filenameAndLine.split(":", 3);
117+
if (stacktrace.includes("at")) {
118+
const rawSourceMaps = new Map<string, RawSourceMap>();
119+
120+
const mappedStacktrace = await replaceAsync(
121+
stacktrace,
122+
stackLineRegex,
123+
async (match, name, file, line, column) => {
124+
if (!rawSourceMaps.has(file)) {
125+
const rawSourceMap: RawSourceMap = await readJSON(
126+
resolve(sourceMapsDirectory, `${basename(file)}.map`),
127+
);
128+
rawSourceMaps.set(file, rawSourceMap);
129+
}
130+
131+
const originalPosition = await SourceMapConsumer.with(
132+
rawSourceMaps.get(file) as RawSourceMap,
133+
null,
134+
async function (consumer) {
135+
return consumer.originalPositionFor({
136+
line: parseInt(line, 10),
137+
column: parseInt(column, 10),
138+
});
139+
},
140+
);
106141

107-
const fileBasename = basename(filename);
142+
if (!originalPosition.source) {
143+
return match;
144+
}
108145

109-
const sourcemapName = `${fileBasename}.map`;
110-
const sourcemapPath = resolve(sourceMapsDirectory, sourcemapName);
146+
const originalFilename = resolve(file, "..", originalPosition.source);
111147

112-
if (!(await pathExists(sourcemapPath))) {
113-
throw new Error(`No source map found for ${fileBasename}`);
114-
}
148+
return `at ${originalPosition.name ?? name} (${originalFilename}:${
149+
originalPosition.line
150+
}:${originalPosition.column})`;
151+
},
152+
);
115153

116-
const rawSourceMap = await readJSON(sourcemapPath);
117-
118-
const originalPosition = await SourceMapConsumer.with(
119-
rawSourceMap,
120-
null,
121-
async function (consumer) {
122-
return consumer.originalPositionFor({
123-
line: parseInt(line),
124-
column: parseInt(column),
125-
});
126-
},
127-
);
154+
console.log(mappedStacktrace);
155+
} else {
156+
// This means it's just a filename:line:column
157+
const [filename, line, column] = stacktrace.split(":", 3);
128158

129-
if (!originalPosition.source) {
130-
throw new Error(`No source found for ${filenameAndLine}`);
131-
}
159+
const fileBasename = basename(filename);
132160

133-
const originalFilename = resolve(filename, "..", originalPosition.source);
161+
const sourcemapName = `${fileBasename}.map`;
162+
const sourcemapPath = resolve(sourceMapsDirectory, sourcemapName);
134163

135-
console.log(
136-
`${originalFilename}:${originalPosition.line}:${originalPosition.column}`,
137-
);
164+
if (!(await pathExists(sourcemapPath))) {
165+
throw new Error(`No source map found for ${fileBasename}`);
166+
}
167+
168+
const rawSourceMap: RawSourceMap = await readJSON(sourcemapPath);
169+
170+
const originalPosition = await SourceMapConsumer.with(
171+
rawSourceMap,
172+
null,
173+
async function (consumer) {
174+
return consumer.originalPositionFor({
175+
line: parseInt(line, 10),
176+
column: parseInt(column, 10),
177+
});
178+
},
179+
);
180+
181+
if (!originalPosition.source) {
182+
throw new Error(`No source found for ${stacktrace}`);
183+
}
184+
185+
const originalFilename = resolve(filename, "..", originalPosition.source);
186+
187+
console.log(
188+
`${originalFilename}:${originalPosition.line}:${originalPosition.column}`,
189+
);
190+
}
138191
}
139192

140193
extractSourceMap().catch((e: unknown) => {
@@ -171,3 +224,18 @@ type WorkflowRunListItem = {
171224
databaseId: number;
172225
number: number;
173226
};
227+
228+
async function replaceAsync(
229+
str: string,
230+
regex: RegExp,
231+
replacer: (substring: string, ...args: any[]) => Promise<string>,
232+
) {
233+
const promises: Array<Promise<string>> = [];
234+
str.replace(regex, (match, ...args) => {
235+
const promise = replacer(match, ...args);
236+
promises.push(promise);
237+
return match;
238+
});
239+
const data = await Promise.all(promises);
240+
return str.replace(regex, () => data.shift() as string);
241+
}

0 commit comments

Comments
 (0)