@@ -7,6 +7,7 @@ import {isMatch} from 'micromatch';
7
7
import { join , posix } from 'path' ;
8
8
import { pathToFileURL , URL } from 'url' ;
9
9
import v8ToIstanbul from 'v8-to-istanbul' ;
10
+ import * as convertSourceMap from 'convert-source-map' ;
10
11
11
12
export const attachmentName = '@bgotink/playwright-coverage' ;
12
13
@@ -44,55 +45,62 @@ export async function getSourceMap(
44
45
url : string ,
45
46
source : string ,
46
47
) : Promise < EncodedSourceMap | undefined > {
47
- const match = source . match ( / \/ \/ # * s o u r c e M a p p i n g U R L = ( .* ) / ) ;
48
-
49
- if ( match == null ) {
50
- try {
51
- const response = await (
52
- await fetch
53
- ) . default ( `${ url } .map` , {
54
- method : 'GET' ,
55
- } ) ;
56
-
57
- return ( await response . json ( ) ) as EncodedSourceMap ;
58
- } catch {
59
- return undefined ;
60
- }
48
+ const inlineMap = convertSourceMap . fromSource ( source ) ;
49
+ if ( inlineMap != null ) {
50
+ return inlineMap . sourcemap ;
61
51
}
62
52
63
- const resolved = new URL ( match [ 1 ] ! , url ) ;
64
-
65
53
try {
66
- switch ( resolved . protocol ) {
67
- case 'file:' :
68
- return JSON . parse ( await fs . readFile ( resolved , 'utf8' ) ) ;
69
- case 'data:' : {
70
- if ( ! / ^ a p p l i c a t i o n \/ j s o n [ , ; ] / . test ( resolved . pathname ) ) {
71
- return undefined ;
54
+ const linkedMap = await convertSourceMap . fromMapFileSource (
55
+ source ,
56
+ async ( file : string ) : Promise < string > => {
57
+ const resolved = new URL ( file , url ) ;
58
+
59
+ switch ( resolved . protocol ) {
60
+ case 'file:' :
61
+ return await fs . readFile ( resolved , 'utf8' ) ;
62
+ case 'data:' : {
63
+ const comma = resolved . pathname . indexOf ( ',' ) ;
64
+ const rawData = resolved . pathname . slice ( comma + 1 ) ;
65
+ const between = resolved . pathname
66
+ . slice ( 'application/json;' . length , comma )
67
+ . split ( ';' ) ;
68
+
69
+ const dataString = between . includes ( 'base64' )
70
+ ? Buffer . from ( rawData , 'base64url' ) . toString ( 'utf8' )
71
+ : rawData ;
72
+
73
+ return dataString ;
74
+ }
75
+ default : {
76
+ const response = await (
77
+ await fetch
78
+ ) . default ( resolved . href , {
79
+ method : 'GET' ,
80
+ } ) ;
81
+
82
+ return await response . text ( ) ;
83
+ }
72
84
}
85
+ } ,
86
+ ) ;
73
87
74
- const comma = resolved . pathname . indexOf ( ',' ) ;
75
- const rawData = resolved . pathname . slice ( comma + 1 ) ;
76
- const between = resolved . pathname
77
- . slice ( 'application/json;' . length , comma )
78
- . split ( ';' ) ;
88
+ if ( linkedMap != null ) {
89
+ return linkedMap . sourcemap ;
90
+ }
91
+ } catch {
92
+ return null ! ;
93
+ }
79
94
80
- const dataString = between . includes ( 'base64' )
81
- ? Buffer . from ( rawData , 'base64url' ) . toString ( 'utf8' )
82
- : rawData ;
95
+ // No source map comments, try to find an implicit sourcemap at `${url}.map`
96
+ try {
97
+ const response = await (
98
+ await fetch
99
+ ) . default ( `${ url } .map` , {
100
+ method : 'GET' ,
101
+ } ) ;
83
102
84
- return JSON . parse ( dataString ) ;
85
- }
86
- default : {
87
- const response = await (
88
- await fetch
89
- ) . default ( resolved . href , {
90
- method : 'GET' ,
91
- } ) ;
92
-
93
- return ( await response . json ( ) ) as EncodedSourceMap ;
94
- }
95
- }
103
+ return ( await response . json ( ) ) as EncodedSourceMap ;
96
104
} catch {
97
105
return undefined ;
98
106
}
@@ -163,10 +171,16 @@ export async function convertToIstanbulCoverage(
163
171
// This path is used to resolve files, but the filename doesn't matter
164
172
join ( sourceRoot , 'unused.js' ) ,
165
173
0 ,
166
- {
167
- source,
168
- sourceMap : { sourcemap : sourceMap } ,
169
- } ,
174
+ sourceMap ?. mappings
175
+ ? {
176
+ source,
177
+ sourceMap : { sourcemap : sourceMap } ,
178
+ }
179
+ : {
180
+ source : convertSourceMap . removeMapFileComments (
181
+ convertSourceMap . removeComments ( source ) ,
182
+ ) ,
183
+ } ,
170
184
path => {
171
185
let isExcluded = isExcludedCache . get ( path ) ;
172
186
0 commit comments