123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Tobias Koppers @sokra
- */
- "use strict";
-
- var Source = require("./Source");
- var SourceNode = require("source-map").SourceNode;
- var SourceListMap = require("source-list-map").SourceListMap;
- var fromStringWithSourceMap = require("source-list-map").fromStringWithSourceMap;
- var SourceMapConsumer = require("source-map").SourceMapConsumer;
-
- class ReplaceSource extends Source {
- constructor(source, name) {
- super();
- this._source = source;
- this._name = name;
- this.replacements = [];
- }
-
- replace(start, end, newValue) {
- if(typeof newValue !== "string")
- throw new Error("insertion must be a string, but is a " + typeof newValue);
- this.replacements.push([start, end, newValue, this.replacements.length]);
- }
-
- insert(pos, newValue) {
- if(typeof newValue !== "string")
- throw new Error("insertion must be a string, but is a " + typeof newValue + ": " + newValue);
- this.replacements.push([pos, pos - 1, newValue, this.replacements.length]);
- }
-
- source(options) {
- return this._replaceString(this._source.source());
- }
-
- original() {
- return this._source;
- }
-
- _sortReplacements() {
- this.replacements.sort(function(a, b) {
- var diff = b[1] - a[1];
- if(diff !== 0)
- return diff;
- diff = b[0] - a[0];
- if(diff !== 0)
- return diff;
- return b[3] - a[3];
- });
- }
-
- _replaceString(str) {
- if(typeof str !== "string")
- throw new Error("str must be a string, but is a " + typeof str + ": " + str);
- this._sortReplacements();
- var result = [str];
- this.replacements.forEach(function(repl) {
- var remSource = result.pop();
- var splitted1 = this._splitString(remSource, Math.floor(repl[1] + 1));
- var splitted2 = this._splitString(splitted1[0], Math.floor(repl[0]));
- result.push(splitted1[1], repl[2], splitted2[0]);
- }, this);
-
- // write out result array in reverse order
- let resultStr = "";
- for(let i = result.length - 1; i >= 0; --i) {
- resultStr += result[i];
- }
- return resultStr;
- }
-
- node(options) {
- this._sortReplacements();
- var result = [this._source.node(options)];
- this.replacements.forEach(function(repl) {
- var remSource = result.pop();
- var splitted1 = this._splitSourceNode(remSource, Math.floor(repl[1] + 1));
- var splitted2;
- if(Array.isArray(splitted1)) {
- splitted2 = this._splitSourceNode(splitted1[0], Math.floor(repl[0]));
- if(Array.isArray(splitted2)) {
- result.push(splitted1[1], this._replacementToSourceNode(splitted2[1], repl[2]), splitted2[0]);
- } else {
- result.push(splitted1[1], this._replacementToSourceNode(splitted1[1], repl[2]), splitted1[0]);
- }
- } else {
- splitted2 = this._splitSourceNode(remSource, Math.floor(repl[0]));
- if(Array.isArray(splitted2)) {
- result.push(this._replacementToSourceNode(splitted2[1], repl[2]), splitted2[0]);
- } else {
- result.push(repl[2], remSource);
- }
- }
- }, this);
- result = result.reverse();
- return new SourceNode(null, null, null, result);
- }
-
- listMap(options) {
- this._sortReplacements();
- var map = this._source.listMap(options);
- var currentIndex = 0;
- var replacements = this.replacements;
- var idxReplacement = replacements.length - 1;
- var removeChars = 0;
- map = map.mapGeneratedCode(function(str) {
- var newCurrentIndex = currentIndex + str.length;
- if(removeChars > str.length) {
- removeChars -= str.length;
- str = "";
- } else {
- if(removeChars > 0) {
- str = str.substr(removeChars);
- currentIndex += removeChars;
- removeChars = 0;
- }
- var finalStr = "";
- while(idxReplacement >= 0 && replacements[idxReplacement][0] < newCurrentIndex) {
- var repl = replacements[idxReplacement];
- var start = Math.floor(repl[0]);
- var end = Math.floor(repl[1] + 1);
- var before = str.substr(0, Math.max(0, start - currentIndex));
- if(end <= newCurrentIndex) {
- var after = str.substr(Math.max(0, end - currentIndex));
- finalStr += before + repl[2];
- str = after;
- currentIndex = Math.max(currentIndex, end);
- } else {
- finalStr += before + repl[2];
- str = "";
- removeChars = end - newCurrentIndex;
- }
- idxReplacement--;
- }
- str = finalStr + str;
- }
- currentIndex = newCurrentIndex;
- return str;
- });
- var extraCode = "";
- while(idxReplacement >= 0) {
- extraCode += replacements[idxReplacement][2];
- idxReplacement--;
- }
- if(extraCode) {
- map.add(extraCode);
- }
- return map;
- }
-
- _replacementToSourceNode(oldNode, newString) {
- var map = oldNode.toStringWithSourceMap({
- file: "?"
- }).map;
- var original = new SourceMapConsumer(map.toJSON()).originalPositionFor({
- line: 1,
- column: 0
- });
- if(original) {
- return new SourceNode(original.line, original.column, original.source, newString);
- } else {
- return newString;
- }
- }
-
- _splitSourceNode(node, position) {
- if(typeof node === "string") {
- if(node.length <= position) return position - node.length;
- return position <= 0 ? ["", node] : [node.substr(0, position), node.substr(position)];
- } else {
- for(var i = 0; i < node.children.length; i++) {
- position = this._splitSourceNode(node.children[i], position);
- if(Array.isArray(position)) {
- var leftNode = new SourceNode(
- node.line,
- node.column,
- node.source,
- node.children.slice(0, i).concat([position[0]]),
- node.name
- );
- var rightNode = new SourceNode(
- node.line,
- node.column,
- node.source, [position[1]].concat(node.children.slice(i + 1)),
- node.name
- );
- leftNode.sourceContents = node.sourceContents;
- return [leftNode, rightNode];
- }
- }
- return position;
- }
- }
-
- _splitString(str, position) {
- return position <= 0 ? ["", str] : [str.substr(0, position), str.substr(position)];
- }
- }
-
- require("./SourceAndMapMixin")(ReplaceSource.prototype);
-
- module.exports = ReplaceSource;
|