πŸš€ Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more β†’
Socket
Book a DemoInstallSign in
Socket

magic-string

Package Overview
Dependencies
Maintainers
1
Versions
121
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

magic-string - npm Package Compare versions

Comparing version

to
0.11.0-alpha

src/Chunk.js
# changelog
## 0.11.0-alpha
* Add `move()` method (WIP – no sourcemap support yet)
* Refactor internals to support `move()`
## 0.10.2

@@ -4,0 +9,0 @@

443

dist/magic-string.cjs.js

@@ -5,17 +5,57 @@ 'use strict';

function Patch(start, end, content, original, storeName) {
function Chunk(start, end, content) {
this.start = start;
this.end = end;
this.original = content;
this.content = content;
this.original = original;
this.storeName = storeName;
this.storeName = false;
this.edited = false;
}
Patch.prototype = {
Chunk.prototype = {
clone: function clone() {
return new Patch(this.start, this.end, this.content, this.original, this.storeName);
var chunk = new Chunk(this.start, this.end, this.original);
chunk.content = this.content;
chunk.storeName = this.storeName;
chunk.edited = this.edited;
return chunk;
},
edit: function edit(content, storeName) {
this.content = content;
this.storeName = storeName;
this.edited = true;
return this;
},
split: function split(index) {
if (index === this.start) return this;
var sliceIndex = index - this.start;
var originalBefore = this.original.slice(0, sliceIndex);
var originalAfter = this.original.slice(sliceIndex);
this.original = originalBefore;
var newChunk = new Chunk(index, this.end, originalAfter);
this.end = index;
if (this.edited) {
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")');
// zero-length edited chunks are a special case (overlapping replacements)
newChunk.edit('', false);
this.content = '';
} else {
this.content = originalBefore;
}
return newChunk;
}
};
var _btoa = undefined;
var _btoa = void 0;

@@ -26,3 +66,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') {

/* global Buffer */
_btoa = function (str) {
_btoa = function _btoa(str) {
return new Buffer(str).toString('base64');

@@ -87,3 +127,3 @@ };

function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) {
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) {
var rawLines = [];

@@ -131,41 +171,42 @@

for (var i = 0; i < patches.length; i += 1) {
var patch = patches[i];
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex;
for (var i = 0; i < chunks.length; i += 1) {
var chunk = chunks[i];
addSegmentsUntil(patch.start);
if (chunk.edited) {
if (i > 0 || chunk.content.length) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1,
sourceIndex: sourceIndex
});
}
if (addSegmentForPatch) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1,
sourceIndex: sourceIndex
});
}
var lines = chunk.content.split('\n');
var lastLine = lines.pop();
var lines = patch.content.split('\n');
var lastLine = lines.pop();
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
lines = chunk.original.split('\n');
lastLine = lines.pop();
lines = patch.original.split('\n');
lastLine = lines.pop();
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
} else {
sourceCodeColumn += lastLine.length;
}
} else {
sourceCodeColumn += lastLine.length;
addSegmentsUntil(chunk.end);
}
originalCharIndex = patch.end;
originalCharIndex = chunk.end;
}

@@ -235,2 +276,4 @@

var chunk = new Chunk(0, string.length, string);
Object.defineProperties(this, {

@@ -240,3 +283,4 @@ original: { writable: true, value: string },

intro: { writable: true, value: '' },
patches: { writable: true, value: [] },
chunks: { writable: true, value: [chunk] },
moves: { writable: true, value: [] },
filename: { writable: true, value: options.filename },

@@ -263,4 +307,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },

cloned.patches = this.patches.map(function (patch) {
return patch.clone();
cloned.chunks = this.chunks.map(function (chunk) {
return chunk.clone();
});

@@ -297,7 +341,5 @@

getMappings: function getMappings(hires, sourceIndex, offsets, names) {
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names);
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names);
},
indent: function indent(indentStr, options) {
var _this = this;
var pattern = /^[^\r\n]/gm;

@@ -337,43 +379,52 @@

var chunkIndex = void 0;
var charIndex = 0;
var patchIndex = 0;
var indentUntil = function indentUntil(end) {
while (charIndex < end) {
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) {
// can't cache this.chunks.length, it may change
var chunk = this.chunks[chunkIndex];
var end = chunk.end;
if (chunk.edited) {
if (!isExcluded[charIndex]) {
var char = _this.original[charIndex];
chunk.content = chunk.content.replace(pattern, replacer);
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false));
shouldIndentNextCharacter = false;
patchIndex += 1;
if (chunk.content.length) {
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n';
}
}
} else {
charIndex = chunk.start;
charIndex += 1;
}
};
while (charIndex < end) {
if (!isExcluded[charIndex]) {
var char = this.original[charIndex];
for (; patchIndex < this.patches.length; patchIndex += 1) {
// can't cache this.patches.length, it may change
var patch = this.patches[patchIndex];
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
shouldIndentNextCharacter = false;
indentUntil(patch.start);
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false);
var remainder = chunk.split(charIndex);
if (!isExcluded[charIndex]) {
patch.content = patch.content.replace(pattern, replacer);
if (charIndex === chunk.start) {
this.chunks.splice(chunkIndex, 0, indentation);
chunkIndex += 1;
} else {
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder);
chunkIndex += 2;
}
if (patch.content.length) {
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n';
chunk = remainder;
}
}
charIndex += 1;
}
}
charIndex = patch.end;
charIndex = chunk.end;
}
indentUntil(this.original.length);
this.outro = this.outro.replace(pattern, replacer);

@@ -384,10 +435,18 @@

insert: function insert(index, content) {
if (typeof content !== 'string') {
throw new TypeError('inserted content must be a string');
}
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
this.patch(index, index, content);
this._split(index);
var next = this.chunks.findIndex(function (chunk) {
return chunk.end > index;
});
if (! ~next) next = this.chunks.length;
var newChunk = new Chunk(index, index, '').edit(content, false);
this.chunks.splice(next, 0, newChunk);
return this;
},
// get current location of character in original string

@@ -400,2 +459,13 @@ locate: function locate(character) {

},
move: function move(start, end, index) {
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself');
this._split(start);
this._split(end);
this._split(index);
this.moves.push({ start: start, end: end, index: index });
return this;
},
overwrite: function overwrite(start, end, content, storeName) {

@@ -406,45 +476,23 @@ if (typeof content !== 'string') {

this.patch(start, end, content, storeName);
return this;
},
patch: function patch(start, end, content, storeName) {
var original = this.original.slice(start, end);
if (storeName) this.storedNames[original] = true;
this._split(start);
this._split(end);
var i = this.patches.length;
while (i--) {
var previous = this.patches[i];
if (storeName) {
var original = this.original.slice(start, end);
this.storedNames[original] = true;
}
// TODO can we tidy this up?
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === start && chunk.original.length;
});
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === end;
});
if (! ~firstIndex) firstIndex = this.chunks.length;
if (! ~lastIndex) lastIndex = this.chunks.length;
// if this completely covers previous patch, remove it
if (start !== end && start <= previous.start && end >= previous.end) {
// unless it's an insert at the start
if (previous.start === previous.end && previous.start === start) break;
// or it's an insert at the end
if (previous.start === previous.end && previous.end === end) continue;
this.patches.splice(i, 1);
}
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName);
// if it overlaps, throw error
else if (start < previous.end && end > previous.start) {
// special case – it's okay to remove overlapping ranges
if (!previous.content.length && !content.length) {
previous.start = Math.min(start, previous.start);
previous.end = Math.max(end, previous.end);
return;
}
throw new Error('Cannot overwrite the same content twice: \'' + original + '\'');
}
// if this precedes previous patch, stop search
else if (start >= previous.end) {
break;
}
}
var patch = new Patch(start, end, content, original, storeName);
this.patches.splice(i + 1, 0, patch);
return patch;
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;
},

