3
3
*--------------------------------------------------------*/
4
4
5
5
import { inject , injectable } from 'inversify' ;
6
- import { RawIndexMap , RawSourceMap , SourceMapConsumer } from 'source-map' ;
6
+ import {
7
+ Position ,
8
+ RawIndexMap ,
9
+ RawSection ,
10
+ RawSourceMap ,
11
+ SourceMapConsumer ,
12
+ StartOfSourceMap ,
13
+ } from 'source-map' ;
7
14
import { IResourceProvider } from '../../adapter/resourceProvider' ;
8
15
import Dap from '../../dap/api' ;
9
16
import { IRootDapApi } from '../../dap/connection' ;
10
17
import { sourceMapParseFailed } from '../../dap/errors' ;
11
18
import { MapUsingProjection } from '../datastructure/mapUsingProjection' ;
12
19
import { IDisposable } from '../disposable' ;
13
20
import { ILogger , LogTag } from '../logging' ;
21
+ import { truthy } from '../objUtils' ;
14
22
import { ISourcePathResolver } from '../sourcePathResolver' ;
15
23
import { fileUrlToAbsolutePath , isDataUri } from '../urlUtils' ;
16
24
import { ISourceMapMetadata , SourceMap } from './sourceMap' ;
@@ -35,6 +43,20 @@ export interface ISourceMapFactory extends IDisposable {
35
43
guardSourceMapFn < T > ( sourceMap : SourceMap , fn : ( ) => T , defaultValue : ( ) => T ) : T ;
36
44
}
37
45
46
+ interface RawExternalSection {
47
+ offset : Position ;
48
+ url : string ;
49
+ }
50
+
51
+ /**
52
+ * The typings for source-map don't support this, but the spec does.
53
+ * @see https://sourcemaps.info/spec.html#h.535es3xeprgt
54
+ */
55
+ export interface RawIndexMapUnresolved extends StartOfSourceMap {
56
+ version : number ;
57
+ sections : ( RawExternalSection | RawSection ) [ ] ;
58
+ }
59
+
38
60
/**
39
61
* Base implementation of the ISourceMapFactory.
40
62
*/
@@ -57,15 +79,7 @@ export class SourceMapFactory implements ISourceMapFactory {
57
79
* @inheritdoc
58
80
*/
59
81
public async load ( metadata : ISourceMapMetadata ) : Promise < SourceMap > {
60
- let basic : RawSourceMap | RawIndexMap | undefined ;
61
- try {
62
- basic = await this . parseSourceMap ( metadata . sourceMapUrl ) ;
63
- } catch ( e ) {
64
- basic = await this . parsePathMappedSourceMap ( metadata . sourceMapUrl ) ;
65
- if ( ! basic ) {
66
- throw e ;
67
- }
68
- }
82
+ const basic = await this . parseSourceMap ( metadata . sourceMapUrl ) ;
69
83
70
84
// The source-map library is destructive with its sources parsing. If the
71
85
// source root is '/', it'll "helpfully" resolve a source like `../foo.ts`
@@ -81,7 +95,7 @@ export class SourceMapFactory implements ISourceMapFactory {
81
95
// preserve them in the same way. Then, rename the sources to prevent any
82
96
// of their names colliding (e.g. "webpack://./index.js" and "webpack://../index.js")
83
97
let actualSources : string [ ] = [ ] ;
84
- if ( 'sections' in basic && Array . isArray ( basic . sections ) ) {
98
+ if ( 'sections' in basic ) {
85
99
actualSources = [ ] ;
86
100
let i = 0 ;
87
101
for ( const section of basic . sections ) {
@@ -104,6 +118,37 @@ export class SourceMapFactory implements ISourceMapFactory {
104
118
) ;
105
119
}
106
120
121
+ private async parseSourceMap ( sourceMapUrl : string ) : Promise < RawSourceMap | RawIndexMap > {
122
+ let sm : RawSourceMap | RawIndexMapUnresolved | undefined ;
123
+ try {
124
+ sm = await this . parseSourceMapDirect ( sourceMapUrl ) ;
125
+ } catch ( e ) {
126
+ sm = await this . parsePathMappedSourceMap ( sourceMapUrl ) ;
127
+ if ( ! sm ) {
128
+ throw e ;
129
+ }
130
+ }
131
+
132
+ if ( 'sections' in sm ) {
133
+ const resolved = await Promise . all (
134
+ sm . sections . map ( ( s , i ) =>
135
+ 'url' in s
136
+ ? this . parseSourceMap ( s . url )
137
+ . then ( map => ( { offset : s . offset , map : map as RawSourceMap } ) )
138
+ . catch ( e => {
139
+ this . logger . warn ( LogTag . SourceMapParsing , `Error parsing nested map ${ i } : ${ e } ` ) ;
140
+ return undefined ;
141
+ } )
142
+ : s ,
143
+ ) ,
144
+ ) ;
145
+
146
+ sm . sections = resolved . filter ( truthy ) ;
147
+ }
148
+
149
+ return sm as RawSourceMap | RawIndexMap ;
150
+ }
151
+
107
152
public async parsePathMappedSourceMap ( url : string ) {
108
153
if ( isDataUri ( url ) ) {
109
154
return ;
@@ -113,7 +158,7 @@ export class SourceMapFactory implements ISourceMapFactory {
113
158
if ( ! localSourceMapUrl ) return ;
114
159
115
160
try {
116
- return this . parseSourceMap ( localSourceMapUrl ) ;
161
+ return this . parseSourceMapDirect ( localSourceMapUrl ) ;
117
162
} catch ( error ) {
118
163
this . logger . info ( LogTag . SourceMapParsing , 'Parsing path mapped source map failed.' , error ) ;
119
164
}
@@ -150,7 +195,9 @@ export class SourceMapFactory implements ISourceMapFactory {
150
195
// no-op
151
196
}
152
197
153
- private async parseSourceMap ( sourceMapUrl : string ) : Promise < RawSourceMap | RawIndexMap > {
198
+ private async parseSourceMapDirect (
199
+ sourceMapUrl : string ,
200
+ ) : Promise < RawSourceMap | RawIndexMapUnresolved > {
154
201
let absolutePath = fileUrlToAbsolutePath ( sourceMapUrl ) ;
155
202
if ( absolutePath ) {
156
203
absolutePath = this . pathResolve . rebaseRemoteToLocal ( absolutePath ) ;
0 commit comments