source-map-resolve-node.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Copyright 2014 Simon Lydell
  2. // X11 (“MIT”) Licensed. (See LICENSE.)
  3. var sourceMappingURL = require("source-map-url")
  4. var resolveUrl = require("./resolve-url")
  5. var urix = require("urix")
  6. var atob = require("atob")
  7. function callbackAsync(callback, error, result) {
  8. setImmediate(function() { callback(error, result) })
  9. }
  10. function parseMapToJSON(string) {
  11. return JSON.parse(string.replace(/^\)\]\}'/, ""))
  12. }
  13. function resolveSourceMap(code, codeUrl, read, callback) {
  14. var mapData
  15. try {
  16. mapData = resolveSourceMapHelper(code, codeUrl)
  17. } catch (error) {
  18. return callbackAsync(callback, error)
  19. }
  20. if (!mapData || mapData.map) {
  21. return callbackAsync(callback, null, mapData)
  22. }
  23. read(mapData.url, function(error, result) {
  24. if (error) {
  25. return callback(error)
  26. }
  27. try {
  28. mapData.map = parseMapToJSON(String(result))
  29. } catch (error) {
  30. return callback(error)
  31. }
  32. callback(null, mapData)
  33. })
  34. }
  35. function resolveSourceMapSync(code, codeUrl, read) {
  36. var mapData = resolveSourceMapHelper(code, codeUrl)
  37. if (!mapData || mapData.map) {
  38. return mapData
  39. }
  40. mapData.map = parseMapToJSON(String(read(mapData.url)))
  41. return mapData
  42. }
  43. var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
  44. var jsonMimeTypeRegex = /^(?:application|text)\/json$/
  45. function resolveSourceMapHelper(code, codeUrl) {
  46. codeUrl = urix(codeUrl)
  47. var url = sourceMappingURL.getFrom(code)
  48. if (!url) {
  49. return null
  50. }
  51. var dataUri = url.match(dataUriRegex)
  52. if (dataUri) {
  53. var mimeType = dataUri[1]
  54. var lastParameter = dataUri[2]
  55. var encoded = dataUri[3]
  56. if (!jsonMimeTypeRegex.test(mimeType)) {
  57. throw new Error("Unuseful data uri mime type: " + (mimeType || "text/plain"))
  58. }
  59. return {
  60. sourceMappingURL: url,
  61. url: null,
  62. sourcesRelativeTo: codeUrl,
  63. map: parseMapToJSON(lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded))
  64. }
  65. }
  66. var mapUrl = resolveUrl(codeUrl, url)
  67. return {
  68. sourceMappingURL: url,
  69. url: mapUrl,
  70. sourcesRelativeTo: mapUrl,
  71. map: null
  72. }
  73. }
  74. function resolveSources(map, mapUrl, read, options, callback) {
  75. if (typeof options === "function") {
  76. callback = options
  77. options = {}
  78. }
  79. var pending = map.sources.length
  80. var errored = false
  81. var result = {
  82. sourcesResolved: [],
  83. sourcesContent: []
  84. }
  85. var done = function(error) {
  86. if (errored) {
  87. return
  88. }
  89. if (error) {
  90. errored = true
  91. return callback(error)
  92. }
  93. pending--
  94. if (pending === 0) {
  95. callback(null, result)
  96. }
  97. }
  98. resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
  99. result.sourcesResolved[index] = fullUrl
  100. if (typeof sourceContent === "string") {
  101. result.sourcesContent[index] = sourceContent
  102. callbackAsync(done, null)
  103. } else {
  104. read(fullUrl, function(error, source) {
  105. result.sourcesContent[index] = String(source)
  106. done(error)
  107. })
  108. }
  109. })
  110. }
  111. function resolveSourcesSync(map, mapUrl, read, options) {
  112. var result = {
  113. sourcesResolved: [],
  114. sourcesContent: []
  115. }
  116. resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
  117. result.sourcesResolved[index] = fullUrl
  118. if (read !== null) {
  119. if (typeof sourceContent === "string") {
  120. result.sourcesContent[index] = sourceContent
  121. } else {
  122. result.sourcesContent[index] = String(read(fullUrl))
  123. }
  124. }
  125. })
  126. return result
  127. }
  128. var endingSlash = /\/?$/
  129. function resolveSourcesHelper(map, mapUrl, options, fn) {
  130. options = options || {}
  131. mapUrl = urix(mapUrl)
  132. var fullUrl
  133. var sourceContent
  134. for (var index = 0, len = map.sources.length; index < len; index++) {
  135. if (map.sourceRoot && !options.ignoreSourceRoot) {
  136. // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes
  137. // `/scripts/subdir/<source>`, not `/scripts/<source>`. Pointing to a file as source root
  138. // does not make sense.
  139. fullUrl = resolveUrl(mapUrl, map.sourceRoot.replace(endingSlash, "/"), map.sources[index])
  140. } else {
  141. fullUrl = resolveUrl(mapUrl, map.sources[index])
  142. }
  143. sourceContent = (map.sourcesContent || [])[index]
  144. fn(fullUrl, sourceContent, index)
  145. }
  146. }
  147. function resolve(code, codeUrl, read, options, callback) {
  148. if (typeof options === "function") {
  149. callback = options
  150. options = {}
  151. }
  152. resolveSourceMap(code, codeUrl, read, function(error, mapData) {
  153. if (error) {
  154. return callback(error)
  155. }
  156. if (!mapData) {
  157. return callback(null, null)
  158. }
  159. resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) {
  160. if (error) {
  161. return callback(error)
  162. }
  163. mapData.sourcesResolved = result.sourcesResolved
  164. mapData.sourcesContent = result.sourcesContent
  165. callback(null, mapData)
  166. })
  167. })
  168. }
  169. function resolveSync(code, codeUrl, read, options) {
  170. var mapData = resolveSourceMapSync(code, codeUrl, read)
  171. if (!mapData) {
  172. return null
  173. }
  174. var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options)
  175. mapData.sourcesResolved = result.sourcesResolved
  176. mapData.sourcesContent = result.sourcesContent
  177. return mapData
  178. }
  179. module.exports = {
  180. resolveSourceMap: resolveSourceMap,
  181. resolveSourceMapSync: resolveSourceMapSync,
  182. resolveSources: resolveSources,
  183. resolveSourcesSync: resolveSourcesSync,
  184. resolve: resolve,
  185. resolveSync: resolveSync
  186. }