[Pkg-javascript-commits] [node-magic-string] 01/04: New upstream version 0.18.0
Julien Puydt
julien.puydt at laposte.net
Fri Dec 2 15:34:28 UTC 2016
This is an automated email from the git hooks/post-receive script.
jpuydt-guest pushed a commit to branch master
in repository node-magic-string.
commit 68f4435bb06cf3f0afc565c357b735cf02fc4bbc
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Fri Dec 2 10:06:55 2016 +0100
New upstream version 0.18.0
---
.eslintrc | 10 +-
CHANGELOG.md | 9 +
README.md | 20 +-
package.json | 37 +-
rollup.config.js | 2 +-
src/Bundle.js | 44 +-
src/Chunk.js | 12 +-
src/MagicString.js | 151 ++--
src/utils/encodeMappings.js | 6 +-
src/utils/getLocator.js | 4 +-
src/utils/getRelativePath.js | 4 +-
test/.eslintrc | 6 +
test/MagicString.Bundle.js | 574 ++++++++++++++
test/MagicString.js | 1127 ++++++++++++++++++++++++++++
test/index.js | 1684 ------------------------------------------
test/mocha.opts | 1 +
16 files changed, 1907 insertions(+), 1784 deletions(-)
diff --git a/.eslintrc b/.eslintrc
index dfefff3..7ed2294 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -2,20 +2,24 @@
"rules": {
"indent": [ 2, "tab", { "SwitchCase": 1 }],
"quotes": [ 2, "single" ],
- "linebreak-style": [ 2, "unix" ],
"semi": [ 2, "always" ],
+ "keyword-spacing": [ 2, { "before": true, "after": true } ],
+ "space-before-blocks": [ 2, "always" ],
+ "space-before-function-paren": [ 2, "always" ],
"no-mixed-spaces-and-tabs": [ 2, "smart-tabs" ],
"object-shorthand": [2, "always" ],
"no-const-assign": 2,
+ "no-unused-vars": 2,
"no-class-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"quote-props": [ 2, "as-needed" ],
"one-var": [ 2, "never" ],
"prefer-arrow-callback": 2,
+ "prefer-const": 2,
"arrow-spacing": 2,
-
- "no-cond-assign": 0
+ "no-cond-assign": 0,
+ "no-constant-condition": 0
},
"env": {
"es6": true,
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20fa5b0..fa2153f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# magic-string changelog
+## 0.18.0
+
+* Optimisation – remove empty chunks following `overwrite` or `remove` ([#113](https://github.com/Rich-Harris/magic-string/pull/113))
+
+## 0.17.0
+
+* Add `appendLeft`, `appendRight`, `prependLeft`, `prependRight` methods ([#109](https://github.com/Rich-Harris/magic-string/issues/109))
+* `insertLeft` and `insertRight` are deprecated in favour of `appendLeft` and `prependRight` respectively
+
## 0.16.0
* Include inserts in range for `overwrite` and `remove` operations ([#89](https://github.com/Rich-Harris/magic-string/pull/89))
diff --git a/README.md b/README.md
index b971bd4..1314c72 100644
--- a/README.md
+++ b/README.md
@@ -90,6 +90,14 @@ Adds the specified character index (with respect to the original string) to sour
Appends the specified content to the end of the string. Returns `this`.
+### s.appendLeft( index, content )
+
+Appends the specified `content` at the `index` in the original string. If a range *ending* with `index` is subsequently moved, the insert will be moved with it. Returns `this`. See also `s.prependLeft(...)`.
+
+### s.appendRight( index, content )
+
+Appends the specified `content` at the `index` in the original string. If a range *starting* with `index` is subsequently moved, the insert will be moved with it. Returns `this`. See also `s.prependRight(...)`.
+
### s.clone()
Does what you'd expect.
@@ -120,11 +128,11 @@ The `options` argument can have an `exclude` property, which is an array of `[st
### s.insertLeft( index, content )
-Inserts the specified `content` at the `index` in the original string. If a range *ending* with `index` is subsequently moved, the insert will be moved with it. Returns `this`.
+**DEPRECATED** since 0.17 – use `s.appendLeft(...)` instead
### s.insertRight( index, content )
-Inserts the specified `content` at the `index` in the original string. If a range *starting* with `index` is subsequently moved, the insert will be moved with it. Returns `this`.
+**DEPRECATED** since 0.17 – use `s.prependRight(...)` instead
### s.locate( index )
@@ -146,6 +154,14 @@ Replaces the characters from `start` to `end` with `content`. The same restricti
Prepends the string with the specified content. Returns `this`.
+### s.prependLeft ( index, content )
+
+Same as `s.appendLeft(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`
+
+### s.prependRight ( index, content )
+
+Same as `s.appendRight(...)`, except that the inserted content will go *before* any previous appends or prepends at `index`
+
### s.remove( start, end )
Removes the characters from `start` to `end` (of the original string, **not** the generated string). Removing the same content twice, or making removals that partially overlap, will cause an error. Returns `this`.
diff --git a/package.json b/package.json
index 3c6f944..486e862 100644
--- a/package.json
+++ b/package.json
@@ -2,29 +2,30 @@
"name": "magic-string",
"description": "Modify strings, generate sourcemaps",
"author": "Rich Harris",
- "version": "0.16.0",
+ "version": "0.18.0",
"repository": "https://github.com/rich-harris/magic-string",
"main": "dist/magic-string.cjs.js",
- "module": "dist/magic-string.es6.js",
- "jsnext:main": "dist/magic-string.es6.js",
+ "module": "dist/magic-string.es.js",
+ "jsnext:main": "dist/magic-string.es.js",
"license": "MIT",
"dependencies": {
"vlq": "^0.2.1"
},
"devDependencies": {
+ "buble": "^0.14.0",
"codecov.io": "^0.1.6",
- "console-group": "^0.2.1",
- "eslint": "^2.11.1",
- "istanbul": "^0.4.3",
- "mocha": "^3.0.1",
+ "console-group": "^0.3.2",
+ "eslint": "^3.7.1",
+ "istanbul": "^0.4.5",
+ "mocha": "^3.1.0",
"remap-istanbul": "^0.6.4",
"resolve": "^1.1.7",
- "rollup": "^0.34.5",
- "rollup-plugin-buble": "^0.12.1",
+ "rollup": "^0.36.1",
+ "rollup-plugin-buble": "^0.14.0",
"rollup-plugin-node-resolve": "^2.0.0",
- "rollup-plugin-replace": "^1.1.0",
+ "rollup-plugin-replace": "^1.1.1",
"source-map": "^0.5.6",
- "source-map-support": "^0.4.0"
+ "source-map-support": "^0.4.3"
},
"keywords": [
"string",
@@ -37,19 +38,19 @@
"test": "mocha",
"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",
+ "test-coverage": "rm -rf coverage/* && istanbul cover --report json node_modules/.bin/_mocha -- -u exports -R spec test/*.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:cjs": "rollup -c",
- "build:es6": "rollup -c --environment ES",
+ "build:es": "rollup -c --environment ES",
"build:umd": "rollup -c --environment DEPS",
- "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",
+ "build": " npm run build:cjs && npm run build:es && npm run build:umd",
+ "prepublish": "rm -rf dist && npm test && npm run build:es && npm run build:umd",
+ "lint": "eslint src test",
"watch:cjs": "rollup -w -c",
- "watch:es6": "rollup -w -c --environment ES",
+ "watch:es": "rollup -w -c --environment ES",
"watch:umd": "rollup -w -c --environment DEPS",
- "watch": "npm run watch:es6"
+ "watch": "npm run watch:es"
},
"files": [
"src/*",
diff --git a/rollup.config.js b/rollup.config.js
index ef0aa20..512a4d9 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -3,7 +3,7 @@ import nodeResolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
var external = process.env.DEPS ? null : [ 'vlq' ];
-var format = process.env.DEPS ? 'umd' : process.env.ES ? 'es6' : 'cjs';
+var format = process.env.DEPS ? 'umd' : process.env.ES ? 'es' : 'cjs';
export default {
entry: process.env.ES ? 'src/index.js' : 'src/index-legacy.js',
diff --git a/src/Bundle.js b/src/Bundle.js
index 7814ea8..0f3786a 100644
--- a/src/Bundle.js
+++ b/src/Bundle.js
@@ -79,19 +79,31 @@ Bundle.prototype = {
return bundle;
},
- generateMap ( options ) {
- options = options || {};
-
- let offsets = {};
-
- let names = [];
+ generateMap ( options = {} ) {
+ const names = [];
this.sources.forEach( source => {
Object.keys( source.content.storedNames ).forEach( name => {
if ( !~names.indexOf( name ) ) names.push( name );
});
});
- const encoded = (
+ return new SourceMap({
+ file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
+ sources: this.uniqueSources.map( source => {
+ return options.file ? getRelativePath( options.file, source.filename ) : source.filename;
+ }),
+ sourcesContent: this.uniqueSources.map( source => {
+ return options.includeContent ? source.content : null;
+ }),
+ names,
+ mappings: this.getMappings( options, names )
+ });
+ },
+
+ getMappings ( options, names ) {
+ const offsets = {};
+
+ return (
getSemis( this.intro ) +
this.sources.map( ( source, i ) => {
const prefix = ( i > 0 ) ? ( getSemis( source.separator ) || ',' ) : '';
@@ -102,28 +114,16 @@ Bundle.prototype = {
mappings = getSemis( source.content.toString() );
} else {
const sourceIndex = this.uniqueSourceIndexByFilename[ source.filename ];
- mappings = source.content.getMappings( options.hires, sourceIndex, offsets, names );
+ mappings = source.content.getMappings( options, sourceIndex, offsets, names );
}
return prefix + mappings;
}).join( '' )
);
-
- return new SourceMap({
- file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
- sources: this.uniqueSources.map( source => {
- return options.file ? getRelativePath( options.file, source.filename ) : source.filename;
- }),
- sourcesContent: this.uniqueSources.map( source => {
- return options.includeContent ? source.content : null;
- }),
- names,
- mappings: encoded
- });
},
getIndentString () {
- let indentStringCounts = {};
+ const indentStringCounts = {};
this.sources.forEach( source => {
const indentStr = source.content.indentStr;
@@ -178,7 +178,7 @@ Bundle.prototype = {
toString () {
const body = this.sources.map( ( source, i ) => {
const separator = source.separator !== undefined ? source.separator : this.separator;
- let str = ( i > 0 ? separator : '' ) + source.content.toString();
+ const str = ( i > 0 ? separator : '' ) + source.content.toString();
return str;
}).join( '' );
diff --git a/src/Chunk.js b/src/Chunk.js
index dfe0152..d83e036 100644
--- a/src/Chunk.js
+++ b/src/Chunk.js
@@ -18,10 +18,14 @@ export default function Chunk ( start, end, content ) {
}
Chunk.prototype = {
- append ( content ) {
+ appendLeft ( content ) {
this.outro += content;
},
+ appendRight ( content ) {
+ this.intro = this.intro + content;
+ },
+
clone () {
const chunk = new Chunk( this.start, this.end, this.original );
@@ -65,7 +69,11 @@ Chunk.prototype = {
return this;
},
- prepend ( content ) {
+ prependLeft ( content ) {
+ this.outro = content + this.outro;
+ },
+
+ prependRight ( content ) {
this.intro = content + this.intro;
},
diff --git a/src/MagicString.js b/src/MagicString.js
index f1756a0..7fc93d1 100644
--- a/src/MagicString.js
+++ b/src/MagicString.js
@@ -7,6 +7,11 @@ import isObject from './utils/isObject.js';
import getLocator from './utils/getLocator.js';
import Stats from './utils/Stats.js';
+const warned = {
+ insertLeft: false,
+ insertRight: false
+};
+
export default function MagicString ( string, options = {} ) {
const chunk = new Chunk( 0, string.length, string );
@@ -46,8 +51,46 @@ MagicString.prototype = {
return this;
},
+ appendLeft ( index, content ) {
+ if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
+
+ if ( DEBUG ) this.stats.time( 'insertLeft' );
+
+ this._split( index );
+
+ const chunk = this.byEnd[ index ];
+
+ if ( chunk ) {
+ chunk.appendLeft( content );
+ } else {
+ this.intro += content;
+ }
+
+ if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
+ return this;
+ },
+
+ appendRight ( index, content ) {
+ if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
+
+ if ( DEBUG ) this.stats.time( 'insertLeft' );
+
+ this._split( index );
+
+ const chunk = this.byStart[ index ];
+
+ if ( chunk ) {
+ chunk.appendRight( content );
+ } else {
+ this.outro += content;
+ }
+
+ if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
+ return this;
+ },
+
clone () {
- let cloned = new MagicString( this.original, { filename: this.filename });
+ const cloned = new MagicString( this.original, { filename: this.filename });
let originalChunk = this.firstChunk;
let clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
@@ -95,7 +138,7 @@ MagicString.prototype = {
sources: [ options.source ? getRelativePath( options.file || '', options.source ) : null ],
sourcesContent: options.includeContent ? [ this.original ] : [ null ],
names,
- mappings: this.getMappings( options.hires, 0, {}, names )
+ mappings: this.getMappings( options, 0, {}, names )
});
if ( DEBUG ) this.stats.timeEnd( 'generateMap' );
@@ -106,8 +149,8 @@ MagicString.prototype = {
return this.indentStr === null ? '\t' : this.indentStr;
},
- getMappings ( hires, sourceIndex, offsets, names ) {
- return encodeMappings( this.original, this.intro, this.outro, this.firstChunk, hires, this.sourcemapLocations, sourceIndex, offsets, names );
+ getMappings ( options, sourceIndex, offsets, names ) {
+ return encodeMappings( this.original, this.intro, this.outro, this.firstChunk, options.hires, this.sourcemapLocations, sourceIndex, offsets, names );
},
indent ( indentStr, options ) {
@@ -125,10 +168,10 @@ MagicString.prototype = {
options = options || {};
// Process exclusion ranges
- let isExcluded = {};
+ const isExcluded = {};
if ( options.exclude ) {
- let exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude;
+ const 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;
@@ -173,10 +216,10 @@ MagicString.prototype = {
shouldIndentNextCharacter = false;
if ( charIndex === chunk.start ) {
- chunk.prepend( indentStr );
+ chunk.prependRight( indentStr );
} else {
const rhs = chunk.split( charIndex );
- rhs.prepend( indentStr );
+ rhs.prependRight( indentStr );
this.byStart[ charIndex ] = rhs;
this.byEnd[ charIndex ] = chunk;
@@ -204,41 +247,21 @@ MagicString.prototype = {
},
insertLeft ( index, content ) {
- if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
-
- if ( DEBUG ) this.stats.time( 'insertLeft' );
-
- this._split( index );
-
- const chunk = this.byEnd[ index ];
-
- if ( chunk ) {
- chunk.append( content );
- } else {
- this.intro += content;
+ if ( !warned.insertLeft ) {
+ console.warn( 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead' ); // eslint-disable-line no-console
+ warned.insertLeft = true;
}
- if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
- return this;
+ return this.appendLeft( index, content );
},
insertRight ( index, content ) {
- if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
-
- if ( DEBUG ) this.stats.time( 'insertRight' );
-
- this._split( index );
-
- const chunk = this.byStart[ index ];
-
- if ( chunk ) {
- chunk.prepend( content );
- } else {
- this.outro += content;
+ if ( !warned.insertRight ) {
+ console.warn( 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead' ); // eslint-disable-line no-console
+ warned.insertRight = true;
}
- if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
- return this;
+ return this.prependRight( index, content );
},
move ( start, end, index ) {
@@ -307,15 +330,15 @@ MagicString.prototype = {
if ( first ) {
first.edit( content, storeName );
- if ( first !== last ) {
- let chunk = first.next;
- while ( chunk !== last ) {
- chunk.edit( '', false );
- chunk = chunk.next;
- }
-
- chunk.edit( '', false );
+ if ( last ) {
+ first.next = last.next;
+ } else {
+ first.next = null;
+ this.lastChunk = first;
}
+
+ first.original = this.original.slice( start, end );
+ first.end = end;
}
else {
@@ -338,6 +361,44 @@ MagicString.prototype = {
return this;
},
+ prependLeft ( index, content ) {
+ if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
+
+ if ( DEBUG ) this.stats.time( 'insertRight' );
+
+ this._split( index );
+
+ const chunk = this.byEnd[ index ];
+
+ if ( chunk ) {
+ chunk.prependLeft( content );
+ } else {
+ this.intro = content + this.intro;
+ }
+
+ if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
+ return this;
+ },
+
+ prependRight ( index, content ) {
+ if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
+
+ if ( DEBUG ) this.stats.time( 'insertRight' );
+
+ this._split( index );
+
+ const chunk = this.byStart[ index ];
+
+ if ( chunk ) {
+ chunk.prependRight( content );
+ } else {
+ this.outro = content + this.outro;
+ }
+
+ if ( DEBUG ) this.stats.timeEnd( 'insertRight' );
+ return this;
+ },
+
remove ( start, end ) {
while ( start < 0 ) start += this.original.length;
while ( end < 0 ) end += this.original.length;
@@ -370,7 +431,7 @@ MagicString.prototype = {
if ( chunk && chunk.edited && chunk.start !== start ) throw new Error(`Cannot use replaced character ${start} as slice start anchor.`);
- let startChunk = chunk;
+ const startChunk = chunk;
while ( chunk ) {
if ( chunk.intro && ( startChunk !== chunk || chunk.start === start ) ) {
result += chunk.intro;
diff --git a/src/utils/encodeMappings.js b/src/utils/encodeMappings.js
index b96efca..de210e0 100644
--- a/src/utils/encodeMappings.js
+++ b/src/utils/encodeMappings.js
@@ -5,7 +5,7 @@ import getLocator from './getLocator.js';
const nonWhitespace = /\S/;
export default function encodeMappings ( original, intro, outro, chunk, hires, sourcemapLocations, sourceIndex, offsets, names ) {
- let rawLines = [];
+ const rawLines = [];
let generatedCodeLine = intro.split( '\n' ).length - 1;
let rawSegments = rawLines[ generatedCodeLine ] = [];
@@ -83,7 +83,7 @@ export default function encodeMappings ( original, intro, outro, chunk, hires, s
let hasContent = false;
while ( chunk ) {
- let loc = locate( chunk.start );
+ const loc = locate( chunk.start );
if ( chunk.intro.length ) {
addEdit( chunk.intro, '', loc, -1, hasContent );
@@ -114,7 +114,7 @@ export default function encodeMappings ( original, intro, outro, chunk, hires, s
let generatedCodeColumn = 0;
return segments.map( segment => {
- let arr = [
+ const arr = [
segment.generatedCodeColumn - generatedCodeColumn,
segment.sourceIndex - offsets.sourceIndex,
segment.sourceCodeLine - offsets.sourceCodeLine,
diff --git a/src/utils/getLocator.js b/src/utils/getLocator.js
index d2ea142..3043f12 100644
--- a/src/utils/getLocator.js
+++ b/src/utils/getLocator.js
@@ -1,8 +1,8 @@
export default function getLocator ( source ) {
- let originalLines = source.split( '\n' );
+ const originalLines = source.split( '\n' );
let start = 0;
- let lineRanges = originalLines.map( ( line, i ) => {
+ const lineRanges = originalLines.map( ( line, i ) => {
const end = start + line.length + 1;
const range = { start, end, line: i };
diff --git a/src/utils/getRelativePath.js b/src/utils/getRelativePath.js
index a05cb19..bf8e35d 100644
--- a/src/utils/getRelativePath.js
+++ b/src/utils/getRelativePath.js
@@ -1,6 +1,6 @@
export default function getRelativePath ( from, to ) {
- let fromParts = from.split( /[\/\\]/ );
- let toParts = to.split( /[\/\\]/ );
+ const fromParts = from.split( /[\/\\]/ );
+ const toParts = to.split( /[\/\\]/ );
fromParts.pop(); // get dirname
diff --git a/test/.eslintrc b/test/.eslintrc
new file mode 100644
index 0000000..434fc13
--- /dev/null
+++ b/test/.eslintrc
@@ -0,0 +1,6 @@
+{
+ "env": {
+ "node": true,
+ "mocha": true
+ }
+}
\ No newline at end of file
diff --git a/test/MagicString.Bundle.js b/test/MagicString.Bundle.js
new file mode 100644
index 0000000..9033450
--- /dev/null
+++ b/test/MagicString.Bundle.js
@@ -0,0 +1,574 @@
+const assert = require( 'assert' );
+const SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
+const MagicString = require( '../' );
+
+require( 'source-map-support' ).install();
+require( 'console-group' ).install();
+
+describe( 'MagicString.Bundle', () => {
+ describe( 'addSource', () => {
+ it( 'should return this', () => {
+ const b = new MagicString.Bundle();
+ const source = new MagicString( 'abcdefghijkl' );
+
+ assert.strictEqual( b.addSource({ content: source }), b );
+ });
+
+ it( 'should accept MagicString instance as a single argument', () => {
+ const b = new MagicString.Bundle();
+ const array = [];
+ const source = new MagicString( 'abcdefghijkl', {
+ filename: 'foo.js',
+ indentExclusionRanges: array
+ });
+
+ b.addSource( source );
+ assert.strictEqual( b.sources[0].content, source );
+ assert.strictEqual( b.sources[0].filename, 'foo.js' );
+ assert.strictEqual( b.sources[0].indentExclusionRanges, array );
+ });
+
+ it( 'respects MagicString init options with { content: source }', () => {
+ const b = new MagicString.Bundle();
+ const array = [];
+ const source = new MagicString( 'abcdefghijkl', {
+ filename: 'foo.js',
+ indentExclusionRanges: array
+ });
+
+ b.addSource({ content: source });
+ assert.strictEqual( b.sources[0].content, source );
+ assert.strictEqual( b.sources[0].filename, 'foo.js' );
+ assert.strictEqual( b.sources[0].indentExclusionRanges, array );
+ });
+ });
+
+ describe( 'append', () => {
+ it( 'should append content', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({ content: new MagicString( '*' ) });
+
+ b.append( '123' ).append( '456' );
+ assert.equal( b.toString(), '*123456' );
+ });
+
+ it( 'should append content before subsequent sources', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource( new MagicString( '*' ) );
+
+ b.append( '123' ).addSource( new MagicString( '-' ) ).append( '456' );
+ assert.equal( b.toString(), '*123\n-456' );
+ });
+
+ it( 'should return this', () => {
+ const b = new MagicString.Bundle();
+ assert.strictEqual( b.append( 'x' ), b );
+ });
+ });
+
+ describe( 'clone', () => {
+ it( 'should clone a bundle', () => {
+ const s1 = new MagicString( 'abcdef' );
+ const s2 = new MagicString( 'ghijkl' );
+ const b = new MagicString.Bundle()
+ .addSource({ content: s1 })
+ .addSource({ content: s2 })
+ .prepend( '>>>' )
+ .append( '<<<' );
+ const clone = b.clone();
+
+ assert.equal( clone.toString(), '>>>abcdef\nghijkl<<<' );
+
+ s1.overwrite( 2, 4, 'XX' );
+ assert.equal( b.toString(), '>>>abXXef\nghijkl<<<' );
+ assert.equal( clone.toString(), '>>>abcdef\nghijkl<<<' );
+ });
+ });
+
+ describe( 'generateMap', () => {
+ it( 'should generate a sourcemap', () => {
+ const b = new MagicString.Bundle()
+ .addSource({
+ filename: 'foo.js',
+ content: new MagicString( 'var answer = 42;' )
+ })
+ .addSource({
+ filename: 'bar.js',
+ content: new MagicString( 'console.log( answer );' )
+ });
+
+
+ const map = b.generateMap({
+ file: 'bundle.js',
+ includeContent: true,
+ hires: true
+ });
+
+ assert.equal( map.version, 3 );
+ assert.equal( map.file, 'bundle.js' );
+ assert.deepEqual( map.sources, [ 'foo.js', 'bar.js' ]);
+ assert.deepEqual( map.sourcesContent, [ 'var answer = 42;', 'console.log( answer );' ]);
+
+ assert.equal( map.toString(), '{"version":3,"file":"bundle.js","sources":["foo.js","bar.js"],"sourcesContent":["var answer = 42;","console.log( answer );"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;ACAf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}' );
+ assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyJmb28uanMiLCJiYXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGFuc3dlciA9IDQyOyIsImNvbnNvbGUubG9nKCBhbnN3ZXIgKTsiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUNBZixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE [...]
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 1, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'bar.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'bar.js' );
+ });
+
+ it( 'should handle Windows-style paths', () => {
+ const b = new MagicString.Bundle()
+ .addSource({
+ filename: 'path\\to\\foo.js',
+ content: new MagicString( 'var answer = 42;' )
+ })
+ .addSource({
+ filename: 'path\\to\\bar.js',
+ content: new MagicString( 'console.log( answer );' )
+ });
+
+ const map = b.generateMap({
+ file: 'bundle.js',
+ includeContent: true,
+ hires: true
+ });
+
+ assert.equal( map.version, 3 );
+ assert.equal( map.file, 'bundle.js' );
+ assert.deepEqual( map.sources, [ 'path/to/foo.js', 'path/to/bar.js' ]);
+ assert.deepEqual( map.sourcesContent, [ 'var answer = 42;', 'console.log( answer );' ]);
+
+ assert.equal( map.toString(), '{"version":3,"file":"bundle.js","sources":["path/to/foo.js","path/to/bar.js"],"sourcesContent":["var answer = 42;","console.log( answer );"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;ACAf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}' );
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'path/to/foo.js' );
+
+ loc = smc.originalPositionFor({ line: 1, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'path/to/foo.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'path/to/bar.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'path/to/bar.js' );
+ });
+
+ it( 'should handle edge case with intro content', () => {
+ const b = new MagicString.Bundle()
+ .addSource({
+ filename: 'foo.js',
+ content: new MagicString( 'var answer = 42;' )
+ })
+ .addSource({
+ filename: 'bar.js',
+ content: new MagicString( '\nconsole.log( answer );' )
+ })
+ .indent().prepend( '(function () {\n' ).append( '\n}());' );
+
+ const map = b.generateMap({
+ file: 'bundle.js',
+ includeContent: true,
+ hires: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 2, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 2 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 4, column: 1 });
+ assert.equal( loc.line, 2 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'bar.js' );
+
+ loc = smc.originalPositionFor({ line: 4, column: 2 });
+ assert.equal( loc.line, 2 );
+ assert.equal( loc.column, 1 );
+ assert.equal( loc.source, 'bar.js' );
+ });
+
+ it( 'should allow missing file option when generating map', () => {
+ new MagicString.Bundle()
+ .addSource({
+ filename: 'foo.js',
+ content: new MagicString( 'var answer = 42;' )
+ })
+ .generateMap({
+ includeContent: true,
+ hires: true
+ });
+ });
+
+ it( 'should handle repeated sources', () => {
+ const b = new MagicString.Bundle();
+
+ const foo = new MagicString( 'var one = 1;\nvar three = 3;', {
+ filename: 'foo.js'
+ });
+
+ const bar = new MagicString( 'var two = 2;\nvar four = 4;', {
+ filename: 'bar.js'
+ });
+
+ b.addSource( foo.snip( 0, 12 ) );
+ b.addSource( bar.snip( 0, 12 ) );
+ b.addSource( foo.snip( 13, 27 ) );
+ b.addSource( bar.snip( 13, 26 ) );
+
+ const code = b.toString();
+ assert.equal( code, 'var one = 1;\nvar two = 2;\nvar three = 3;\nvar four = 4;' );
+
+ const map = b.generateMap({
+ includeContent: true,
+ hires: true
+ });
+
+ assert.equal( map.sources.length, 2 );
+ assert.equal( map.sourcesContent.length, 2 );
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'bar.js' );
+
+ loc = smc.originalPositionFor({ line: 3, column: 0 });
+ assert.equal( loc.line, 2 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'foo.js' );
+
+ loc = smc.originalPositionFor({ line: 4, column: 0 });
+ assert.equal( loc.line, 2 );
+ assert.equal( loc.column, 0 );
+ assert.equal( loc.source, 'bar.js' );
+ });
+
+ it( 'should recover original names', () => {
+ const b = new MagicString.Bundle();
+
+ const one = new MagicString( 'function one () {}', { filename: 'one.js' });
+ const two = new MagicString( 'function two () {}', { filename: 'two.js' });
+
+ one.overwrite( 9, 12, 'three', true );
+ two.overwrite( 9, 12, 'four', true );
+
+ b.addSource( one );
+ b.addSource( two );
+
+ const map = b.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.name, 'one' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 9 });
+ assert.equal( loc.name, 'two' );
+ });
+
+ it( 'should exclude sources without filename from sourcemap', () => {
+ const b = new MagicString.Bundle();
+
+ const one = new MagicString( 'function one () {}', { filename: 'one.js' });
+ const two = new MagicString( 'function two () {}', { filename: null });
+ const three = new MagicString( 'function three () {}', { filename: 'three.js' });
+
+ b.addSource( one );
+ b.addSource( two );
+ b.addSource( three );
+
+ const map = b.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.source, 'one.js' );
+
+ loc = smc.originalPositionFor({ line: 2, column: 9 });
+ assert.equal( loc.source, null );
+
+ loc = smc.originalPositionFor({ line: 3, column: 9 });
+ assert.equal( loc.source, 'three.js' );
+ });
+
+ it( 'handles prepended content', () => {
+ const b = new MagicString.Bundle();
+
+ const one = new MagicString( 'function one () {}', { filename: 'one.js' });
+ const two = new MagicString( 'function two () {}', { filename: 'two.js' });
+ two.prepend( 'function oneAndAHalf() {}\n' );
+
+ b.addSource( one );
+ b.addSource( two );
+
+ const map = b.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.source, 'one.js' );
+
+ loc = smc.originalPositionFor({ line: 3, column: 9 });
+ assert.equal( loc.source, 'two.js' );
+ });
+
+ it( 'handles appended content', () => {
+ const b = new MagicString.Bundle();
+
+ const one = new MagicString( 'function one () {}', { filename: 'one.js' });
+ one.append( '\nfunction oneAndAHalf() {}' );
+ const two = new MagicString( 'function two () {}', { filename: 'two.js' });
+
+ b.addSource( one );
+ b.addSource( two );
+
+ const map = b.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.source, 'one.js' );
+
+ loc = smc.originalPositionFor({ line: 3, column: 9 });
+ assert.equal( loc.source, 'two.js' );
+ });
+ });
+
+ describe( 'indent', () => {
+ it( 'should indent a bundle', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({ content: new MagicString( 'abcdef' ) });
+ b.addSource({ content: new MagicString( 'ghijkl' ) });
+
+ b.indent().prepend( '>>>\n' ).append( '\n<<<' );
+ assert.equal( b.toString(), '>>>\n\tabcdef\n\tghijkl\n<<<' );
+ });
+
+ it( 'should ignore non-indented sources when guessing indentation', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({ content: new MagicString( 'abcdef' ) });
+ b.addSource({ content: new MagicString( 'ghijkl' ) });
+ b.addSource({ content: new MagicString( ' mnopqr' ) });
+
+ b.indent();
+ assert.equal( b.toString(), ' abcdef\n ghijkl\n mnopqr' );
+ });
+
+ it( 'should respect indent exclusion ranges', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({
+ content: new MagicString( 'abc\ndef\nghi\njkl' ),
+ indentExclusionRanges: [ 7, 15 ]
+ });
+
+ b.indent( ' ' );
+ assert.equal( b.toString(), ' abc\n def\nghi\njkl' );
+
+ b.indent( '>>' );
+ assert.equal( b.toString(), '>> abc\n>> def\nghi\njkl' );
+ });
+
+ it( 'does not indent sources with no preceding newline, i.e. append()', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource( new MagicString( 'abcdef' ) );
+ b.addSource( new MagicString( 'ghijkl' ) );
+
+ b.prepend( '>>>' ).append( '<<<' ).indent();
+ assert.equal( b.toString(), '\t>>>abcdef\n\tghijkl<<<' );
+ });
+
+ it( 'should noop with an empty string', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource( new MagicString( 'abcdef' ) );
+ b.addSource( new MagicString( 'ghijkl' ) );
+
+ b.indent( '' );
+ assert.equal( b.toString(), 'abcdef\nghijkl' );
+ });
+
+ it( 'indents prepended content', () => {
+ const b = new MagicString.Bundle();
+ b.prepend( 'a\nb' ).indent();
+
+ assert.equal( b.toString(), '\ta\n\tb' );
+ });
+
+ it( 'indents content immediately following intro with trailing newline', () => {
+ const b = new MagicString.Bundle({ separator: '\n\n' });
+
+ const s = new MagicString( '2' );
+ b.addSource({ content: s });
+
+ b.prepend( '1\n' );
+
+ assert.equal( b.indent().toString(), '\t1\n\t2' );
+ });
+
+ it( 'should return this', () => {
+ const b = new MagicString.Bundle();
+ assert.strictEqual( b.indent(), b );
+ });
+
+ it( 'should return this on noop', () => {
+ const b = new MagicString.Bundle();
+ assert.strictEqual( b.indent( '' ), b );
+ });
+ });
+
+ describe( 'prepend', () => {
+ it( 'should append content', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({ content: new MagicString( '*' ) });
+
+ b.prepend( '123' ).prepend( '456' );
+ assert.equal( b.toString(), '456123*' );
+ });
+
+ it( 'should return this', () => {
+ const b = new MagicString.Bundle();
+ assert.strictEqual( b.prepend( 'x' ), b );
+ });
+ });
+
+ describe( 'trim', () => {
+ it( 'should trim bundle', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({
+ content: new MagicString( ' abcdef ' )
+ });
+
+ b.addSource({
+ content: new MagicString( ' ghijkl ' )
+ });
+
+ b.trim();
+ assert.equal( b.toString(), 'abcdef \n ghijkl' );
+ });
+
+ it( 'should handle funky edge cases', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource({
+ content: new MagicString( ' abcdef ' )
+ });
+
+ b.addSource({
+ content: new MagicString( ' x ' )
+ });
+
+ b.prepend( '\n>>>\n' ).append( ' ' );
+
+ b.trim();
+ assert.equal( b.toString(), '>>>\n abcdef \n x' );
+ });
+
+ it( 'should return this', () => {
+ const b = new MagicString.Bundle();
+ assert.strictEqual( b.trim(), b );
+ });
+ });
+
+ describe( 'toString', () => {
+ it( 'should separate with a newline by default', () => {
+ const b = new MagicString.Bundle();
+
+ b.addSource( new MagicString( 'abc' ) );
+ b.addSource( new MagicString( 'def' ) );
+
+ assert.strictEqual( b.toString(), 'abc\ndef' );
+ });
+
+ it( 'should accept separator option', () => {
+ const b = new MagicString.Bundle({ separator: '==' });
+
+ b.addSource( new MagicString( 'abc' ) );
+ b.addSource( new MagicString( 'def' ) );
+
+ assert.strictEqual( b.toString(), 'abc==def' );
+ });
+
+ it( 'should accept empty string separator option', () => {
+ const b = new MagicString.Bundle({ separator: '' });
+
+ b.addSource( new MagicString( 'abc' ) );
+ b.addSource( new MagicString( 'def' ) );
+
+ assert.strictEqual( b.toString(), 'abcdef' );
+ });
+ });
+});
diff --git a/test/MagicString.js b/test/MagicString.js
new file mode 100644
index 0000000..d34a269
--- /dev/null
+++ b/test/MagicString.js
@@ -0,0 +1,1127 @@
+const assert = require( 'assert' );
+const SourceMapConsumer = require( 'source-map' ).SourceMapConsumer;
+const MagicString = require( '../' );
+
+require( 'source-map-support' ).install();
+require( 'console-group' ).install();
+
+describe( 'MagicString', () => {
+ describe( 'options', () => {
+ it( 'stores source file information', () => {
+ const s = new MagicString( 'abc', {
+ filename: 'foo.js'
+ });
+
+ assert.equal( s.filename, 'foo.js' );
+ });
+ });
+
+ describe( 'append', () => {
+ it( 'should append content', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.append( 'xyz' );
+ assert.equal( s.toString(), 'abcdefghijklxyz' );
+
+ s.append( 'xyz' );
+ assert.equal( s.toString(), 'abcdefghijklxyzxyz' );
+ });
+
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.append( 'xyz' ), s );
+ });
+
+ it( 'should throw when given non-string content', () => {
+ const s = new MagicString( '' );
+ assert.throws( () => s.append( [] ), TypeError );
+ });
+ });
+
+ describe( '(ap|pre)pend(Left|Right)', () => {
+ it( 'preserves intended order', () => {
+ const s = new MagicString( '0123456789' );
+
+ s.insertLeft( 5, 'A' );
+ s.insertRight( 5, 'a' );
+ s.insertRight( 5, 'b' );
+ s.insertLeft( 5, 'B' );
+ s.insertLeft( 5, 'C' );
+ s.insertRight( 5, 'c' );
+
+ assert.equal( s.toString(), '01234ABCcba56789' );
+ assert.equal( s.slice(0, 5) , '01234ABC' );
+ assert.equal( s.slice(5), 'cba56789' );
+
+ s.prependLeft( 5, '<' );
+ s.prependLeft( 5, '{' );
+ assert.equal( s.toString(), '01234{<ABCcba56789' );
+
+ s.appendRight( 5, '>' );
+ s.appendRight( 5, '}' );
+ assert.equal( s.toString(), '01234{<ABCcba>}56789' );
+
+ s.appendLeft(5, '('); // appendLeft is a synonym for insertLeft
+ s.appendLeft(5, '['); // appendLeft is a synonym for insertLeft
+ assert.equal( s.toString(), '01234{<ABC([cba>}56789' );
+
+ s.prependRight(5, ')'); // prependRight is a synonym for insertRight
+ s.prependRight(5, ']'); // prependRight is a synonym for insertRight
+ assert.equal( s.toString(), '01234{<ABC([])cba>}56789' );
+
+ assert.equal( s.slice(0, 5), '01234{<ABC([' );
+ assert.equal( s.slice(5), '])cba>}56789' );
+ });
+
+ it( 'preserves intended order at beginning of string', () => {
+ const s = new MagicString( 'x' );
+
+ s.appendLeft( 0, '1' );
+ s.prependLeft( 0, '2' );
+ s.appendLeft( 0, '3' );
+ s.prependLeft( 0, '4' );
+
+ assert.equal( s.toString(), '4213x' );
+ });
+
+ it( 'preserves intended order at end of string', () => {
+ const s = new MagicString( 'x' );
+
+ s.appendRight( 1, '1' );
+ s.prependRight( 1, '2' );
+ s.appendRight( 1, '3' );
+ s.prependRight( 1, '4' );
+
+ assert.equal( s.toString(), 'x4213' );
+ });
+ });
+
+ describe( 'appendLeft', () => {
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.appendLeft( 0, 'a' ), s );
+ });
+ });
+
+ describe( 'appendRight', () => {
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.appendRight( 0, 'a' ), s );
+ });
+ });
+
+ describe( 'clone', () => {
+ it( 'should clone a magic string', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 3, 9, 'XYZ' );
+ const c = s.clone();
+
+ assert.notEqual( s, c );
+ assert.equal( c.toString(), 'abcXYZjkl' );
+ });
+
+ it( 'should clone filename info', () => {
+ const s = new MagicString( 'abcdefghijkl', { filename: 'foo.js' });
+ const c = s.clone();
+
+ assert.equal( c.filename, 'foo.js' );
+ });
+
+ it( 'should clone indentExclusionRanges', () => {
+ const array = [ 3, 6 ];
+ const source = new MagicString( 'abcdefghijkl', {
+ filename: 'foo.js',
+ indentExclusionRanges: array
+ });
+
+ const clone = source.clone();
+
+ assert.notStrictEqual( source.indentExclusionRanges, clone.indentExclusionRanges );
+ assert.deepEqual( source.indentExclusionRanges, clone.indentExclusionRanges );
+ });
+
+ it( 'should clone sourcemapLocations', () => {
+ const source = new MagicString( 'abcdefghijkl', {
+ filename: 'foo.js'
+ });
+
+ source.addSourcemapLocation( 3 );
+
+ const clone = source.clone();
+
+ assert.notStrictEqual( source.sourcemapLocations, clone.sourcemapLocations );
+ assert.deepEqual( source.sourcemapLocations, clone.sourcemapLocations );
+ });
+ });
+
+ describe( 'generateMap', () => {
+ it( 'should generate a sourcemap', () => {
+ const s = new MagicString( 'abcdefghijkl' ).remove( 3, 9 );
+
+ const map = s.generateMap({
+ file: 'output.md',
+ source: 'input.md',
+ includeContent: true,
+ hires: true
+ });
+
+ assert.equal( map.version, 3 );
+ assert.equal( map.file, 'output.md' );
+ assert.deepEqual( map.sources, [ 'input.md' ]);
+ assert.deepEqual( map.sourcesContent, [ 'abcdefghijkl' ]);
+ assert.equal( map.mappings, 'AAAA,CAAC,CAAC,CAAC,AAAM,CAAC,CAAC' );
+
+ 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==' );
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+
+ loc = smc.originalPositionFor({ line: 1, column: 1 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 1 );
+
+ loc = smc.originalPositionFor({ line: 1, column: 4 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 10 );
+ });
+
+ it( 'should generate a correct sourcemap for indented content', () => {
+ const s = new MagicString( 'var answer = 42;\nconsole.log("the answer is %s", answer);' );
+
+ s.prepend( '\'use strict\';\n\n' );
+ s.indent( '\t' ).prepend( '(function () {\n' ).append( '\n}).call(global);' );
+
+ const map = s.generateMap({
+ source: 'input.md',
+ includeContent: true,
+ hires: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+
+ const originLoc = smc.originalPositionFor({ line: 5, column: 1 });
+ assert.equal( originLoc.line, 2 );
+ assert.equal( originLoc.column, 0 );
+ });
+
+ it( 'should generate a sourcemap using specified locations', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.addSourcemapLocation( 0 );
+ s.addSourcemapLocation( 3 );
+ s.addSourcemapLocation( 10 );
+
+ s.remove( 6, 9 );
+ const map = s.generateMap({
+ file: 'output.md',
+ source: 'input.md',
+ includeContent: true
+ });
+
+ assert.equal( map.version, 3 );
+ assert.equal( map.file, 'output.md' );
+ 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,GAAG,AAAG,CAAC"}' );
+ assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsR0FBRyxHQUFHLEFBQUcsQ0FBQyJ9' );
+
+ const smc = new SourceMapConsumer( map );
+ let loc;
+
+ loc = smc.originalPositionFor({ line: 1, column: 0 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 0 );
+
+ loc = smc.originalPositionFor({ line: 1, column: 3 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 3 );
+
+ loc = smc.originalPositionFor({ line: 1, column: 7 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 10 );
+ });
+
+ it( 'should correctly map inserted content', () => {
+ const s = new MagicString( 'function Foo () {}' );
+
+ s.overwrite( 9, 12, 'Bar' );
+
+ const map = s.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+
+ const loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, 9 );
+ });
+
+ it( 'should yield consistent results between insertLeft and insertRight', () => {
+ const s1 = new MagicString( 'abcdefghijkl' );
+ s1.insertLeft( 6, 'X' );
+
+ const s2 = new MagicString( 'abcdefghijkl' );
+ s2.insertRight( 6, 'X' );
+
+ const m1 = s1.generateMap({ file: 'output', source: 'input', includeContent: true });
+ const m2 = s2.generateMap({ file: 'output', source: 'input', includeContent: true });
+
+ assert.deepEqual( m1, m2 );
+ });
+
+ it( 'should recover original names', () => {
+ const s = new MagicString( 'function Foo () {}' );
+
+ s.overwrite( 9, 12, 'Bar', true );
+
+ const map = s.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+
+ const loc = smc.originalPositionFor({ line: 1, column: 9 });
+ assert.equal( loc.name, 'Foo' );
+ });
+
+ it( 'should generate one segment per replacement', () => {
+ const s = new MagicString( 'var answer = 42' );
+ s.overwrite( 4, 10, 'number', true );
+
+ const map = s.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+
+ let numMappings = 0;
+ smc.eachMapping( () => numMappings += 1 );
+
+ assert.equal( numMappings, 3 ); // one at 0, one at the edit, one afterwards
+ });
+
+ it( 'should generate a sourcemap that correctly locates moved content', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 3, 6, 9 );
+
+ const result = s.toString();
+ const map = s.generateMap({
+ file: 'output.js',
+ source: 'input.js',
+ includeContent: true,
+ hires: true
+ });
+
+ const smc = new SourceMapConsumer( map );
+
+ 'abcdefghijkl'.split( '' ).forEach( ( letter, i ) => {
+ const column = result.indexOf( letter );
+ const loc = smc.originalPositionFor({ line: 1, column });
+
+ assert.equal( loc.line, 1 );
+ assert.equal( loc.column, i );
+ });
+ });
+
+ it( 'generates a map with trimmed content (#53)', () => {
+ const s1 = new MagicString( 'abcdefghijkl ' ).trim();
+ const map1 = s1.generateMap({
+ file: 'output',
+ source: 'input',
+ includeContent: true,
+ hires: true
+ });
+
+ const smc1 = new SourceMapConsumer( map1 );
+ const loc1 = smc1.originalPositionFor({ line: 1, column: 11 });
+
+ assert.equal( loc1.column, 11 );
+
+ const s2 = new MagicString( ' abcdefghijkl' ).trim();
+ const map2 = s2.generateMap({
+ file: 'output',
+ source: 'input',
+ includeContent: true,
+ hires: true
+ });
+
+ const smc2 = new SourceMapConsumer( map2 );
+ const loc2 = smc2.originalPositionFor({ line: 1, column: 1 });
+
+ assert.equal( loc2.column, 2 );
+ });
+
+ it( 'skips empty segments at the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.remove( 0, 3 ).remove( 3, 6 );
+
+ const map = s.generateMap();
+ const smc = new SourceMapConsumer( map );
+ const loc = smc.originalPositionFor({ line: 1, column: 6 });
+
+ assert.equal( loc.column, 6 );
+ });
+
+ it( 'skips indentation at the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.indent( ' ' );
+
+ const map = s.generateMap();
+ assert.equal( map.mappings, 'IAAA' );
+ });
+ });
+
+ describe( 'getIndentString', () => {
+ it( 'should guess the indent string', () => {
+ const s = new MagicString( 'abc\n def\nghi' );
+ assert.equal( s.getIndentString(), ' ' );
+ });
+
+ it( 'should return a tab if no lines are indented', () => {
+ const s = new MagicString( 'abc\ndef\nghi' );
+ assert.equal( s.getIndentString(), '\t' );
+ });
+ });
+
+ describe( 'indent', () => {
+ it( 'should indent content with a single tab character by default', () => {
+ const s = new MagicString( 'abc\ndef\nghi\njkl' );
+
+ s.indent();
+ assert.equal( s.toString(), '\tabc\n\tdef\n\tghi\n\tjkl' );
+
+ s.indent();
+ assert.equal( s.toString(), '\t\tabc\n\t\tdef\n\t\tghi\n\t\tjkl' );
+ });
+
+ it( 'should indent content, using existing indentation as a guide', () => {
+ const s = new MagicString( 'abc\n def\n ghi\n jkl' );
+
+ s.indent();
+ assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
+
+ s.indent();
+ assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
+ });
+
+ it( 'should disregard single-space indentation when auto-indenting', () => {
+ const s = new MagicString( 'abc\n/**\n *comment\n */' );
+
+ s.indent();
+ assert.equal( s.toString(), '\tabc\n\t/**\n\t *comment\n\t */' );
+ });
+
+ it( 'should indent content using the supplied indent string', () => {
+ const s = new MagicString( 'abc\ndef\nghi\njkl' );
+
+ s.indent( ' ');
+ assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
+
+ s.indent( '>>' );
+ assert.equal( s.toString(), '>> abc\n>> def\n>> ghi\n>> jkl' );
+ });
+
+ it( 'should indent content using the empty string if specified (i.e. noop)', () => {
+ const s = new MagicString( 'abc\ndef\nghi\njkl' );
+
+ s.indent( '');
+ assert.equal( s.toString(), 'abc\ndef\nghi\njkl' );
+ });
+
+ it( 'should prevent excluded characters from being indented', () => {
+ const s = new MagicString( 'abc\ndef\nghi\njkl' );
+
+ s.indent( ' ', { exclude: [ 7, 15 ] });
+ assert.equal( s.toString(), ' abc\n def\nghi\njkl' );
+
+ s.indent( '>>', { exclude: [ 7, 15 ] });
+ assert.equal( s.toString(), '>> abc\n>> def\nghi\njkl' );
+ });
+
+ it( 'should not add characters to empty lines', () => {
+ const s = new MagicString( '\n\nabc\ndef\n\nghi\njkl' );
+
+ s.indent();
+ assert.equal( s.toString(), '\n\n\tabc\n\tdef\n\n\tghi\n\tjkl' );
+
+ s.indent();
+ assert.equal( s.toString(), '\n\n\t\tabc\n\t\tdef\n\n\t\tghi\n\t\tjkl' );
+ });
+
+ it( 'should not add characters to empty lines, even on Windows', () => {
+ const s = new MagicString( '\r\n\r\nabc\r\ndef\r\n\r\nghi\r\njkl' );
+
+ s.indent();
+ assert.equal( s.toString(), '\r\n\r\n\tabc\r\n\tdef\r\n\r\n\tghi\r\n\tjkl' );
+
+ s.indent();
+ 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', () => {
+ const s = new MagicString( '/* remove this line */\nvar foo = 1;' );
+
+ s.remove( 0, 23 );
+ s.indent();
+
+ assert.equal( s.toString(), '\tvar foo = 1;' );
+ });
+
+ it( 'should not indent patches in the middle of a line', () => {
+ const 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', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.indent(), s );
+ });
+
+ it( 'should return this on noop', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.indent( '' ), s );
+ });
+ });
+
+ describe( 'insert', () => {
+ it( 'is deprecated', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.throws( () => s.insert( 6, 'X' ), /deprecated/ );
+ });
+
+ // TODO move this into insertRight and insertLeft tests
+
+ // it( 'should insert characters in the correct location', () => {
+ // const s = new MagicString( 'abcdefghijkl' );
+ //
+ // s.insert( 0, '>>>' );
+ // s.insert( 6, '***' );
+ // s.insert( 12, '<<<' );
+ //
+ // assert.equal( s.toString(), '>>>abcdef***ghijkl<<<' );
+ // });
+ //
+ // it( 'should return this', () => {
+ // const s = new MagicString( 'abcdefghijkl' );
+ // assert.strictEqual( s.insert( 0, 'a' ), s );
+ // });
+ //
+ // it( 'should insert repeatedly at the same position correctly', () => {
+ // const s = new MagicString( 'ab' );
+ // assert.equal( s.insert(1, '1').toString(), 'a1b' );
+ // assert.equal( s.insert(1, '2').toString(), 'a12b' );
+ // });
+ //
+ // it( 'should insert repeatedly at the beginning correctly', () => {
+ // const s = new MagicString( 'ab' );
+ // assert.equal( s.insert(0, '1').toString(), '1ab' );
+ // assert.equal( s.insert(0, '2').toString(), '12ab' );
+ // });
+ //
+ // it( 'should throw when given non-string content', () => {
+ // const s = new MagicString( '' );
+ // assert.throws(
+ // function () { s.insert( 0, [] ); },
+ // TypeError
+ // );
+ // });
+ //
+ // it( 'should allow inserting after removed range', () => {
+ // const s = new MagicString( 'abcd' );
+ // s.remove( 1, 2 );
+ // s.insert( 2, 'z' );
+ // assert.equal( s.toString(), 'azcd' );
+ // });
+ });
+
+ describe( 'move', () => {
+ it( 'moves content from the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 0, 3, 6 );
+
+ assert.equal( s.toString(), 'defabcghijkl' );
+ });
+
+ it( 'moves content to the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 3, 6, 0 );
+
+ assert.equal( s.toString(), 'defabcghijkl' );
+ });
+
+ it( 'moves content from the end', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 9, 12, 6 );
+
+ assert.equal( s.toString(), 'abcdefjklghi' );
+ });
+
+ it( 'moves content to the end', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 6, 9, 12 );
+
+ assert.equal( s.toString(), 'abcdefjklghi' );
+ });
+
+ it( 'ignores redundant move', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.insertRight( 9, 'X' );
+ s.move( 9, 12, 6 );
+ s.insertLeft( 12, 'Y' );
+ s.move( 6, 9, 12 ); // this is redundant – [6,9] is already after [9,12]
+
+ assert.equal( s.toString(), 'abcdefXjklYghi' );
+ });
+
+ it( 'moves content to the middle', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 3, 6, 9 );
+
+ assert.equal( s.toString(), 'abcghidefjkl' );
+ });
+
+ it( 'handles multiple moves of the same snippet', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.move( 0, 3, 6 );
+ assert.equal( s.toString(), 'defabcghijkl' );
+
+ s.move( 0, 3, 9 );
+ assert.equal( s.toString(), 'defghiabcjkl' );
+ });
+
+ it( 'handles moves of adjacent snippets', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.move( 0, 2, 6 );
+ assert.equal( s.toString(), 'cdefabghijkl' );
+
+ s.move( 2, 4, 6 );
+ assert.equal( s.toString(), 'efabcdghijkl' );
+ });
+
+ it( 'handles moves to same index', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ s.move( 0, 2, 6 ).move( 3, 5, 6 );
+
+ assert.equal( s.toString(), 'cfabdeghijkl' );
+ });
+
+ it( 'refuses to move a selection to inside itself', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ assert.throws( () => s.move( 3, 6, 3 ), /Cannot move a selection inside itself/ );
+
+ assert.throws( () => s.move( 3, 6, 4 ), /Cannot move a selection inside itself/ );
+
+ assert.throws( () => s.move( 3, 6, 6 ), /Cannot move a selection inside itself/ );
+ });
+
+ it( 'allows edits of moved content', () => {
+ const s1 = new MagicString( 'abcdefghijkl' );
+
+ s1.move( 3, 6, 9 );
+ s1.overwrite( 3, 6, 'DEF' );
+
+ assert.equal( s1.toString(), 'abcghiDEFjkl' );
+
+ const s2 = new MagicString( 'abcdefghijkl' );
+
+ s2.move( 3, 6, 9 );
+ s2.overwrite( 4, 5, 'E' );
+
+ assert.equal( s2.toString(), 'abcghidEfjkl' );
+ });
+
+ // it( 'move follows inserts', () => {
+ // const s = new MagicString( 'abcdefghijkl' );
+ //
+ // s.insertLeft( 3, 'X' ).move( 6, 9, 3 );
+ // assert.equal( s.toString(), 'abcXghidefjkl' );
+ // });
+ //
+ // it( 'inserts follow move', () => {
+ // const s = new MagicString( 'abcdefghijkl' );
+ //
+ // s.insert( 3, 'X' ).move( 6, 9, 3 ).insert( 3, 'Y' );
+ // assert.equal( s.toString(), 'abcXghiYdefjkl' );
+ // });
+ //
+ // it( 'discards inserts at end of move by default', () => {
+ // const s = new MagicString( 'abcdefghijkl' );
+ //
+ // s.insert( 6, 'X' ).move( 3, 6, 9 );
+ // assert.equal( s.toString(), 'abcXghidefjkl' );
+ // });
+
+ it( 'moves content inserted at end of range', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.insertLeft( 6, 'X' ).move( 3, 6, 9 );
+ assert.equal( s.toString(), 'abcghidefXjkl' );
+ });
+
+ it( 'returns this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.move( 3, 6, 9 ), s );
+ });
+ });
+
+ describe( 'overwrite', () => {
+ it( 'should replace characters', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 5, 8, 'FGH' );
+ assert.equal( s.toString(), 'abcdeFGHijkl' );
+ });
+
+ it( 'should throw an error if overlapping replacements are attempted', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 7, 11, 'xx' );
+
+ assert.throws( () => s.overwrite( 8, 12, 'yy' ), /Cannot split a chunk that has already been edited/ );
+
+ assert.equal( s.toString(), 'abcdefgxxl' );
+
+ s.overwrite( 6, 12, 'yes' );
+ assert.equal( s.toString(), 'abcdefyes' );
+ });
+
+ it( 'should allow contiguous but non-overlapping replacements', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 3, 6, 'DEF' );
+ assert.equal( s.toString(), 'abcDEFghijkl' );
+
+ s.overwrite( 6, 9, 'GHI' );
+ assert.equal( s.toString(), 'abcDEFGHIjkl' );
+
+ s.overwrite( 0, 3, 'ABC' );
+ assert.equal( s.toString(), 'ABCDEFGHIjkl' );
+
+ s.overwrite( 9, 12, 'JKL' );
+ assert.equal( s.toString(), 'ABCDEFGHIJKL' );
+ });
+
+ it( 'does not replace zero-length inserts at overwrite start location', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.remove( 0, 6 );
+ s.insertLeft( 6, 'DEF' );
+ s.overwrite( 6, 9, 'GHI' );
+ assert.equal( s.toString(), 'DEFGHIjkl' );
+ });
+
+ it( 'replaces zero-length inserts inside overwrite', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.insertLeft( 6, 'XXX' );
+ s.overwrite( 3, 9, 'DEFGHI' );
+ assert.equal( s.toString(), 'abcDEFGHIjkl' );
+ });
+
+ it( 'replaces non-zero-length inserts inside overwrite', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 3, 4, 'XXX' );
+ s.overwrite( 3, 5, 'DE' );
+ assert.equal( s.toString(), 'abcDEfghijkl' );
+
+ s.overwrite( 7, 8, 'YYY' );
+ s.overwrite( 6, 8, 'GH' );
+ assert.equal( s.toString(), 'abcDEfGHijkl' );
+ });
+
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.overwrite( 3, 4, 'D' ), s );
+ });
+
+ it( 'should disallow overwriting zero-length ranges', () => {
+ const s = new MagicString( 'x' );
+ assert.throws( () => s.overwrite( 0, 0, 'anything' ), /Cannot overwrite a zero-length range – use insertLeft or insertRight instead/ );
+ });
+
+ it( 'should throw when given non-string content', () => {
+ const s = new MagicString( '' );
+ assert.throws( () => s.overwrite( 0, 1, [] ), TypeError );
+ });
+
+ it ( 'replaces interior inserts', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.insertLeft( 1, '&' );
+ s.insertRight( 1, '^' );
+ s.insertLeft( 3, '!' );
+ s.insertRight( 3, '?' );
+ s.overwrite( 1, 3, '...' );
+ assert.equal( s.toString(), 'a&...?defghijkl' );
+ });
+ });
+
+ describe( 'prepend', () => {
+ it( 'should prepend content', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.prepend( 'xyz' );
+ assert.equal( s.toString(), 'xyzabcdefghijkl' );
+
+ s.prepend( '123' );
+ assert.equal( s.toString(), '123xyzabcdefghijkl' );
+ });
+
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.prepend( 'xyz' ), s );
+ });
+ });
+
+ describe( 'prependLeft', () => {
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.prependLeft( 0, 'a' ), s );
+ });
+ });
+
+ describe( 'prependRight', () => {
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.prependRight( 0, 'a' ), s );
+ });
+ });
+
+ describe( 'remove', () => {
+ it( 'should remove characters from the original string', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.remove( 1, 5 );
+ assert.equal( s.toString(), 'afghijkl' );
+
+ s.remove( 9, 12 );
+ assert.equal( s.toString(), 'afghi' );
+ });
+
+ it( 'should remove from the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.remove( 0, 6 );
+ assert.equal( s.toString(), 'ghijkl' );
+ });
+
+ it( 'should remove from the end', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.remove( 6, 12 );
+ assert.equal( s.toString(), 'abcdef' );
+ });
+
+ it( 'should treat zero-length removals as a no-op', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.remove( 0, 0 ).remove( 6, 6 ).remove( 9, -3 );
+ assert.equal( s.toString(), 'abcdefghijkl' );
+ });
+
+ it( 'should remove overlapping ranges', () => {
+ const s1 = new MagicString( 'abcdefghijkl' );
+
+ s1.remove( 3, 7 ).remove( 5, 9 );
+ assert.equal( s1.toString(), 'abcjkl' );
+
+ const s2 = new MagicString( 'abcdefghijkl' );
+
+ s2.remove( 3, 7 ).remove( 4, 6 );
+ assert.equal( s2.toString(), 'abchijkl' );
+ });
+
+ it( 'should remove overlapping ranges, redux', () => {
+ const s = new MagicString( 'abccde' );
+
+ s.remove( 2, 3 ); // c
+ s.remove( 1, 3 ); // bc
+ assert.equal( s.toString(), 'acde' );
+ });
+
+ it( 'should remove modified ranges', () => {
+ const s = new MagicString( 'abcdefghi' );
+
+ s.overwrite( 3, 6, 'DEF' );
+ s.remove( 2, 7 ); // cDEFg
+ assert.equal( s.slice( 1, 8 ), 'bh' );
+ assert.equal( s.toString(), 'abhi' );
+ });
+
+ it( 'should not remove content inserted after the end of removed range', () => {
+ const s = new MagicString( 'ab.c;' );
+
+ s.insertRight( 0, '(' );
+ s.insertRight( 4, ')' );
+ s.remove( 2, 4 );
+ assert.equal( s.toString(), '(ab);' );
+ });
+
+ it( 'should remove interior inserts', () => {
+ const s = new MagicString( 'abc;' );
+
+ s.insertLeft( 1, '[' );
+ s.insertRight( 1, '(' );
+ s.insertLeft( 2, ')' );
+ s.insertRight( 2, ']' );
+ s.remove( 1, 2 );
+ assert.equal( s.toString(), 'a[]c;' );
+ });
+
+ it( 'should provide a useful error when illegal removals are attempted', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 5, 7, 'XX' );
+
+ assert.throws( () => s.remove( 4, 6 ), /Cannot split a chunk that has already been edited/ );
+ });
+
+ it( 'should return this', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.strictEqual( s.remove( 3, 4 ), s );
+ });
+ });
+
+ describe( 'slice', () => {
+ it( 'should return the generated content between the specified original characters', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ assert.equal( s.slice( 3, 9 ), 'defghi' );
+ s.overwrite( 4, 8, 'XX' );
+ 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( () => s.slice( 3, 9 ));
+ });
+
+ it( 'defaults `end` to the original string length', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.equal( s.slice( 3 ), 'defghijkl' );
+ });
+
+ it( 'allows negative numbers as arguments', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ assert.equal( s.slice( -3 ), 'jkl' );
+ assert.equal( s.slice( 0, -3 ), 'abcdefghi' );
+ });
+
+ it( 'includes inserted characters, respecting insertion direction', () => {
+ const s = new MagicString( 'abefij' );
+
+ s.insertRight( 2, 'cd' );
+ s.insertLeft( 4, 'gh' );
+
+ assert.equal( s.slice(), 'abcdefghij' );
+ assert.equal( s.slice( 1, 5 ), 'bcdefghi' );
+ assert.equal( s.slice( 2, 4 ), 'cdefgh' );
+ assert.equal( s.slice( 3, 4 ), 'fgh' );
+ assert.equal( s.slice( 0, 2 ), 'ab' );
+ assert.equal( s.slice( 0, 3 ), 'abcde' );
+ assert.equal( s.slice( 4, 6 ), 'ij' );
+ assert.equal( s.slice( 3, 6 ), 'fghij' );
+ });
+
+ it( 'supports characters moved outward', () => {
+ const s = new MagicString( 'abcdEFghIJklmn' );
+
+ s.move( 4, 6, 2 );
+ s.move( 8, 10, 12 );
+ assert.equal( s.toString(), 'abEFcdghklIJmn' );
+
+ assert.equal( s.slice( 1, -1 ), 'bEFcdghklIJm' );
+ assert.equal( s.slice( 2, -2 ), 'cdghkl' );
+ assert.equal( s.slice( 3, -3 ), 'dghk' );
+ assert.equal( s.slice( 4, -4 ), 'EFcdghklIJ' );
+ assert.equal( s.slice( 5, -5 ), 'FcdghklI' );
+ assert.equal( s.slice( 6, -6 ), 'gh' );
+ });
+
+ it( 'supports characters moved inward', () => {
+ const s = new MagicString( 'abCDefghijKLmn' );
+
+ s.move( 2, 4, 6 );
+ s.move( 10, 12, 8 );
+ assert.equal( s.toString(), 'abefCDghKLijmn' );
+
+ assert.equal( s.slice( 1, -1 ), 'befCDghKLijm' );
+ assert.equal( s.slice( 2, -2 ), 'CDghKL' );
+ assert.equal( s.slice( 3, -3 ), 'DghK' );
+ assert.equal( s.slice( 4, -4 ), 'efCDghKLij' );
+ assert.equal( s.slice( 5, -5 ), 'fCDghKLi' );
+ assert.equal( s.slice( 6, -6 ), 'gh' );
+ });
+
+ it( 'supports characters moved opposing', () => {
+ const s = new MagicString( 'abCDefghIJkl' );
+
+ s.move( 2, 4, 8 );
+ s.move( 8, 10, 4 );
+ assert.equal( s.toString(), 'abIJefghCDkl' );
+
+ assert.equal( s.slice( 1, -1 ), 'bIJefghCDk' );
+ assert.equal( s.slice( 2, -2 ), '' );
+ assert.equal( s.slice( 3, -3 ), '' );
+ assert.equal( s.slice( -3, 3 ), 'JefghC' );
+ assert.equal( s.slice( 4, -4 ), 'efgh' );
+ assert.equal( s.slice( 0, 3 ), 'abIJefghC' );
+ assert.equal( s.slice( 3 ), 'Dkl' );
+ assert.equal( s.slice( 0, -3 ), 'abI' );
+ assert.equal( s.slice( -3 ), 'JefghCDkl' );
+ });
+
+ it( 'errors if replaced characters are used as slice anchors', () => {
+ const s = new MagicString( 'abcdef' );
+ s.overwrite( 2, 4, 'CD' );
+
+ assert.throws( () => s.slice( 2, 3 ), /slice end anchor/ );
+
+ assert.throws( () => s.slice( 3, 4 ), /slice start anchor/ );
+
+ assert.throws( () => s.slice( 3, 5 ), /slice start anchor/ );
+
+ assert.equal( s.slice( 1, 5 ), 'bCDe' );
+ });
+
+ it( 'does not error if slice is after removed characters', () => {
+ const s = new MagicString( 'abcdef' );
+
+ s.remove( 0, 2 );
+
+ assert.equal( s.slice( 2, 4 ), 'cd' );
+ });
+ });
+
+ describe( 'snip', () => {
+ it( 'should return a clone with content outside `start` and `end` removed', () => {
+ const s = new MagicString( 'abcdefghijkl', {
+ filename: 'foo.js'
+ });
+
+ s.overwrite( 6, 9, 'GHI' );
+
+ const snippet = s.snip( 3, 9 );
+ assert.equal( snippet.toString(), 'defGHI' );
+ assert.equal( snippet.filename, 'foo.js' );
+ });
+
+ it( 'should snip from the start', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ const snippet = s.snip( 0, 6 );
+
+ assert.equal( snippet.toString(), 'abcdef' );
+ });
+
+ it( 'should snip from the end', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ const snippet = s.snip( 6, 12 );
+
+ assert.equal( snippet.toString(), 'ghijkl' );
+ });
+
+ it( 'should respect original indices', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+ const snippet = s.snip( 3, 9 );
+
+ snippet.overwrite( 6, 9, 'GHI' );
+ assert.equal( snippet.toString(), 'defGHI' );
+ });
+ });
+
+ describe( 'trim', () => {
+ it( 'should trim original content', () => {
+ 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', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.overwrite( 0, 3, ' ' ).overwrite( 9, 12, ' ' ).trim();
+ assert.equal( s.toString(), 'defghi' );
+ });
+
+ it( 'should trim original content before replaced content', () => {
+ const s = new MagicString( 'abc def' );
+
+ s.remove( 6, 9 );
+ assert.equal( s.toString(), 'abc ' );
+
+ s.trim();
+ assert.equal( s.toString(), 'abc' );
+ });
+
+ it( 'should trim original content after replaced content', () => {
+ const s = new MagicString( 'abc def' );
+
+ s.remove( 0, 3 );
+ assert.equal( s.toString(), ' def' );
+
+ s.trim();
+ assert.equal( s.toString(), 'def' );
+ });
+
+ it( 'should trim original content before and after replaced content', () => {
+ const s = new MagicString( 'abc def ghi' );
+
+ s.remove( 0, 3 );
+ s.remove( 12, 15 );
+ assert.equal( s.toString(), ' def ' );
+
+ s.trim();
+ assert.equal( s.toString(), 'def' );
+ });
+
+ it( 'should trim appended/prepended content', () => {
+ const s = new MagicString( ' abcdefghijkl ' );
+
+ s.prepend( ' ' ).append( ' ' ).trim();
+ assert.equal( s.toString(), 'abcdefghijkl' );
+ });
+
+ it( 'should trim empty string', () => {
+ const s = new MagicString( ' ' );
+
+ assert.equal( s.trim().toString(), '' );
+ });
+
+ it( 'should return this', () => {
+ const s = new MagicString( ' abcdefghijkl ' );
+ assert.strictEqual( s.trim(), s );
+ });
+ });
+
+ describe( 'trimLines', () => {
+ it( 'should trim original content', () => {
+ const s = new MagicString( '\n\n abcdefghijkl \n\n' );
+
+ s.trimLines();
+ assert.equal( s.toString(), ' abcdefghijkl ' );
+ });
+ });
+});
diff --git a/test/index.js b/test/index.js
deleted file mode 100644
index 5305692..0000000
--- a/test/index.js
+++ /dev/null
@@ -1,1684 +0,0 @@
-/*global require, describe, it, console */
-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 () {
- describe( 'options', function () {
- it( 'stores source file information', function () {
- var s = new MagicString( 'abc', {
- filename: 'foo.js'
- });
-
- assert.equal( s.filename, 'foo.js' );
- });
- });
-
- describe( 'append', function () {
- it( 'should append content', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.append( 'xyz' );
- assert.equal( s.toString(), 'abcdefghijklxyz' );
-
- s.append( 'xyz' );
- assert.equal( s.toString(), 'abcdefghijklxyzxyz' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.append( 'xyz' ), s );
- });
-
- it( 'should throw when given non-string content', function () {
- var s = new MagicString( '' );
- assert.throws(
- function () { s.append( [] ); },
- TypeError
- );
- });
- });
-
- describe( 'clone', function () {
- it( 'should clone a magic string', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 3, 9, 'XYZ' );
- var c = s.clone();
-
- assert.notEqual( s, c );
- assert.equal( c.toString(), 'abcXYZjkl' );
- });
-
- it( 'should clone filename info', function () {
- var s = new MagicString( 'abcdefghijkl', { filename: 'foo.js' });
- var c = s.clone();
-
- assert.equal( c.filename, 'foo.js' );
- });
-
- it( 'should clone indentExclusionRanges', function () {
- var array = [ 3, 6 ];
- var source = new MagicString( 'abcdefghijkl', {
- filename: 'foo.js',
- indentExclusionRanges: array
- });
-
- var clone = source.clone();
-
- assert.notStrictEqual( source.indentExclusionRanges, clone.indentExclusionRanges );
- assert.deepEqual( source.indentExclusionRanges, clone.indentExclusionRanges );
- });
-
- it( 'should clone sourcemapLocations', function () {
- var source = new MagicString( 'abcdefghijkl', {
- filename: 'foo.js'
- });
-
- source.addSourcemapLocation( 3 );
-
- var clone = source.clone();
-
- assert.notStrictEqual( source.sourcemapLocations, clone.sourcemapLocations );
- assert.deepEqual( source.sourcemapLocations, clone.sourcemapLocations );
- });
- });
-
- describe( 'generateMap', function () {
- it( 'should generate a sourcemap', function () {
- var s = new MagicString( 'abcdefghijkl' ).remove( 3, 9 );
-
- var map = s.generateMap({
- file: 'output.md',
- source: 'input.md',
- includeContent: true,
- hires: true
- });
-
- assert.equal( map.version, 3 );
- assert.equal( map.file, 'output.md' );
- assert.deepEqual( map.sources, [ 'input.md' ]);
- assert.deepEqual( map.sourcesContent, [ 'abcdefghijkl' ]);
- assert.equal( map.mappings, 'AAAA,CAAC,CAAC,CAAC,AAAM,CAAC,CAAC' );
-
- 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==' );
-
- var smc = new SourceMapConsumer( map );
- var loc;
-
- loc = smc.originalPositionFor({ line: 1, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
-
- loc = smc.originalPositionFor({ line: 1, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
-
- loc = smc.originalPositionFor({ line: 1, column: 4 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 10 );
- });
-
- it( 'should generate a correct sourcemap for indented content', function () {
- var s, map, smc, originLoc;
-
- s = new MagicString( 'var answer = 42;\nconsole.log("the answer is %s", answer);' );
-
- s.prepend( "'use strict';\n\n" );
- s.indent( '\t' ).prepend( '(function () {\n' ).append( '\n}).call(global);' );
-
- map = s.generateMap({
- source: 'input.md',
- includeContent: true,
- hires: true
- });
-
- smc = new SourceMapConsumer( map );
-
- originLoc = smc.originalPositionFor({ line: 5, column: 1 });
- assert.equal( originLoc.line, 2 );
- assert.equal( originLoc.column, 0 );
- });
-
- it( 'should generate a sourcemap using specified locations', function () {
- var s, map, smc, loc;
-
- s = new MagicString( 'abcdefghijkl' );
-
- s.addSourcemapLocation( 0 );
- s.addSourcemapLocation( 3 );
- s.addSourcemapLocation( 10 );
-
- s.remove( 6, 9 );
- map = s.generateMap({
- file: 'output.md',
- source: 'input.md',
- includeContent: true
- });
-
- assert.equal( map.version, 3 );
- assert.equal( map.file, 'output.md' );
- 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,GAAG,AAAG,CAAC"}' );
- assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3V0cHV0Lm1kIiwic291cmNlcyI6WyJpbnB1dC5tZCJdLCJzb3VyY2VzQ29udGVudCI6WyJhYmNkZWZnaGlqa2wiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsR0FBRyxHQUFHLEFBQUcsQ0FBQyJ9' );
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
-
- loc = smc.originalPositionFor({ line: 1, column: 3 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 3 );
-
- loc = smc.originalPositionFor({ line: 1, column: 7 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 10 );
- });
-
- it( 'should correctly map inserted content', function () {
- var s = new MagicString( 'function Foo () {}' );
-
- s.overwrite( 9, 12, 'Bar' );
-
- map = s.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 9 );
- });
-
- it( 'should yield consistent results between insertLeft and insertRight', function () {
- var s1 = new MagicString( 'abcdefghijkl' );
- s1.insertLeft( 6, 'X' );
-
- var s2 = new MagicString( 'abcdefghijkl' );
- s2.insertRight( 6, 'X' );
-
- var m1 = s1.generateMap({ file: 'output', source: 'input', includeContent: true });
- var m2 = s2.generateMap({ file: 'output', source: 'input', includeContent: true });
-
- assert.deepEqual( m1, m2 );
- });
-
- it( 'should recover original names', function () {
- var s = new MagicString( 'function Foo () {}' );
-
- s.overwrite( 9, 12, 'Bar', true );
-
- map = s.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.name, 'Foo' );
- });
-
- it( 'should generate one segment per replacement', function () {
- var s = new MagicString( 'var answer = 42' );
- s.overwrite( 4, 10, 'number', true );
-
- map = s.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- var numMappings = 0;
- smc.eachMapping( function ( mapping ) {
- numMappings += 1;
- });
-
- assert.equal( numMappings, 3 ); // one at 0, one at the edit, one afterwards
- });
-
- it( 'should generate a sourcemap that correctly locates moved content', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 3, 6, 9 );
-
- var result = s.toString();
- var map = s.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true,
- hires: true
- });
-
- var smc = new SourceMapConsumer( map );
-
- 'abcdefghijkl'.split( '' ).forEach( function ( letter, i ) {
- var column = result.indexOf( letter );
- var loc = smc.originalPositionFor({ line: 1, column: column });
-
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, i );
- });
- });
-
- it( 'generates a map with trimmed content (#53)', function () {
- var s = new MagicString( 'abcdefghijkl ' ).trim();
- var map = s.generateMap({
- file: 'output',
- source: 'input',
- includeContent: true,
- hires: true
- });
-
- var smc = new SourceMapConsumer( map );
- var loc = smc.originalPositionFor({ line: 1, column: 11 });
-
- assert.equal( loc.column, 11 );
-
- s = new MagicString( ' abcdefghijkl' ).trim();
- map = s.generateMap({
- file: 'output',
- source: 'input',
- includeContent: true,
- hires: true
- });
-
- smc = new SourceMapConsumer( map );
- loc = smc.originalPositionFor({ line: 1, column: 1 });
-
- assert.equal( loc.column, 2 );
- });
-
- it( 'skips empty segments at the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.remove( 0, 3 ).remove( 3, 6 );
-
- var map = s.generateMap();
- var smc = new SourceMapConsumer( map );
- var loc = smc.originalPositionFor({ line: 1, column: 6 });
-
- assert.equal( loc.column, 6 );
- });
-
- it( 'skips indentation at the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.indent( ' ' );
-
- var map = s.generateMap();
- assert.equal( map.mappings, 'IAAA' );
- });
- });
-
- describe( 'getIndentString', function () {
- it( 'should guess the indent string', function () {
- var s = new MagicString( 'abc\n def\nghi' );
- assert.equal( s.getIndentString(), ' ' );
- });
-
- it( 'should return a tab if no lines are indented', function () {
- var s = new MagicString( 'abc\ndef\nghi' );
- assert.equal( s.getIndentString(), '\t' );
- });
- });
-
- describe( 'indent', function () {
- it( 'should indent content with a single tab character by default', function () {
- var s = new MagicString( 'abc\ndef\nghi\njkl' );
-
- s.indent();
- assert.equal( s.toString(), '\tabc\n\tdef\n\tghi\n\tjkl' );
-
- s.indent();
- assert.equal( s.toString(), '\t\tabc\n\t\tdef\n\t\tghi\n\t\tjkl' );
- });
-
- it( 'should indent content, using existing indentation as a guide', function () {
- var s = new MagicString( 'abc\n def\n ghi\n jkl' );
-
- s.indent();
- assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
-
- s.indent();
- assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
- });
-
- it( 'should disregard single-space indentation when auto-indenting', function () {
- var s = new MagicString( 'abc\n/**\n *comment\n */' );
-
- s.indent();
- assert.equal( s.toString(), '\tabc\n\t/**\n\t *comment\n\t */' );
- });
-
- it( 'should indent content using the supplied indent string', function () {
- var s = new MagicString( 'abc\ndef\nghi\njkl' );
-
- s.indent( ' ');
- assert.equal( s.toString(), ' abc\n def\n ghi\n jkl' );
-
- s.indent( '>>' );
- assert.equal( s.toString(), '>> abc\n>> def\n>> ghi\n>> jkl' );
- });
-
- it( 'should indent content using the empty string if specified (i.e. noop)', function () {
- var s = new MagicString( 'abc\ndef\nghi\njkl' );
-
- s.indent( '');
- assert.equal( s.toString(), 'abc\ndef\nghi\njkl' );
- });
-
- it( 'should prevent excluded characters from being indented', function () {
- var s = new MagicString( 'abc\ndef\nghi\njkl' );
-
- s.indent( ' ', { exclude: [ 7, 15 ] });
- assert.equal( s.toString(), ' abc\n def\nghi\njkl' );
-
- s.indent( '>>', { exclude: [ 7, 15 ] });
- assert.equal( s.toString(), '>> abc\n>> def\nghi\njkl' );
- });
-
- it( 'should not add characters to empty lines', function () {
- var s = new MagicString( '\n\nabc\ndef\n\nghi\njkl' );
-
- s.indent();
- assert.equal( s.toString(), '\n\n\tabc\n\tdef\n\n\tghi\n\tjkl' );
-
- s.indent();
- assert.equal( s.toString(), '\n\n\t\tabc\n\t\tdef\n\n\t\tghi\n\t\tjkl' );
- });
-
- it( 'should not add characters to empty lines, even on Windows', function () {
- var s = new MagicString( '\r\n\r\nabc\r\ndef\r\n\r\nghi\r\njkl' );
-
- s.indent();
- assert.equal( s.toString(), '\r\n\r\n\tabc\r\n\tdef\r\n\r\n\tghi\r\n\tjkl' );
-
- s.indent();
- 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 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 );
- });
-
- it( 'should return this on noop', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.indent( '' ), s );
- });
- });
-
- describe( 'insert', function () {
- it( 'is deprecated', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.throws( function () { s.insert( 6, 'X' ); }, /deprecated/ );
- });
-
- // TODO move this into insertRight and insertLeft tests
-
- // it( 'should insert characters in the correct location', function () {
- // var s = new MagicString( 'abcdefghijkl' );
- //
- // s.insert( 0, '>>>' );
- // s.insert( 6, '***' );
- // s.insert( 12, '<<<' );
- //
- // assert.equal( s.toString(), '>>>abcdef***ghijkl<<<' );
- // });
- //
- // it( 'should return this', function () {
- // var s = new MagicString( 'abcdefghijkl' );
- // assert.strictEqual( s.insert( 0, 'a' ), s );
- // });
- //
- // it( 'should insert repeatedly at the same position correctly', function () {
- // var s = new MagicString( 'ab' );
- // assert.equal( s.insert(1, '1').toString(), 'a1b' );
- // assert.equal( s.insert(1, '2').toString(), 'a12b' );
- // });
- //
- // it( 'should insert repeatedly at the beginning correctly', function () {
- // var s = new MagicString( 'ab' );
- // assert.equal( s.insert(0, '1').toString(), '1ab' );
- // assert.equal( s.insert(0, '2').toString(), '12ab' );
- // });
- //
- // it( 'should throw when given non-string content', function () {
- // var s = new MagicString( '' );
- // assert.throws(
- // function () { s.insert( 0, [] ); },
- // TypeError
- // );
- // });
- //
- // it( 'should allow inserting after removed range', function () {
- // var s = new MagicString( 'abcd' );
- // s.remove( 1, 2 );
- // s.insert( 2, 'z' );
- // assert.equal( s.toString(), 'azcd' );
- // });
- });
-
- describe( 'insertLeft', function () {
- it( 'inserts repeatedly in correct order', function () {
- var s = new MagicString( 'ab' );
- assert.equal( s.insertLeft(1, '1').toString(), 'a1b' );
- assert.equal( s.insertLeft(1, '2').toString(), 'a12b' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.insertLeft( 0, 'a' ), s );
- });
- });
-
- describe( 'insertRight', function () {
- it( 'inserts repeatedly in correct order', function () {
- var s = new MagicString( 'ab' );
- assert.equal( s.insertRight(1, '1').toString(), 'a1b' );
- assert.equal( s.insertRight(1, '2').toString(), 'a21b' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.insertLeft( 0, 'a' ), s );
- });
- });
-
- describe( 'move', function () {
- it( 'moves content from the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 0, 3, 6 );
-
- assert.equal( s.toString(), 'defabcghijkl' );
- });
-
- it( 'moves content to the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 3, 6, 0 );
-
- assert.equal( s.toString(), 'defabcghijkl' );
- });
-
- it( 'moves content from the end', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 9, 12, 6 );
-
- assert.equal( s.toString(), 'abcdefjklghi' );
- });
-
- it( 'moves content to the end', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 6, 9, 12 );
-
- assert.equal( s.toString(), 'abcdefjklghi' );
- });
-
- it( 'ignores redundant move', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.insertRight( 9, 'X' );
- s.move( 9, 12, 6 );
- s.insertLeft( 12, 'Y' );
- s.move( 6, 9, 12 ); // this is redundant – [6,9] is already after [9,12]
-
- assert.equal( s.toString(), 'abcdefXjklYghi' );
- });
-
- it( 'moves content to the middle', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 3, 6, 9 );
-
- assert.equal( s.toString(), 'abcghidefjkl' );
- });
-
- it( 'handles multiple moves of the same snippet', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.move( 0, 3, 6 );
- assert.equal( s.toString(), 'defabcghijkl' );
-
- s.move( 0, 3, 9 );
- assert.equal( s.toString(), 'defghiabcjkl' );
- });
-
- it( 'handles moves of adjacent snippets', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.move( 0, 2, 6 );
- assert.equal( s.toString(), 'cdefabghijkl' );
-
- s.move( 2, 4, 6 );
- assert.equal( s.toString(), 'efabcdghijkl' );
- });
-
- it( 'handles moves to same index', function () {
- var s = new MagicString( 'abcdefghijkl' );
- s.move( 0, 2, 6 ).move( 3, 5, 6 );
-
- assert.equal( s.toString(), 'cfabdeghijkl' );
- });
-
- it( 'refuses to move a selection to inside itself', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- assert.throws( function () {
- s.move( 3, 6, 3 );
- }, /Cannot move a selection inside itself/ );
-
- assert.throws( function () {
- s.move( 3, 6, 4 );
- }, /Cannot move a selection inside itself/ );
-
- assert.throws( function () {
- s.move( 3, 6, 6 );
- }, /Cannot move a selection inside itself/ );
- });
-
- it( 'allows edits of moved content', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.move( 3, 6, 9 );
- s.overwrite( 3, 6, 'DEF' );
-
- assert.equal( s.toString(), 'abcghiDEFjkl' );
-
- s = new MagicString( 'abcdefghijkl' );
-
- s.move( 3, 6, 9 );
- s.overwrite( 4, 5, 'E' );
-
- assert.equal( s.toString(), 'abcghidEfjkl' );
- });
-
- // it( 'move follows inserts', function () {
- // var s = new MagicString( 'abcdefghijkl' );
- //
- // s.insertLeft( 3, 'X' ).move( 6, 9, 3 );
- // assert.equal( s.toString(), 'abcXghidefjkl' );
- // });
- //
- // it( 'inserts follow move', function () {
- // var s = new MagicString( 'abcdefghijkl' );
- //
- // s.insert( 3, 'X' ).move( 6, 9, 3 ).insert( 3, 'Y' );
- // assert.equal( s.toString(), 'abcXghiYdefjkl' );
- // });
- //
- // it( 'discards inserts at end of move by default', function () {
- // var s = new MagicString( 'abcdefghijkl' );
- //
- // s.insert( 6, 'X' ).move( 3, 6, 9 );
- // assert.equal( s.toString(), 'abcXghidefjkl' );
- // });
-
- it( 'moves content inserted at end of range', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.insertLeft( 6, 'X' ).move( 3, 6, 9 );
- assert.equal( s.toString(), 'abcghidefXjkl' );
- });
-
- it( 'returns this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.move( 3, 6, 9 ), s );
- });
- });
-
- 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 throw an error if overlapping replacements are attempted', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 7, 11, 'xx' );
-
- assert.throws( function () {
- s.overwrite( 8, 12, 'yy' );
- }, /Cannot split a chunk that has already been edited/ );
-
- assert.equal( s.toString(), 'abcdefgxxl' );
-
- s.overwrite( 6, 12, 'yes' );
- assert.equal( s.toString(), 'abcdefyes' );
- });
-
- it( 'should allow contiguous but non-overlapping replacements', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 3, 6, 'DEF' );
- assert.equal( s.toString(), 'abcDEFghijkl' );
-
- s.overwrite( 6, 9, 'GHI' );
- assert.equal( s.toString(), 'abcDEFGHIjkl' );
-
- s.overwrite( 0, 3, 'ABC' );
- assert.equal( s.toString(), 'ABCDEFGHIjkl' );
-
- s.overwrite( 9, 12, 'JKL' );
- assert.equal( s.toString(), 'ABCDEFGHIJKL' );
- });
-
- it( 'does not replace zero-length inserts at overwrite start location', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 0, 6 );
- s.insertLeft( 6, 'DEF' );
- s.overwrite( 6, 9, 'GHI' );
- assert.equal( s.toString(), 'DEFGHIjkl' );
- });
-
- it( 'replaces zero-length inserts inside overwrite', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.insertLeft( 6, 'XXX' );
- s.overwrite( 3, 9, 'DEFGHI' );
- assert.equal( s.toString(), 'abcDEFGHIjkl' );
- });
-
- it( 'replaces non-zero-length inserts inside overwrite', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 3, 4, 'XXX' );
- s.overwrite( 3, 5, 'DE' );
- assert.equal( s.toString(), 'abcDEfghijkl' );
-
- s.overwrite( 7, 8, 'YYY' );
- s.overwrite( 6, 8, 'GH' );
- assert.equal( s.toString(), 'abcDEfGHijkl' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.overwrite( 3, 4, 'D' ), s );
- });
-
- it( 'should disallow overwriting zero-length ranges', function () {
- var s = new MagicString( 'x' );
- assert.throws( function () {
- s.overwrite( 0, 0, 'anything' );
- }, /Cannot overwrite a zero-length range – use insertLeft or insertRight instead/ );
- });
-
- it( 'should throw when given non-string content', function () {
- var s = new MagicString( '' );
- assert.throws(
- function () { s.overwrite( 0, 1, [] ); },
- TypeError
- );
- });
-
- it ( 'replaces interior inserts', function() {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.insertLeft( 1, '&' );
- s.insertRight( 1, '^' );
- s.insertLeft( 3, '!' );
- s.insertRight( 3, '?' );
- s.overwrite( 1, 3, '...' );
- assert.equal( s.toString(), 'a&...?defghijkl' );
- })
- });
-
- describe( 'prepend', function () {
- it( 'should prepend content', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.prepend( 'xyz' );
- assert.equal( s.toString(), 'xyzabcdefghijkl' );
-
- s.prepend( '123' );
- assert.equal( s.toString(), '123xyzabcdefghijkl' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.prepend( 'xyz' ), s );
- });
- });
-
- describe( 'remove', function () {
- it( 'should remove characters from the original string', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 1, 5 );
- assert.equal( s.toString(), 'afghijkl' );
-
- s.remove( 9, 12 );
- assert.equal( s.toString(), 'afghi' );
- });
-
- it( 'should remove from the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 0, 6 );
- assert.equal( s.toString(), 'ghijkl' );
- });
-
- it( 'should remove from the end', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 6, 12 );
- assert.equal( s.toString(), 'abcdef' );
- });
-
- it( 'should treat zero-length removals as a no-op', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 0, 0 ).remove( 6, 6 ).remove( 9, -3 );
- assert.equal( s.toString(), 'abcdefghijkl' );
- });
-
- it( 'should remove overlapping ranges', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 3, 7 ).remove( 5, 9 );
- assert.equal( s.toString(), 'abcjkl' );
-
- s = new MagicString( 'abcdefghijkl' );
-
- s.remove( 3, 7 ).remove( 4, 6 );
- assert.equal( s.toString(), 'abchijkl' );
- });
-
- it( 'should remove overlapping ranges, redux', function () {
- var s = new MagicString( 'abccde' );
-
- s.remove( 2, 3 ); // c
- s.remove( 1, 3 ); // bc
- assert.equal( s.toString(), 'acde' );
- });
-
- it( 'should remove modified ranges', function () {
- var s = new MagicString( 'abcdefghi' );
-
- s.overwrite( 3, 6, 'DEF' );
- s.remove( 2, 7 ); // cDEFg
- assert.equal( s.slice( 1, 8 ), 'bh' );
- assert.equal( s.toString(), 'abhi' );
- });
-
- it( 'should not remove content inserted after the end of removed range', function () {
- var s = new MagicString( 'ab.c;' );
-
- s.insertRight( 0, '(' );
- s.insertRight( 4, ')' );
- s.remove( 2, 4 );
- assert.equal( s.toString(), '(ab);' );
- });
-
- it( 'should remove interior inserts', function () {
- var s = new MagicString( 'abc;' );
-
- s.insertLeft( 1, '[' );
- s.insertRight( 1, '(' );
- s.insertLeft( 2, ')' );
- s.insertRight( 2, ']' );
- s.remove( 1, 2 );
- assert.equal( s.toString(), 'a[]c;' );
- });
-
- it( 'should provide a useful error when illegal removals are attempted', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 5, 7, 'XX' );
-
- assert.throws( function () {
- s.remove( 4, 6 );
- }, /Cannot split a chunk that has already been edited/ );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.strictEqual( s.remove( 3, 4 ), s );
- });
- });
-
- describe( 'slice', function () {
- it( 'should return the generated content between the specified original characters', function () {
- var s = new MagicString( 'abcdefghijkl' );
-
- assert.equal( s.slice( 3, 9 ), 'defghi' );
- s.overwrite( 4, 8, 'XX' );
- 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( 3, 9 );
- });
- });
-
- it( 'defaults `end` to the original string length', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.equal( s.slice( 3 ), 'defghijkl' );
- });
-
- it( 'allows negative numbers as arguments', function () {
- var s = new MagicString( 'abcdefghijkl' );
- assert.equal( s.slice( -3 ), 'jkl' );
- assert.equal( s.slice( 0, -3 ), 'abcdefghi' );
- });
-
- it( 'includes inserted characters, respecting insertion direction', function () {
- var s = new MagicString( 'abefij' );
-
- s.insertRight( 2, 'cd' );
- s.insertLeft( 4, 'gh' );
-
- assert.equal( s.slice(), 'abcdefghij' );
- assert.equal( s.slice( 1, 5 ), 'bcdefghi' );
- assert.equal( s.slice( 2, 4 ), 'cdefgh' );
- assert.equal( s.slice( 3, 4 ), 'fgh' );
- assert.equal( s.slice( 0, 2 ), 'ab' );
- assert.equal( s.slice( 0, 3 ), 'abcde' );
- assert.equal( s.slice( 4, 6 ), 'ij' );
- assert.equal( s.slice( 3, 6 ), 'fghij' );
- });
-
- it( 'supports characters moved outward', function () {
- var s = new MagicString( 'abcdEFghIJklmn' );
-
- s.move( 4, 6, 2 );
- s.move( 8, 10, 12 );
- assert.equal( s.toString(), 'abEFcdghklIJmn' );
-
- assert.equal( s.slice( 1, -1 ), 'bEFcdghklIJm' );
- assert.equal( s.slice( 2, -2 ), 'cdghkl' );
- assert.equal( s.slice( 3, -3 ), 'dghk' );
- assert.equal( s.slice( 4, -4 ), 'EFcdghklIJ' );
- assert.equal( s.slice( 5, -5 ), 'FcdghklI' );
- assert.equal( s.slice( 6, -6 ), 'gh' );
- });
-
- it( 'supports characters moved inward', function () {
- var s = new MagicString( 'abCDefghijKLmn' );
-
- s.move( 2, 4, 6 );
- s.move( 10, 12, 8 );
- assert.equal( s.toString(), 'abefCDghKLijmn' );
-
- assert.equal( s.slice( 1, -1 ), 'befCDghKLijm' );
- assert.equal( s.slice( 2, -2 ), 'CDghKL' );
- assert.equal( s.slice( 3, -3 ), 'DghK' );
- assert.equal( s.slice( 4, -4 ), 'efCDghKLij' );
- assert.equal( s.slice( 5, -5 ), 'fCDghKLi' );
- assert.equal( s.slice( 6, -6 ), 'gh' );
- });
-
- it( 'supports characters moved opposing', function () {
- var s = new MagicString( 'abCDefghIJkl' );
-
- s.move( 2, 4, 8 );
- s.move( 8, 10, 4 );
- assert.equal( s.toString(), 'abIJefghCDkl' );
-
- assert.equal( s.slice( 1, -1 ), 'bIJefghCDk' );
- assert.equal( s.slice( 2, -2 ), '' );
- assert.equal( s.slice( 3, -3 ), '' );
- assert.equal( s.slice( -3, 3 ), 'JefghC' );
- assert.equal( s.slice( 4, -4 ), 'efgh' );
- assert.equal( s.slice( 0, 3 ), 'abIJefghC' );
- assert.equal( s.slice( 3 ), 'Dkl' );
- assert.equal( s.slice( 0, -3 ), 'abI' );
- assert.equal( s.slice( -3 ), 'JefghCDkl' );
- });
-
- it( 'errors if replaced characters are used as slice anchors', function () {
- var s = new MagicString( 'abcdef' );
- s.overwrite( 2, 4, 'CD' );
-
- assert.throws( function () {
- s.slice( 2, 3 );
- }, /slice end anchor/ );
-
- assert.throws( function () {
- s.slice( 3, 4 );
- }, /slice start anchor/ );
-
- assert.throws( function () {
- s.slice( 3, 5 );
- }, /slice start anchor/ );
-
- assert.equal( s.slice( 1, 5 ), 'bCDe' );
- });
-
- it( 'does not error if slice is after removed characters', function () {
- var s = new MagicString( 'abcdef' );
-
- s.remove( 0, 2 );
-
- assert.equal( s.slice( 2, 4 ), 'cd' );
- });
- });
-
- describe( 'snip', function () {
- it( 'should return a clone with content outside `start` and `end` removed', function () {
- var s = new MagicString( 'abcdefghijkl', {
- filename: 'foo.js'
- });
-
- s.overwrite( 6, 9, 'GHI' );
-
- var snippet = s.snip( 3, 9 );
- assert.equal( snippet.toString(), 'defGHI' );
- assert.equal( snippet.filename, 'foo.js' );
- });
-
- it( 'should snip from the start', function () {
- var s = new MagicString( 'abcdefghijkl' );
- var snippet = s.snip( 0, 6 );
-
- assert.equal( snippet.toString(), 'abcdef' );
- });
-
- it( 'should snip from the end', function () {
- var s = new MagicString( 'abcdefghijkl' );
- var snippet = s.snip( 6, 12 );
-
- assert.equal( snippet.toString(), 'ghijkl' );
- });
-
- it( 'should respect original indices', function () {
- var s = new MagicString( 'abcdefghijkl' );
- var snippet = s.snip( 3, 9 );
-
- snippet.overwrite( 6, 9, 'GHI' );
- assert.equal( snippet.toString(), 'defGHI' );
- });
- });
-
- describe( 'trim', function () {
- it( 'should trim original content', function () {
- 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 () {
- var s = new MagicString( 'abcdefghijkl' );
-
- s.overwrite( 0, 3, ' ' ).overwrite( 9, 12, ' ' ).trim();
- assert.equal( s.toString(), 'defghi' );
- });
-
- it( 'should trim original content before replaced content', function () {
- var s = new MagicString( 'abc def' );
-
- s.remove( 6, 9 );
- assert.equal( s.toString(), 'abc ' );
-
- s.trim();
- assert.equal( s.toString(), 'abc' );
- });
-
- it( 'should trim original content after replaced content', function () {
- var s = new MagicString( 'abc def' );
-
- s.remove( 0, 3 );
- assert.equal( s.toString(), ' def' );
-
- s.trim();
- assert.equal( s.toString(), 'def' );
- });
-
- it( 'should trim original content before and after replaced content', function () {
- var s = new MagicString( 'abc def ghi' );
-
- s.remove( 0, 3 );
- s.remove( 12, 15 );
- assert.equal( s.toString(), ' def ' );
-
- s.trim();
- assert.equal( s.toString(), 'def' );
- });
-
- it( 'should trim appended/prepended content', function () {
- var s = new MagicString( ' abcdefghijkl ' );
-
- s.prepend( ' ' ).append( ' ' ).trim();
- assert.equal( s.toString(), 'abcdefghijkl' );
- });
-
- it( 'should trim empty string', function () {
- var s = new MagicString( ' ' );
-
- assert.equal( s.trim().toString(), '' );
- });
-
- it( 'should return this', function () {
- var s = new MagicString( ' abcdefghijkl ' );
- assert.strictEqual( s.trim(), s );
- });
- });
-
- describe( 'trimLines', function () {
- it( 'should trim original content', function () {
- var s = new MagicString( '\n\n abcdefghijkl \n\n' );
-
- s.trimLines();
- assert.equal( s.toString(), ' abcdefghijkl ' );
- });
- });
-});
-
-describe( 'MagicString.Bundle', function () {
- describe( 'addSource', function () {
- it( 'should return this', function () {
- var b = new MagicString.Bundle();
- var source = new MagicString( 'abcdefghijkl' );
-
- assert.strictEqual( b.addSource({ content: source }), b );
- });
-
- it( 'should accept MagicString instance as a single argument', function () {
- var b = new MagicString.Bundle();
- var array = [];
- var source = new MagicString( 'abcdefghijkl', {
- filename: 'foo.js',
- indentExclusionRanges: array
- });
-
- b.addSource( source );
- assert.strictEqual( b.sources[0].content, source );
- assert.strictEqual( b.sources[0].filename, 'foo.js' );
- assert.strictEqual( b.sources[0].indentExclusionRanges, array );
- });
-
- it( 'respects MagicString init options with { content: source }', function () {
- var b = new MagicString.Bundle();
- var array = [];
- var source = new MagicString( 'abcdefghijkl', {
- filename: 'foo.js',
- indentExclusionRanges: array
- });
-
- b.addSource({ content: source });
- assert.strictEqual( b.sources[0].content, source );
- assert.strictEqual( b.sources[0].filename, 'foo.js' );
- assert.strictEqual( b.sources[0].indentExclusionRanges, array );
- });
- });
-
- describe( 'append', function () {
- it( 'should append content', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({ content: new MagicString( '*' ) });
-
- b.append( '123' ).append( '456' );
- assert.equal( b.toString(), '*123456' );
- });
-
- it( 'should append content before subsequent sources', function () {
- var b = new MagicString.Bundle();
-
- b.addSource( new MagicString( '*' ) );
-
- b.append( '123' ).addSource( new MagicString( '-' ) ).append( '456' );
- assert.equal( b.toString(), '*123\n-456' );
- });
-
- it( 'should return this', function () {
- var b = new MagicString.Bundle();
- assert.strictEqual( b.append( 'x' ), b );
- });
- });
-
- describe( 'clone', function () {
- it( 'should clone a bundle', function () {
- var b = new MagicString.Bundle(),
- s1 = new MagicString( 'abcdef' ),
- s2 = new MagicString( 'ghijkl' ),
- clone;
-
- b
- .addSource({
- content: s1
- })
- .addSource({
- content: s2
- })
- .prepend( '>>>' )
- .append( '<<<' );
-
- clone = b.clone();
-
- assert.equal( clone.toString(), '>>>abcdef\nghijkl<<<' );
-
- s1.overwrite( 2, 4, 'XX' );
- assert.equal( b.toString(), '>>>abXXef\nghijkl<<<' );
- assert.equal( clone.toString(), '>>>abcdef\nghijkl<<<' );
- });
- });
-
- describe( 'generateMap', function () {
- it( 'should generate a sourcemap', function () {
- var b, map, smc, loc;
-
- b = new MagicString.Bundle();
-
- b.addSource({
- filename: 'foo.js',
- content: new MagicString( 'var answer = 42;' )
- });
-
- b.addSource({
- filename: 'bar.js',
- content: new MagicString( 'console.log( answer );' )
- });
-
- map = b.generateMap({
- file: 'bundle.js',
- includeContent: true,
- hires: true
- });
-
- assert.equal( map.version, 3 );
- assert.equal( map.file, 'bundle.js' );
- assert.deepEqual( map.sources, [ 'foo.js', 'bar.js' ]);
- assert.deepEqual( map.sourcesContent, [ 'var answer = 42;', 'console.log( answer );' ]);
-
- assert.equal( map.toString(), '{"version":3,"file":"bundle.js","sources":["foo.js","bar.js"],"sourcesContent":["var answer = 42;","console.log( answer );"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;ACAf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}' );
- assert.equal( map.toUrl(), 'data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlcyI6WyJmb28uanMiLCJiYXIuanMiXSwic291cmNlc0NvbnRlbnQiOlsidmFyIGFuc3dlciA9IDQyOyIsImNvbnNvbGUubG9nKCBhbnN3ZXIgKTsiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7QUNBZixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE [...]
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 1, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'bar.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'bar.js' );
- });
-
- it( 'should handle Windows-style paths', function () {
- var b, map, smc, loc;
-
- b = new MagicString.Bundle();
-
- b.addSource({
- filename: 'path\\to\\foo.js',
- content: new MagicString( 'var answer = 42;' )
- });
-
- b.addSource({
- filename: 'path\\to\\bar.js',
- content: new MagicString( 'console.log( answer );' )
- });
-
- map = b.generateMap({
- file: 'bundle.js',
- includeContent: true,
- hires: true
- });
-
- assert.equal( map.version, 3 );
- assert.equal( map.file, 'bundle.js' );
- assert.deepEqual( map.sources, [ 'path/to/foo.js', 'path/to/bar.js' ]);
- assert.deepEqual( map.sourcesContent, [ 'var answer = 42;', 'console.log( answer );' ]);
-
- assert.equal( map.toString(), '{"version":3,"file":"bundle.js","sources":["path/to/foo.js","path/to/bar.js"],"sourcesContent":["var answer = 42;","console.log( answer );"],"names":[],"mappings":"AAAA,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;ACAf,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC"}' );
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'path/to/foo.js' );
-
- loc = smc.originalPositionFor({ line: 1, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'path/to/foo.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'path/to/bar.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'path/to/bar.js' );
- });
-
- it( 'should handle edge case with intro content', function () {
- var b, map, smc, loc;
-
- b = new MagicString.Bundle();
-
- b.addSource({
- filename: 'foo.js',
- content: new MagicString( 'var answer = 42;' )
- });
-
- b.addSource({
- filename: 'bar.js',
- content: new MagicString( '\nconsole.log( answer );' )
- });
-
- b.indent().prepend( '(function () {\n' ).append( '\n}());' );
-
- map = b.generateMap({
- file: 'bundle.js',
- includeContent: true,
- hires: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 2, column: 1 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 2 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 4, column: 1 });
- assert.equal( loc.line, 2 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'bar.js' );
-
- loc = smc.originalPositionFor({ line: 4, column: 2 });
- assert.equal( loc.line, 2 );
- assert.equal( loc.column, 1 );
- assert.equal( loc.source, 'bar.js' );
- });
-
- it( 'should allow missing file option when generating map', function () {
- var b, map;
-
- b = new MagicString.Bundle();
-
- b.addSource({
- filename: 'foo.js',
- content: new MagicString( 'var answer = 42;' )
- });
-
- map = b.generateMap({
- includeContent: true,
- hires: true
- });
- });
-
- it( 'should handle repeated sources', function () {
- var b = new MagicString.Bundle();
-
- var foo = new MagicString( 'var one = 1;\nvar three = 3;', {
- filename: 'foo.js'
- });
-
- var bar = new MagicString( 'var two = 2;\nvar four = 4;', {
- filename: 'bar.js'
- });
-
- b.addSource( foo.snip( 0, 12 ) );
- b.addSource( bar.snip( 0, 12 ) );
- b.addSource( foo.snip( 13, 27 ) );
- b.addSource( bar.snip( 13, 26 ) );
-
- var code = b.toString();
- assert.equal( code, 'var one = 1;\nvar two = 2;\nvar three = 3;\nvar four = 4;' );
-
- var map = b.generateMap({
- includeContent: true,
- hires: true
- });
-
- assert.equal( map.sources.length, 2 );
- assert.equal( map.sourcesContent.length, 2 );
-
- var smc = new SourceMapConsumer( map );
- var loc;
-
- loc = smc.originalPositionFor({ line: 1, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 0 });
- assert.equal( loc.line, 1 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'bar.js' );
-
- loc = smc.originalPositionFor({ line: 3, column: 0 });
- assert.equal( loc.line, 2 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'foo.js' );
-
- loc = smc.originalPositionFor({ line: 4, column: 0 });
- assert.equal( loc.line, 2 );
- assert.equal( loc.column, 0 );
- assert.equal( loc.source, 'bar.js' );
- });
-
- it( 'should recover original names', function () {
- var b = new MagicString.Bundle();
-
- var one = new MagicString( 'function one () {}', { filename: 'one.js' });
- var two = new MagicString( 'function two () {}', { filename: 'two.js' });
-
- one.overwrite( 9, 12, 'three', true );
- two.overwrite( 9, 12, 'four', true );
-
- b.addSource( one );
- b.addSource( two );
-
- map = b.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.name, 'one' );
-
- loc = smc.originalPositionFor({ line: 2, column: 9 });
- assert.equal( loc.name, 'two' );
- });
-
- it( 'should exclude sources without filename from sourcemap', function () {
- var b = new MagicString.Bundle();
-
- var one = new MagicString( 'function one () {}', { filename: 'one.js' });
- var two = new MagicString( 'function two () {}', { filename: null });
- var three = new MagicString( 'function three () {}', { filename: 'three.js' });
-
- b.addSource( one );
- b.addSource( two );
- b.addSource( three );
-
- map = b.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.source, 'one.js' );
-
- loc = smc.originalPositionFor({ line: 2, column: 9 });
- assert.equal( loc.source, null );
-
- loc = smc.originalPositionFor({ line: 3, column: 9 });
- assert.equal( loc.source, 'three.js' );
- });
-
- it( 'handles prepended content', function () {
- var b = new MagicString.Bundle();
-
- var one = new MagicString( 'function one () {}', { filename: 'one.js' });
- var two = new MagicString( 'function two () {}', { filename: 'two.js' });
- two.prepend( 'function oneAndAHalf() {}\n' );
-
- b.addSource( one );
- b.addSource( two );
-
- map = b.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.source, 'one.js' );
-
- loc = smc.originalPositionFor({ line: 3, column: 9 });
- assert.equal( loc.source, 'two.js' );
- });
-
- it( 'handles appended content', function () {
- var b = new MagicString.Bundle();
-
- var one = new MagicString( 'function one () {}', { filename: 'one.js' });
- one.append( '\nfunction oneAndAHalf() {}' );
- var two = new MagicString( 'function two () {}', { filename: 'two.js' });
-
- b.addSource( one );
- b.addSource( two );
-
- map = b.generateMap({
- file: 'output.js',
- source: 'input.js',
- includeContent: true
- });
-
- smc = new SourceMapConsumer( map );
-
- loc = smc.originalPositionFor({ line: 1, column: 9 });
- assert.equal( loc.source, 'one.js' );
-
- loc = smc.originalPositionFor({ line: 3, column: 9 });
- assert.equal( loc.source, 'two.js' );
- });
- });
-
- describe( 'indent', function () {
- it( 'should indent a bundle', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({ content: new MagicString( 'abcdef' ) });
- b.addSource({ content: new MagicString( 'ghijkl' ) });
-
- b.indent().prepend( '>>>\n' ).append( '\n<<<' );
- assert.equal( b.toString(), '>>>\n\tabcdef\n\tghijkl\n<<<' );
- });
-
- it( 'should ignore non-indented sources when guessing indentation', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({ content: new MagicString( 'abcdef' ) });
- b.addSource({ content: new MagicString( 'ghijkl' ) });
- b.addSource({ content: new MagicString( ' mnopqr' ) });
-
- b.indent();
- assert.equal( b.toString(), ' abcdef\n ghijkl\n mnopqr' );
- });
-
- it( 'should respect indent exclusion ranges', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({
- content: new MagicString( 'abc\ndef\nghi\njkl' ),
- indentExclusionRanges: [ 7, 15 ]
- });
-
- b.indent( ' ' );
- assert.equal( b.toString(), ' abc\n def\nghi\njkl' );
-
- b.indent( '>>' );
- assert.equal( b.toString(), '>> abc\n>> def\nghi\njkl' );
- });
-
- it( 'does not indent sources with no preceding newline, i.e. append()', function () {
- var b = new MagicString.Bundle();
-
- b.addSource( new MagicString( 'abcdef' ) );
- b.addSource( new MagicString( 'ghijkl' ) );
-
- b.prepend( '>>>' ).append( '<<<' ).indent();
- assert.equal( b.toString(), '\t>>>abcdef\n\tghijkl<<<' );
- });
-
- it( 'should noop with an empty string', function () {
- var b = new MagicString.Bundle();
-
- b.addSource( new MagicString( 'abcdef' ) );
- b.addSource( new MagicString( 'ghijkl' ) );
-
- b.indent( '' );
- assert.equal( b.toString(), 'abcdef\nghijkl' );
- });
-
- it( 'indents prepended content', function () {
- var b = new MagicString.Bundle();
- b.prepend( 'a\nb' ).indent();
-
- assert.equal( b.toString(), '\ta\n\tb' );
- });
-
- it( 'indents content immediately following intro with trailing newline', function () {
- var b = new MagicString.Bundle({ separator: '\n\n' });
-
- var s = new MagicString( '2' );
- b.addSource({ content: s });
-
- b.prepend( '1\n' );
-
- assert.equal( b.indent().toString(), '\t1\n\t2' );
- });
-
- it( 'should return this', function () {
- var b = new MagicString.Bundle();
- assert.strictEqual( b.indent(), b );
- });
-
- it( 'should return this on noop', function () {
- var b = new MagicString.Bundle();
- assert.strictEqual( b.indent( '' ), b );
- });
- });
-
- describe( 'prepend', function () {
- it( 'should append content', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({ content: new MagicString( '*' ) });
-
- b.prepend( '123' ).prepend( '456' );
- assert.equal( b.toString(), '456123*' );
- });
-
- it( 'should return this', function () {
- var b = new MagicString.Bundle();
- assert.strictEqual( b.prepend( 'x' ), b );
- });
- });
-
- describe( 'trim', function () {
- it( 'should trim bundle', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({
- content: new MagicString( ' abcdef ' )
- });
-
- b.addSource({
- content: new MagicString( ' ghijkl ' )
- });
-
- b.trim();
- assert.equal( b.toString(), 'abcdef \n ghijkl' );
- });
-
- it( 'should handle funky edge cases', function () {
- var b = new MagicString.Bundle();
-
- b.addSource({
- content: new MagicString( ' abcdef ' )
- });
-
- b.addSource({
- content: new MagicString( ' x ' )
- });
-
- b.prepend( '\n>>>\n' ).append( ' ' );
-
- b.trim();
- assert.equal( b.toString(), '>>>\n abcdef \n x' );
- });
-
- it( 'should return this', function () {
- var b = new MagicString.Bundle();
- assert.strictEqual( b.trim(), b );
- });
- });
-
- describe( 'toString', function () {
- it( 'should separate with a newline by default', function () {
- var b = new MagicString.Bundle();
-
- b.addSource( new MagicString( 'abc' ) );
- b.addSource( new MagicString( 'def' ) );
-
- assert.strictEqual( b.toString(), 'abc\ndef' );
- });
-
- it( 'should accept separator option', function () {
- var b = new MagicString.Bundle({ separator: '==' });
-
- b.addSource( new MagicString( 'abc' ) );
- b.addSource( new MagicString( 'def' ) );
-
- assert.strictEqual( b.toString(), 'abc==def' );
- });
-
- it( 'should accept empty string separator option', function () {
- var b = new MagicString.Bundle({ separator: '' });
-
- b.addSource( new MagicString( 'abc' ) );
- b.addSource( new MagicString( 'def' ) );
-
- assert.strictEqual( b.toString(), 'abcdef' );
- });
- });
-});
diff --git a/test/mocha.opts b/test/mocha.opts
new file mode 100644
index 0000000..c3af56e
--- /dev/null
+++ b/test/mocha.opts
@@ -0,0 +1 @@
+--compilers js:buble/register
\ No newline at end of file
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-magic-string.git
More information about the Pkg-javascript-commits
mailing list