1 var sourceMappingURL = require("source-map-url")
3 var resolveUrl = require("./resolve-url")
4 var decodeUriComponent = require("./decode-uri-component")
5 var urix = require("urix")
6 var atob = require("atob")
10 function callbackAsync(callback, error, result) {
11 setImmediate(function() { callback(error, result) })
14 function parseMapToJSON(string, data) {
16 return JSON.parse(string.replace(/^\)\]\}'/, ""))
18 error.sourceMapData = data
23 function readSync(read, url, data) {
24 var readUrl = decodeUriComponent(url)
26 return String(read(readUrl))
28 error.sourceMapData = data
35 function resolveSourceMap(code, codeUrl, read, callback) {
38 mapData = resolveSourceMapHelper(code, codeUrl)
40 return callbackAsync(callback, error)
42 if (!mapData || mapData.map) {
43 return callbackAsync(callback, null, mapData)
45 var readUrl = decodeUriComponent(mapData.url)
46 read(readUrl, function(error, result) {
48 error.sourceMapData = mapData
49 return callback(error)
51 mapData.map = String(result)
53 mapData.map = parseMapToJSON(mapData.map, mapData)
55 return callback(error)
57 callback(null, mapData)
61 function resolveSourceMapSync(code, codeUrl, read) {
62 var mapData = resolveSourceMapHelper(code, codeUrl)
63 if (!mapData || mapData.map) {
66 mapData.map = readSync(read, mapData.url, mapData)
67 mapData.map = parseMapToJSON(mapData.map, mapData)
71 var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
74 * The media type for JSON text is application/json.
76 * {@link https://tools.ietf.org/html/rfc8259#section-11 | IANA Considerations }
78 * `text/json` is non-standard media type
80 var jsonMimeTypeRegex = /^(?:application|text)\/json$/
83 * JSON text exchanged between systems that are not part of a closed ecosystem
84 * MUST be encoded using UTF-8.
86 * {@link https://tools.ietf.org/html/rfc8259#section-8.1 | Character Encoding}
88 var jsonCharacterEncoding = "utf-8"
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)
100 function decodeBase64String(b64) {
101 if (typeof TextDecoder === "undefined" || typeof Uint8Array === "undefined") {
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);
111 function resolveSourceMapHelper(code, codeUrl) {
112 codeUrl = urix(codeUrl)
114 var url = sourceMappingURL.getFrom(code)
119 var dataUri = url.match(dataUriRegex)
121 var mimeType = dataUri[1] || "text/plain"
122 var lastParameter = dataUri[2] || ""
123 var encoded = dataUri[3] || ""
125 sourceMappingURL: url,
127 sourcesRelativeTo: codeUrl,
130 if (!jsonMimeTypeRegex.test(mimeType)) {
131 var error = new Error("Unuseful data uri mime type: " + mimeType)
132 error.sourceMapData = data
136 data.map = parseMapToJSON(
137 lastParameter === ";base64" ? decodeBase64String(encoded) : decodeURIComponent(encoded),
141 error.sourceMapData = data
147 var mapUrl = resolveUrl(codeUrl, url)
149 sourceMappingURL: url,
151 sourcesRelativeTo: mapUrl,
158 function resolveSources(map, mapUrl, read, options, callback) {
159 if (typeof options === "function") {
163 var pending = map.sources ? map.sources.length : 0
170 callbackAsync(callback, null, result)
174 var done = function() {
177 callback(null, result)
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)
187 var readUrl = decodeUriComponent(fullUrl)
188 read(readUrl, function(error, source) {
189 result.sourcesContent[index] = error ? error : String(source)
196 function resolveSourcesSync(map, mapUrl, read, options) {
202 if (!map.sources || map.sources.length === 0) {
206 resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
207 result.sourcesResolved[index] = fullUrl
209 if (typeof sourceContent === "string") {
210 result.sourcesContent[index] = sourceContent
212 var readUrl = decodeUriComponent(fullUrl)
214 result.sourcesContent[index] = String(read(readUrl))
216 result.sourcesContent[index] = error
225 var endingSlash = /\/?$/
227 function resolveSourcesHelper(map, mapUrl, options, fn) {
228 options = options || {}
229 mapUrl = urix(mapUrl)
233 for (var index = 0, len = map.sources.length; index < len; index++) {
235 if (typeof options.sourceRoot === "string") {
236 sourceRoot = options.sourceRoot
237 } else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) {
238 sourceRoot = map.sourceRoot
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])
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])
250 sourceContent = (map.sourcesContent || [])[index]
251 fn(fullUrl, sourceContent, index)
257 function resolve(code, codeUrl, read, options, callback) {
258 if (typeof options === "function") {
265 sourceMappingURL: null,
267 sourcesRelativeTo: mapUrl,
270 var readUrl = decodeUriComponent(mapUrl)
271 read(readUrl, function(error, result) {
273 error.sourceMapData = data
274 return callback(error)
276 data.map = String(result)
278 data.map = parseMapToJSON(data.map, data)
280 return callback(error)
282 _resolveSources(data)
285 resolveSourceMap(code, codeUrl, read, function(error, mapData) {
287 return callback(error)
290 return callback(null, null)
292 _resolveSources(mapData)
296 function _resolveSources(mapData) {
297 resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) {
299 return callback(error)
301 mapData.sourcesResolved = result.sourcesResolved
302 mapData.sourcesContent = result.sourcesContent
303 callback(null, mapData)
308 function resolveSync(code, codeUrl, read, options) {
313 sourceMappingURL: null,
315 sourcesRelativeTo: mapUrl,
318 mapData.map = readSync(read, mapUrl, mapData)
319 mapData.map = parseMapToJSON(mapData.map, mapData)
321 mapData = resolveSourceMapSync(code, codeUrl, read)
326 var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options)
327 mapData.sourcesResolved = result.sourcesResolved
328 mapData.sourcesContent = result.sourcesContent
335 resolveSourceMap: resolveSourceMap,
336 resolveSourceMapSync: resolveSourceMapSync,
337 resolveSources: resolveSources,
338 resolveSourcesSync: resolveSourcesSync,
340 resolveSync: resolveSync,
341 parseMapToJSON: parseMapToJSON