source-map-from-file.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. /*
  2. * Copyright Node.js contributors. All rights reserved.
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to
  6. * deal in the Software without restriction, including without limitation the
  7. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. * sell copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. * IN THE SOFTWARE.
  21. */
  22. // TODO(bcoe): this logic is ported from Node.js' internal source map
  23. // helpers:
  24. // https://github.com/nodejs/node/blob/master/lib/internal/source_map/source_map_cache.js
  25. // we should to upstream and downstream fixes.
  26. const { readFileSync } = require('fs')
  27. const { fileURLToPath, pathToFileURL } = require('url')
  28. const util = require('util')
  29. const debuglog = util.debuglog('c8')
  30. /**
  31. * Extract the sourcemap url from a source file
  32. * reference: https://sourcemaps.info/spec.html
  33. * @param {String} file - compilation target file
  34. * @returns {String} full path to source map file
  35. * @private
  36. */
  37. function getSourceMapFromFile (filename) {
  38. const fileBody = readFileSync(filename).toString()
  39. const sourceMapLineRE = /\/[*/]#\s+sourceMappingURL=(?<sourceMappingURL>[^\s]+)/
  40. const results = fileBody.match(sourceMapLineRE)
  41. if (results !== null) {
  42. const sourceMappingURL = results.groups.sourceMappingURL
  43. const sourceMap = dataFromUrl(pathToFileURL(filename), sourceMappingURL)
  44. return sourceMap
  45. } else {
  46. return null
  47. }
  48. }
  49. function dataFromUrl (sourceURL, sourceMappingURL) {
  50. try {
  51. const url = new URL(sourceMappingURL)
  52. switch (url.protocol) {
  53. case 'data:':
  54. return sourceMapFromDataUrl(url.pathname)
  55. default:
  56. return null
  57. }
  58. } catch (err) {
  59. debuglog(err)
  60. // If no scheme is present, we assume we are dealing with a file path.
  61. const mapURL = new URL(sourceMappingURL, sourceURL).href
  62. return sourceMapFromFile(mapURL)
  63. }
  64. }
  65. function sourceMapFromFile (mapURL) {
  66. try {
  67. const content = readFileSync(fileURLToPath(mapURL), 'utf8')
  68. return JSON.parse(content)
  69. } catch (err) {
  70. debuglog(err)
  71. return null
  72. }
  73. }
  74. // data:[<mediatype>][;base64],<data> see:
  75. // https://tools.ietf.org/html/rfc2397#section-2
  76. function sourceMapFromDataUrl (url) {
  77. const { 0: format, 1: data } = url.split(',')
  78. const splitFormat = format.split(';')
  79. const contentType = splitFormat[0]
  80. const base64 = splitFormat[splitFormat.length - 1] === 'base64'
  81. if (contentType === 'application/json') {
  82. const decodedData = base64 ? Buffer.from(data, 'base64').toString('utf8') : data
  83. try {
  84. return JSON.parse(decodedData)
  85. } catch (err) {
  86. debuglog(err)
  87. return null
  88. }
  89. } else {
  90. debuglog(`unexpected content-type ${contentType}`)
  91. return null
  92. }
  93. }
  94. module.exports = getSourceMapFromFile