@@ -458,7 +506,39 @@ prepend: function prepend(content) {

remove: function remove(start, end) {
if (start < 0 || end > this.original.length) {
throw new Error('Character is out of bounds');
while (start < 0) {
start += this.original.length;
}while (end < 0) {
end += this.original.length;
}if (start === end) return this;
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds');
if (start > end) throw new Error('end must be greater than start');
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start <= start && chunk.end > start;
});
var chunk = this.chunks[firstIndex];
// if the chunk contains `start`, split
if (chunk.start < start) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(start);
firstIndex += 1;
}
this.patch(start, end, '');
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start < end && chunk.end >= end;
});
chunk = this.chunks[lastIndex];
// if the chunk contains `end`, split
if (chunk.start < end) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(end);
}
lastIndex += 1;
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false);
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;

@@ -481,36 +561,24 @@ },

end += this.original.length;
}var firstPatchIndex = 0;
var lastPatchIndex = this.patches.length;
}var result = '';
while (lastPatchIndex--) {
var patch = this.patches[lastPatchIndex];
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
// TODO handle moves
// TODO this is weird, rewrite it
if (patch.start > end) continue;
break;
}
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) {
var patch = this.patches[firstPatchIndex];
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
if (chunk.end <= start) continue;
if (chunk.start >= end) break;
if (start <= patch.start) {
break;
}
}
if (chunk.start < start || chunk.end > end) {
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
var result = '';
var lastIndex = start;
var sliceStart = Math.max(start - chunk.start, 0);
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length);
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) {
var patch = this.patches[i];
result += this.original.slice(lastIndex, patch.start);
result += patch.content;
lastIndex = patch.end;
result += chunk.content.slice(sliceStart, sliceEnd);
} else {
result += chunk.content;
}
}
result += this.original.slice(lastIndex, end);
return result;

@@ -525,4 +593,47 @@ },

},
_sortChunks: function _sortChunks() {
var chunks = this.chunks.slice();
// TODO there must be a better way than this...
this.moves.forEach(function (move, i) {
var firstIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.start;
});
var lastIndex = chunks.findIndex(function (chunk, i) {
return i >= firstIndex && chunk.end === move.end;
}) + 1;
if (!lastIndex) lastIndex = chunks.length;
var insertionIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.index;
});
if (! ~insertionIndex) insertionIndex = chunks.length;
var num = lastIndex - firstIndex;
if (firstIndex < insertionIndex) insertionIndex -= num;
var toMove = chunks.splice(firstIndex, num);
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove));
});
return chunks;
},
_split: function _split(index) {
// TODO bisect
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (chunk.start === index || chunk.end === index) return;
if (chunk.start < index && chunk.end > index) {
var newChunk = chunk.split(index);
this.chunks.splice(i + 1, 0, newChunk);
return;
}
}
},
toString: function toString() {
return this.intro + this.slice(0, this.original.length) + this.outro;
return this.intro + this._sortChunks().map(function (chunk) {
return chunk.content;
}).join('') + this.outro;
},

