web 3d图形渲染器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

377 lines
9.5 KiB

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