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