ReplaceSource.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. var Source = require("./Source");
  6. var SourceNode = require("source-map").SourceNode;
  7. var SourceListMap = require("source-list-map").SourceListMap;
  8. var fromStringWithSourceMap = require("source-list-map").fromStringWithSourceMap;
  9. var SourceMapConsumer = require("source-map").SourceMapConsumer;
  10. function ReplaceSource(source, name) {
  11. Source.call(this);
  12. this._source = source;
  13. this._name = name;
  14. this.replacements = [];
  15. }
  16. module.exports = ReplaceSource;
  17. ReplaceSource.prototype = Object.create(Source.prototype);
  18. ReplaceSource.prototype.constructor = ReplaceSource;
  19. ReplaceSource.prototype.replace = function(start, end, newValue) {
  20. this.replacements.push([start, end, newValue]);
  21. };
  22. ReplaceSource.prototype.insert = function(pos, newValue) {
  23. this.replacements.push([pos, pos-1, newValue]);
  24. };
  25. ReplaceSource.prototype.source = function(options) {
  26. return this._replaceString(this._source.source());
  27. };
  28. ReplaceSource.prototype._sortReplacements = function() {
  29. this.replacements.forEach(function(item, idx) {
  30. item[3] = idx;
  31. });
  32. this.replacements.sort(function(a, b) {
  33. var diff = b[1] - a[1];
  34. if(diff !== 0)
  35. return diff;
  36. return b[3] - a[3];
  37. });
  38. };
  39. ReplaceSource.prototype._replaceString = function(str) {
  40. this._sortReplacements();
  41. var result = [str];
  42. this.replacements.forEach(function(repl) {
  43. var remSource = result.pop();
  44. var splitted1 = this._splitString(remSource, Math.floor(repl[1]+1));
  45. var splitted2 = this._splitString(splitted1[0], Math.floor(repl[0]));
  46. result.push(splitted1[1], repl[2], splitted2[0]);
  47. }, this);
  48. result = result.reverse();
  49. return result.join("");
  50. };
  51. require("./SourceAndMapMixin")(ReplaceSource.prototype);
  52. ReplaceSource.prototype.node = function(options) {
  53. this._sortReplacements();
  54. var result = [this._source.node(options)];
  55. this.replacements.forEach(function(repl) {
  56. var remSource = result.pop();
  57. var splitted1 = this._splitSourceNode(remSource, Math.floor(repl[1]+1));
  58. if(Array.isArray(splitted1)) {
  59. var splitted2 = this._splitSourceNode(splitted1[0], Math.floor(repl[0]));
  60. if(Array.isArray(splitted2)) {
  61. result.push(splitted1[1], this._replacementToSourceNode(splitted2[1], repl[2]), splitted2[0]);
  62. } else {
  63. result.push(splitted1[1], this._replacementToSourceNode(splitted1[1], repl[2]), splitted1[0]);
  64. }
  65. } else {
  66. var splitted2 = this._splitSourceNode(remSource, Math.floor(repl[0]));
  67. if(Array.isArray(splitted2)) {
  68. result.push(this._replacementToSourceNode(splitted2[1], repl[2]), splitted2[0]);
  69. } else {
  70. result.push(repl[2], remSource);
  71. }
  72. }
  73. }, this);
  74. result = result.reverse();
  75. return new SourceNode(null, null, null, result);
  76. };
  77. ReplaceSource.prototype.listMap = function(options) {
  78. var map = this._source.listMap(options);
  79. if(map.children.length !== 1) {
  80. var code = map.toString();
  81. code = this._replaceString(code).split("\n");
  82. var currentIndex = 0;
  83. map.mapGeneratedCode(function(str) {
  84. var idx = -1;
  85. var count = -1;
  86. do {
  87. count++;
  88. idx = str.indexOf("\n", idx + 1);
  89. } while(idx >= 0);
  90. if(!count) return "";
  91. var result = code.slice(currentIndex, currentIndex + count).join("\n") + "\n";
  92. currentIndex += count;
  93. return result;
  94. });
  95. map.add(code.slice(currentIndex).join("\n"));
  96. } else {
  97. map.mapGeneratedCode(this._replaceString.bind(this));
  98. }
  99. return map;
  100. };
  101. ReplaceSource.prototype._replacementToSourceNode = function(oldNode, newString) {
  102. var map = oldNode.toStringWithSourceMap({ file: "?" }).map;
  103. var original = new SourceMapConsumer(map.toJSON()).originalPositionFor({ line: 1, column: 0 });
  104. if(original) {
  105. return new SourceNode(original.line, original.column, original.source, newString);
  106. } else {
  107. return newString;
  108. }
  109. };
  110. ReplaceSource.prototype._splitSourceNode = function(node, position) {
  111. if(typeof node === "string") {
  112. if(node.length <= position) return position - node.length;
  113. return [node.substr(0, position), node.substr(position)];
  114. } else {
  115. for(var i = 0; i < node.children.length; i++) {
  116. position = this._splitSourceNode(node.children[i], position);
  117. if(Array.isArray(position)) {
  118. var leftNode = new SourceNode(
  119. node.line,
  120. node.column,
  121. node.source,
  122. node.children.slice(0, i).concat([position[0]]),
  123. node.name
  124. );
  125. var rightNode = new SourceNode(
  126. node.line,
  127. node.column,
  128. node.source,
  129. [position[1]].concat(node.children.slice(i+1)),
  130. node.name
  131. );
  132. leftNode.sourceContents = node.sourceContents;
  133. return [leftNode, rightNode];
  134. }
  135. }
  136. return position;
  137. }
  138. };
  139. ReplaceSource.prototype._splitString = function(str, position) {
  140. return [str.substr(0, position), str.substr(position)];
  141. };