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 }));