@@ -542,17 +653,17 @@ trimLines: function trimLines() {

var charIndex = this.original.length;
var i = this.patches.length;
var i = this.chunks.length;
while (i--) {
var patch = this.patches[i];
var chunk = this.chunks[i];
if (charIndex > patch.end) {
var _slice = this.original.slice(patch.end, charIndex);
if (charIndex > chunk.end) {
var _slice = this.original.slice(chunk.end, charIndex);
var _match = rx.exec(_slice);
if (_match) {
this.patch(charIndex - _match[0].length, charIndex, '');
this.chunk(charIndex - _match[0].length, charIndex, '');
}
if (!_match || _match[0].length < _slice.length) {
// there is non-whitespace after the patch
// there is non-whitespace after the chunk
return this;

@@ -562,6 +673,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.start;
charIndex = chunk.start;
}

@@ -572,3 +683,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex - match[0].length, charIndex, '');
if (match) this.chunk(charIndex - match[0].length, charIndex, '');

@@ -585,13 +696,13 @@ return this;

for (var i = 0; i < this.patches.length; i += 1) {
var patch = this.patches[i];
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (charIndex < patch.start) {
var _slice2 = this.original.slice(charIndex, patch.start);
if (charIndex < chunk.start) {
var _slice2 = this.original.slice(charIndex, chunk.start);
var _match2 = rx.exec(_slice2);
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, '');
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, '');
if (!_match2 || _match2[0].length < _slice2.length) {
// there is non-whitespace before the patch
// there is non-whitespace before the chunk
return this;

@@ -601,6 +712,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.end;
charIndex = chunk.end;
}

@@ -611,3 +722,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex, charIndex + match[0].length, '');
if (match) this.chunk(charIndex, charIndex + match[0].length, '');

@@ -708,3 +819,3 @@ return this;

var prefix = i > 0 ? getSemis(source.separator) || ',' : '';
var mappings = undefined;
var mappings = void 0;

@@ -809,3 +920,3 @@ // we don't bother encoding sources without a filename

if (!this.intro) {
var source = undefined;
var source = void 0;
var i = 0;

@@ -830,3 +941,3 @@

var source = undefined;
var source = void 0;
var i = this.sources.length - 1;

@@ -833,0 +944,0 @@

import { encode } from 'vlq';
function Patch(start, end, content, original, storeName) {
function Chunk(start, end, content) {
this.start = start;
this.end = end;
this.original = content;
this.content = content;
this.original = original;
this.storeName = storeName;
this.storeName = false;
this.edited = false;
}
Patch.prototype = {
Chunk.prototype = {
clone: function clone() {
return new Patch(this.start, this.end, this.content, this.original, this.storeName);
var chunk = new Chunk(this.start, this.end, this.original);
chunk.content = this.content;
chunk.storeName = this.storeName;
chunk.edited = this.edited;
return chunk;
},
edit: function edit(content, storeName) {
this.content = content;
this.storeName = storeName;
this.edited = true;
return this;
},
split: function split(index) {
if (index === this.start) return this;
var sliceIndex = index - this.start;
var originalBefore = this.original.slice(0, sliceIndex);
var originalAfter = this.original.slice(sliceIndex);
this.original = originalBefore;
var newChunk = new Chunk(index, this.end, originalAfter);
this.end = index;
if (this.edited) {
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")');
// zero-length edited chunks are a special case (overlapping replacements)
newChunk.edit('', false);
this.content = '';
} else {
this.content = originalBefore;
}
return newChunk;
}
};
var _btoa = undefined;
var _btoa = void 0;

@@ -23,3 +63,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') {

/* global Buffer */
_btoa = function (str) {
_btoa = function _btoa(str) {
return new Buffer(str).toString('base64');

@@ -84,3 +124,3 @@ };

function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) {
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) {
var rawLines = [];

@@ -128,41 +168,42 @@

for (var i = 0; i < patches.length; i += 1) {
var patch = patches[i];
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex;
for (var i = 0; i < chunks.length; i += 1) {
var chunk = chunks[i];
addSegmentsUntil(patch.start);
if (chunk.edited) {
if (i > 0 || chunk.content.length) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1,
sourceIndex: sourceIndex
});
}
if (addSegmentForPatch) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1,
sourceIndex: sourceIndex
});
}
var lines = chunk.content.split('\n');
var lastLine = lines.pop();
var lines = patch.content.split('\n');
var lastLine = lines.pop();
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
lines = chunk.original.split('\n');
lastLine = lines.pop();
lines = patch.original.split('\n');
lastLine = lines.pop();
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
} else {
sourceCodeColumn += lastLine.length;
}
} else {
sourceCodeColumn += lastLine.length;
addSegmentsUntil(chunk.end);
}
originalCharIndex = patch.end;
originalCharIndex = chunk.end;
}

@@ -232,2 +273,4 @@

var chunk = new Chunk(0, string.length, string);
Object.defineProperties(this, {

@@ -237,3 +280,4 @@ original: { writable: true, value: string },

intro: { writable: true, value: '' },
patches: { writable: true, value: [] },
chunks: { writable: true, value: [chunk] },
moves: { writable: true, value: [] },
filename: { writable: true, value: options.filename },

@@ -260,4 +304,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },

cloned.patches = this.patches.map(function (patch) {
return patch.clone();
cloned.chunks = this.chunks.map(function (chunk) {
return chunk.clone();
});

@@ -294,7 +338,5 @@

getMappings: function getMappings(hires, sourceIndex, offsets, names) {
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names);
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names);
},
indent: function indent(indentStr, options) {
var _this = this;
var pattern = /^[^\r\n]/gm;

@@ -334,43 +376,52 @@

var chunkIndex = void 0;
var charIndex = 0;
var patchIndex = 0;
var indentUntil = function indentUntil(end) {
while (charIndex < end) {
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) {
// can't cache this.chunks.length, it may change
var chunk = this.chunks[chunkIndex];
var end = chunk.end;
if (chunk.edited) {
if (!isExcluded[charIndex]) {
var char = _this.original[charIndex];
chunk.content = chunk.content.replace(pattern, replacer);
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false));
shouldIndentNextCharacter = false;
patchIndex += 1;
if (chunk.content.length) {
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n';
}
}
} else {
charIndex = chunk.start;
charIndex += 1;
}
};
while (charIndex < end) {
if (!isExcluded[charIndex]) {
var char = this.original[charIndex];
for (; patchIndex < this.patches.length; patchIndex += 1) {
// can't cache this.patches.length, it may change
var patch = this.patches[patchIndex];
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
shouldIndentNextCharacter = false;
indentUntil(patch.start);
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false);
var remainder = chunk.split(charIndex);
if (!isExcluded[charIndex]) {
patch.content = patch.content.replace(pattern, replacer);
if (charIndex === chunk.start) {
this.chunks.splice(chunkIndex, 0, indentation);
chunkIndex += 1;
} else {
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder);
chunkIndex += 2;
}
if (patch.content.length) {
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n';
chunk = remainder;
}
}
charIndex += 1;
}
}
charIndex = patch.end;
charIndex = chunk.end;
}
indentUntil(this.original.length);
this.outro = this.outro.replace(pattern, replacer);

@@ -381,10 +432,18 @@

insert: function insert(index, content) {
if (typeof content !== 'string') {
throw new TypeError('inserted content must be a string');
}
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
this.patch(index, index, content);
this._split(index);
var next = this.chunks.findIndex(function (chunk) {
return chunk.end > index;
});
if (! ~next) next = this.chunks.length;
var newChunk = new Chunk(index, index, '').edit(content, false);
this.chunks.splice(next, 0, newChunk);
return this;
},
// get current location of character in original string

@@ -397,2 +456,13 @@ locate: function locate(character) {

},
move: function move(start, end, index) {
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself');
this._split(start);
this._split(end);
this._split(index);
this.moves.push({ start: start, end: end, index: index });
return this;
},
overwrite: function overwrite(start, end, content, storeName) {

@@ -403,45 +473,23 @@ if (typeof content !== 'string') {

this.patch(start, end, content, storeName);
return this;
},
patch: function patch(start, end, content, storeName) {
var original = this.original.slice(start, end);
if (storeName) this.storedNames[original] = true;
this._split(start);
this._split(end);
var i = this.patches.length;
while (i--) {
var previous = this.patches[i];
if (storeName) {
var original = this.original.slice(start, end);
this.storedNames[original] = true;
}
// TODO can we tidy this up?
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === start && chunk.original.length;
});
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === end;
});
if (! ~firstIndex) firstIndex = this.chunks.length;
if (! ~lastIndex) lastIndex = this.chunks.length;
// if this completely covers previous patch, remove it
if (start !== end && start <= previous.start && end >= previous.end) {
// unless it's an insert at the start
if (previous.start === previous.end && previous.start === start) break;
// or it's an insert at the end
if (previous.start === previous.end && previous.end === end) continue;
this.patches.splice(i, 1);
}
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName);
// if it overlaps, throw error
else if (start < previous.end && end > previous.start) {
// special case – it's okay to remove overlapping ranges
if (!previous.content.length && !content.length) {
previous.start = Math.min(start, previous.start);
previous.end = Math.max(end, previous.end);
return;
}
throw new Error('Cannot overwrite the same content twice: \'' + original + '\'');
}
// if this precedes previous patch, stop search
else if (start >= previous.end) {
break;
}
}
var patch = new Patch(start, end, content, original, storeName);
this.patches.splice(i + 1, 0, patch);
return patch;
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;
},

@@ -455,7 +503,39 @@ prepend: function prepend(content) {

remove: function remove(start, end) {
if (start < 0 || end > this.original.length) {
throw new Error('Character is out of bounds');
while (start < 0) {
start += this.original.length;
}while (end < 0) {
end += this.original.length;
}if (start === end) return this;
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds');
if (start > end) throw new Error('end must be greater than start');
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start <= start && chunk.end > start;
});
var chunk = this.chunks[firstIndex];
// if the chunk contains `start`, split
if (chunk.start < start) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(start);
firstIndex += 1;
}
this.patch(start, end, '');
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start < end && chunk.end >= end;
});
chunk = this.chunks[lastIndex];
// if the chunk contains `end`, split
if (chunk.start < end) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(end);
}
lastIndex += 1;
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false);
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;

@@ -478,36 +558,24 @@ },

