source-map-resolve-node.js 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2014, 2015, 2016, 2017 Simon Lydell
  2. // X11 (“MIT”) Licensed. (See LICENSE.)
  3. var sourceMappingURL = require("source-map-url")
  4. var resolveUrl = require("./resolve-url")
  5. var decodeUriComponent = require("./decode-uri-component")
  6. var urix = require("urix")
  7. var atob = require("atob")
  8. function callbackAsync(callback, error, result) {
  9. setImmediate(function() { callback(error, result) })
  10. }
  11. function parseMapToJSON(string, data) {
  12. try {
  13. return JSON.parse(string.replace(/^\)\]\}'/, ""))
  14. } catch (error) {
  15. error.sourceMapData = data
  16. throw error
  17. }
  18. }
  19. function readSync(read, url, data) {
  20. var readUrl = decodeUriComponent(url)
  21. try {
  22. return String(read(readUrl))
  23. } catch (error) {
  24. error.sourceMapData = data
  25. throw error
  26. }
  27. }
  28. function resolveSourceMap(code, codeUrl, read, callback) {
  29. var mapData
  30. try {
  31. mapData = resolveSourceMapHelper(code, codeUrl)
  32. } catch (error) {
  33. return callbackAsync(callback, error)
  34. }
  35. if (!mapData || mapData.map) {
  36. return callbackAsync(callback, null, mapData)
  37. }
  38. var readUrl = decodeUriComponent(mapData.url)
  39. read(readUrl, function(error, result) {
  40. if (error) {
  41. error.sourceMapData = mapData
  42. return callback(error)
  43. }
  44. mapData.map = String(result)
  45. try {
  46. mapData.map = parseMapToJSON(mapData.map, mapData)
  47. } catch (error) {
  48. return callback(error)
  49. }
  50. callback(null, mapData)
  51. })
  52. }
  53. function resolveSourceMapSync(code, codeUrl, read) {
  54. var mapData = resolveSourceMapHelper(code, codeUrl)
  55. if (!mapData || mapData.map) {
  56. return mapData
  57. }
  58. mapData.map = readSync(read, mapData.url, mapData)
  59. mapData.map = parseMapToJSON(mapData.map, mapData)
  60. return mapData
  61. }
  62. var dataUriRegex = /^data:([^,;]*)(;[^,;]*)*(?:,(.*))?$/
  63. var jsonMimeTypeRegex = /^(?:application|text)\/json$/
  64. function resolveSourceMapHelper(code, codeUrl) {
  65. codeUrl = urix(codeUrl)
  66. var url = sourceMappingURL.getFrom(code)
  67. if (!url) {
  68. return null
  69. }
  70. var dataUri = url.match(dataUriRegex)
  71. if (dataUri) {
  72. var mimeType = dataUri[1]
  73. var lastParameter = dataUri[2] || ""
  74. var encoded = dataUri[3] || ""
  75. var data = {
  76. sourceMappingURL: url,
  77. url: null,
  78. sourcesRelativeTo: codeUrl,
  79. map: encoded
  80. }
  81. if (!jsonMimeTypeRegex.test(mimeType)) {
  82. var error = new Error("Unuseful data uri mime type: " + (mimeType || "text/plain"))
  83. error.sourceMapData = data
  84. throw error
  85. }
  86. data.map = parseMapToJSON(
  87. lastParameter === ";base64" ? atob(encoded) : decodeURIComponent(encoded),
  88. data
  89. )
  90. return data
  91. }
  92. var mapUrl = resolveUrl(codeUrl, url)
  93. return {
  94. sourceMappingURL: url,
  95. url: mapUrl,
  96. sourcesRelativeTo: mapUrl,
  97. map: null
  98. }
  99. }
  100. function resolveSources(map, mapUrl, read, options, callback) {
  101. if (typeof options === "function") {
  102. callback = options
  103. options = {}
  104. }
  105. var pending = map.sources ? map.sources.length : 0
  106. var result = {
  107. sourcesResolved: [],
  108. sourcesContent: []
  109. }
  110. if (pending === 0) {
  111. callbackAsync(callback, null, result)
  112. return
  113. }
  114. var done = function() {
  115. pending--
  116. if (pending === 0) {
  117. callback(null, result)
  118. }
  119. }
  120. resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
  121. result.sourcesResolved[index] = fullUrl
  122. if (typeof sourceContent === "string") {
  123. result.sourcesContent[index] = sourceContent
  124. callbackAsync(done, null)
  125. } else {
  126. var readUrl = decodeUriComponent(fullUrl)
  127. read(readUrl, function(error, source) {
  128. result.sourcesContent[index] = error ? error : String(source)
  129. done()
  130. })
  131. }
  132. })
  133. }
  134. function resolveSourcesSync(map, mapUrl, read, options) {
  135. var result = {
  136. sourcesResolved: [],
  137. sourcesContent: []
  138. }
  139. if (!map.sources || map.sources.length === 0) {
  140. return result
  141. }
  142. resolveSourcesHelper(map, mapUrl, options, function(fullUrl, sourceContent, index) {
  143. result.sourcesResolved[index] = fullUrl
  144. if (read !== null) {
  145. if (typeof sourceContent === "string") {
  146. result.sourcesContent[index] = sourceContent
  147. } else {
  148. var readUrl = decodeUriComponent(fullUrl)
  149. try {
  150. result.sourcesContent[index] = String(read(readUrl))
  151. } catch (error) {
  152. result.sourcesContent[index] = error
  153. }
  154. }
  155. }
  156. })
  157. return result
  158. }
  159. var endingSlash = /\/?$/
  160. function resolveSourcesHelper(map, mapUrl, options, fn) {
  161. options = options || {}
  162. mapUrl = urix(mapUrl)
  163. var fullUrl
  164. var sourceContent
  165. var sourceRoot
  166. for (var index = 0, len = map.sources.length; index < len; index++) {
  167. sourceRoot = null
  168. if (typeof options.sourceRoot === "string") {
  169. sourceRoot = options.sourceRoot
  170. } else if (typeof map.sourceRoot === "string" && options.sourceRoot !== false) {
  171. sourceRoot = map.sourceRoot
  172. }
  173. // If the sourceRoot is the empty string, it is equivalent to not setting
  174. // the property at all.
  175. if (sourceRoot === null || sourceRoot === '') {
  176. fullUrl = resolveUrl(mapUrl, map.sources[index])
  177. } else {
  178. // Make sure that the sourceRoot ends with a slash, so that `/scripts/subdir` becomes
  179. // `/scripts/subdir/<source>`, not `/scripts/<source>`. Pointing to a file as source root
  180. // does not make sense.
  181. fullUrl = resolveUrl(mapUrl, sourceRoot.replace(endingSlash, "/"), map.sources[index])
  182. }
  183. sourceContent = (map.sourcesContent || [])[index]
  184. fn(fullUrl, sourceContent, index)
  185. }
  186. }
  187. function resolve(code, codeUrl, read, options, callback) {
  188. if (typeof options === "function") {
  189. callback = options
  190. options = {}
  191. }
  192. if (code === null) {
  193. var mapUrl = codeUrl
  194. var data = {
  195. sourceMappingURL: null,
  196. url: mapUrl,
  197. sourcesRelativeTo: mapUrl,
  198. map: null
  199. }
  200. var readUrl = decodeUriComponent(mapUrl)
  201. read(readUrl, function(error, result) {
  202. if (error) {
  203. error.sourceMapData = data
  204. return callback(error)
  205. }
  206. data.map = String(result)
  207. try {
  208. data.map = parseMapToJSON(data.map, data)
  209. } catch (error) {
  210. return callback(error)
  211. }
  212. _resolveSources(data)
  213. })
  214. } else {
  215. resolveSourceMap(code, codeUrl, read, function(error, mapData) {
  216. if (error) {
  217. return callback(error)
  218. }
  219. if (!mapData) {
  220. return callback(null, null)
  221. }
  222. _resolveSources(mapData)
  223. })
  224. }
  225. function _resolveSources(mapData) {
  226. resolveSources(mapData.map, mapData.sourcesRelativeTo, read, options, function(error, result) {
  227. if (error) {
  228. return callback(error)
  229. }
  230. mapData.sourcesResolved = result.sourcesResolved
  231. mapData.sourcesContent = result.sourcesContent
  232. callback(null, mapData)
  233. })
  234. }
  235. }
  236. function resolveSync(code, codeUrl, read, options) {
  237. var mapData
  238. if (code === null) {
  239. var mapUrl = codeUrl
  240. mapData = {
  241. sourceMappingURL: null,
  242. url: mapUrl,
  243. sourcesRelativeTo: mapUrl,
  244. map: null
  245. }
  246. mapData.map = readSync(read, mapUrl, mapData)
  247. mapData.map = parseMapToJSON(mapData.map, mapData)
  248. } else {
  249. mapData = resolveSourceMapSync(code, codeUrl, read)
  250. if (!mapData) {
  251. return null
  252. }
  253. }
  254. var result = resolveSourcesSync(mapData.map, mapData.sourcesRelativeTo, read, options)
  255. mapData.sourcesResolved = result.sourcesResolved
  256. mapData.sourcesContent = result.sourcesContent
  257. return mapData
  258. }
  259. module.exports = {
  260. resolveSourceMap: resolveSourceMap,
  261. resolveSourceMapSync: resolveSourceMapSync,
  262. resolveSources: resolveSources,
  263. resolveSourcesSync: resolveSourcesSync,
  264. resolve: resolve,
  265. resolveSync: resolveSync,
  266. parseMapToJSON: parseMapToJSON
  267. }