From 26d688fedb03d4b1abe30ea3fae320e4665337d6 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 13:37:11 -0500 Subject: [PATCH 01/26] first stab at patches --- package.json | 1 + src/MagicString.js | 148 ++++++++++++------------ src/Patch.js | 12 ++ test/index.js | 282 +++++++++++++++++++++++---------------------- 4 files changed, 230 insertions(+), 213 deletions(-) create mode 100644 src/Patch.js diff --git a/package.json b/package.json index dd6c4f9..2e52de7 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "devDependencies": { "babel-preset-es2015-rollup": "^1.0.0", "codecov.io": "^0.1.6", + "console-group": "^0.1.2", "es6-promise": "^3.0.2", "eslint": "^1.10.3", "istanbul": "^0.4.0", diff --git a/src/MagicString.js b/src/MagicString.js index f3c6fe0..e9e7018 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -1,3 +1,4 @@ +import Patch from './Patch.js'; import SourceMap from './utils/SourceMap.js'; import guessIndent from './utils/guessIndent.js'; import encodeMappings from './utils/encodeMappings.js'; @@ -9,8 +10,9 @@ let warned = false; export default function MagicString ( string, options = {} ) { Object.defineProperties( this, { original: { writable: true, value: string }, - str: { writable: true, value: string }, - mappings: { writable: true, value: initMappings( string.length ) }, + appended: { writable: true, value: '' }, + prepended: { writable: true, value: '' }, + patches: { writable: true, value: [] }, filename: { writable: true, value: options.filename }, indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, sourcemapLocations: { writable: true, value: {} }, @@ -25,22 +27,16 @@ MagicString.prototype = { }, append ( content ) { - if ( typeof content !== 'string' ) { - throw new TypeError( 'appended content must be a string' ); - } + if ( typeof content !== 'string' ) throw new TypeError( 'appended content must be a string' ); - this.str += content; + this.appended += content; return this; }, clone () { let cloned = new MagicString( this.original, { filename: this.filename }); - cloned.str = this.str; - let i = cloned.mappings.length; - while ( i-- ) { - cloned.mappings[i] = this.mappings[i]; - } + cloned.patches = this.patches.map( patch => patch.clone() ); if ( this.indentExclusionRanges ) { cloned.indentExclusionRanges = typeof this.indentExclusionRanges[0] === 'number' ? @@ -192,30 +188,13 @@ MagicString.prototype = { throw new TypeError( 'inserted content must be a string' ); } - if ( index === this.original.length ) { - this.append( content ); - } else { - const mapped = 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 ); - } - + this.patch( index, index, content ); return this; }, // 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' ); - } - - const loc = this.mappings[ character ]; - return ~loc ? loc : null; + throw new Error( 'magicString.locate is deprecated' ); }, locateOrigin ( character ) { @@ -238,60 +217,53 @@ MagicString.prototype = { throw new TypeError( 'replacement content must be a string' ); } - const firstChar = this.locate( start ); - const lastChar = this.locate( end - 1 ); + this.patch( start, end, content, this.original.slice( start, end ) ); + return this; + }, - if ( firstChar === null || lastChar === null ) { - throw new Error( `Cannot overwrite the same content twice: '${this.original.slice(start, end).replace(/\n/g, '\\n')}'` ); - } + patch ( start, end, content ) { + const original = this.original.slice( start, end ); + const patch = new Patch( start, end, content, original ); - if ( firstChar > lastChar + 1 ) { - throw new Error( - 'BUG! First character mapped to a position after the last character: ' + - '[' + start + ', ' + end + '] -> [' + firstChar + ', ' + ( lastChar + 1 ) + ']' - ); - } + let i = this.patches.length; + while ( i-- ) { + const previous = this.patches[i]; - if ( storeName ) { - this.nameLocations[ start ] = this.original.slice( start, end ); - } + // TODO can we tidy this up? + + // if this completely covers previous patch, remove it + if ( start !== end && start <= previous.start && end >= previous.end ) { + this.patches.splice( i, 1 ); + } - this.str = this.str.substr( 0, firstChar ) + content + this.str.substring( lastChar + 1 ); + // if it overlaps, throw error + else if ( start < previous.end && end > previous.end ) { + throw new Error( `Cannot overwrite the same content twice: '${original}'` ); + } - const d = content.length - ( lastChar + 1 - firstChar ); + // if this precedes previous patch, stop search + else if ( start >= previous.end ) { + break; + } + } - blank( this.mappings, start, end ); - adjust( this.mappings, end, this.mappings.length, d ); - return this; + this.patches.splice( i + 1, 0, patch ); + return patch; }, prepend ( content ) { - this.str = content + this.str; - adjust( this.mappings, 0, this.mappings.length, content.length ); + if ( typeof content !== 'string' ) throw new TypeError( 'appended content must be a string' ); + + this.prepended = content + this.prepended; return this; }, remove ( start, end ) { - if ( start < 0 || end > this.mappings.length ) { + if ( start < 0 || end > this.original.length ) { throw new Error( 'Character is out of bounds' ); } - let currentStart = -1; - let currentEnd = -1; - for ( let i = start; i < end; i += 1 ) { - const loc = this.mappings[i]; - - if ( ~loc ) { - if ( !~currentStart ) currentStart = loc; - - currentEnd = loc + 1; - this.mappings[i] = -1; - } - } - - this.str = this.str.slice( 0, currentStart ) + this.str.slice( currentEnd ); - - adjust( this.mappings, end, this.mappings.length, currentStart - currentEnd ); + this.patch( start, end, '' ); return this; }, @@ -308,14 +280,41 @@ MagicString.prototype = { while ( start < 0 ) start += this.original.length; while ( end < 0 ) end += this.original.length; - const firstChar = this.locate( start ); - const lastChar = this.locate( end - 1 ); + let firstPatchIndex = 0; + let lastPatchIndex = this.patches.length; - if ( firstChar === null || lastChar === null ) { - throw new Error( 'Cannot use replaced characters as slice anchors' ); + 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 this is weird, rewrite it + if ( patch.start >= end ) continue; + break; + } + + 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 ( start <= patch.start ) { + break; + } } - return this.str.slice( firstChar, lastChar + 1 ); + let result = ''; + let lastIndex = start; + + 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 += this.original.slice( lastIndex, end ); + + return result; }, snip ( start, end ) { @@ -327,7 +326,8 @@ MagicString.prototype = { }, toString () { - return this.str; + if ( !this.patches.length ) return this.original; + return this.prepended + this.slice( 0, this.original.length ) + this.appended; }, trimLines () { diff --git a/src/Patch.js b/src/Patch.js new file mode 100644 index 0000000..cc07702 --- /dev/null +++ b/src/Patch.js @@ -0,0 +1,12 @@ +export default function Patch ( start, end, content, original ) { + this.start = start; + this.end = end; + this.content = content; + this.original = original || null; +} + +Patch.prototype = { + clone () { + return new Patch( this.start, this.end, this.content, this.original ); + } +}; diff --git a/test/index.js b/test/index.js index 9ca5fc9..ac0e39c 100644 --- a/test/index.js +++ b/test/index.js @@ -3,6 +3,8 @@ var assert = require( 'assert' ); var SourceMapConsumer = require( 'source-map' ).SourceMapConsumer; var MagicString = require( '../' ); +require( 'console-group' ).install(); + describe( 'MagicString', function () { describe( 'options', function () { it( 'stores source file information', function () { @@ -354,10 +356,6 @@ describe( 'MagicString', function () { s.insert( 12, '<<<' ); 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 () { @@ -387,138 +385,147 @@ describe( 'MagicString', function () { }); describe( 'locate', function () { - it( 'should correctly locate characters in an unmodified string', function () { - var s = new MagicString( 'abcdefghijkl' ); - assert.equal( s.locate( 0 ), 0 ); - assert.equal( s.locate( 6 ), 6 ); - assert.equal( s.locate( 11 ), 11 ); - }); - - it ( 'should throw an error if character is out of bounds', function () { - var s = new MagicString( 'abcdefghijkl' ); - - assert.throws( function () { s.locate( -1 ); }); - assert.throws( function () { s.locate( 13 ); }); - }); - - it( 'should correctly locate characters in a string with characters removed', function () { - var s = new MagicString( 'abcdefghijkl' ); - - s.remove( 1, 5 ); - assert.equal( s.locate( 0 ), 0 ); - assert.equal( s.locate( 1 ), null ); - assert.equal( s.locate( 2 ), null ); - assert.equal( s.locate( 5 ), 1 ); - assert.equal( s.locate( 11 ), 7 ); - }); - - it( 'should correctly locate characters in a string with characters replaced', function () { - var s = new MagicString( 'abcdefghijkl' ); - - s.overwrite( 5, 8, 'FGH' ); - assert.equal( s.locate( 0 ), 0 ); - assert.equal( s.locate( 4 ), 4 ); - assert.equal( s.locate( 5 ), null ); - assert.equal( s.locate( 7 ), null ); - assert.equal( s.locate( 8 ), 8 ); - - s.overwrite( 1, 4, 'X' ); - assert.equal( s.toString(), 'aXeFGHijkl' ); - assert.equal( s.locate( 2 ), null ); - assert.equal( s.locate( 4 ), 2 ); - assert.equal( s.locate( 8 ), 6 ); - }); - - it( 'should correctly locate characters in a string that has had content prepended', function () { - var s = new MagicString( 'abcdefghijkl' ); - - s.prepend( 'xyz' ); - assert.equal( s.locate( 0 ), 3 ); - assert.equal( s.locate( 11 ), 14 ); - - s.prepend( 'xyz' ); - assert.equal( s.locate( 0 ), 6 ); - assert.equal( s.locate( 11 ), 17 ); - }); - - it( 'should correctly locate characters in a string that has had content appended', function () { - var s = new MagicString( 'abcdefghijkl' ); - - s.append( 'xyz' ); - assert.equal( s.locate( 0 ), 0 ); - assert.equal( s.locate( 11 ), 11 ); - - s.append( 'xyz' ); - assert.equal( s.locate( 0 ), 0 ); - assert.equal( s.locate( 11 ), 11 ); - }); - - it( 'should correctly locate characters in indented code', function () { - var s = new MagicString( 'abc\ndef\nghi\njkl' ); - - s.indent(); - - assert.equal( s.locate( 0 ), 1 ); - assert.equal( s.locate( 4 ), 6 ); - assert.equal( s.locate( 5 ), 7 ); - assert.equal( s.locate( 8 ), 11 ); - assert.equal( s.locate( 12 ), 16 ); - assert.equal( s.locate( 13 ), 17 ); - - s.indent(); - - assert.equal( s.locate( 0 ), 2 ); - assert.equal( s.locate( 4 ), 8 ); - assert.equal( s.locate( 8 ), 14 ); - assert.equal( s.locate( 12 ), 20 ); - }); - - it( 'should correctly locate characters in trimmed original content', function () { - var s = new MagicString( ' abcdefghijkl ' ); - - s.trim(); - assert.equal( s.locate( 0 ), null ); - assert.equal( s.locate( 2 ), null ); - assert.equal( s.locate( 3 ), 0 ); - assert.equal( s.locate( 14 ), 11 ); - assert.equal( s.locate( 15 ), null ); - }); - - it( 'should correctly locate characters in trimmed replaced content', function () { + it( 'is deprecated', function () { var s = new MagicString( 'abcdefghijkl' ); - - s.overwrite( 0, 3, ' ' ).overwrite( 9, 12, ' ' ).trim(); - assert.equal( s.locate( 0 ), null ); - assert.equal( s.locate( 2 ), null ); - assert.equal( s.locate( 3 ), 0 ); - - assert.equal( s.locate( 8 ), 5 ); - assert.equal( s.locate( 9 ), null ); - }); - - it( 'should correctly locate characters in trimmed appended/prepended content', function () { - var s = new MagicString( ' abcdefghijkl ' ); - - s.prepend( ' ' ).append( ' ' ).trim(); - assert.equal( s.locate( 0 ), null ); - assert.equal( s.locate( 1 ), 0 ); - assert.equal( s.locate( 12 ), 11 ); - assert.equal( s.locate( 13 ), null ); - }); + assert.throws( function () { s.locate( 6 ); }, /deprecated/ ); + }); + // it( 'should correctly locate characters in an unmodified string', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // assert.equal( s.locate( 0 ), 0 ); + // assert.equal( s.locate( 6 ), 6 ); + // assert.equal( s.locate( 11 ), 11 ); + // }); + // + // it ( 'should throw an error if character is out of bounds', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // assert.throws( function () { s.locate( -1 ); }); + // assert.throws( function () { s.locate( 13 ); }); + // }); + // + // it( 'should correctly locate characters in a string with characters removed', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // s.remove( 1, 5 ); + // assert.equal( s.locate( 0 ), 0 ); + // assert.equal( s.locate( 1 ), null ); + // assert.equal( s.locate( 2 ), null ); + // assert.equal( s.locate( 5 ), 1 ); + // assert.equal( s.locate( 11 ), 7 ); + // }); + // + // it( 'should correctly locate characters in a string with characters replaced', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // s.overwrite( 5, 8, 'FGH' ); + // assert.equal( s.locate( 0 ), 0 ); + // assert.equal( s.locate( 4 ), 4 ); + // assert.equal( s.locate( 5 ), null ); + // assert.equal( s.locate( 7 ), null ); + // assert.equal( s.locate( 8 ), 8 ); + // + // s.overwrite( 1, 4, 'X' ); + // assert.equal( s.toString(), 'aXeFGHijkl' ); + // assert.equal( s.locate( 2 ), null ); + // assert.equal( s.locate( 4 ), 2 ); + // assert.equal( s.locate( 8 ), 6 ); + // }); + // + // it( 'should correctly locate characters in a string that has had content prepended', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // s.prepend( 'xyz' ); + // assert.equal( s.locate( 0 ), 3 ); + // assert.equal( s.locate( 11 ), 14 ); + // + // s.prepend( 'xyz' ); + // assert.equal( s.locate( 0 ), 6 ); + // assert.equal( s.locate( 11 ), 17 ); + // }); + // + // it( 'should correctly locate characters in a string that has had content appended', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // s.append( 'xyz' ); + // assert.equal( s.locate( 0 ), 0 ); + // assert.equal( s.locate( 11 ), 11 ); + // + // s.append( 'xyz' ); + // assert.equal( s.locate( 0 ), 0 ); + // assert.equal( s.locate( 11 ), 11 ); + // }); + // + // it( 'should correctly locate characters in indented code', function () { + // var s = new MagicString( 'abc\ndef\nghi\njkl' ); + // + // s.indent(); + // + // assert.equal( s.locate( 0 ), 1 ); + // assert.equal( s.locate( 4 ), 6 ); + // assert.equal( s.locate( 5 ), 7 ); + // assert.equal( s.locate( 8 ), 11 ); + // assert.equal( s.locate( 12 ), 16 ); + // assert.equal( s.locate( 13 ), 17 ); + // + // s.indent(); + // + // assert.equal( s.locate( 0 ), 2 ); + // assert.equal( s.locate( 4 ), 8 ); + // assert.equal( s.locate( 8 ), 14 ); + // assert.equal( s.locate( 12 ), 20 ); + // }); + // + // it( 'should correctly locate characters in trimmed original content', function () { + // var s = new MagicString( ' abcdefghijkl ' ); + // + // s.trim(); + // assert.equal( s.locate( 0 ), null ); + // assert.equal( s.locate( 2 ), null ); + // assert.equal( s.locate( 3 ), 0 ); + // assert.equal( s.locate( 14 ), 11 ); + // assert.equal( s.locate( 15 ), null ); + // }); + // + // it( 'should correctly locate characters in trimmed replaced content', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // s.overwrite( 0, 3, ' ' ).overwrite( 9, 12, ' ' ).trim(); + // assert.equal( s.locate( 0 ), null ); + // assert.equal( s.locate( 2 ), null ); + // assert.equal( s.locate( 3 ), 0 ); + // + // assert.equal( s.locate( 8 ), 5 ); + // assert.equal( s.locate( 9 ), null ); + // }); + // + // it( 'should correctly locate characters in trimmed appended/prepended content', function () { + // var s = new MagicString( ' abcdefghijkl ' ); + // + // s.prepend( ' ' ).append( ' ' ).trim(); + // assert.equal( s.locate( 0 ), null ); + // assert.equal( s.locate( 1 ), 0 ); + // assert.equal( s.locate( 12 ), 11 ); + // assert.equal( s.locate( 13 ), null ); + // }); }); describe( 'locateOrigin', function () { - it( 'should locate the origin of characters in the generated string', function () { + it( 'is deprecated', function () { var s = new MagicString( 'abcdefghijkl' ); - - assert.equal( s.locateOrigin( 4 ), 4 ); - assert.equal( s.locateOrigin( 11 ), 11 ); - - s.remove( 1, 3 ); - assert.equal( s.locateOrigin( 0 ), 0 ); - assert.equal( s.locateOrigin( 1 ), 3 ); - assert.equal( s.locateOrigin( 2 ), 4 ); - }); + assert.throws( function () { s.locateOrigin( 6 ); }, /deprecated/ ); + }); + + // it( 'should locate the origin of characters in the generated string', function () { + // var s = new MagicString( 'abcdefghijkl' ); + // + // assert.equal( s.locateOrigin( 4 ), 4 ); + // assert.equal( s.locateOrigin( 11 ), 11 ); + // + // s.remove( 1, 3 ); + // assert.equal( s.locateOrigin( 0 ), 0 ); + // assert.equal( s.locateOrigin( 1 ), 3 ); + // assert.equal( s.locateOrigin( 2 ), 4 ); + // }); }); describe( 'overwrite', function () { @@ -651,9 +658,10 @@ describe( 'MagicString', function () { assert.equal( s.slice( 3, 9 ), 'dXXi' ); s.overwrite( 2, 10, 'ZZ' ); assert.equal( s.slice( 1, 11 ), 'bZZk' ); + assert.equal( s.slice( 2, 10 ), 'ZZ' ); assert.throws( function () { - s.slice( 2, 10 ); + s.slice( 3, 9 ); }); }); @@ -670,22 +678,18 @@ describe( 'MagicString', function () { it( 'errors if replaced characters are used as slice anchors', function () { var s = new MagicString( 'abcdef' ); - s.replace( 2, 4, 'CD' ); + s.overwrite( 2, 4, 'CD' ); assert.throws( function () { s.slice( 2, 3 ); }, /slice anchors/ ); assert.throws( function () { - s.slice( 2, 4 ); - }, /slice anchors/ ); - - assert.throws( function () { - s.slice( 1, 4 ); + s.slice( 3, 4 ); }, /slice anchors/ ); assert.throws( function () { - s.slice( 2, 5 ); + s.slice( 3, 5 ); }, /slice anchors/ ); assert.equal( s.slice( 1, 5 ), 'bCDe' ); From ac243840a41c25ee6e2f9218a201e169cf6ce2ba Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 15:07:33 -0500 Subject: [PATCH 02/26] various --- src/MagicString.js | 79 ++++++++++++---------------------------------- 1 file changed, 20 insertions(+), 59 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index e9e7018..d1b8084 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -198,18 +198,7 @@ MagicString.prototype = { }, locateOrigin ( character ) { - if ( character < 0 || character >= this.str.length ) { - throw new Error( 'Character is out of bounds' ); - } - - let i = this.mappings.length; - while ( i-- ) { - if ( this.mappings[i] === character ) { - return i; - } - } - - return null; + throw new Error( 'magicString.locateOrigin is deprecated' ); }, overwrite ( start, end, content, storeName ) { @@ -288,7 +277,7 @@ MagicString.prototype = { if ( end >= patch.start && end < patch.end ) throw new Error( `Cannot use replaced characters (${start}, ${end}) as slice anchors` ); // TODO this is weird, rewrite it - if ( patch.start >= end ) continue; + if ( patch.start > end ) continue; break; } @@ -326,7 +315,6 @@ MagicString.prototype = { }, toString () { - if ( !this.patches.length ) return this.original; return this.prepended + this.slice( 0, this.original.length ) + this.appended; }, @@ -334,63 +322,36 @@ MagicString.prototype = { return this.trim('[\\r\\n]'); }, - trim (charType) { - return this.trimStart(charType).trimEnd(charType); + trim ( charType ) { + return this.trimStart( charType ).trimEnd( charType ); }, - trimEnd (charType) { + trimEnd ( charType ) { const rx = new RegExp( ( charType || '\\s' ) + '+$' ); - this.str = this.str.replace( rx, ( trailing, index, str ) => { - const strLength = str.length; - const length = trailing.length; - - let chars = []; - - let i = strLength; - while ( i-- > strLength - length ) { - chars.push( this.locateOrigin( i ) ); - } - - i = chars.length; - while ( i-- ) { - if ( chars[i] !== null ) { - this.mappings[ chars[i] ] = -1; - } - } + this.appended = this.appended.replace( rx, '' ); + if ( this.appended.length ) return this; - return ''; - }); + // TODO trim patches + const match = rx.exec( this.original ); + if ( match ) { + this.patch( this.original.length - match[0].length, this.original.length, '' ); + } return this; }, - trimStart (charType) { + trimStart ( charType ) { const rx = new RegExp( '^' + ( charType || '\\s' ) + '+' ); - this.str = this.str.replace( rx, leading => { - const length = leading.length; + this.prepended = this.prepended.replace( rx, '' ); + if ( this.prepended.length ) return this; - let chars = []; - let adjustmentStart = 0; - - let i = length; - while ( i-- ) { - chars.push( this.locateOrigin( i ) ); - } - - i = chars.length; - while ( i-- ) { - if ( chars[i] !== null ) { - this.mappings[ chars[i] ] = -1; - adjustmentStart += 1; - } - } - - adjust( this.mappings, adjustmentStart, this.mappings.length, -length ); - - return ''; - }); + // TODO trim patches + const match = rx.exec( this.original ); + if ( match ) { + this.patch( 0, match[0].length, '' ); + } return this; } From 67e0947612c93421b735669a6e6c65402727e144 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 17:40:30 -0500 Subject: [PATCH 03/26] rudimentary sourcemaps --- src/MagicString.js | 2 +- src/utils/encodeMappings.js | 157 +++++++++++++++++------------------- 2 files changed, 73 insertions(+), 86 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index d1b8084..4c8debe 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -74,7 +74,7 @@ MagicString.prototype = { }, getMappings ( hires, sourceIndex, offsets, names ) { - return encodeMappings( this.original, this.str, this.mappings, hires, this.sourcemapLocations, sourceIndex, offsets, names, this.nameLocations ); + return encodeMappings( this.original, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names, this.nameLocations ); }, indent ( indentStr, options ) { diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index 83190ab..7843b33 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -1,72 +1,94 @@ import { encode } from 'vlq'; -export default function encodeMappings ( original, str, mappings, hires, sourcemapLocations, sourceIndex, offsets, names, nameLocations ) { - // store locations, for fast lookup - let lineStart = 0; - const locations = original.split( '\n' ).map( line => { - const start = lineStart; - lineStart += line.length + 1; // +1 for the newline - - return start; - }); - - const inverseMappings = invert( str, mappings ); - - let charOffset = 0; - const lines = str.split( '\n' ).map( line => { - let segments = []; +export default function encodeMappings ( original, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) { + console.log( 'sourcemapLocations', sourcemapLocations ) + + let rawLines = [ [] ]; + let rawSegments = rawLines[0]; + + let generatedCharIndex = 0; + let originalCharIndex = 0; + + let generatedCodeLine = 0; + let generatedCodeColumn = 0; + let sourceCodeLine = 0; + let sourceCodeColumn = 0; + + function addSegments ( end ) { + while ( originalCharIndex < end ) { + if ( hires || sourcemapLocations[ originalCharIndex ] ) { + rawSegments.push({ + generatedCodeLine, + generatedCodeColumn, + sourceCodeLine, + sourceCodeColumn, + sourceCodeName: -1, + sourceIndex + }); + } - let char; // TODO put these inside loop, once we've determined it's safe to do so transpilation-wise - let origin; - let lastOrigin = -1; - let location; - let nameIndex; + if ( original[ originalCharIndex ] === '\n' ) { + sourceCodeLine += 1; + sourceCodeColumn = 0; + generatedCodeLine += 1; + generatedCodeColumn = 0; + } else { + sourceCodeColumn += 1; + generatedCodeColumn += 1; + } - let i; + originalCharIndex += 1; + } + } - const len = line.length; - for ( i = 0; i < len; i += 1 ) { - char = i + charOffset; - origin = inverseMappings[ char ]; + for ( let i = 0; i < patches.length; i += 1 ) { + const patch = patches[i]; - nameIndex = -1; - location = null; + addSegments( patch.start ); - // if this character has no mapping, but the last one did, - // create a new segment - if ( !~origin && ~lastOrigin ) { - location = getLocation( locations, lastOrigin + 1 ); + if ( patch.content.length ) { // TODO is it correct to omit this? + rawSegments.push({ + generatedCodeLine, + generatedCodeColumn, + sourceCodeLine, + sourceCodeColumn, + sourceCodeName: names.indexOf( patch.name ), + sourceIndex: null + }); + } - if ( ( lastOrigin + 1 ) in nameLocations ) nameIndex = names.indexOf( nameLocations[ lastOrigin + 1 ] ); - } + let lines = patch.content.split( '\n' ); + let lastLine = lines.pop(); - else if ( ~origin && ( hires || ( ~lastOrigin && origin !== lastOrigin + 1 ) || sourcemapLocations[ origin ] ) ) { - location = getLocation( locations, origin ); - } + if ( lines.length ) { + generatedCodeLine += lines.length; + rawLines[ generatedCodeLine ] = rawSegments = []; + generatedCodeColumn = lastLine.length; + } else { + generatedCodeColumn += lastLine.length; + } - if ( location ) { - segments.push({ - generatedCodeColumn: i, - sourceIndex, - sourceCodeLine: location.line, - sourceCodeColumn: location.column, - sourceCodeName: nameIndex - }); - } + lines = patch.original.split( '\n' ); + lastLine = lines.pop(); - lastOrigin = origin; + if ( lines.length ) { + sourceCodeLine += lines.length; + sourceCodeColumn = lastLine.length; + } else { + sourceCodeColumn += lastLine.length; } - charOffset += line.length + 1; - return segments; - }); + originalCharIndex = patch.end; + } + + addSegments( original.length ); offsets.sourceIndex = offsets.sourceIndex || 0; offsets.sourceCodeLine = offsets.sourceCodeLine || 0; offsets.sourceCodeColumn = offsets.sourceCodeColumn || 0; offsets.sourceCodeName = offsets.sourceCodeName || 0; - const encoded = lines.map( segments => { + const encoded = rawLines.map( segments => { let generatedCodeColumn = 0; return segments.map( segment => { @@ -93,38 +115,3 @@ export default function encodeMappings ( original, str, mappings, hires, sourcem return encoded; } - - -function invert ( str, mappings ) { - let inverted = new Uint32Array( str.length ); - - // initialise everything to -1 - let i = str.length; - while ( i-- ) { - inverted[i] = -1; - } - - // then apply the actual mappings - i = mappings.length; - while ( i-- ) { - if ( ~mappings[i] ) { - inverted[ mappings[i] ] = i; - } - } - - return inverted; -} - -function getLocation ( locations, char ) { - let i = locations.length; - while ( i-- ) { - if ( locations[i] <= char ) { - return { - line: i, - column: char - locations[i] - }; - } - } - - throw new Error( 'Character out of bounds' ); -} From 7468b29db70e18b229fa8d59c88b665d20977715 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 17:53:39 -0500 Subject: [PATCH 04/26] sourcemaps behaving as before --- src/MagicString.js | 13 ++++++------- src/Patch.js | 4 +++- src/utils/encodeMappings.js | 14 +++++++------- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 4c8debe..c1d8e36 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -16,7 +16,7 @@ export default function MagicString ( string, options = {} ) { filename: { writable: true, value: options.filename }, indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, sourcemapLocations: { writable: true, value: {} }, - nameLocations: { writable: true, value: {} }, + storedNames: { writable: true, value: {} }, indentStr: { writable: true, value: guessIndent( string ) } }); } @@ -54,11 +54,7 @@ MagicString.prototype = { generateMap ( options ) { options = options || {}; - let names = []; - Object.keys( this.nameLocations ).forEach( location => { - const name = this.nameLocations[ location ]; - if ( !~names.indexOf( name ) ) names.push( name ); - }); + const names = Object.keys( this.storedNames ); return new SourceMap({ file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ), @@ -206,7 +202,10 @@ MagicString.prototype = { throw new TypeError( 'replacement content must be a string' ); } - this.patch( start, end, content, this.original.slice( start, end ) ); + const original = this.original.slice( start, end ); + if ( storeName ) this.storedNames[ original ] = true; + + this.patch( start, end, content, original, storeName ); return this; }, diff --git a/src/Patch.js b/src/Patch.js index cc07702..0742675 100644 --- a/src/Patch.js +++ b/src/Patch.js @@ -1,8 +1,10 @@ -export default function Patch ( start, end, content, original ) { +export default function Patch ( start, end, content, original, storeName ) { this.start = start; this.end = end; this.content = content; this.original = original || null; + + this.storeName = true; } Patch.prototype = { diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index 7843b33..b37a1e8 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -1,12 +1,9 @@ import { encode } from 'vlq'; export default function encodeMappings ( original, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) { - console.log( 'sourcemapLocations', sourcemapLocations ) + let rawSegments = []; + let rawLines = [ rawSegments ]; - let rawLines = [ [] ]; - let rawSegments = rawLines[0]; - - let generatedCharIndex = 0; let originalCharIndex = 0; let generatedCodeLine = 0; @@ -15,8 +12,10 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca let sourceCodeColumn = 0; function addSegments ( end ) { + let first = true; + while ( originalCharIndex < end ) { - if ( hires || sourcemapLocations[ originalCharIndex ] ) { + if ( hires || first || sourcemapLocations[ originalCharIndex ] ) { rawSegments.push({ generatedCodeLine, generatedCodeColumn, @@ -38,6 +37,7 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca } originalCharIndex += 1; + first = false; } } @@ -52,7 +52,7 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca generatedCodeColumn, sourceCodeLine, sourceCodeColumn, - sourceCodeName: names.indexOf( patch.name ), + sourceCodeName: patch.storeName ? names.indexOf( patch.original ) : -1, sourceIndex: null }); } From bdf33cc1094fb9293446e44bdfd10339293dbf06 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 18:53:23 -0500 Subject: [PATCH 05/26] indentation --- src/MagicString.js | 106 ++++++++++++------------------------ src/Patch.js | 4 +- src/utils/encodeMappings.js | 6 +- 3 files changed, 39 insertions(+), 77 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index c1d8e36..ab23537 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -74,8 +74,6 @@ MagicString.prototype = { }, indent ( indentStr, options ) { - const mappings = this.mappings; - const reverseMappings = reverse( mappings, this.str.length ); const pattern = /^[^\r\n]/gm; if ( isObject( indentStr ) ) { @@ -90,93 +88,57 @@ MagicString.prototype = { options = options || {}; // Process exclusion ranges - let exclusions; + let isExcluded = {}; if ( options.exclude ) { - exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude; - - exclusions = exclusions.map( range => { - const rangeStart = this.locate( range[0] ); - const rangeEnd = this.locate( range[1] ); - - if ( rangeStart === null || rangeEnd === null ) { - throw new Error( 'Cannot use indices of replaced characters as exclusion ranges' ); - } - - return [ rangeStart, rangeEnd ]; - }); - - exclusions.sort( ( a, b ) => a[0] - b[0] ); - - // check for overlaps - lastEnd = -1; - exclusions.forEach( range => { - if ( range[0] < lastEnd ) { - throw new Error( 'Exclusion ranges cannot overlap' ); + let exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude; + exclusions.forEach( exclusion => { + for ( let i = exclusion[0]; i < exclusion[1]; i += 1 ) { + isExcluded[i] = true; } - - lastEnd = range[1]; }); } - const indentStart = options.indentStart !== false; - let inserts = []; + let charIndex = 0; + let patchIndex = 0; + let shouldIndentNextCharacter = true; - if ( !exclusions ) { - this.str = this.str.replace( pattern, ( match, index ) => { - if ( !indentStart && index === 0 ) { - return match; - } + const indentUntil = end => { + while ( charIndex < end ) { + if ( !isExcluded[ charIndex ] ) { + const char = this.original[ charIndex ]; - inserts.push( index ); - return indentStr + match; - }); - } else { - this.str = this.str.replace( pattern, ( match, index ) => { - if ( ( !indentStart && index === 0 ) || isExcluded( index - 1 ) ) { - return match; - } - - inserts.push( index ); - return indentStr + match; - }); - } + if ( char === '\n' ) { + shouldIndentNextCharacter = true; + } else if ( char !== '\r' && shouldIndentNextCharacter ) { + this.patches.splice( patchIndex, 0, new Patch( charIndex, charIndex, indentStr, '' ) ); + shouldIndentNextCharacter = false; - const adjustments = inserts.map( index => { - let origin; - - do { - origin = reverseMappings[ index++ ]; - } while ( !~origin && index < this.str.length ); - - return origin; - }); - - let i = adjustments.length; - let lastEnd = this.mappings.length; - while ( i-- ) { - adjust( this.mappings, adjustments[i], lastEnd, ( ( i + 1 ) * indentStr.length ) ); - lastEnd = adjustments[i]; - } + patchIndex += 1; + } + } - return this; + charIndex += 1; + } + }; - function isExcluded ( index ) { - let i = exclusions.length; - let range; + for ( ; patchIndex < this.patches.length; patchIndex += 1 ) { // can't cache this.patches.length, it may change + const patch = this.patches[ patchIndex ]; - while ( i-- ) { - range = exclusions[i]; + indentUntil( patch.start ); - if ( range[1] < index ) { - return false; - } + if ( !isExcluded[ charIndex ] ) { + patch.content = patch.content.replace( pattern, match => `${indentStr}${match}` ); - if ( range[0] <= index ) { - return true; + if ( patch.content.length ) { + shouldIndentNextCharacter = patch.content[ patch.content.length - 1 ] === '\n'; } } } + + indentUntil( this.original.length ); + + return this; }, insert ( index, content ) { diff --git a/src/Patch.js b/src/Patch.js index 0742675..bf2c4d9 100644 --- a/src/Patch.js +++ b/src/Patch.js @@ -2,9 +2,9 @@ export default function Patch ( start, end, content, original, storeName ) { this.start = start; this.end = end; this.content = content; - this.original = original || null; + this.original = original || null; // TODO this isn't right, because of empty strings - this.storeName = true; + this.storeName = storeName; } Patch.prototype = { diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index b37a1e8..faf7083 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -11,7 +11,7 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca let sourceCodeLine = 0; let sourceCodeColumn = 0; - function addSegments ( end ) { + function addSegmentsUntil ( end ) { let first = true; while ( originalCharIndex < end ) { @@ -44,7 +44,7 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca for ( let i = 0; i < patches.length; i += 1 ) { const patch = patches[i]; - addSegments( patch.start ); + addSegmentsUntil( patch.start ); if ( patch.content.length ) { // TODO is it correct to omit this? rawSegments.push({ @@ -81,7 +81,7 @@ export default function encodeMappings ( original, patches, hires, sourcemapLoca originalCharIndex = patch.end; } - addSegments( original.length ); + addSegmentsUntil( original.length ); offsets.sourceIndex = offsets.sourceIndex || 0; offsets.sourceCodeLine = offsets.sourceCodeLine || 0; From 7112ac0f84df4f607e038266e4b8734133833ba1 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 19:06:57 -0500 Subject: [PATCH 06/26] update tests --- package.json | 3 ++- test/index.js | 5 ++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 2e52de7..a29025d 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "rollup": "^0.22.0", "rollup-plugin-babel": "^2.2.0", "rollup-plugin-npm": "^1.1.0", - "source-map": "^0.5.3" + "source-map": "^0.5.3", + "source-map-support": "^0.4.0" }, "keywords": [ "string", diff --git a/test/index.js b/test/index.js index ac0e39c..88c381b 100644 --- a/test/index.js +++ b/test/index.js @@ -3,6 +3,7 @@ var assert = require( 'assert' ); var SourceMapConsumer = require( 'source-map' ).SourceMapConsumer; var MagicString = require( '../' ); +require( 'source-map-support' ).install(); require( 'console-group' ).install(); describe( 'MagicString', function () { @@ -51,7 +52,6 @@ describe( 'MagicString', function () { assert.notEqual( s, c ); assert.equal( c.toString(), 'abcXYZjkl' ); - assert.equal( c.locate( 9 ), 6 ); }); it( 'should clone filename info', function () { @@ -245,7 +245,7 @@ describe( 'MagicString', function () { numMappings += 1; }); - assert.equal( numMappings, 1 ); + assert.equal( numMappings, 3 ); // one at 0, one at the edit, one afterwards }); }); @@ -706,7 +706,6 @@ describe( 'MagicString', function () { var snippet = s.snip( 3, 9 ); assert.equal( snippet.toString(), 'defGHI' ); - assert.equal( snippet.locate( 0, 3 ) ); assert.equal( snippet.filename, 'foo.js' ); }); From 8335a398e5c69f8083dd49585417ba52933a4d1d Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 19:08:41 -0500 Subject: [PATCH 07/26] various fixes --- src/Bundle.js | 7 ++++--- src/MagicString.js | 15 +++++++-------- src/Patch.js | 5 ++--- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 830b26a..69fd55f 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -85,8 +85,8 @@ Bundle.prototype = { let names = []; this.sources.forEach( source => { - Object.keys( source.content.nameLocations ).forEach( location => { - const name = source.content.nameLocations[ location ]; + Object.keys( source.content.storedNames ).forEach( location => { + const name = source.content.storedNames[ location ]; if ( !~names.indexOf( name ) ) names.push( name ); }); }); @@ -158,7 +158,8 @@ Bundle.prototype = { indentStart//: trailingNewline || /\r?\n$/.test( separator ) //true///\r?\n/.test( separator ) }); - trailingNewline = source.content.str.slice( 0, -1 ) === '\n'; + // TODO this is a very slow way to determine this + trailingNewline = source.content.toString().slice( 0, -1 ) === '\n'; }); if ( this.intro ) { diff --git a/src/MagicString.js b/src/MagicString.js index ab23537..5d75188 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -70,7 +70,7 @@ MagicString.prototype = { }, getMappings ( hires, sourceIndex, offsets, names ) { - return encodeMappings( this.original, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names, this.nameLocations ); + return encodeMappings( this.original, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names ); }, indent ( indentStr, options ) { @@ -111,7 +111,7 @@ MagicString.prototype = { if ( char === '\n' ) { shouldIndentNextCharacter = true; } else if ( char !== '\r' && shouldIndentNextCharacter ) { - this.patches.splice( patchIndex, 0, new Patch( charIndex, charIndex, indentStr, '' ) ); + this.patches.splice( patchIndex, 0, new Patch( charIndex, charIndex, indentStr, '', false ) ); shouldIndentNextCharacter = false; patchIndex += 1; @@ -164,16 +164,15 @@ MagicString.prototype = { throw new TypeError( 'replacement content must be a string' ); } - const original = this.original.slice( start, end ); - if ( storeName ) this.storedNames[ original ] = true; - - this.patch( start, end, content, original, storeName ); + this.patch( start, end, content, storeName ); return this; }, - patch ( start, end, content ) { + patch ( start, end, content, storeName ) { const original = this.original.slice( start, end ); - const patch = new Patch( start, end, content, original ); + const patch = new Patch( start, end, content, original, storeName ); + + if ( storeName ) this.storedNames[ original ] = true; let i = this.patches.length; while ( i-- ) { diff --git a/src/Patch.js b/src/Patch.js index bf2c4d9..1c333b3 100644 --- a/src/Patch.js +++ b/src/Patch.js @@ -2,13 +2,12 @@ export default function Patch ( start, end, content, original, storeName ) { this.start = start; this.end = end; this.content = content; - this.original = original || null; // TODO this isn't right, because of empty strings - + this.original = original; this.storeName = storeName; } Patch.prototype = { clone () { - return new Patch( this.start, this.end, this.content, this.original ); + return new Patch( this.start, this.end, this.content, this.original, this.storeName ); } }; From ee3c2e2b5975c7f10dc055680807c5392823e88c Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 19:22:31 -0500 Subject: [PATCH 08/26] allow overlapping ranges to be removed --- src/MagicString.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 5d75188..849abbf 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -170,8 +170,6 @@ MagicString.prototype = { patch ( start, end, content, storeName ) { const original = this.original.slice( start, end ); - const patch = new Patch( start, end, content, original, storeName ); - if ( storeName ) this.storedNames[ original ] = true; let i = this.patches.length; @@ -186,7 +184,14 @@ MagicString.prototype = { } // if it overlaps, throw error - else if ( start < previous.end && end > previous.end ) { + 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}'` ); } @@ -196,6 +201,7 @@ MagicString.prototype = { } } + const patch = new Patch( start, end, content, original, storeName ); this.patches.splice( i + 1, 0, patch ); return patch; }, From 0a2119dff0e281bfdbcba588e22bfa519c712bc4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 20:05:04 -0500 Subject: [PATCH 09/26] rename things --- src/MagicString.js | 24 ++++++++++++------------ src/utils/encodeMappings.js | 9 +++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 849abbf..454d431 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -10,8 +10,8 @@ let warned = false; export default function MagicString ( string, options = {} ) { Object.defineProperties( this, { original: { writable: true, value: string }, - appended: { writable: true, value: '' }, - prepended: { writable: true, value: '' }, + outro: { writable: true, value: '' }, + intro: { writable: true, value: '' }, patches: { writable: true, value: [] }, filename: { writable: true, value: options.filename }, indentExclusionRanges: { writable: true, value: options.indentExclusionRanges }, @@ -27,9 +27,9 @@ MagicString.prototype = { }, append ( content ) { - if ( typeof content !== 'string' ) throw new TypeError( 'appended content must be a string' ); + if ( typeof content !== 'string' ) throw new TypeError( 'outro content must be a string' ); - this.appended += content; + this.outro += content; return this; }, @@ -70,7 +70,7 @@ MagicString.prototype = { }, getMappings ( hires, sourceIndex, offsets, names ) { - return encodeMappings( this.original, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names ); + return encodeMappings( this.original, this.intro, this.patches, hires, this.sourcemapLocations, sourceIndex, offsets, names ); }, indent ( indentStr, options ) { @@ -207,9 +207,9 @@ MagicString.prototype = { }, prepend ( content ) { - if ( typeof content !== 'string' ) throw new TypeError( 'appended content must be a string' ); + if ( typeof content !== 'string' ) throw new TypeError( 'outro content must be a string' ); - this.prepended = content + this.prepended; + this.intro = content + this.intro; return this; }, @@ -281,7 +281,7 @@ MagicString.prototype = { }, toString () { - return this.prepended + this.slice( 0, this.original.length ) + this.appended; + return this.intro + this.slice( 0, this.original.length ) + this.outro; }, trimLines () { @@ -295,8 +295,8 @@ MagicString.prototype = { trimEnd ( charType ) { const rx = new RegExp( ( charType || '\\s' ) + '+$' ); - this.appended = this.appended.replace( rx, '' ); - if ( this.appended.length ) return this; + this.outro = this.outro.replace( rx, '' ); + if ( this.outro.length ) return this; // TODO trim patches const match = rx.exec( this.original ); @@ -310,8 +310,8 @@ MagicString.prototype = { trimStart ( charType ) { const rx = new RegExp( '^' + ( charType || '\\s' ) + '+' ); - this.prepended = this.prepended.replace( rx, '' ); - if ( this.prepended.length ) return this; + this.intro = this.intro.replace( rx, '' ); + if ( this.intro.length ) return this; // TODO trim patches const match = rx.exec( this.original ); diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index faf7083..a6b4220 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -1,12 +1,13 @@ import { encode } from 'vlq'; -export default function encodeMappings ( original, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) { - let rawSegments = []; - let rawLines = [ rawSegments ]; +export default function encodeMappings ( original, intro, patches, hires, sourcemapLocations, sourceIndex, offsets, names ) { + let rawLines = []; + + let generatedCodeLine = intro.split( '\n' ).length - 1; + let rawSegments = rawLines[ generatedCodeLine ] = []; let originalCharIndex = 0; - let generatedCodeLine = 0; let generatedCodeColumn = 0; let sourceCodeLine = 0; let sourceCodeColumn = 0; From cb4dbd15c5638f8a6187e995d6e2b744e96a3482 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 20:16:07 -0500 Subject: [PATCH 10/26] sourcemap fixes --- src/MagicString.js | 10 +++++++++- src/utils/encodeMappings.js | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/MagicString.js b/src/MagicString.js index 454d431..416d8d5 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -99,9 +99,11 @@ MagicString.prototype = { }); } + this.intro = this.intro.replace( pattern, match => `${indentStr}${match}` ); + let charIndex = 0; let patchIndex = 0; - let shouldIndentNextCharacter = true; + let shouldIndentNextCharacter = this.intro ? this.intro.slice( -1 ) === '\n' : true; const indentUntil = end => { while ( charIndex < end ) { @@ -138,6 +140,12 @@ MagicString.prototype = { indentUntil( this.original.length ); + this.outro = this.outro.replace( pattern, match => { + if ( shouldIndentNextCharacter ) return `${indentStr}${match}`; + shouldIndentNextCharacter = true; + return match; + }) + return this; }, diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index a6b4220..be7522a 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -31,6 +31,7 @@ export default function encodeMappings ( original, intro, patches, hires, source sourceCodeLine += 1; sourceCodeColumn = 0; generatedCodeLine += 1; + rawLines[ generatedCodeLine ] = rawSegments = []; generatedCodeColumn = 0; } else { sourceCodeColumn += 1; From 2a9bc2bd7822f53fe40a0bd7d93d3c1243cf2cde Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 21:16:47 -0500 Subject: [PATCH 11/26] fix bundle sourcemap names --- src/Bundle.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 69fd55f..431f770 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -85,8 +85,7 @@ Bundle.prototype = { let names = []; this.sources.forEach( source => { - Object.keys( source.content.storedNames ).forEach( location => { - const name = source.content.storedNames[ location ]; + Object.keys( source.content.storedNames ).forEach( name => { if ( !~names.indexOf( name ) ) names.push( name ); }); }); From 202d571a0f3dab25bbe939654e4f569049578361 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 22:27:55 -0500 Subject: [PATCH 12/26] trim replaced content at start --- src/MagicString.js | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 416d8d5..cbcbc01 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -321,10 +321,29 @@ MagicString.prototype = { this.intro = this.intro.replace( rx, '' ); if ( this.intro.length ) return this; - // TODO trim patches - const match = rx.exec( this.original ); - if ( match ) { - this.patch( 0, match[0].length, '' ); + let charIndex = 0; + + for ( let i = 0; i < this.patches.length; i += 1 ) { + const patch = this.patches[i]; + + if ( charIndex < patch.start ) { + const slice = patch ? this.original.slice( charIndex, patch.start ) : this.original; + + const match = rx.exec( slice ); + if ( match ) { + this.patch( charIndex, charIndex + match[0].length, '' ); + } + + if ( !match || match[0].length < head.length ) { + // there is non-whitespace before the patch + break; + } + } + + patch.content = patch.content.replace( rx, '' ); + if ( patch.content ) break; + + charIndex = patch.end; } return this; From 6afd938669429b5f5481e3dc629dd17ebf241a61 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Wed, 23 Dec 2015 23:06:21 -0500 Subject: [PATCH 13/26] trimEnd --- src/MagicString.js | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index cbcbc01..3ef12db 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -306,10 +306,30 @@ MagicString.prototype = { this.outro = this.outro.replace( rx, '' ); if ( this.outro.length ) return this; - // TODO trim patches - const match = rx.exec( this.original ); - if ( match ) { - this.patch( this.original.length - match[0].length, this.original.length, '' ); + let charIndex = this.original.length; + let i = this.patches.length; + + while ( i-- ) { + const patch = this.patches[i]; + + if ( charIndex > patch.end ) { + const slice = this.original.slice( patch.end, charIndex ); + + const match = rx.exec( slice ); + if ( match ) { + this.patch( charIndex - match[0].length, charIndex, '' ); + } + + if ( !match || match[0].length < slice.length ) { + // there is non-whitespace after the patch + break; + } + } + + patch.content = patch.content.replace( rx, '' ); + if ( patch.content ) break; + + charIndex = patch.end; } return this; @@ -327,14 +347,14 @@ MagicString.prototype = { const patch = this.patches[i]; if ( charIndex < patch.start ) { - const slice = patch ? this.original.slice( charIndex, patch.start ) : this.original; + const slice = this.original.slice( charIndex, patch.start ); const match = rx.exec( slice ); if ( match ) { this.patch( charIndex, charIndex + match[0].length, '' ); } - if ( !match || match[0].length < head.length ) { + if ( !match || match[0].length < slice.length ) { // there is non-whitespace before the patch break; } From 5206f31494a04cdb03ff93081ca2c1a0bcf73a0b Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 07:48:29 -0500 Subject: [PATCH 14/26] only build CJS version before tests --- package.json | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index a29025d..17f2b74 100644 --- a/package.json +++ b/package.json @@ -35,13 +35,16 @@ ], "scripts": { "test": "mocha", - "pretest": "npm run build", - "pretest-coverage": "npm run build", + "pretest": "npm run build:cjs", + "pretest-coverage": "npm run build:cjs", "test-coverage": "rm -rf coverage/* && istanbul cover --report json node_modules/.bin/_mocha -- -u exports -R spec test/index.js", "posttest-coverage": "remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.json -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped.lcov -t lcovonly -b dist && remap-istanbul -i coverage/coverage-final.json -o coverage/coverage-remapped -t html -b dist", "ci": "npm run test-coverage && codecov < coverage/coverage-remapped.lcov", - "build": "rm -rf dist && rollup -c -f cjs -o dist/magic-string.cjs.js && rollup -c -f es6 -o dist/magic-string.es6.js && export DEPS=true && rollup -c -f umd -o dist/magic-string.umd.js", - "prepublish": "npm test", + "build:cjs": "rollup -c -f cjs -o dist/magic-string.cjs.js", + "build:es6": "rollup -c -f es6 -o dist/magic-string.es6.js", + "build:umd": "export DEPS=true && rollup -c -f umd -o dist/magic-string.umd.js", + "build": " npm run build:cjs && npm run build:es6 && npm run build:umd", + "prepublish": "rm -rf dist && npm test && npm run build:es6 && npm run build:umd", "lint": "eslint src" }, "files": [ From f026e5a5009856bc0c16b41b3a2c035b07fa2149 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 07:57:25 -0500 Subject: [PATCH 15/26] fixed trimming --- src/MagicString.js | 22 +++++++++++++++------- test/index.js | 7 +++---- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 3ef12db..af5a94f 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -322,16 +322,21 @@ MagicString.prototype = { if ( !match || match[0].length < slice.length ) { // there is non-whitespace after the patch - break; + return this; } } patch.content = patch.content.replace( rx, '' ); - if ( patch.content ) break; + if ( patch.content ) return this; charIndex = patch.end; } + const slice = this.original.slice( 0, charIndex ); + + const match = rx.exec( slice ); + if ( match ) this.patch( charIndex - match[0].length, charIndex, '' ); + return this; }, @@ -350,22 +355,25 @@ MagicString.prototype = { const slice = this.original.slice( charIndex, patch.start ); const match = rx.exec( slice ); - if ( match ) { - this.patch( charIndex, charIndex + match[0].length, '' ); - } + if ( match ) this.patch( charIndex, charIndex + match[0].length, '' ); if ( !match || match[0].length < slice.length ) { // there is non-whitespace before the patch - break; + return this; } } patch.content = patch.content.replace( rx, '' ); - if ( patch.content ) break; + if ( patch.content ) return this; charIndex = patch.end; } + const slice = this.original.slice( charIndex, this.original.length ); + + const match = rx.exec( slice ); + if ( match ) this.patch( charIndex, charIndex + match[0].length, '' ); + return this; } } diff --git a/test/index.js b/test/index.js index 88c381b..f2dc7fb 100644 --- a/test/index.js +++ b/test/index.js @@ -720,10 +720,9 @@ describe( 'MagicString', function () { describe( 'trim', function () { it( 'should trim original content', function () { - var s = new MagicString( ' abcdefghijkl ' ); - - s.trim(); - assert.equal( s.toString(), 'abcdefghijkl' ); + assert.equal( new MagicString( ' abcdefghijkl ' ).trim().toString(), 'abcdefghijkl' ); + assert.equal( new MagicString( ' abcdefghijkl' ).trim().toString(), 'abcdefghijkl' ); + assert.equal( new MagicString( 'abcdefghijkl ' ).trim().toString(), 'abcdefghijkl' ); }); it( 'should trim replaced content', function () { From 928ef896b2f9b0d653333bc454e50c5489ce4ab5 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 07:58:09 -0500 Subject: [PATCH 16/26] remove unused code --- src/MagicString.js | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index af5a94f..4e2cd5b 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -377,45 +377,3 @@ MagicString.prototype = { return this; } } - -function adjust ( mappings, start, end, d ) { - if ( !d ) return; // replacement is same length as replaced string - - let i = end; - while ( i-- > start ) { - if ( ~mappings[i] ) { - mappings[i] += d; - } - } -} - -function initMappings ( i ) { - let mappings = new Uint32Array( i ); - - while ( i-- ) mappings[i] = i; - return mappings; -} - -function blank ( mappings, start, i ) { - while ( i-- > start ) mappings[i] = -1; -} - -function reverse ( mappings, i ) { - let result = new Uint32Array( i ); - - while ( i-- ) { - result[i] = -1; - } - - let location; - i = mappings.length; - while ( i-- ) { - location = mappings[i]; - - if ( ~location ) { - result[ location ] = i; - } - } - - return result; -} From 6d6cde4e8b80c81e9f2b212158714d55cc5d8e78 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 08:03:21 -0500 Subject: [PATCH 17/26] correct bundle indentation --- src/MagicString.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index 4e2cd5b..d0595b6 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -99,11 +99,16 @@ MagicString.prototype = { }); } - this.intro = this.intro.replace( pattern, match => `${indentStr}${match}` ); + let shouldIndentNextCharacter = options.indentStart !== false; + + this.intro = this.intro.replace( pattern, match => { + if ( shouldIndentNextCharacter ) return `${indentStr}${match}`; + shouldIndentNextCharacter = true; + return match; + }); let charIndex = 0; let patchIndex = 0; - let shouldIndentNextCharacter = this.intro ? this.intro.slice( -1 ) === '\n' : true; const indentUntil = end => { while ( charIndex < end ) { @@ -144,7 +149,7 @@ MagicString.prototype = { if ( shouldIndentNextCharacter ) return `${indentStr}${match}`; shouldIndentNextCharacter = true; return match; - }) + }); return this; }, From bed9f9bee749eb5beae44ba2a909f41d6e2684c4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 08:24:31 -0500 Subject: [PATCH 18/26] remove unused bundle.outro --- src/Bundle.js | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 431f770..0dedd6b 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -6,7 +6,6 @@ import isObject from './utils/isObject.js'; export default function Bundle ( options = {} ) { this.intro = options.intro || ''; - this.outro = options.outro || ''; this.separator = options.separator !== undefined ? options.separator : '\n'; this.sources = []; @@ -65,7 +64,6 @@ Bundle.prototype = { clone () { const bundle = new Bundle({ intro: this.intro, - outro: this.outro, separator: this.separator }); @@ -105,8 +103,7 @@ Bundle.prototype = { } return prefix + mappings; - }).join( '' ) + - getSemis( this.outro ) + }).join( '' ) ); return new SourceMap({ @@ -167,8 +164,6 @@ Bundle.prototype = { }); } - this.outro = this.outro.replace( /^[^\n]/gm, indentStr + '$&' ); - return this; }, @@ -185,7 +180,7 @@ Bundle.prototype = { return str; }).join( '' ); - return this.intro + body + this.outro; + return this.intro + body; }, trimLines () { @@ -208,11 +203,10 @@ Bundle.prototype = { source = this.sources[i]; if ( !source ) { - this.outro = this.outro.replace( rx, '' ); break; } - source.content.trimStart(); + source.content.trimStart( charType ); i += 1; } while ( source.content.str === '' ); } @@ -222,24 +216,21 @@ Bundle.prototype = { trimEnd ( charType ) { const rx = new RegExp( ( charType || '\\s' ) + '+$' ); - this.outro = this.outro.replace( rx, '' ); - if ( !this.outro ) { - let source; - let i = this.sources.length - 1; + let source; + let i = this.sources.length - 1; - do { - source = this.sources[i]; + do { + source = this.sources[i]; - if ( !source ) { - this.intro = this.intro.replace( rx, '' ); - break; - } + if ( !source ) { + this.intro = this.intro.replace( rx, '' ); + break; + } - source.content.trimEnd(charType); - i -= 1; - } while ( source.content.str === '' ); - } + source.content.trimEnd( charType ); + i -= 1; + } while ( source.content.str === '' ); return this; } From f6dd30136798ad964223686a4a8428265214ab29 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 08:31:57 -0500 Subject: [PATCH 19/26] fix tricky bundle.trim() case --- src/Bundle.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bundle.js b/src/Bundle.js index 0dedd6b..a6097d0 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -208,7 +208,7 @@ Bundle.prototype = { source.content.trimStart( charType ); i += 1; - } while ( source.content.str === '' ); + } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source? } return this; @@ -230,7 +230,7 @@ Bundle.prototype = { source.content.trimEnd( charType ); i -= 1; - } while ( source.content.str === '' ); + } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source? return this; } From f1de6fd7a5aea800523c6b1ed0f1362da6e53a63 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 08:37:20 -0500 Subject: [PATCH 20/26] remove TODO --- src/Bundle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bundle.js b/src/Bundle.js index a6097d0..e5d9090 100644 --- a/src/Bundle.js +++ b/src/Bundle.js @@ -196,7 +196,7 @@ Bundle.prototype = { this.intro = this.intro.replace( rx, '' ); if ( !this.intro ) { - let source; // TODO put inside loop if safe + let source; let i = 0; do { From f0fe293c1d403b6b387d603d2c2ddb565b1386a4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 09:52:00 -0500 Subject: [PATCH 21/26] fix indentation around removed content --- src/MagicString.js | 2 ++ test/index.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/MagicString.js b/src/MagicString.js index d0595b6..b4da175 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -141,6 +141,8 @@ MagicString.prototype = { shouldIndentNextCharacter = patch.content[ patch.content.length - 1 ] === '\n'; } } + + charIndex = patch.end; } indentUntil( this.original.length ); diff --git a/test/index.js b/test/index.js index f2dc7fb..6dfd590 100644 --- a/test/index.js +++ b/test/index.js @@ -336,6 +336,15 @@ describe( 'MagicString', function () { assert.equal( s.toString(), '\r\n\r\n\t\tabc\r\n\t\tdef\r\n\r\n\t\tghi\r\n\t\tjkl' ); }); + it( 'should indent content with removals', function () { + var s = new MagicString( '/* remove this line */\nvar foo = 1;' ); + + s.remove( 0, 23 ); + s.indent(); + + assert.equal( s.toString(), '\tvar foo = 1;' ); + }); + it( 'should return this', function () { var s = new MagicString( 'abcdefghijkl' ); assert.strictEqual( s.indent(), s ); From 9191d6483e6f8f2b0840844e93ed7c0c1e265c4e Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 10:27:47 -0500 Subject: [PATCH 22/26] dont indent patches in the middle of lines --- src/MagicString.js | 15 ++++++--------- test/index.js | 10 ++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/MagicString.js b/src/MagicString.js index b4da175..bed9637 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -100,12 +100,13 @@ MagicString.prototype = { } let shouldIndentNextCharacter = options.indentStart !== false; - - this.intro = this.intro.replace( pattern, match => { + const replacer = match => { if ( shouldIndentNextCharacter ) return `${indentStr}${match}`; shouldIndentNextCharacter = true; return match; - }); + }; + + this.intro = this.intro.replace( pattern, replacer ); let charIndex = 0; let patchIndex = 0; @@ -135,7 +136,7 @@ MagicString.prototype = { indentUntil( patch.start ); if ( !isExcluded[ charIndex ] ) { - patch.content = patch.content.replace( pattern, match => `${indentStr}${match}` ); + patch.content = patch.content.replace( pattern, replacer ); if ( patch.content.length ) { shouldIndentNextCharacter = patch.content[ patch.content.length - 1 ] === '\n'; @@ -147,11 +148,7 @@ MagicString.prototype = { indentUntil( this.original.length ); - this.outro = this.outro.replace( pattern, match => { - if ( shouldIndentNextCharacter ) return `${indentStr}${match}`; - shouldIndentNextCharacter = true; - return match; - }); + this.outro = this.outro.replace( pattern, replacer ); return this; }, diff --git a/test/index.js b/test/index.js index 6dfd590..5596374 100644 --- a/test/index.js +++ b/test/index.js @@ -345,6 +345,16 @@ describe( 'MagicString', function () { assert.equal( s.toString(), '\tvar foo = 1;' ); }); + it( 'should not indent patches in the middle of a line', function () { + var s = new MagicString( 'class Foo extends Bar {}' ); + + s.overwrite( 18, 21, 'Baz' ); + assert.equal( s.toString(), 'class Foo extends Baz {}' ); + + s.indent(); + assert.equal( s.toString(), '\tclass Foo extends Baz {}' ); + }); + it( 'should return this', function () { var s = new MagicString( 'abcdefghijkl' ); assert.strictEqual( s.indent(), s ); From ffaca6e850c2510db129f2ba43fd4375cd439fad Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 10:37:50 -0500 Subject: [PATCH 23/26] fix trimming --- src/MagicString.js | 2 +- test/index.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/MagicString.js b/src/MagicString.js index bed9637..367ace7 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -333,7 +333,7 @@ MagicString.prototype = { patch.content = patch.content.replace( rx, '' ); if ( patch.content ) return this; - charIndex = patch.end; + charIndex = patch.start; } const slice = this.original.slice( 0, charIndex ); diff --git a/test/index.js b/test/index.js index 5596374..ef0393d 100644 --- a/test/index.js +++ b/test/index.js @@ -751,6 +751,34 @@ describe( 'MagicString', function () { assert.equal( s.toString(), 'defghi' ); }); + it( 'should trim original content before or after replaced content', function () { + var s = new MagicString( 'abc defghi' ); + + s.remove( 6, 9 ); + s.remove( 9, 12 ); + assert.equal( s.toString(), 'abc ' ); + + s.trim(); + assert.equal( s.toString(), 'abc' ); + + s = new MagicString( 'xyz abc defghi' ); + + s.remove( 0, 3 ); + s.remove( 12, 18 ); + assert.equal( s.toString(), ' abc ' ); + + s.trim(); + assert.equal( s.toString(), 'abc' ); + + s = new MagicString( 'xyz abc' ); + + s.remove( 0, 3 ); + assert.equal( s.toString(), ' abc' ); + + s.trim(); + assert.equal( s.toString(), 'abc' ); + }); + it( 'should trim appended/prepended content', function () { var s = new MagicString( ' abcdefghijkl ' ); From 7a5c76acdfdd1b85546f95c52a4c145b66291c9a Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 11:17:15 -0500 Subject: [PATCH 24/26] sourcemap tweaks --- src/utils/encodeMappings.js | 3 ++- test/index.js | 18 +++++------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index be7522a..04122b4 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -45,10 +45,11 @@ export default function encodeMappings ( original, intro, patches, hires, source for ( let i = 0; i < patches.length; i += 1 ) { const patch = patches[i]; + const addSegmentForPatch = patch.storeName || patch.start > originalCharIndex; addSegmentsUntil( patch.start ); - if ( patch.content.length ) { // TODO is it correct to omit this? + if ( addSegmentForPatch ) { rawSegments.push({ generatedCodeLine, generatedCodeColumn, diff --git a/test/index.js b/test/index.js index ef0393d..067c04b 100644 --- a/test/index.js +++ b/test/index.js @@ -107,8 +107,8 @@ describe( 'MagicString', function () { assert.deepEqual( map.sources, [ 'input.md' ]); assert.deepEqual( map.sourcesContent, [ 'abcdefghijkl' ]); - assert.equal( map.toString(), '{"version":3,"file":"output.md","sources":["input.md"],"sourcesContent":["abcdefghijkl"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAO,CAAC,CAAC"}' ); - assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxDQUFDLENBQU8sQ0FBQyxDQUFDIn0=' ); + assert.equal( map.toString(), '{"version":3,"file":"output.md","sources":["input.md"],"sourcesContent":["abcdefghijkl"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,AAAM,CAAC,CAAC"}' ); + assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxDQUFDLENBQUMsQUFBTSxDQUFDLENBQUMifQ==' ); smc = new SourceMapConsumer( map ); @@ -120,10 +120,6 @@ describe( 'MagicString', function () { assert.equal( loc.line, 1 ); assert.equal( loc.column, 1 ); - loc = smc.originalPositionFor({ line: 1, column: 3 }); - assert.equal( loc.line, 1 ); - assert.equal( loc.column, 9 ); - loc = smc.originalPositionFor({ line: 1, column: 4 }); assert.equal( loc.line, 1 ); assert.equal( loc.column, 10 ); @@ -150,7 +146,7 @@ describe( 'MagicString', function () { assert.equal( originLoc.column, 0 ); }); - it( 'should generate a sourcemap using specified locations', function () { + it.only( 'should generate a sourcemap using specified locations', function () { var s, map, smc, loc; s = new MagicString( 'abcdefghijkl' ); @@ -171,8 +167,8 @@ describe( 'MagicString', function () { assert.deepEqual( map.sources, [ 'input.md' ]); assert.deepEqual( map.sourcesContent, [ 'abcdefghijkl' ]); - assert.equal( map.toString(), '{"version":3,"file":"output.md","sources":["input.md"],"sourcesContent":["abcdefghijkl"],"names":[],"mappings":"AAAA,GAAG,GAAM,CAAC"}' ); - assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsR0FBRyxHQUFNLENBQUMifQ==' ); + assert.equal( map.toString(), '{"version":3,"file":"output.md","sources":["input.md"],"sourcesContent":["abcdefghijkl"],"names":[],"mappings":"AAAA,GAAG,GAAG,AAAG,CAAC"}' ); + assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsR0FBRyxHQUFHLEFBQUcsQ0FBQyJ9' ); smc = new SourceMapConsumer( map ); @@ -184,10 +180,6 @@ describe( 'MagicString', function () { assert.equal( loc.line, 1 ); assert.equal( loc.column, 3 ); - loc = smc.originalPositionFor({ line: 1, column: 6 }); - assert.equal( loc.line, 1 ); - assert.equal( loc.column, 9 ); - loc = smc.originalPositionFor({ line: 1, column: 7 }); assert.equal( loc.line, 1 ); assert.equal( loc.column, 10 ); From 83c83edfd5cd593a63c7d4ebef6f87c881fde4ad Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 12:43:12 -0500 Subject: [PATCH 25/26] more sourcemap fixes --- src/utils/encodeMappings.js | 2 +- test/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js index 04122b4..404ca18 100644 --- a/src/utils/encodeMappings.js +++ b/src/utils/encodeMappings.js @@ -56,7 +56,7 @@ export default function encodeMappings ( original, intro, patches, hires, source sourceCodeLine, sourceCodeColumn, sourceCodeName: patch.storeName ? names.indexOf( patch.original ) : -1, - sourceIndex: null + sourceIndex }); } diff --git a/test/index.js b/test/index.js index 067c04b..fb81ec4 100644 --- a/test/index.js +++ b/test/index.js @@ -146,7 +146,7 @@ describe( 'MagicString', function () { assert.equal( originLoc.column, 0 ); }); - it.only( 'should generate a sourcemap using specified locations', function () { + it( 'should generate a sourcemap using specified locations', function () { var s, map, smc, loc; s = new MagicString( 'abcdefghijkl' ); From a3bd72cb41c82407824fc1e4e1a46d02bf532bc4 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 24 Dec 2015 19:20:00 -0500 Subject: [PATCH 26/26] -> 0.10.0 --- CHANGELOG.md | 6 ++++++ README.md | 6 ++---- package.json | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b19d478..4fb0ca4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # changelog +## 0.10.0 + +* Complete rewrite, resulting in ~40x speed increase ([#30](https://github.com/Rich-Harris/magic-string/pull/30)) +* Breaking – `magicString.locate` and `locateOrigin` are deprecated +* More forgiving rules about contiguous patches, and which ranges are valid with `magicString.slice(...)` + ## 0.9.1 * Update deps diff --git a/README.md b/README.md index 7fad524..ef6eadd 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ var s = new MagicString( 'problems = 99' ); s.overwrite( 0, 8, 'answer' ); s.toString(); // 'answer = 99' -s.locate( 9 ); // 7 - the character originally at index 9 ('=') is now at index 7 -s.locateOrigin( 7 ); // 9 s.overwrite( 11, 13, '42' ); // character indices always refer to the original string s.toString(); // 'answer = 42' @@ -126,11 +124,11 @@ Inserts the specified `content` at the `index` in the original string. Returns ` ### s.locate( index ) -Finds the location, in the generated string, of the character at `index` in the original string. Returns `null` if the character in question has been removed or overwritten. +**DEPRECATED** since 0.10 – see [#30](https://github.com/Rich-Harris/magic-string/pull/30) ### s.locateOrigin( index ) -The opposite of `s.locate()`. Returns `null` if the character in question was inserted with `s.append()`, `s.prepend()` or `s.overwrite()`. +**DEPRECATED** since 0.10 – see [#30](https://github.com/Rich-Harris/magic-string/pull/30) ### s.overwrite( start, end, content[, storeName] ) diff --git a/package.json b/package.json index 17f2b74..4347b62 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "magic-string", "description": "Modify strings, generate sourcemaps", "author": "Rich Harris", - "version": "0.9.1", + "version": "0.10.0", "repository": "https://github.com/rich-harris/magic-string", "main": "dist/magic-string.cjs.js", "jsnext:main": "dist/magic-string.es6.js",