end += this.original.length;
}var firstPatchIndex = 0;
var lastPatchIndex = this.patches.length;
}var result = '';
while (lastPatchIndex--) {
var patch = this.patches[lastPatchIndex];
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
// TODO handle moves
// TODO this is weird, rewrite it
if (patch.start > end) continue;
break;
}
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) {
var patch = this.patches[firstPatchIndex];
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
if (chunk.end <= start) continue;
if (chunk.start >= end) break;
if (start <= patch.start) {
break;
}
}
if (chunk.start < start || chunk.end > end) {
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
var result = '';
var lastIndex = start;
var sliceStart = Math.max(start - chunk.start, 0);
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length);
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) {
var patch = this.patches[i];
result += this.original.slice(lastIndex, patch.start);
result += patch.content;
lastIndex = patch.end;
result += chunk.content.slice(sliceStart, sliceEnd);
} else {
result += chunk.content;
}
}
result += this.original.slice(lastIndex, end);
return result;

@@ -522,4 +590,47 @@ },

},
_sortChunks: function _sortChunks() {
var chunks = this.chunks.slice();
// TODO there must be a better way than this...
this.moves.forEach(function (move, i) {
var firstIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.start;
});
var lastIndex = chunks.findIndex(function (chunk, i) {
return i >= firstIndex && chunk.end === move.end;
}) + 1;
if (!lastIndex) lastIndex = chunks.length;
var insertionIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.index;
});
if (! ~insertionIndex) insertionIndex = chunks.length;
var num = lastIndex - firstIndex;
if (firstIndex < insertionIndex) insertionIndex -= num;
var toMove = chunks.splice(firstIndex, num);
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove));
});
return chunks;
},
_split: function _split(index) {
// TODO bisect
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (chunk.start === index || chunk.end === index) return;
if (chunk.start < index && chunk.end > index) {
var newChunk = chunk.split(index);
this.chunks.splice(i + 1, 0, newChunk);
return;
}
}
},
toString: function toString() {
return this.intro + this.slice(0, this.original.length) + this.outro;
return this.intro + this._sortChunks().map(function (chunk) {
return chunk.content;
}).join('') + this.outro;
},

