Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added 'isLocated' flag to operations that receive indexes as arguments. #26

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ sandbox
!.babelrc
coverage
dist
.idea
69 changes: 45 additions & 24 deletions src/MagicString/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import getRelativePath from '../utils/getRelativePath';

let warned = false;


class MagicString {
constructor ( string, options = {} ) {
Object.defineProperties( this, {
Expand Down Expand Up @@ -80,7 +81,7 @@ class MagicString {
return encodeMappings( this.original, this.str, this.mappings, hires, this.sourcemapLocations, sourceIndex, offsets, names, this.nameLocations );
}

indent ( indentStr, options ) {
indent ( indentStr, options = {} ) {
const mappings = this.mappings;
const reverseMappings = reverse( mappings, this.str.length );
const pattern = /^[^\r\n]/gm;
Expand All @@ -94,8 +95,6 @@ class MagicString {

if ( indentStr === '' ) return this; // noop

options = options || {};

// Process exclusion ranges
let exclusions;

Expand Down Expand Up @@ -186,22 +185,22 @@ class MagicString {
}
}

insert ( index, content ) {
insert ( index, content, isLocated) {
if ( typeof content !== 'string' ) {
throw new TypeError( 'inserted content must be a string' );
}

if ( index === this.original.length ) {
var origins = extractOrigins(isLocated, this, index, index);
if ( origins.end === this.original.length ) {
this.append( content );
} else {
const mapped = this.locate( index );
const mapped = isLocated? index : this.locate( index );

if ( mapped === null ) {
throw new Error( 'Cannot insert at replaced character index: ' + index );
}

this.str = this.str.substr( 0, mapped ) + content + this.str.substr( mapped );
adjust( this.mappings, index, this.mappings.length, content.length );
adjust( this.mappings, origins.end, this.mappings.length, content.length );
}

return this;
Expand All @@ -210,7 +209,7 @@ class MagicString {
// get current location of character in original string
locate ( character ) {
if ( character < 0 || character > this.mappings.length ) {
throw new Error( 'Character is out of bounds' );
throw new Error( `Character ${character} is out of bounds` );
}

const loc = this.mappings[ character ];
Expand All @@ -232,16 +231,18 @@ class MagicString {
return null;
}

overwrite ( start, end, content, storeName ) {
overwrite ( start, end, content, storeName, isLocated) {
if ( typeof content !== 'string' ) {
throw new TypeError( 'replacement content must be a string' );
}

const firstChar = this.locate( start );
const lastChar = this.locate( end - 1 );
const firstChar = isLocated? start : this.locate( start );
const lastChar = isLocated? end - 1 : this.locate( end - 1 );

var origins = extractOrigins(isLocated, this, start, end);

if ( firstChar === null || lastChar === null ) {
throw new Error( `Cannot overwrite the same content twice: '${this.original.slice(start, end).replace(/\n/g, '\\n')}'` );
throw new Error( `Cannot overwrite the same content twice: '${this.original.slice(origins.start, origins.end).replace(/\n/g, '\\n')}'` );
}

if ( firstChar > lastChar + 1 ) {
Expand All @@ -259,8 +260,8 @@ class MagicString {

const d = content.length - ( lastChar + 1 - firstChar );

blank( this.mappings, start, end );
adjust( this.mappings, end, this.mappings.length, d );
blank( this.mappings, origins.start, origins.end );
adjust( this.mappings, origins.end, this.mappings.length, d );
return this;
}

Expand All @@ -270,14 +271,15 @@ class MagicString {
return this;
}

remove ( start, end ) {
if ( start < 0 || end > this.mappings.length ) {
remove ( start, end, isLocated ) {
if ( start < 0 || end > (isLocated? this.str : this.mappings).length) {
throw new Error( 'Character is out of bounds' );
}
var origins = extractOrigins(isLocated, this, start, end);

let currentStart = -1;
let currentEnd = -1;
for ( let i = start; i < end; i += 1 ) {
for ( let i = origins.start; i < origins.end; i += 1 ) {
const loc = this.mappings[i];

if ( ~loc ) {
Expand All @@ -290,7 +292,7 @@ class MagicString {

this.str = this.str.slice( 0, currentStart ) + this.str.slice( currentEnd );

adjust( this.mappings, end, this.mappings.length, currentStart - currentEnd );
adjust( this.mappings, origins.end, this.mappings.length, currentStart - currentEnd );
return this;
}

Expand All @@ -303,12 +305,12 @@ class MagicString {
return this.overwrite( start, end, content );
}

slice ( start, end = this.original.length ) {
while ( start < 0 ) start += this.original.length;
while ( end < 0 ) end += this.original.length;
slice ( start, end = this.original.length, isLocated ) {
while ( start < 0 ) start += (isLocated? this.str : this.original).length;
while ( end < 0 ) end += (isLocated? this.str : this.original).length;

const firstChar = this.locate( start );
const lastChar = this.locate( end - 1 );
const firstChar = isLocated ? start : this.locate( start );
const lastChar = isLocated? end - 1 : this.locate( end - 1 );

if ( firstChar === null || lastChar === null ) {
throw new Error( 'Cannot use replaced characters as slice anchors' );
Expand Down Expand Up @@ -437,4 +439,23 @@ function reverse ( mappings, i ) {
return result;
}

function extractOrigins (notOrigins, s, start, end) {
if (notOrigins) {
var result = {};
if (end === s.str.length){
result.end = s.mappings.length;
}
let i = s.mappings.length;
while (i-- && s.mappings[i] >= start) {
if (s.mappings[i] <= end && !result.end) {
result.end = i;
}
result.start = i;
}
return result;
} else {
return {end, start};
}
}

export default MagicString;
75 changes: 74 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,20 @@ describe( 'MagicString', function () {
assert.equal( s.locate( 11 ), 17 );
});

it( 'should insert characters in the correct location when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl' );

s.insert( s.locate(0), '>>>', true );
s.insert( s.locate(6), '***', true );
s.insert( s.toString().length, '<<<', true );

assert.equal( s.toString(), '>>>abcdef***ghijkl<<<' );
assert.equal( s.locate( 0 ), 3 );
assert.equal( s.locate( 5 ), 8 );
assert.equal( s.locate( 6 ), 12 );
assert.equal( s.locate( 11 ), 17 );
});

it( 'should return this', function () {
var s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.insert( 0, 'a' ), s );
Expand Down Expand Up @@ -549,6 +563,16 @@ describe( 'MagicString', function () {
assert.equal( s.toString(), 'afghi' );
});

it( 'should remove characters from the original string when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl' );

s.remove( s.locate(1), s.locate(5), true );
assert.equal( s.toString(), 'afghijkl' );

s.remove( s.locate(9), s.toString().length, true );
assert.equal( s.toString(), 'afghi' );
});

it( 'should remove overlapping ranges', function () {
var s = new MagicString( 'abcdefghijkl' );

Expand Down Expand Up @@ -584,14 +608,21 @@ describe( 'MagicString', function () {
});
});

describe( 'replace', function () {
describe( 'overwrite', function () {
it( 'should replace characters', function () {
var s = new MagicString( 'abcdefghijkl' );

s.overwrite( 5, 8, 'FGH' );
assert.equal( s.toString(), 'abcdeFGHijkl' );
});

it( 'should replace characters when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl' );

s.overwrite( s.locate(5), s.locate(8), 'FGH', true );
assert.equal( s.toString(), 'abcdeFGHijkl' );
});

it( 'should throw an error if overlapping replacements are attempted', function () {
var s = new MagicString( 'abcdefghijkl' );

Expand All @@ -605,6 +636,21 @@ describe( 'MagicString', function () {
assert.equal( s.toString(), 'abcdefyes' );
});

it( 'should not throw an error if overlapping replacements are attempted when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl' );

s.overwrite( 7, 11, 'xx' );
s.overwrite( 7, 9, 'yyyyy', 'FGH', true );
assert.equal( s.toString(), 'abcdefgyyyyyl' );

s.overwrite( 1, 3, 'yes' );
assert.equal( s.toString(), 'ayesdefgyyyyyl' );

s.overwrite( 1, 8, 'foo', 'FGH', true );
assert.equal( s.toString(), 'afooyyyyyl' );

});

it( 'should replace characters at the end of the original string', function () {
var s = new MagicString( 'abcdefghijkl' );

Expand Down Expand Up @@ -641,6 +687,20 @@ describe( 'MagicString', function () {
});
});

it( 'should return the generated content between the specified original characters when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl' );

assert.equal( s.slice( s.locate(3), s.locate(9), true ), 'defghi' );
s.overwrite( 4, 8, 'XX' );
assert.equal( s.slice( s.locate(3), s.locate(9), true ), 'dXXi' );
s.overwrite( 2, 10, 'ZZ' );
assert.equal( s.slice( s.locate(1), s.locate(11), true ), 'bZZk' );

assert.throws( function () {
s.slice( s.locate(2), s.locate(10), true );
});
});

it( 'defaults `end` to the original string length', function () {
var s = new MagicString( 'abcdefghijkl' );
assert.equal( s.slice( 3 ), 'defghijkl' );
Expand Down Expand Up @@ -690,6 +750,19 @@ describe( 'MagicString', function () {
assert.equal( snippet.filename, 'foo.js' );
});

it( 'should return a clone with content outside `start` and `end` removed when using located indexes', function () {
var s = new MagicString( 'abcdefghijkl', {
filename: 'foo.js'
});

s.overwrite( 6, 9, 'GHI' );

var snippet = s.snip( s.locate(3), s.locate(9), true );
assert.equal( snippet.toString(), 'defGHI' );
assert.equal( snippet.locate( 0, 3 ) );
assert.equal( snippet.filename, 'foo.js' );
});

it( 'should respect original indices', function () {
var s = new MagicString( 'abcdefghijkl' );
var snippet = s.snip( 3, 9 );
Expand Down