replace-and-update-source-map.js 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. "use strict";
  2. /*
  3. Copyright 2019 Google LLC
  4. Use of this source code is governed by an MIT-style
  5. license that can be found in the LICENSE file or at
  6. https://opensource.org/licenses/MIT.
  7. */
  8. const {
  9. SourceMapConsumer,
  10. SourceMapGenerator
  11. } = require('source-map');
  12. /**
  13. * Adapted from https://github.com/nsams/sourcemap-aware-replace, with modern
  14. * JavaScript updates, along with additional properties copied from originalMap.
  15. *
  16. * @param {Object} options
  17. * @param {string} options.jsFilename The name for the file whose contents
  18. * correspond to originalSource.
  19. * @param {Object} options.originalMap The sourcemap for originalSource,
  20. * prior to any replacements.
  21. * @param {string} options.originalSource The source code, prior to any
  22. * replacements.
  23. * @param {string} options.replaceString A string to swap in for searchString.
  24. * @param {string} options.searchString A string in originalSource to replace.
  25. * Only the first occurrence will be replaced.
  26. * @return {{source: string, map: string}} An object containing both
  27. * originalSource with the replacement applied, and the modified originalMap.
  28. *
  29. * @private
  30. */
  31. async function replaceAndUpdateSourceMap({
  32. jsFilename,
  33. originalMap,
  34. originalSource,
  35. replaceString,
  36. searchString
  37. }) {
  38. const generator = new SourceMapGenerator({
  39. file: jsFilename
  40. });
  41. const consumer = await new SourceMapConsumer(originalMap);
  42. let pos;
  43. let src = originalSource;
  44. const replacements = [];
  45. let lineNum = 0;
  46. let filePos = 0;
  47. const lines = src.split('\n');
  48. for (let line of lines) {
  49. lineNum++;
  50. let searchPos = 0;
  51. while ((pos = line.indexOf(searchString, searchPos)) !== -1) {
  52. src = src.substring(0, filePos + pos) + replaceString + src.substring(filePos + pos + searchString.length);
  53. line = line.substring(0, pos) + replaceString + line.substring(pos + searchString.length);
  54. replacements.push({
  55. line: lineNum,
  56. column: pos
  57. });
  58. searchPos = pos + replaceString.length;
  59. }
  60. filePos += line.length + 1;
  61. }
  62. replacements.reverse();
  63. consumer.eachMapping(mapping => {
  64. for (const replacement of replacements) {
  65. if (replacement.line == mapping.generatedLine && mapping.generatedColumn > replacement.column) {
  66. const offset = searchString.length - replaceString.length;
  67. mapping.generatedColumn -= offset;
  68. }
  69. }
  70. if (mapping.source) {
  71. const newMapping = {
  72. generated: {
  73. line: mapping.generatedLine,
  74. column: mapping.generatedColumn
  75. },
  76. original: {
  77. line: mapping.originalLine,
  78. column: mapping.originalColumn
  79. },
  80. source: mapping.source
  81. };
  82. return generator.addMapping(newMapping);
  83. }
  84. return mapping;
  85. });
  86. consumer.destroy();
  87. const updatedSourceMap = Object.assign(JSON.parse(generator.toString()), {
  88. names: originalMap.names,
  89. sourceRoot: originalMap.sourceRoot,
  90. sources: originalMap.sources,
  91. sourcesContent: originalMap.sourcesContent
  92. });
  93. return {
  94. map: JSON.stringify(updatedSourceMap),
  95. source: src
  96. };
  97. }
  98. module.exports = replaceAndUpdateSourceMap;