1
|
var sourceMappingURL = require("source-map-url")
|
2
|
|
3
|
var resolveUrl = require("./resolve-url")
|
4
|
var decodeUriComponent = require("./decode-uri-component")
|
5
|
var urix = require("urix")
|
6
|
var atob = require("atob")
|
7
|
|
8
|
|
9
|
|
10
|
function callbackAsync(callback, error, result) {
|
11
|
setImmediate(function() { callback(error, result) })
|
12
|
}
|
13
|
|
14
|
function parseMapToJSON(string, data) {
|
15
|
try {
|
16
|
return JSON.parse(string.replace(/^\)\]\}'/, ""))
|
17
|
} catch (error) {
|
18
|
error.sourceMapData = data
|
19
|
throw error
|
20
|
}
|
21
|
}
|
22
|
|
23
|
function readSync(read, url, data) {
|
24
|
var readUrl = decodeUriComponent(url)
|
25
|
try {
|
26
|
return String(read(readUrl))
|
27
|
} catch (error) {
|
28
|
error.sourceMapData = data
|
29
|
throw error
|
30
|
}
|
31
|
}
|
32
|
|
33
|
|
34
|
|
35
|
function resolveSourceMap(code, codeUrl, read, callback) {
|
36
|
var mapData
|
37
|
try {
|
38
|
mapData = resolveSourceMapHelper(code, codeUrl)
|
39
|
} catch (error) {
|
40
|
return callbackAsync(callback, error)
|
41
|
}
|
42
|
if (!mapData || mapData.map) {
|
43
|
return callbackAsync(callback, null, mapData)
|
44
|
}
|
45
|
var readUrl = decodeUriComponent(mapData.url)
|
46
|
read(readUrl, function(error, result) {
|
47
|
if (error) {
|
48
|
error.sourceMapData = mapData
|
49
|
return callback(error)
|
50
|
}
|
51
|
mapData.map = String(result)
|
52
|
try {
|
53
|
mapData.map = parseMapToJSON(mapData.map, mapData)
|
54
|
} catch (error) {
|
55
|
return callback(error)
|
56
|
}
|
57
|
callback(null, mapData)
|
58
|
})
|
59
|
}
|
60
|
|
61
|
function resolveSourceMapSync(code, codeUrl, read) {
|
62
|
var mapData = resolveSourceMapHelper(code, codeUrl)
|
63
|
if (!mapData || mapData.map) {
|
64
|
return mapData
|
65
|
}
|
66
|
mapData.map = readSync(read, mapData.url, mapData)
|
67
|
mapData.map = parseMapToJSON(mapData.map, mapData)
|
68
|
return mapData
|
69
|
}
|
70
|
|
71
|
var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
|
72
|
|
73
|
/**
|
74
|
* The media type for JSON text is application/json.
|
75
|
*
|
76
|
* {@link https://tools.ietf.org/html/rfc8259#section-11 | IANA Considerations }
|
77
|
*
|
78
|
* `text/json` is non-standard media type
|
79
|
*/
|
80
|
var jsonMimeTypeRegex = /^(?:application|text)\/json$/
|
81
|
|
82
|
/**
|
83
|
* JSON text exchanged between systems that are not part of a closed ecosystem
|
84
|
* MUST be encoded using UTF-8.
|
85
|
*
|
86
|
* {@link https://tools.ietf.org/html/rfc8259#section-8.1 | Character Encoding}
|
87
|
*/
|
88
|
var jsonCharacterEncoding = "utf-8"
|
89
|
|
90
|
function base64ToBuf(b64) {
|
91
|
var binStr = atob(b64)
|
92
|
var len = binStr.length
|
93
|
var arr = new Uint8Array(len)
|
94
|
for (var i = 0; i < len; i++) {
|
95
|
arr[i] = binStr.charCodeAt(i)
|
96
|
}
|
97
|
return arr
|
98
|
}
|
99
|
|
100
|
function decodeBase64String(b64) {
|
101
|
if (typeof TextDecoder === "undefined" || typeof Uint8Array === "undefined") {
|
102
|
return atob(b64)
|
103
|
}
|
104
|
var buf = base64ToBuf(b64);
|
105
|
// Note: `decoder.decode` method will throw a `DOMException` with the
|
106
|
// `"EncodingError"` value when an coding error is found.
|
107
|
var decoder = new TextDecoder(jsonCharacterEncoding, {fatal: true})
|
108
|
return decoder.decode(buf);
|
109
|
}
|
110
|
|
111
|
function resolveSourceMapHelper(code, codeUrl) {
|
112
|
codeUrl = urix(codeUrl)
|
113
|
|
114
|
var url = sourceMappingURL.getFrom(code)
|
115
|
if (!url) {
|
116
|
return null
|
117
|
}
|
118
|
|
119
|
var dataUri = url.match(dataUriRegex)
|
120
|
if (dataUri) {
|
121
|
var mimeType = dataUri[1] || "text/plain"
|
122
|
var lastParameter = dataUri[2] || ""
|
123
|
var encoded = dataUri[3] || ""
|
124
|
var data = {
|
125
|
sourceMappingURL: url,
|
126
|
url: null,
|
127
|
sourcesRelativeTo: codeUrl,
|
128
|
map: encoded
|
129
|
}
|
130
|
if (!jsonMimeTypeRegex.test(mimeType)) {
|
131
|
var error = new Error("Unuseful data uri mime type: " + mimeType)
|
132
|
error.sourceMapData = data
|
133
|
throw error
|
134
|
}
|
135
|
try {
|
136
|
data.map = parseMapToJSON(
|
137
|
lastParameter === ";base64" ? decodeBase64String(encoded) : decodeURIComponent(encoded),
|
138
|
data
|
139
|
)
|
140
|
} catch (error) {
|
141
|
error.sourceMapData = data
|
142
|
throw error
|
143
|
}
|
144
|
return data
|
145
|
}
|
146
|
|
147
|
var mapUrl = resolveUrl(codeUrl, url)
|
148
|
return {
|
149
|
sourceMappingURL: url,
|
150
|
url: mapUrl,
|
151
|
sourcesRelativeTo: mapUrl,
|
152
|
map: null
|
153
|
}
|
154
|
}
|
155
|
|
156
|
|
157
|
|
158
|
function resolveSources(map, mapUrl, read, options, callback) {
|
159
|
if (typeof options === "function") {
|
160
|
callback = options
|
161
|
options = {}
|
162
|
}
|
163
|
var pending = map.sources ? map.sources.length : 0
|
164
|
var result = {
|
165
|
sourcesResolved: [],
|
166
|
sourcesContent: []
|
167
|
}
|
168
|
|
169
|
if (pending === 0) {
|
170
|
callbackAsync(callback, null, result)
|
171
|
return
|
172
|
}
|
173
|
|
174
|
var done = function() {
|
175
|
pending--
|
176
|
if (pending === 0) {
|
177
|
callback(null, result)
|
178
|
}
|
179
|
}
|
180
|
|
181
|
resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
|
182
|
result.sourcesResolved[index] = fullUrl
|
183
|
if (typeof sourceContent === "string") {
|
184
|
result.sourcesContent[index] = sourceContent
|
185
|
callbackAsync(done, null)
|
186
|
} else {
|
187
|
var readUrl = decodeUriComponent(fullUrl)
|
188
|
read(readUrl, function(error, source) {
|
189
|
result.sourcesContent[index] = error ? error : String(source)
|
190
|
done()
|
191
|
})
|
192
|
}
|
193
|
})
|
194
|
}
|
195
|
|
196
|
function resolveSourcesSync(map, mapUrl, read, options) {
|
197
|
var result = {
|
198
|
sourcesResolved: [],
|
199
|
sourcesContent: []
|
200
|
}
|
201
|
|
202
|
if (!map.sources || map.sources.length === 0) {
|
203
|
return result
|
204
|
}
|
205
|
|
206
|
resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
|
207
|
result.sourcesResolved[index] = fullUrl
|
208
|
if (read !== null) {
|
209
|
if (typeof sourceContent === "string") {
|
210
|
result.sourcesContent[index] = sourceContent
|
211
|
} else {
|
212
|
var readUrl = decodeUriComponent(fullUrl)
|
213
|
try {
|
214
|
result.sourcesContent[index] = String(read(readUrl))
|
215
|
} catch (error) {
|
216
|
result.sourcesContent[index] = error
|
217
|
}
|
218
|
}
|
219
|
}
|
220
|
})
|
221
|
|
222
|
return result
|
223
|
}
|
224
|
|
225
|
var endingSlash = /\/?$/
|
226
|
|
227
|
function resolveSourcesHelper(map, mapUrl, options, fn) {
|
228
|
options = options || {}
|
229
|
mapUrl = urix(mapUrl)
|
230
|
var fullUrl
|
231
|
var sourceContent
|
232
|
var sourceRoot
|
233
|
for (var index = 0, len = map.sources.length; index < len; index++) {
|
234
|
sourceRoot = null
|
235
|
if (typeof options.sourceRoot === "string") {
|
236
|
sourceRoot = options.sourceRoot
|
237
|
} else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) {
|
238
|
sourceRoot = map.sourceRoot
|
239
|
}
|
240
|
// If the sourceRoot is the empty string, it is equivalent to not setting
|
241
|
// the property at all.
|
242
|
if (sourceRoot === null || sourceRoot === '') {
|
243
|
fullUrl = resolveUrl(mapUrl, map.sources[index])
|
244
|
} else {
|
245
|
// Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes
|
246
|
// `/scripts/subdir/<source>`, not `/scripts/<source>`. Pointing to a file as source root
|
247
|
// does not make sense.
|
248
|
fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index])
|
249
|
}
|
250
|
sourceContent = (map.sourcesContent || [])[index]
|
251
|
fn(fullUrl, sourceContent, index)
|
252
|
}
|
253
|
}
|
254
|
|
255
|
|
256
|
|
257
|
function resolve(code, codeUrl, read, options, callback) {
|
258
|
if (typeof options === "function") {
|
259
|
callback = options
|
260
|
options = {}
|
261
|
}
|
262
|
if (code === null) {
|
263
|
var mapUrl = codeUrl
|
264
|
var data = {
|
265
|
sourceMappingURL: null,
|
266
|
url: mapUrl,
|
267
|
sourcesRelativeTo: mapUrl,
|
268
|
map: null
|
269
|
}
|
270
|
var readUrl = decodeUriComponent(mapUrl)
|
271
|
read(readUrl, function(error, result) {
|
272
|
if (error) {
|
273
|
error.sourceMapData = data
|
274
|
return callback(error)
|
275
|
}
|
276
|
data.map = String(result)
|
277
|
try {
|
278
|
data.map = parseMapToJSON(data.map, data)
|
279
|
} catch (error) {
|
280
|
return callback(error)
|
281
|
}
|
282
|
_resolveSources(data)
|
283
|
})
|
284
|
} else {
|
285
|
resolveSourceMap(code, codeUrl, read, function(error, mapData) {
|
286
|
if (error) {
|
287
|
return callback(error)
|
288
|
}
|
289
|
if (!mapData) {
|
290
|
return callback(null, null)
|
291
|
}
|
292
|
_resolveSources(mapData)
|
293
|
})
|
294
|
}
|
295
|
|
296
|
function _resolveSources(mapData) {
|
297
|
resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) {
|
298
|
if (error) {
|
299
|
return callback(error)
|
300
|
}
|
301
|
mapData.sourcesResolved = result.sourcesResolved
|
302
|
mapData.sourcesContent = result.sourcesContent
|
303
|
callback(null, mapData)
|
304
|
})
|
305
|
}
|
306
|
}
|
307
|
|
308
|
function resolveSync(code, codeUrl, read, options) {
|
309
|
var mapData
|
310
|
if (code === null) {
|
311
|
var mapUrl = codeUrl
|
312
|
mapData = {
|
313
|
sourceMappingURL: null,
|
314
|
url: mapUrl,
|
315
|
sourcesRelativeTo: mapUrl,
|
316
|
map: null
|
317
|
}
|
318
|
mapData.map = readSync(read, mapUrl, mapData)
|
319
|
mapData.map = parseMapToJSON(mapData.map, mapData)
|
320
|
} else {
|
321
|
mapData = resolveSourceMapSync(code, codeUrl, read)
|
322
|
if (!mapData) {
|
323
|
return null
|
324
|
}
|
325
|
}
|
326
|
var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options)
|
327
|
mapData.sourcesResolved = result.sourcesResolved
|
328
|
mapData.sourcesContent = result.sourcesContent
|
329
|
return mapData
|
330
|
}
|
331
|
|
332
|
|
333
|
|
334
|
module.exports = {
|
335
|
resolveSourceMap: resolveSourceMap,
|
336
|
resolveSourceMapSync: resolveSourceMapSync,
|
337
|
resolveSources: resolveSources,
|
338
|
resolveSourcesSync: resolveSourcesSync,
|
339
|
resolve: resolve,
|
340
|
resolveSync: resolveSync,
|
341
|
parseMapToJSON: parseMapToJSON
|
342
|
}
|