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" ;
1423
1524if ( process . argv . length !== 4 ) {
1625 console . error (
1726 "Expected 2 arguments - the version number and the filename:line number" ,
1827 ) ;
1928}
2029
30+ const stackLineRegex =
31+ / a t (?< name > .* ) ? \( (?< file > .* ) : (?< line > \d + ) : (?< column > \d + ) \) / gm;
32+
2133const versionNumber = process . argv [ 2 ] . startsWith ( "v" )
2234 ? process . argv [ 2 ]
2335 : `v${ process . argv [ 2 ] } ` ;
24- const filenameAndLine = process . argv [ 3 ] ;
36+ const stacktrace = process . argv [ 3 ] ;
2537
2638async function extractSourceMap ( ) {
2739 const sourceMapsDirectory = resolve (
@@ -65,39 +77,80 @@ async function extractSourceMap() {
6577 ] ) ;
6678 }
6779
68- const [ filename , line , column ] = filenameAndLine . split ( ":" , 3 ) ;
80+ if ( stacktrace . includes ( "at" ) ) {
81+ const rawSourceMaps = new Map < string , RawSourceMap > ( ) ;
82+
83+ const mappedStacktrace = await replaceAsync (
84+ stacktrace ,
85+ stackLineRegex ,
86+ async ( match , name , file , line , column ) => {
87+ if ( ! rawSourceMaps . has ( file ) ) {
88+ const rawSourceMap : RawSourceMap = await readJSON (
89+ resolve ( sourceMapsDirectory , `${ basename ( file ) } .map` ) ,
90+ ) ;
91+ rawSourceMaps . set ( file , rawSourceMap ) ;
92+ }
93+
94+ const originalPosition = await SourceMapConsumer . with (
95+ rawSourceMaps . get ( file ) as RawSourceMap ,
96+ null ,
97+ async function ( consumer ) {
98+ return consumer . originalPositionFor ( {
99+ line : parseInt ( line , 10 ) ,
100+ column : parseInt ( column , 10 ) ,
101+ } ) ;
102+ } ,
103+ ) ;
104+
105+ if ( ! originalPosition . source ) {
106+ return match ;
107+ }
108+
109+ const originalFilename = resolve ( file , ".." , originalPosition . source ) ;
110+
111+ return `at ${ originalPosition . name ?? name } (${ originalFilename } :${
112+ originalPosition . line
113+ } :${ originalPosition . column } )`;
114+ } ,
115+ ) ;
69116
70- const fileBasename = basename ( filename ) ;
117+ console . log ( mappedStacktrace ) ;
118+ } else {
119+ // This means it's just a filename:line:column
120+ const [ filename , line , column ] = stacktrace . split ( ":" , 3 ) ;
71121
72- const sourcemapName = `${ fileBasename } .map` ;
73- const sourcemapPath = resolve ( sourceMapsDirectory , sourcemapName ) ;
122+ const fileBasename = basename ( filename ) ;
74123
75- if ( ! ( await pathExists ( sourcemapPath ) ) ) {
76- throw new Error ( `No source map found for ${ fileBasename } ` ) ;
77- }
124+ const sourcemapName = `${ fileBasename } .map` ;
125+ const sourcemapPath = resolve ( sourceMapsDirectory , sourcemapName ) ;
78126
79- const rawSourceMap = await readJSON ( sourcemapPath ) ;
80-
81- const originalPosition = await SourceMapConsumer . with (
82- rawSourceMap ,
83- null ,
84- async function ( consumer ) {
85- return consumer . originalPositionFor ( {
86- line : parseInt ( line ) ,
87- column : parseInt ( column ) ,
88- } ) ;
89- } ,
90- ) ;
127+ if ( ! ( await pathExists ( sourcemapPath ) ) ) {
128+ throw new Error ( `No source map found for ${ fileBasename } ` ) ;
129+ }
91130
92- if ( ! originalPosition . source ) {
93- throw new Error ( `No source found for ${ filenameAndLine } ` ) ;
94- }
131+ const rawSourceMap : RawSourceMap = await readJSON ( sourcemapPath ) ;
132+
133+ const originalPosition = await SourceMapConsumer . with (
134+ rawSourceMap ,
135+ null ,
136+ async function ( consumer ) {
137+ return consumer . originalPositionFor ( {
138+ line : parseInt ( line , 10 ) ,
139+ column : parseInt ( column , 10 ) ,
140+ } ) ;
141+ } ,
142+ ) ;
95143
96- const originalFilename = resolve ( filename , ".." , originalPosition . source ) ;
144+ if ( ! originalPosition . source ) {
145+ throw new Error ( `No source found for ${ stacktrace } ` ) ;
146+ }
97147
98- console . log (
99- `${ originalFilename } :${ originalPosition . line } :${ originalPosition . column } ` ,
100- ) ;
148+ const originalFilename = resolve ( filename , ".." , originalPosition . source ) ;
149+
150+ console . log (
151+ `${ originalFilename } :${ originalPosition . line } :${ originalPosition . column } ` ,
152+ ) ;
153+ }
101154}
102155
103156extractSourceMap ( ) . catch ( ( e : unknown ) => {
@@ -123,3 +176,18 @@ type WorkflowRunListItem = {
123176 databaseId : number ;
124177 number : number ;
125178} ;
179+
180+ async function replaceAsync (
181+ str : string ,
182+ regex : RegExp ,
183+ replacer : ( substring : string , ...args : any [ ] ) => Promise < string > ,
184+ ) {
185+ const promises : Array < Promise < string > > = [ ] ;
186+ str . replace ( regex , ( match , ...args ) => {
187+ const promise = replacer ( match , ...args ) ;
188+ promises . push ( promise ) ;
189+ return match ;
190+ } ) ;
191+ const data = await Promise . all ( promises ) ;
192+ return str . replace ( regex , ( ) => data . shift ( ) as string ) ;
193+ }
0 commit comments