44 * works with released extensions.
55 *
66 * Usage: npx ts-node scripts/source-map.ts <version-number> <filename>:<line>:<column>
7+ * Alternative usage: npx ts-node scripts/source-map.ts <version-number> <multi-line-stacktrace>
78 */
89
910import { spawnSync } from "child_process" ;
1011import { basename , resolve } from "path" ;
1112import { pathExists , readJSON } from "fs-extra" ;
12- import { SourceMapConsumer } from "source-map" ;
13+ import { RawSourceMap , SourceMapConsumer } from "source-map" ;
1314
1415if ( process . argv . length !== 4 ) {
1516 console . error (
1617 "Expected 2 arguments - the version number and the filename:line number" ,
1718 ) ;
1819}
1920
21+ const stackLineRegex =
22+ / a t (?< name > .* ) ? \( (?< file > .* ) : (?< line > \d + ) : (?< column > \d + ) \) / gm;
23+
2024const versionNumber = process . argv [ 2 ] . startsWith ( "v" )
2125 ? process . argv [ 2 ]
2226 : `v${ process . argv [ 2 ] } ` ;
23- const filenameAndLine = process . argv [ 3 ] ;
27+ const stacktrace = process . argv [ 3 ] ;
2428
2529async function extractSourceMap ( ) {
2630 const sourceMapsDirectory = resolve (
@@ -64,39 +68,80 @@ async function extractSourceMap() {
6468 ] ) ;
6569 }
6670
67- const [ filename , line , column ] = filenameAndLine . split ( ":" , 3 ) ;
71+ if ( stacktrace . includes ( "at" ) ) {
72+ const rawSourceMaps = new Map < string , RawSourceMap > ( ) ;
73+
74+ const mappedStacktrace = await replaceAsync (
75+ stacktrace ,
76+ stackLineRegex ,
77+ async ( match , name , file , line , column ) => {
78+ if ( ! rawSourceMaps . has ( file ) ) {
79+ const rawSourceMap : RawSourceMap = await readJSON (
80+ resolve ( sourceMapsDirectory , `${ basename ( file ) } .map` ) ,
81+ ) ;
82+ rawSourceMaps . set ( file , rawSourceMap ) ;
83+ }
84+
85+ const originalPosition = await SourceMapConsumer . with (
86+ rawSourceMaps . get ( file ) as RawSourceMap ,
87+ null ,
88+ async function ( consumer ) {
89+ return consumer . originalPositionFor ( {
90+ line : parseInt ( line ) ,
91+ column : parseInt ( column ) ,
92+ } ) ;
93+ } ,
94+ ) ;
95+
96+ if ( ! originalPosition . source ) {
97+ return match ;
98+ }
99+
100+ const originalFilename = resolve ( file , ".." , originalPosition . source ) ;
101+
102+ return `at ${ originalPosition . name ?? name } (${ originalFilename } :${
103+ originalPosition . line
104+ } :${ originalPosition . column } )`;
105+ } ,
106+ ) ;
68107
69- const fileBasename = basename ( filename ) ;
108+ console . log ( mappedStacktrace ) ;
109+ } else {
110+ // This means it's just a filename:line:column
111+ const [ filename , line , column ] = stacktrace . split ( ":" , 3 ) ;
70112
71- const sourcemapName = `${ fileBasename } .map` ;
72- const sourcemapPath = resolve ( sourceMapsDirectory , sourcemapName ) ;
113+ const fileBasename = basename ( filename ) ;
73114
74- if ( ! ( await pathExists ( sourcemapPath ) ) ) {
75- throw new Error ( `No source map found for ${ fileBasename } ` ) ;
76- }
115+ const sourcemapName = `${ fileBasename } .map` ;
116+ const sourcemapPath = resolve ( sourceMapsDirectory , sourcemapName ) ;
77117
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- ) ;
118+ if ( ! ( await pathExists ( sourcemapPath ) ) ) {
119+ throw new Error ( `No source map found for ${ fileBasename } ` ) ;
120+ }
90121
91- if ( ! originalPosition . source ) {
92- throw new Error ( `No source found for ${ filenameAndLine } ` ) ;
93- }
122+ const rawSourceMap : RawSourceMap = await readJSON ( sourcemapPath ) ;
123+
124+ const originalPosition = await SourceMapConsumer . with (
125+ rawSourceMap ,
126+ null ,
127+ async function ( consumer ) {
128+ return consumer . originalPositionFor ( {
129+ line : parseInt ( line ) ,
130+ column : parseInt ( column ) ,
131+ } ) ;
132+ } ,
133+ ) ;
94134
95- const originalFilename = resolve ( filename , ".." , originalPosition . source ) ;
135+ if ( ! originalPosition . source ) {
136+ throw new Error ( `No source found for ${ stacktrace } ` ) ;
137+ }
96138
97- console . log (
98- `${ originalFilename } :${ originalPosition . line } :${ originalPosition . column } ` ,
99- ) ;
139+ const originalFilename = resolve ( filename , ".." , originalPosition . source ) ;
140+
141+ console . log (
142+ `${ originalFilename } :${ originalPosition . line } :${ originalPosition . column } ` ,
143+ ) ;
144+ }
100145}
101146
102147extractSourceMap ( ) . catch ( ( e : unknown ) => {
@@ -122,3 +167,18 @@ type WorkflowRunListItem = {
122167 databaseId : number ;
123168 number : number ;
124169} ;
170+
171+ async function replaceAsync (
172+ str : string ,
173+ regex : RegExp ,
174+ replacer : ( substring : string , ...args : any [ ] ) => Promise < string > ,
175+ ) {
176+ const promises : Array < Promise < string > > = [ ] ;
177+ str . replace ( regex , ( match , ...args ) => {
178+ const promise = replacer ( match , ...args ) ;
179+ promises . push ( promise ) ;
180+ return match ;
181+ } ) ;
182+ const data = await Promise . all ( promises ) ;
183+ return str . replace ( regex , ( ) => data . shift ( ) as string ) ;
184+ }
0 commit comments