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
1019import { spawnSync } from "child_process" ;
1120import { basename , resolve } from "path" ;
1221import { pathExists , readJSON } from "fs-extra" ;
13- import { SourceMapConsumer } from "source-map" ;
22+ import { RawSourceMap , SourceMapConsumer } from "source-map" ;
1423import { Open } from "unzipper" ;
1524
1625if ( process . argv . length !== 4 ) {
@@ -19,10 +28,13 @@ if (process.argv.length !== 4) {
1928 ) ;
2029}
2130
31+ const stackLineRegex =
32+ / a t (?< name > .* ) ? \( (?< file > .* ) : (?< line > \d + ) : (?< column > \d + ) \) / gm;
33+
2234const 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
2739async 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
140193extractSourceMap ( ) . 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