1
1
/*
2
- MIT License http://www.opensource.org/licenses/mit-license.php
3
- Author Tobias Koppers @sokra
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
4
*/
5
5
import path from 'path' ;
6
6
7
- import { promisify } from 'util' ;
8
-
9
7
import validateOptions from 'schema-utils' ;
10
- import parseDataURL from 'data-urls' ;
11
-
12
- import { SourceMapConsumer } from 'source-map' ;
13
-
14
- import { labelToName , decode } from 'whatwg-encoding' ;
15
- import { getOptions , urlToRequest } from 'loader-utils' ;
8
+ import { getOptions } from 'loader-utils' ;
16
9
17
10
import schema from './options.json' ;
18
- import {
19
- flattenSourceMap ,
20
- readFile ,
21
- getContentFromSourcesContent ,
22
- getSourceMappingUrl ,
23
- getRequestedUrl ,
24
- } from './utils' ;
11
+ import { getSourceMappingURL , fetchFromURL , flattenSourceMap } from './utils' ;
25
12
26
13
export default async function loader ( input , inputMap ) {
27
14
const options = getOptions ( this ) ;
@@ -31,179 +18,117 @@ export default async function loader(input, inputMap) {
31
18
baseDataPath : 'options' ,
32
19
} ) ;
33
20
34
- let { url } = getSourceMappingUrl ( input ) ;
35
- const { replacementString } = getSourceMappingUrl ( input ) ;
21
+ const { sourceMappingURL, replacementString } = getSourceMappingURL ( input ) ;
36
22
const callback = this . async ( ) ;
37
23
38
- if ( ! url ) {
24
+ if ( ! sourceMappingURL ) {
39
25
callback ( null , input , inputMap ) ;
40
26
41
27
return ;
42
28
}
43
29
44
- const { fs, context, resolve, addDependency, emitWarning } = this ;
45
- const resolver = promisify ( resolve ) ;
46
- const reader = promisify ( fs . readFile ) . bind ( fs ) ;
30
+ let sourceURL ;
31
+ let sourceContent ;
47
32
48
- if ( url . toLowerCase ( ) . startsWith ( 'data:' ) ) {
49
- const dataURL = parseDataURL ( url ) ;
50
-
51
- if ( dataURL ) {
52
- let map ;
53
-
54
- try {
55
- dataURL . encodingName =
56
- labelToName ( dataURL . mimeType . parameters . get ( 'charset' ) ) || 'UTF-8' ;
57
-
58
- map = decode ( dataURL . body , dataURL . encodingName ) ;
59
- map = JSON . parse ( map . replace ( / ^ \) \] \} ' / , '' ) ) ;
60
- } catch ( error ) {
61
- emitWarning (
62
- `Cannot parse inline SourceMap with Charset ${ dataURL . encodingName } : ${ error } `
63
- ) ;
33
+ try {
34
+ ( { sourceURL, sourceContent } = await fetchFromURL (
35
+ this ,
36
+ this . context ,
37
+ sourceMappingURL
38
+ ) ) ;
39
+ } catch ( error ) {
40
+ this . emitWarning ( error ) ;
64
41
65
- callback ( null , input , inputMap ) ;
42
+ callback ( null , input , inputMap ) ;
66
43
67
- return ;
68
- }
44
+ return ;
45
+ }
69
46
70
- processMap ( map , context , callback ) ;
47
+ if ( sourceURL ) {
48
+ this . addDependency ( sourceURL ) ;
49
+ }
71
50
72
- return ;
73
- }
51
+ let map ;
74
52
75
- emitWarning ( `Cannot parse inline SourceMap: ${ url } ` ) ;
53
+ try {
54
+ map = JSON . parse ( sourceContent . replace ( / ^ \) \] \} ' / , '' ) ) ;
55
+ } catch ( parseError ) {
56
+ this . emitWarning (
57
+ new Error ( `Cannot parse source map from '${ sourceURL } ': ${ parseError } ` )
58
+ ) ;
76
59
77
60
callback ( null , input , inputMap ) ;
78
61
79
62
return ;
80
63
}
81
64
82
- try {
83
- url = getRequestedUrl ( url ) ;
84
- } catch ( error ) {
85
- emitWarning ( error . message ) ;
65
+ const context = sourceURL ? path . dirname ( sourceURL ) : this . context ;
86
66
87
- callback ( null , input , inputMap ) ;
88
-
89
- return ;
67
+ if ( map . sections ) {
68
+ // eslint-disable-next-line no-param-reassign
69
+ map = await flattenSourceMap ( map ) ;
90
70
}
91
71
92
- let urlResolved ;
72
+ const resolvedSources = await Promise . all (
73
+ map . sources . map ( async ( source , i ) => {
74
+ // eslint-disable-next-line no-shadow
75
+ let sourceURL ;
76
+ // eslint-disable-next-line no-shadow
77
+ let sourceContent ;
93
78
94
- try {
95
- urlResolved = await resolver ( context , urlToRequest ( url , true ) ) ;
96
- } catch ( resolveError ) {
97
- emitWarning ( `Cannot find SourceMap '${ url } ': ${ resolveError } ` ) ;
79
+ const originalSourceContent =
80
+ map . sourcesContent && map . sourcesContent [ i ]
81
+ ? map . sourcesContent [ i ]
82
+ : null ;
83
+ const skipReading = originalSourceContent !== null ;
98
84
99
- callback ( null , input , inputMap ) ;
85
+ try {
86
+ ( { sourceURL, sourceContent } = await fetchFromURL (
87
+ this ,
88
+ context ,
89
+ source ,
90
+ map . sourceRoot ,
91
+ skipReading
92
+ ) ) ;
93
+ } catch ( error ) {
94
+ this . emitWarning ( error ) ;
100
95
101
- return ;
102
- }
96
+ sourceURL = source ;
97
+ }
103
98
104
- urlResolved = urlResolved . toString ( ) ;
99
+ if ( originalSourceContent ) {
100
+ sourceContent = originalSourceContent ;
101
+ }
105
102
106
- addDependency ( urlResolved ) ;
103
+ if ( sourceURL ) {
104
+ this . addDependency ( sourceURL ) ;
105
+ }
107
106
108
- const content = await reader ( urlResolved ) ;
109
- let map ;
107
+ return { sourceURL, sourceContent } ;
108
+ } )
109
+ ) ;
110
110
111
- try {
112
- map = JSON . parse ( content . toString ( ) ) ;
113
- } catch ( parseError ) {
114
- emitWarning ( `Cannot parse SourceMap '${ url } ': ${ parseError } ` ) ;
111
+ const newMap = { ...map } ;
115
112
116
- callback ( null , input , inputMap ) ;
113
+ newMap . sources = [ ] ;
114
+ newMap . sourcesContent = [ ] ;
117
115
118
- return ;
119
- }
116
+ delete newMap . sourceRoot ;
120
117
121
- processMap ( map , path . dirname ( urlResolved ) , callback ) ;
122
-
123
- // eslint-disable-next-line no-shadow
124
- async function processMap ( map , context , callback ) {
125
- if ( map . sections ) {
126
- // eslint-disable-next-line no-param-reassign
127
- map = await flattenSourceMap ( map ) ;
128
- }
129
-
130
- const mapConsumer = await new SourceMapConsumer ( map ) ;
131
-
132
- let resolvedSources ;
133
-
134
- try {
135
- resolvedSources = await Promise . all (
136
- map . sources . map ( async ( source ) => {
137
- const fullPath = map . sourceRoot
138
- ? `${ map . sourceRoot } ${ path . sep } ${ source } `
139
- : source ;
140
-
141
- const originalData = getContentFromSourcesContent (
142
- mapConsumer ,
143
- source
144
- ) ;
145
-
146
- if ( path . isAbsolute ( fullPath ) ) {
147
- return originalData
148
- ? { source : fullPath , content : originalData }
149
- : readFile ( fullPath , emitWarning , reader ) ;
150
- }
151
-
152
- let fullPathResolved ;
153
-
154
- try {
155
- fullPathResolved = await resolver (
156
- context ,
157
- urlToRequest ( fullPath , true )
158
- ) ;
159
- } catch ( resolveError ) {
160
- emitWarning ( `Cannot find source file '${ source } ': ${ resolveError } ` ) ;
161
-
162
- return originalData
163
- ? {
164
- source : fullPath ,
165
- content : originalData ,
166
- }
167
- : { source : fullPath , content : null } ;
168
- }
169
-
170
- return originalData
171
- ? {
172
- source : fullPathResolved ,
173
- content : originalData ,
174
- }
175
- : readFile ( fullPathResolved , emitWarning , reader ) ;
176
- } )
177
- ) ;
178
- } catch ( error ) {
179
- emitWarning ( error ) ;
180
-
181
- callback ( null , input , inputMap ) ;
182
- }
183
-
184
- const resultMap = { ...map } ;
185
- resultMap . sources = [ ] ;
186
- resultMap . sourcesContent = [ ] ;
187
-
188
- delete resultMap . sourceRoot ;
189
-
190
- resolvedSources . forEach ( ( res ) => {
191
- // eslint-disable-next-line no-param-reassign
192
- resultMap . sources . push ( path . normalize ( res . source ) ) ;
193
- resultMap . sourcesContent . push ( res . content ) ;
194
-
195
- if ( res . source ) {
196
- addDependency ( res . source ) ;
197
- }
198
- } ) ;
118
+ resolvedSources . forEach ( ( source ) => {
119
+ // eslint-disable-next-line no-shadow
120
+ const { sourceURL, sourceContent } = source ;
199
121
200
- const sourcesContentIsEmpty =
201
- resultMap . sourcesContent . filter ( ( entry ) => ! ! entry ) . length === 0 ;
122
+ newMap . sources . push ( sourceURL || '' ) ;
123
+ newMap . sourcesContent . push ( sourceContent || '' ) ;
124
+ } ) ;
202
125
203
- if ( sourcesContentIsEmpty ) {
204
- delete resultMap . sourcesContent ;
205
- }
126
+ const sourcesContentIsEmpty =
127
+ newMap . sourcesContent . filter ( ( entry ) => Boolean ( entry ) ) . length === 0 ;
206
128
207
- callback ( null , input . replace ( replacementString , '' ) , resultMap ) ;
129
+ if ( sourcesContentIsEmpty ) {
130
+ delete newMap . sourcesContent ;
208
131
}
132
+
133
+ callback ( null , input . replace ( replacementString , '' ) , newMap ) ;
209
134
}
0 commit comments