@@ -539,17 +650,17 @@ trimLines: function trimLines() {

var charIndex = this.original.length;
var i = this.patches.length;
var i = this.chunks.length;
while (i--) {
var patch = this.patches[i];
var chunk = this.chunks[i];
if (charIndex > patch.end) {
var _slice = this.original.slice(patch.end, charIndex);
if (charIndex > chunk.end) {
var _slice = this.original.slice(chunk.end, charIndex);
var _match = rx.exec(_slice);
if (_match) {
this.patch(charIndex - _match[0].length, charIndex, '');
this.chunk(charIndex - _match[0].length, charIndex, '');
}
if (!_match || _match[0].length < _slice.length) {
// there is non-whitespace after the patch
// there is non-whitespace after the chunk
return this;

@@ -559,6 +670,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.start;
charIndex = chunk.start;
}

@@ -569,3 +680,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex - match[0].length, charIndex, '');
if (match) this.chunk(charIndex - match[0].length, charIndex, '');

@@ -582,13 +693,13 @@ return this;

for (var i = 0; i < this.patches.length; i += 1) {
var patch = this.patches[i];
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (charIndex < patch.start) {
var _slice2 = this.original.slice(charIndex, patch.start);
if (charIndex < chunk.start) {
var _slice2 = this.original.slice(charIndex, chunk.start);
var _match2 = rx.exec(_slice2);
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, '');
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, '');
if (!_match2 || _match2[0].length < _slice2.length) {
// there is non-whitespace before the patch
// there is non-whitespace before the chunk
return this;

@@ -598,6 +709,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.end;
charIndex = chunk.end;
}

@@ -608,3 +719,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex, charIndex + match[0].length, '');
if (match) this.chunk(charIndex, charIndex + match[0].length, '');

@@ -705,3 +816,3 @@ return this;

var prefix = i > 0 ? getSemis(source.separator) || ',' : '';
var mappings = undefined;
var mappings = void 0;

@@ -806,3 +917,3 @@ // we don't bother encoding sources without a filename

if (!this.intro) {
var source = undefined;
var source = void 0;
var i = 0;

@@ -827,3 +938,3 @@

var source = undefined;
var source = void 0;
var i = this.sources.length - 1;

@@ -830,0 +941,0 @@

@@ -7,17 +7,57 @@ (function (global, factory) {

function Patch(start, end, content, original, storeName) {
function Chunk(start, end, content) {
this.start = start;
this.end = end;
this.original = content;
this.content = content;
this.original = original;
this.storeName = storeName;
this.storeName = false;
this.edited = false;
}
Patch.prototype = {
Chunk.prototype = {
clone: function clone() {
return new Patch(this.start, this.end, this.content, this.original, this.storeName);
var chunk = new Chunk(this.start, this.end, this.original);
chunk.content = this.content;
chunk.storeName = this.storeName;
chunk.edited = this.edited;
return chunk;
},
edit: function edit(content, storeName) {
this.content = content;
this.storeName = storeName;
this.edited = true;
return this;
},
split: function split(index) {
if (index === this.start) return this;
var sliceIndex = index - this.start;
var originalBefore = this.original.slice(0, sliceIndex);
var originalAfter = this.original.slice(sliceIndex);
this.original = originalBefore;
var newChunk = new Chunk(index, this.end, originalAfter);
this.end = index;
if (this.edited) {
if (this.content.length) throw new Error('Cannot split a chunk that has already been edited ("' + this.original + '")');
// zero-length edited chunks are a special case (overlapping replacements)
newChunk.edit('', false);
this.content = '';
} else {
this.content = originalBefore;
}
return newChunk;
}
};
var _btoa = undefined;
var _btoa = void 0;

@@ -28,3 +68,3 @@ if (typeof window !== 'undefined' && typeof window.btoa === 'function') {

/* global Buffer */
_btoa = function (str) {
_btoa = function _btoa(str) {
return new Buffer(str).toString('base64');

@@ -135,3 +175,3 @@ };

function encodeMappings(original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names) {
function encodeMappings(original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names) {
var rawLines = [];

@@ -179,41 +219,42 @@

for (var i = 0; i < patches.length; i += 1) {
var patch = patches[i];
var addSegmentForPatch = patch.storeName || patch.start > originalCharIndex;
for (var i = 0; i < chunks.length; i += 1) {
var chunk = chunks[i];
addSegmentsUntil(patch.start);
if (chunk.edited) {
if (i > 0 || chunk.content.length) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: chunk.storeName ? names.indexOf(chunk.original) : -1,
sourceIndex: sourceIndex
});
}
if (addSegmentForPatch) {
rawSegments.push({
generatedCodeLine: generatedCodeLine,
generatedCodeColumn: generatedCodeColumn,
sourceCodeLine: sourceCodeLine,
sourceCodeColumn: sourceCodeColumn,
sourceCodeName: patch.storeName ? names.indexOf(patch.original) : -1,
sourceIndex: sourceIndex
});
}
var lines = chunk.content.split('\n');
var lastLine = lines.pop();
var lines = patch.content.split('\n');
var lastLine = lines.pop();
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
if (lines.length) {
generatedCodeLine += lines.length;
rawLines[generatedCodeLine] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
lines = chunk.original.split('\n');
lastLine = lines.pop();
lines = patch.original.split('\n');
lastLine = lines.pop();
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
if (lines.length) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
} else {
sourceCodeColumn += lastLine.length;
}
} else {
sourceCodeColumn += lastLine.length;
addSegmentsUntil(chunk.end);
}
originalCharIndex = patch.end;
originalCharIndex = chunk.end;
}

@@ -283,2 +324,4 @@

var chunk = new Chunk(0, string.length, string);
Object.defineProperties(this, {

@@ -288,3 +331,4 @@ original: { writable: true, value: string },

intro: { writable: true, value: '' },
patches: { writable: true, value: [] },
chunks: { writable: true, value: [chunk] },
moves: { writable: true, value: [] },
filename: { writable: true, value: options.filename },

@@ -311,4 +355,4 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },

cloned.patches = this.patches.map(function (patch) {
return patch.clone();
cloned.chunks = this.chunks.map(function (chunk) {
return chunk.clone();
});

@@ -345,7 +389,5 @@

getMappings: function getMappings(hires, sourceIndex, offsets, names) {
return encodeMappings(this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names);
return encodeMappings(this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names);
},
indent: function indent(indentStr, options) {
var _this = this;
var pattern = /^[^\r\n]/gm;

@@ -385,43 +427,52 @@

var chunkIndex = void 0;
var charIndex = 0;
var patchIndex = 0;
var indentUntil = function indentUntil(end) {
while (charIndex < end) {
for (chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1) {
// can't cache this.chunks.length, it may change
var chunk = this.chunks[chunkIndex];
var end = chunk.end;
if (chunk.edited) {
if (!isExcluded[charIndex]) {
var char = _this.original[charIndex];
chunk.content = chunk.content.replace(pattern, replacer);
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
_this.patches.splice(patchIndex, 0, new Patch(charIndex, charIndex, indentStr, '', false));
shouldIndentNextCharacter = false;
patchIndex += 1;
if (chunk.content.length) {
shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n';
}
}
} else {
charIndex = chunk.start;
charIndex += 1;
}
};
while (charIndex < end) {
if (!isExcluded[charIndex]) {
var char = this.original[charIndex];
for (; patchIndex < this.patches.length; patchIndex += 1) {
// can't cache this.patches.length, it may change
var patch = this.patches[patchIndex];
if (char === '\n') {
shouldIndentNextCharacter = true;
} else if (char !== '\r' && shouldIndentNextCharacter) {
shouldIndentNextCharacter = false;
indentUntil(patch.start);
var indentation = new Chunk(charIndex, charIndex, '').edit(indentStr, false);
var remainder = chunk.split(charIndex);
if (!isExcluded[charIndex]) {
patch.content = patch.content.replace(pattern, replacer);
if (charIndex === chunk.start) {
this.chunks.splice(chunkIndex, 0, indentation);
chunkIndex += 1;
} else {
this.chunks.splice(chunkIndex + 1, 0, indentation, remainder);
chunkIndex += 2;
}
if (patch.content.length) {
shouldIndentNextCharacter = patch.content[patch.content.length - 1] === '\n';
chunk = remainder;
}
}
charIndex += 1;
}
}
charIndex = patch.end;
charIndex = chunk.end;
}
indentUntil(this.original.length);
this.outro = this.outro.replace(pattern, replacer);

@@ -432,10 +483,18 @@

insert: function insert(index, content) {
if (typeof content !== 'string') {
throw new TypeError('inserted content must be a string');
}
if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
this.patch(index, index, content);
this._split(index);
var next = this.chunks.findIndex(function (chunk) {
return chunk.end > index;
});
if (! ~next) next = this.chunks.length;
var newChunk = new Chunk(index, index, '').edit(content, false);
this.chunks.splice(next, 0, newChunk);
return this;
},
// get current location of character in original string

@@ -448,2 +507,13 @@ locate: function locate(character) {

},
move: function move(start, end, index) {
if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself');
this._split(start);
this._split(end);
this._split(index);
this.moves.push({ start: start, end: end, index: index });
return this;
},
overwrite: function overwrite(start, end, content, storeName) {

@@ -454,45 +524,23 @@ if (typeof content !== 'string') {

this.patch(start, end, content, storeName);
return this;
},
patch: function patch(start, end, content, storeName) {
var original = this.original.slice(start, end);
if (storeName) this.storedNames[original] = true;
this._split(start);
this._split(end);
var i = this.patches.length;
while (i--) {
var previous = this.patches[i];
if (storeName) {
var original = this.original.slice(start, end);
this.storedNames[original] = true;
}
// TODO can we tidy this up?
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === start && chunk.original.length;
});
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start === end;
});
if (! ~firstIndex) firstIndex = this.chunks.length;
if (! ~lastIndex) lastIndex = this.chunks.length;
// if this completely covers previous patch, remove it
if (start !== end && start <= previous.start && end >= previous.end) {
// unless it's an insert at the start
if (previous.start === previous.end && previous.start === start) break;
// or it's an insert at the end
if (previous.start === previous.end && previous.end === end) continue;
this.patches.splice(i, 1);
}
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit(content, storeName);
// if it overlaps, throw error
else if (start < previous.end && end > previous.start) {
// special case – it's okay to remove overlapping ranges
if (!previous.content.length && !content.length) {
previous.start = Math.min(start, previous.start);
previous.end = Math.max(end, previous.end);
return;
}
throw new Error('Cannot overwrite the same content twice: \'' + original + '\'');
}
// if this precedes previous patch, stop search
else if (start >= previous.end) {
break;
}
}
var patch = new Patch(start, end, content, original, storeName);
this.patches.splice(i + 1, 0, patch);
return patch;
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;
},

@@ -506,7 +554,39 @@ prepend: function prepend(content) {

remove: function remove(start, end) {
if (start < 0 || end > this.original.length) {
throw new Error('Character is out of bounds');
while (start < 0) {
start += this.original.length;
}while (end < 0) {
end += this.original.length;
}if (start === end) return this;
if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds');
if (start > end) throw new Error('end must be greater than start');
var firstIndex = this.chunks.findIndex(function (chunk) {
return chunk.start <= start && chunk.end > start;
});
var chunk = this.chunks[firstIndex];
// if the chunk contains `start`, split
if (chunk.start < start) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(start);
firstIndex += 1;
}
this.patch(start, end, '');
var lastIndex = this.chunks.findIndex(function (chunk) {
return chunk.start < end && chunk.end >= end;
});
chunk = this.chunks[lastIndex];
// if the chunk contains `end`, split
if (chunk.start < end) {
if (chunk.edited && chunk.content.length) throw new Error('nope');
this._split(end);
}
lastIndex += 1;
var newChunk = new Chunk(start, end, this.original.slice(start, end)).edit('', false);
this.chunks.splice(firstIndex, lastIndex - firstIndex, newChunk);
return this;

@@ -529,36 +609,24 @@ },

end += this.original.length;
}var firstPatchIndex = 0;
var lastPatchIndex = this.patches.length;
}var result = '';
while (lastPatchIndex--) {
var patch = this.patches[lastPatchIndex];
if (end >= patch.start && end < patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
// TODO handle moves
// TODO this is weird, rewrite it
if (patch.start > end) continue;
break;
}
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
for (firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1) {
var patch = this.patches[firstPatchIndex];
if (start > patch.start && start <= patch.end) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
if (chunk.end <= start) continue;
if (chunk.start >= end) break;
if (start <= patch.start) {
break;
}
}
if (chunk.start < start || chunk.end > end) {
if (chunk.edited) throw new Error('Cannot use replaced characters (' + start + ', ' + end + ') as slice anchors');
var result = '';
var lastIndex = start;
var sliceStart = Math.max(start - chunk.start, 0);
var sliceEnd = Math.min(chunk.content.length - (chunk.end - end), chunk.content.length);
for (var i = firstPatchIndex; i <= lastPatchIndex; i += 1) {
var patch = this.patches[i];
result += this.original.slice(lastIndex, patch.start);
result += patch.content;
lastIndex = patch.end;
result += chunk.content.slice(sliceStart, sliceEnd);
} else {
result += chunk.content;
}
}
result += this.original.slice(lastIndex, end);
return result;

@@ -573,4 +641,47 @@ },

},
_sortChunks: function _sortChunks() {
var chunks = this.chunks.slice();
// TODO there must be a better way than this...
this.moves.forEach(function (move, i) {
var firstIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.start;
});
var lastIndex = chunks.findIndex(function (chunk, i) {
return i >= firstIndex && chunk.end === move.end;
}) + 1;
if (!lastIndex) lastIndex = chunks.length;
var insertionIndex = chunks.findIndex(function (chunk) {
return chunk.start === move.index;
});
if (! ~insertionIndex) insertionIndex = chunks.length;
var num = lastIndex - firstIndex;
if (firstIndex < insertionIndex) insertionIndex -= num;
var toMove = chunks.splice(firstIndex, num);
chunks.splice.apply(chunks, [insertionIndex, 0].concat(toMove));
});
return chunks;
},
_split: function _split(index) {
// TODO bisect
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (chunk.start === index || chunk.end === index) return;
if (chunk.start < index && chunk.end > index) {
var newChunk = chunk.split(index);
this.chunks.splice(i + 1, 0, newChunk);
return;
}
}
},
toString: function toString() {
return this.intro + this.slice(0, this.original.length) + this.outro;
return this.intro + this._sortChunks().map(function (chunk) {
return chunk.content;
}).join('') + this.outro;
},

@@ -590,17 +701,17 @@ trimLines: function trimLines() {

var charIndex = this.original.length;
var i = this.patches.length;
var i = this.chunks.length;
while (i--) {
var patch = this.patches[i];
var chunk = this.chunks[i];
if (charIndex > patch.end) {
var _slice = this.original.slice(patch.end, charIndex);
if (charIndex > chunk.end) {
var _slice = this.original.slice(chunk.end, charIndex);
var _match = rx.exec(_slice);
if (_match) {
this.patch(charIndex - _match[0].length, charIndex, '');
this.chunk(charIndex - _match[0].length, charIndex, '');
}
if (!_match || _match[0].length < _slice.length) {
// there is non-whitespace after the patch
// there is non-whitespace after the chunk
return this;

@@ -610,6 +721,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.start;
charIndex = chunk.start;
}

@@ -620,3 +731,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex - match[0].length, charIndex, '');
if (match) this.chunk(charIndex - match[0].length, charIndex, '');

@@ -633,13 +744,13 @@ return this;

for (var i = 0; i < this.patches.length; i += 1) {
var patch = this.patches[i];
for (var i = 0; i < this.chunks.length; i += 1) {
var chunk = this.chunks[i];
if (charIndex < patch.start) {
var _slice2 = this.original.slice(charIndex, patch.start);
if (charIndex < chunk.start) {
var _slice2 = this.original.slice(charIndex, chunk.start);
var _match2 = rx.exec(_slice2);
if (_match2) this.patch(charIndex, charIndex + _match2[0].length, '');
if (_match2) this.chunk(charIndex, charIndex + _match2[0].length, '');
if (!_match2 || _match2[0].length < _slice2.length) {
// there is non-whitespace before the patch
// there is non-whitespace before the chunk
return this;

@@ -649,6 +760,6 @@ }

patch.content = patch.content.replace(rx, '');
if (patch.content) return this;
chunk.content = chunk.content.replace(rx, '');
if (chunk.content) return this;
charIndex = patch.end;
charIndex = chunk.end;
}

@@ -659,3 +770,3 @@

var match = rx.exec(slice);
if (match) this.patch(charIndex, charIndex + match[0].length, '');
if (match) this.chunk(charIndex, charIndex + match[0].length, '');

@@ -756,3 +867,3 @@ return this;

var prefix = i > 0 ? getSemis(source.separator) || ',' : '';
var mappings = undefined;
var mappings = void 0;

@@ -857,3 +968,3 @@ // we don't bother encoding sources without a filename

if (!this.intro) {
var source = undefined;
var source = void 0;
var i = 0;

@@ -878,3 +989,3 @@

var source = undefined;
var source = void 0;
var i = this.sources.length - 1;

@@ -881,0 +992,0 @@

@@ -5,3 +5,3 @@ {

"author": "Rich Harris",
"version": "0.10.2",
"version": "0.11.0-alpha",
"repository": "https://github.com/rich-harris/magic-string",

@@ -19,3 +19,3 @@ "main": "dist/magic-string.cjs.js",

"es6-promise": "^3.0.2",
"eslint": "^1.10.3",
"eslint": "^2.0.0",
"istanbul": "^0.4.1",

@@ -25,3 +25,3 @@ "mocha": "^2.3.4",

"resolve": "^1.1.6",
"rollup": "^0.24.0",
"rollup": "^0.25.0",
"rollup-plugin-babel": "^2.3.5",

@@ -28,0 +28,0 @@ "rollup-plugin-npm": "^1.2.0",

@@ -1,2 +0,2 @@

import Patch from './Patch.js';
import Chunk from './Chunk.js';
import SourceMap from './utils/SourceMap.js';

@@ -11,2 +11,4 @@ import guessIndent from './utils/guessIndent.js';

export default function MagicString ( string, options = {} ) {
const chunk = new Chunk( 0, string.length, string );
Object.defineProperties( this, {

@@ -16,3 +18,4 @@ original: { writable: true, value: string },

intro: { writable: true, value: '' },
patches: { writable: true, value: [] },
chunks: { writable: true, value: [ chunk ] },
moves: { writable: true, value: [] },
filename: { writable: true, value: options.filename },

@@ -41,3 +44,3 @@ indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },

cloned.patches = this.patches.map( patch => patch.clone() );
cloned.chunks = this.chunks.map( chunk => chunk.clone() );

@@ -76,3 +79,3 @@ if ( this.indentExclusionRanges ) {

getMappings ( hires, sourceIndex, offsets, names ) {
return encodeMappings( this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names );
return encodeMappings( this.original, this.intro, this.chunks, hires, this.sourcemapLocations, sourceIndex, offsets, names );
},

@@ -115,42 +118,51 @@

let chunkIndex;
let charIndex = 0;
let patchIndex = 0;
const indentUntil = end => {
while ( charIndex < end ) {
for ( chunkIndex = 0; chunkIndex < this.chunks.length; chunkIndex += 1 ) { // can't cache this.chunks.length, it may change
let chunk = this.chunks[ chunkIndex ];
const end = chunk.end;
if ( chunk.edited ) {
if ( !isExcluded[ charIndex ] ) {
const char = this.original[ charIndex ];
chunk.content = chunk.content.replace( pattern, replacer );
if ( char === '\n' ) {
shouldIndentNextCharacter = true;
} else if ( char !== '\r' && shouldIndentNextCharacter ) {
this.patches.splice( patchIndex, 0, new Patch( charIndex, charIndex, indentStr, '', false ) );
shouldIndentNextCharacter = false;
patchIndex += 1;
if ( chunk.content.length ) {
shouldIndentNextCharacter = chunk.content[ chunk.content.length - 1 ] === '\n';
}
}
} else {
charIndex = chunk.start;
charIndex += 1;
}
};
while ( charIndex < end ) {
if ( !isExcluded[ charIndex ] ) {
const char = this.original[ charIndex ];
for ( ; patchIndex < this.patches.length; patchIndex += 1 ) { // can't cache this.patches.length, it may change
const patch = this.patches[ patchIndex ];
if ( char === '\n' ) {
shouldIndentNextCharacter = true;
} else if ( char !== '\r' && shouldIndentNextCharacter ) {
shouldIndentNextCharacter = false;
indentUntil( patch.start );
const indentation = new Chunk( charIndex, charIndex, '' ).edit( indentStr, false );
const remainder = chunk.split( charIndex );
if ( !isExcluded[ charIndex ] ) {
patch.content = patch.content.replace( pattern, replacer );
if ( charIndex === chunk.start ) {
this.chunks.splice( chunkIndex, 0, indentation );
chunkIndex += 1;
} else {
this.chunks.splice( chunkIndex + 1, 0, indentation, remainder );
chunkIndex += 2;
}
if ( patch.content.length ) {
shouldIndentNextCharacter = patch.content[ patch.content.length - 1 ] === '\n';
chunk = remainder;
}
}
charIndex += 1;
}
}
charIndex = patch.end;
charIndex = chunk.end;
}
indentUntil( this.original.length );
this.outro = this.outro.replace( pattern, replacer );

@@ -162,7 +174,12 @@

insert ( index, content ) {
if ( typeof content !== 'string' ) {
throw new TypeError( 'inserted content must be a string' );
}
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
this.patch( index, index, content );
this._split( index );
let next = this.chunks.findIndex( chunk => chunk.end > index );
if ( !~next ) next = this.chunks.length;
const newChunk = new Chunk( index, index, '' ).edit( content, false );
this.chunks.splice( next, 0, newChunk );
return this;

@@ -180,2 +197,14 @@ },

move ( start, end, index ) {
if ( index >= start && index <= end ) throw new Error( 'Cannot move a selection inside itself' );
this._split( start );
this._split( end );
this._split( index );
this.moves.push({ start, end, index });
return this;
},
overwrite ( start, end, content, storeName ) {

@@ -186,46 +215,19 @@ if ( typeof content !== 'string' ) {

this.patch( start, end, content, storeName );
return this;
},
this._split( start );
this._split( end );
patch ( start, end, content, storeName ) {
const original = this.original.slice( start, end );
if ( storeName ) this.storedNames[ original ] = true;
if ( storeName ) {
const original = this.original.slice( start, end );
this.storedNames[ original ] = true;
}
let i = this.patches.length;
while ( i-- ) {
const previous = this.patches[i];
let firstIndex = this.chunks.findIndex( chunk => chunk.start === start && chunk.original.length );
let lastIndex = this.chunks.findIndex( chunk => chunk.start === end );
if ( !~firstIndex ) firstIndex = this.chunks.length;
if ( !~lastIndex ) lastIndex = this.chunks.length;
// TODO can we tidy this up?
const newChunk = new Chunk( start, end, this.original.slice( start, end ) ).edit( content, storeName );
// if this completely covers previous patch, remove it
if ( start !== end && start <= previous.start && end >= previous.end ) {
// unless it's an insert at the start
if ( previous.start === previous.end && previous.start === start ) break;
// or it's an insert at the end
if ( previous.start === previous.end && previous.end === end ) continue;
this.patches.splice( i, 1 );
}
// if it overlaps, throw error
else if ( start < previous.end && end > previous.start ) {
// special case – it's okay to remove overlapping ranges
if ( !previous.content.length && !content.length ) {
previous.start = Math.min( start, previous.start );
previous.end = Math.max( end, previous.end );
return;
}
throw new Error( `Cannot overwrite the same content twice: '${original}'` );
}
// if this precedes previous patch, stop search
else if ( start >= previous.end ) {
break;
}
}
const patch = new Patch( start, end, content, original, storeName );
this.patches.splice( i + 1, 0, patch );
return patch;
this.chunks.splice( firstIndex, lastIndex - firstIndex, newChunk );
return this;
},

@@ -241,7 +243,34 @@

remove ( start, end ) {
if ( start < 0 || end > this.original.length ) {
throw new Error( 'Character is out of bounds' );
while ( start < 0 ) start += this.original.length;
while ( end < 0 ) end += this.original.length;
if ( start === end ) return this;
if ( start < 0 || end > this.original.length ) throw new Error( 'Character is out of bounds' );
if ( start > end ) throw new Error( 'end must be greater than start' );
let firstIndex = this.chunks.findIndex( chunk => chunk.start <= start && chunk.end > start );
let chunk = this.chunks[ firstIndex ];
// if the chunk contains `start`, split
if ( chunk.start < start ) {
if ( chunk.edited && chunk.content.length ) throw new Error( 'nope' );
this._split( start );
firstIndex += 1;
}
this.patch( start, end, '' );
let lastIndex = this.chunks.findIndex( chunk => chunk.start < end && chunk.end >= end );
chunk = this.chunks[ lastIndex ];
// if the chunk contains `end`, split
if ( chunk.start < end ) {
if ( chunk.edited && chunk.content.length ) throw new Error( 'nope' );
this._split( end );
}
lastIndex += 1;
const newChunk = new Chunk( start, end, this.original.slice( start, end ) ).edit( '', false );
this.chunks.splice( firstIndex, lastIndex - firstIndex, newChunk );
return this;

@@ -263,36 +292,24 @@ },

let firstPatchIndex = 0;
let lastPatchIndex = this.patches.length;
let result = '';
while ( lastPatchIndex-- ) {
const patch = this.patches[ lastPatchIndex ];
if ( end >= patch.start && end < patch.end ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` );
// TODO handle moves
// TODO this is weird, rewrite it
if ( patch.start > end ) continue;
break;
}
for ( let i = 0; i < this.chunks.length; i += 1 ) {
const chunk = this.chunks[i];
for ( firstPatchIndex = 0; firstPatchIndex <= lastPatchIndex; firstPatchIndex += 1 ) {
const patch = this.patches[ firstPatchIndex ];
if ( start > patch.start && start <= patch.end ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` );
if ( chunk.end <= start ) continue;
if ( chunk.start >= end ) break;
if ( start <= patch.start ) {
break;
}
}
if ( chunk.start < start || chunk.end > end ) {
if ( chunk.edited ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` );
let result = '';
let lastIndex = start;
const sliceStart = Math.max( start - chunk.start, 0 );
const sliceEnd = Math.min( chunk.content.length - ( chunk.end - end ), chunk.content.length );
for ( let i = firstPatchIndex; i <= lastPatchIndex; i += 1 ) {
const patch = this.patches[i];
result += this.original.slice( lastIndex, patch.start );
result += patch.content;
lastIndex = patch.end;
result += chunk.content.slice( sliceStart, sliceEnd );
} else {
result += chunk.content;
}
}
result += this.original.slice( lastIndex, end );
return result;

@@ -309,4 +326,41 @@ },

_sortChunks () {
let chunks = this.chunks.slice();
// TODO there must be a better way than this...
this.moves.forEach( ( move, i ) => {
let firstIndex = chunks.findIndex( chunk => chunk.start === move.start );
let lastIndex = chunks.findIndex( ( chunk, i ) => i >= firstIndex && chunk.end === move.end ) + 1;
if ( !lastIndex ) lastIndex = chunks.length;
let insertionIndex = chunks.findIndex( chunk => chunk.start === move.index );
if ( !~insertionIndex ) insertionIndex = chunks.length;
const num = lastIndex - firstIndex;
if ( firstIndex < insertionIndex ) insertionIndex -= num;
const toMove = chunks.splice( firstIndex, num );
chunks.splice.apply( chunks, [ insertionIndex, 0 ].concat( toMove ) );
});
return chunks;
},
_split ( index ) {
// TODO bisect
for ( let i = 0; i < this.chunks.length; i += 1 ) {
const chunk = this.chunks[i];
if ( chunk.start === index || chunk.end === index ) return;
if ( chunk.start < index && chunk.end > index ) {
const newChunk = chunk.split( index );
this.chunks.splice( i + 1, 0, newChunk );
return;
}
}
},
toString () {
return this.intro + this.slice( 0, this.original.length ) + this.outro;
return this.intro + this._sortChunks().map( chunk => chunk.content ).join( '' ) + this.outro;
},

@@ -329,17 +383,17 @@

let charIndex = this.original.length;
let i = this.patches.length;
let i = this.chunks.length;
while ( i-- ) {
const patch = this.patches[i];
const chunk = this.chunks[i];
if ( charIndex > patch.end ) {
const slice = this.original.slice( patch.end, charIndex );
if ( charIndex > chunk.end ) {
const slice = this.original.slice( chunk.end, charIndex );
const match = rx.exec( slice );
if ( match ) {
this.patch( charIndex - match[0].length, charIndex, '' );
this.chunk( charIndex - match[0].length, charIndex, '' );
}
if ( !match || match[0].length < slice.length ) {
// there is non-whitespace after the patch
// there is non-whitespace after the chunk
return this;

@@ -349,6 +403,6 @@ }

patch.content = patch.content.replace( rx, '' );
if ( patch.content ) return this;
chunk.content = chunk.content.replace( rx, '' );
if ( chunk.content ) return this;
charIndex = patch.start;
charIndex = chunk.start;
}

@@ -359,3 +413,3 @@

const match = rx.exec( slice );
if ( match ) this.patch( charIndex - match[0].length, charIndex, '' );
if ( match ) this.chunk( charIndex - match[0].length, charIndex, '' );

@@ -373,13 +427,13 @@ return this;

for ( let i = 0; i < this.patches.length; i += 1 ) {
const patch = this.patches[i];
for ( let i = 0; i < this.chunks.length; i += 1 ) {
const chunk = this.chunks[i];
if ( charIndex < patch.start ) {
const slice = this.original.slice( charIndex, patch.start );
if ( charIndex < chunk.start ) {
const slice = this.original.slice( charIndex, chunk.start );
const match = rx.exec( slice );
if ( match ) this.patch( charIndex, charIndex + match[0].length, '' );
if ( match ) this.chunk( charIndex, charIndex + match[0].length, '' );
if ( !match || match[0].length < slice.length ) {
// there is non-whitespace before the patch
// there is non-whitespace before the chunk
return this;

@@ -389,6 +443,6 @@ }

patch.content = patch.content.replace( rx, '' );
if ( patch.content ) return this;
chunk.content = chunk.content.replace( rx, '' );
if ( chunk.content ) return this;
charIndex = patch.end;
charIndex = chunk.end;
}

@@ -399,3 +453,3 @@

const match = rx.exec( slice );
if ( match ) this.patch( charIndex, charIndex + match[0].length, '' );
if ( match ) this.chunk( charIndex, charIndex + match[0].length, '' );

@@ -402,0 +456,0 @@ return this;

import { encode } from 'vlq';
export default function encodeMappings ( original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) {
export default function encodeMappings ( original, intro, chunks, hires, sourcemapLocations, sourceIndex, offsets, names ) {
let rawLines = [];

@@ -46,41 +46,42 @@

for ( let i = 0; i < patches.length; i += 1 ) {
const patch = patches[i];
const addSegmentForPatch = patch.storeName || patch.start > originalCharIndex;
for ( let i = 0; i < chunks.length; i += 1 ) {
const chunk = chunks[i];
addSegmentsUntil( patch.start );
if ( chunk.edited ) {
if ( i > 0 || chunk.content.length ) {
rawSegments.push({
generatedCodeLine,
generatedCodeColumn,
sourceCodeLine,
sourceCodeColumn,
sourceCodeName: chunk.storeName ? names.indexOf( chunk.original ) : -1,
sourceIndex
});
}
if ( addSegmentForPatch ) {
rawSegments.push({
generatedCodeLine,
generatedCodeColumn,
sourceCodeLine,
sourceCodeColumn,
sourceCodeName: patch.storeName ? names.indexOf( patch.original ) : -1,
sourceIndex
});
}
let lines = chunk.content.split( '\n' );
let lastLine = lines.pop();
let lines = patch.content.split( '\n' );
let lastLine = lines.pop();
if ( lines.length ) {
generatedCodeLine += lines.length;
rawLines[ generatedCodeLine ] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
if ( lines.length ) {
generatedCodeLine += lines.length;
rawLines[ generatedCodeLine ] = rawSegments = [];
generatedCodeColumn = lastLine.length;
} else {
generatedCodeColumn += lastLine.length;
}
lines = chunk.original.split( '\n' );
lastLine = lines.pop();
lines = patch.original.split( '\n' );
lastLine = lines.pop();
if ( lines.length ) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
if ( lines.length ) {
sourceCodeLine += lines.length;
sourceCodeColumn = lastLine.length;
} else {
sourceCodeColumn += lastLine.length;
}
} else {
sourceCodeColumn += lastLine.length;
addSegmentsUntil( chunk.end );
}
originalCharIndex = patch.end;
originalCharIndex = chunk.end;
}

@@ -87,0 +88,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet