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

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