[Pkg-javascript-commits] [node-magic-string] 01/06: New upstream version 0.21.3
Julien Puydt
julien.puydt at laposte.net
Thu Jul 6 15:49:53 UTC 2017
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 50019fc26bed240bf8299b2d28e4eb12b9f8e788
Author: Julien Puydt <julien.puydt at laposte.net>
Date: Thu Jul 6 17:25:35 2017 +0200
New upstream version 0.21.3
---
.travis.yml | 3 +-
CHANGELOG.md | 26 +++++++++++
README.md | 10 ++--
index.d.ts | 78 +++++++++++++++++++++++++++++++
package.json | 4 +-
src/Chunk.js | 8 ++--
src/MagicString.js | 56 +++++++++++++++++------
src/index-legacy.js | 1 +
test/MagicString.Bundle.js | 4 +-
test/MagicString.js | 112 ++++++++++++++++++++++++++++++---------------
10 files changed, 241 insertions(+), 61 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index f5bf60c..8bb7c70 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,8 @@
sudo: false
language: node_js
node_js:
- - "0.12"
- "4"
- - "5"
+ - "node"
env:
global:
- BUILD_TIMEOUT=10000
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1f13db..eda770a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,31 @@
# magic-string changelog
+## 0.21.3
+
+* Clone `indentExclusionRanges` correctly ([#122](https://github.com/Rich-Harris/magic-string/pull/122))
+* Fix more typings ([#122](https://github.com/Rich-Harris/magic-string/pull/122))
+
+## 0.21.2
+
+* Add `default` property referencing self in index-legacy.js, to work around TypeScript bug ([#121](https://github.com/Rich-Harris/magic-string/pull/121))
+
+## 0.21.1
+
+* Add typings file to package
+
+## 0.21.0
+
+* Add TypeScript bindings ([#119](https://github.com/Rich-Harris/magic-string/pull/119))
+
+## 0.20.0
+
+* The fourth argument to `overwrite` is a `{storeName, contentOnly}` options object. `storeName: true` is equivalent to `true` before. `contentOnly` will preserve existing appends/prepends to the chunk in question
+
+## 0.19.1
+
+* Prevent overwrites across a split point (i.e. following a `move`)
+* Implement `remove` separately to `overwrite`
+
## 0.19.0
* More accurate bundle sourcemaps ([#114](https://github.com/Rich-Harris/magic-string/pull/114))
diff --git a/README.md b/README.md
index 1314c72..73c07fc 100644
--- a/README.md
+++ b/README.md
@@ -34,10 +34,10 @@ magic-string works in both node.js and browser environments. For node, install w
npm i magic-string
```
-To use in browser, grab the [magic-string.deps.js](https://raw.githubusercontent.com/Rich-Harris/magic-string/master/dist/magic-string.deps.js) file and add it to your page:
+To use in browser, grab the [magic-string.umd.js](https://unpkg.com/magic-string/dist/magic-string.umd.js) file and add it to your page:
```html
-<script src='magic-string.deps.js'></script>
+<script src='magic-string.umd.js'></script>
```
(It also works with various module systems, if you prefer that sort of thing - it has a dependency on [vlq](https://github.com/Rich-Harris/vlq).)
@@ -146,9 +146,11 @@ The `options` argument can have an `exclude` property, which is an array of `[st
Moves the characters from `start` and `end` to `index`. Returns `this`.
-### s.overwrite( start, end, content[, storeName] )
+### s.overwrite( start, end, content[, options] )
-Replaces the characters from `start` to `end` with `content`. The same restrictions as `s.remove()` apply. Returns `this`. If `storeName` is `true`, the original name will be stored for later inclusion in a sourcemap's `names` array.
+Replaces the characters from `start` to `end` with `content`. The same restrictions as `s.remove()` apply. Returns `this`.
+
+The fourth argument is optional. It can have a `storeName` property — if `true`, the original name will be stored for later inclusion in a sourcemap's `names` array — and a `contentOnly` property which determines whether only the content is overwritten, or anything that was appended/prepended to the range as well.
### s.prepend( content )
diff --git a/index.d.ts b/index.d.ts
new file mode 100644
index 0000000..ebc9c1d
--- /dev/null
+++ b/index.d.ts
@@ -0,0 +1,78 @@
+declare module "magic-string" {
+ export interface BundleOptions {
+ intro?: string;
+ separator?: string;
+ }
+
+ export interface SourceMapOptions {
+ hires: boolean;
+ file: string;
+ sources: string[];
+ sourcesContent: string;
+ includeContent: boolean;
+ names: string[];
+ mappings: string[];
+ }
+
+ class SourceMap {
+ constructor(properties: SourceMapOptions);
+ toString(): string;
+ toUrl(): string;
+ }
+
+ export class Bundle {
+ constructor(options?: BundleOptions);
+ addSource(source: MagicString | { filename?: string, content: MagicString }): Bundle;
+ append(str: string, options: BundleOptions): Bundle;
+ clone(): Bundle;
+ generateMap(options?: Partial<SourceMapOptions>): SourceMap;
+ getIndentString(): string;
+ indent(indentStr?: string): Bundle;
+ prepend(str: string): Bundle;
+ toString(): string;
+ trimLines(): string;
+ trim(charType: string): string;
+ trimStart(charType: string): Bundle;
+ trimEnd(charType: string): Bundle;
+ }
+
+ export type ExclusionRange = [ number, number ];
+
+ export interface MagicStringOptions {
+ filename: string,
+ indentExclusionRanges: ExclusionRange | Array<ExclusionRange>;
+ }
+
+ export interface IndentOptions {
+ exclude: ExclusionRange | Array<ExclusionRange>;
+ indentStart: boolean;
+ }
+
+ export interface OverwriteOptions {
+ storeName: boolean;
+ contentOnly: boolean;
+ }
+
+ export default class MagicString {
+ constructor(str: string, options?: MagicStringOptions);
+ addSourcemapLocation(char: number): void;
+ append(content: string): MagicString;
+ appendLeft(index: number, content: string): MagicString;
+ appendRight(index: number, content: string): MagicString;
+ clone(): MagicString;
+ generateMap(options?: Partial<SourceMapOptions>): SourceMap;
+ getIndentString(): string;
+
+ indent(options?: IndentOptions): MagicString;
+ indent(indentStr?: string, options?: IndentOptions): MagicString;
+
+ move(start: number, end: number, index: number): MagicString;
+ overwrite(start: number, end: number, content: string, options?: boolean | OverwriteOptions): MagicString;
+ prepend(content: string): MagicString;
+ prependLeft(index: number, content: string): MagicString;
+ prependRight(index: number, content: string): MagicString;
+ remove(start: number, end: number): MagicString;
+ slice(start: number, end: number): string;
+ snip(start: number, end: number): MagicString;
+ }
+}
diff --git a/package.json b/package.json
index 245ac73..98a57f7 100644
--- a/package.json
+++ b/package.json
@@ -2,11 +2,12 @@
"name": "magic-string",
"description": "Modify strings, generate sourcemaps",
"author": "Rich Harris",
- "version": "0.19.0",
+ "version": "0.21.3",
"repository": "https://github.com/rich-harris/magic-string",
"main": "dist/magic-string.cjs.js",
"module": "dist/magic-string.es.js",
"jsnext:main": "dist/magic-string.es.js",
+ "typings": "index.d.ts",
"license": "MIT",
"dependencies": {
"vlq": "^0.2.1"
@@ -55,6 +56,7 @@
"files": [
"src/*",
"dist/*",
+ "index.d.ts",
"README.md"
]
}
diff --git a/src/Chunk.js b/src/Chunk.js
index d83e036..a147b07 100644
--- a/src/Chunk.js
+++ b/src/Chunk.js
@@ -58,10 +58,12 @@ Chunk.prototype = {
}
},
- edit ( content, storeName ) {
+ edit ( content, storeName, contentOnly ) {
this.content = content;
- this.intro = '';
- this.outro = '';
+ if ( !contentOnly ) {
+ this.intro = '';
+ this.outro = '';
+ }
this.storeName = storeName;
this.edited = true;
diff --git a/src/MagicString.js b/src/MagicString.js
index af851f2..d20c946 100644
--- a/src/MagicString.js
+++ b/src/MagicString.js
@@ -9,7 +9,8 @@ import Stats from './utils/Stats.js';
const warned = {
insertLeft: false,
- insertRight: false
+ insertRight: false,
+ storeName: false
};
export default function MagicString ( string, options = {} ) {
@@ -54,7 +55,7 @@ MagicString.prototype = {
appendLeft ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
- if ( DEBUG ) this.stats.time( 'insertLeft' );
+ if ( DEBUG ) this.stats.time( 'appendLeft' );
this._split( index );
@@ -66,14 +67,14 @@ MagicString.prototype = {
this.intro += content;
}
- if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
+ if ( DEBUG ) this.stats.timeEnd( 'appendLeft' );
return this;
},
appendRight ( index, content ) {
if ( typeof content !== 'string' ) throw new TypeError( 'inserted content must be a string' );
- if ( DEBUG ) this.stats.time( 'insertLeft' );
+ if ( DEBUG ) this.stats.time( 'appendRight' );
this._split( index );
@@ -85,7 +86,7 @@ MagicString.prototype = {
this.outro += content;
}
- if ( DEBUG ) this.stats.timeEnd( 'insertLeft' );
+ if ( DEBUG ) this.stats.timeEnd( 'appendRight' );
return this;
},
@@ -115,9 +116,7 @@ MagicString.prototype = {
cloned.lastChunk = clonedChunk;
if ( this.indentExclusionRanges ) {
- cloned.indentExclusionRanges = typeof this.indentExclusionRanges[0] === 'number' ?
- [ this.indentExclusionRanges[0], this.indentExclusionRanges[1] ] :
- this.indentExclusionRanges.map( range => [ range.start, range.end ] );
+ cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
}
Object.keys( this.sourcemapLocations ).forEach( loc => {
@@ -261,7 +260,7 @@ MagicString.prototype = {
},
insert () {
- throw new Error( 'magicString.insert(...) is deprecated. Use insertRight(...) or insertLeft(...)' );
+ throw new Error( 'magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)' );
},
insertLeft ( index, content ) {
@@ -323,20 +322,31 @@ MagicString.prototype = {
return this;
},
- overwrite ( start, end, content, storeName ) {
+ overwrite ( start, end, content, options ) {
if ( typeof content !== 'string' ) throw new TypeError( 'replacement content must be a string' );
while ( start < 0 ) start += this.original.length;
while ( end < 0 ) end += this.original.length;
if ( end > this.original.length ) throw new Error( 'end is out of bounds' );
- if ( start === end ) throw new Error( 'Cannot overwrite a zero-length range – use insertLeft or insertRight instead' );
+ if ( start === end ) throw new Error( 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead' );
if ( DEBUG ) this.stats.time( 'overwrite' );
this._split( start );
this._split( end );
+ if ( options === true ) {
+ if ( !warned.storeName ) {
+ console.warn( 'The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string' ); // eslint-disable-line no-console
+ warned.storeName = true;
+ }
+
+ options = { storeName: true };
+ }
+ const storeName = options !== undefined ? options.storeName : false;
+ const contentOnly = options !== undefined ? options.contentOnly : false;
+
if ( storeName ) {
const original = this.original.slice( start, end );
this.storedNames[ original ] = true;
@@ -346,7 +356,11 @@ MagicString.prototype = {
const last = this.byEnd[ end ];
if ( first ) {
- first.edit( content, storeName );
+ if ( end > first.end && first.next !== this.byStart[ first.end ] ) {
+ throw new Error( 'Cannot overwrite across a split point' );
+ }
+
+ first.edit( content, storeName, contentOnly );
if ( last ) {
first.next = last.next;
@@ -426,7 +440,23 @@ MagicString.prototype = {
if ( start < 0 || end > this.original.length ) throw new Error( 'Character is out of bounds' );
if ( start > end ) throw new Error( 'end must be greater than start' );
- return this.overwrite( start, end, '', false );
+ if ( DEBUG ) this.stats.time( 'remove' );
+
+ this._split( start );
+ this._split( end );
+
+ let chunk = this.byStart[ start ];
+
+ while ( chunk ) {
+ chunk.intro = '';
+ chunk.outro = '';
+ chunk.edit( '' );
+
+ chunk = end > chunk.end ? this.byStart[ chunk.end ] : null;
+ }
+
+ if ( DEBUG ) this.stats.timeEnd( 'remove' );
+ return this;
},
slice ( start = 0, end = this.original.length ) {
diff --git a/src/index-legacy.js b/src/index-legacy.js
index 16d6f4d..fcaaf5d 100644
--- a/src/index-legacy.js
+++ b/src/index-legacy.js
@@ -2,5 +2,6 @@ import MagicString from './MagicString.js';
import Bundle from './Bundle.js';
MagicString.Bundle = Bundle;
+MagicString.default = MagicString; // work around TypeScript bug https://github.com/Rich-Harris/magic-string/pull/121
export default MagicString;
diff --git a/test/MagicString.Bundle.js b/test/MagicString.Bundle.js
index f0ce009..bf68788 100644
--- a/test/MagicString.Bundle.js
+++ b/test/MagicString.Bundle.js
@@ -294,8 +294,8 @@ describe( '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 );
+ one.overwrite( 9, 12, 'three', { storeName: true });
+ two.overwrite( 9, 12, 'four', { storeName: true });
b.addSource( one );
b.addSource( two );
diff --git a/test/MagicString.js b/test/MagicString.js
index d34a269..c9388e9 100644
--- a/test/MagicString.js
+++ b/test/MagicString.js
@@ -42,12 +42,12 @@ describe( 'MagicString', () => {
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' );
+ s.appendLeft( 5, 'A' );
+ s.prependRight( 5, 'a' );
+ s.prependRight( 5, 'b' );
+ s.appendLeft( 5, 'B' );
+ s.appendLeft( 5, 'C' );
+ s.prependRight( 5, 'c' );
assert.equal( s.toString(), '01234ABCcba56789' );
assert.equal( s.slice(0, 5) , '01234ABC' );
@@ -61,12 +61,12 @@ describe( 'MagicString', () => {
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
+ s.appendLeft(5, '(');
+ s.appendLeft(5, '[');
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
+ s.prependRight(5, ')');
+ s.prependRight(5, ']');
assert.equal( s.toString(), '01234{<ABC([])cba>}56789' );
assert.equal( s.slice(0, 5), '01234{<ABC([' );
@@ -141,6 +141,19 @@ describe( 'MagicString', () => {
assert.deepEqual( source.indentExclusionRanges, clone.indentExclusionRanges );
});
+ it( 'should clone complex indentExclusionRanges', () => {
+ const array = [ [ 3, 6 ], [ 7, 9 ] ];
+ 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'
@@ -266,12 +279,12 @@ describe( 'MagicString', () => {
assert.equal( loc.column, 9 );
});
- it( 'should yield consistent results between insertLeft and insertRight', () => {
+ it( 'should yield consistent results between appendLeft and prependRight', () => {
const s1 = new MagicString( 'abcdefghijkl' );
- s1.insertLeft( 6, 'X' );
+ s1.appendLeft( 6, 'X' );
const s2 = new MagicString( 'abcdefghijkl' );
- s2.insertRight( 6, 'X' );
+ s2.prependRight( 6, 'X' );
const m1 = s1.generateMap({ file: 'output', source: 'input', includeContent: true });
const m2 = s2.generateMap({ file: 'output', source: 'input', includeContent: true });
@@ -282,7 +295,7 @@ describe( 'MagicString', () => {
it( 'should recover original names', () => {
const s = new MagicString( 'function Foo () {}' );
- s.overwrite( 9, 12, 'Bar', true );
+ s.overwrite( 9, 12, 'Bar', { storeName: true });
const map = s.generateMap({
file: 'output.js',
@@ -298,7 +311,7 @@ describe( 'MagicString', () => {
it( 'should generate one segment per replacement', () => {
const s = new MagicString( 'var answer = 42' );
- s.overwrite( 4, 10, 'number', true );
+ s.overwrite( 4, 10, 'number', { storeName: true });
const map = s.generateMap({
file: 'output.js',
@@ -508,7 +521,7 @@ describe( 'MagicString', () => {
assert.throws( () => s.insert( 6, 'X' ), /deprecated/ );
});
- // TODO move this into insertRight and insertLeft tests
+ // TODO move this into prependRight and appendLeft tests
// it( 'should insert characters in the correct location', () => {
// const s = new MagicString( 'abcdefghijkl' );
@@ -584,9 +597,9 @@ describe( 'MagicString', () => {
it( 'ignores redundant move', () => {
const s = new MagicString( 'abcdefghijkl' );
- s.insertRight( 9, 'X' );
+ s.prependRight( 9, 'X' );
s.move( 9, 12, 6 );
- s.insertLeft( 12, 'Y' );
+ s.appendLeft( 12, 'Y' );
s.move( 6, 9, 12 ); // this is redundant – [6,9] is already after [9,12]
assert.equal( s.toString(), 'abcdefXjklYghi' );
@@ -655,7 +668,7 @@ describe( 'MagicString', () => {
// it( 'move follows inserts', () => {
// const s = new MagicString( 'abcdefghijkl' );
//
- // s.insertLeft( 3, 'X' ).move( 6, 9, 3 );
+ // s.appendLeft( 3, 'X' ).move( 6, 9, 3 );
// assert.equal( s.toString(), 'abcXghidefjkl' );
// });
//
@@ -676,7 +689,7 @@ describe( 'MagicString', () => {
it( 'moves content inserted at end of range', () => {
const s = new MagicString( 'abcdefghijkl' );
- s.insertLeft( 6, 'X' ).move( 3, 6, 9 );
+ s.appendLeft( 6, 'X' ).move( 3, 6, 9 );
assert.equal( s.toString(), 'abcghidefXjkl' );
});
@@ -727,7 +740,7 @@ describe( 'MagicString', () => {
const s = new MagicString( 'abcdefghijkl' );
s.remove( 0, 6 );
- s.insertLeft( 6, 'DEF' );
+ s.appendLeft( 6, 'DEF' );
s.overwrite( 6, 9, 'GHI' );
assert.equal( s.toString(), 'DEFGHIjkl' );
});
@@ -735,7 +748,7 @@ describe( 'MagicString', () => {
it( 'replaces zero-length inserts inside overwrite', () => {
const s = new MagicString( 'abcdefghijkl' );
- s.insertLeft( 6, 'XXX' );
+ s.appendLeft( 6, 'XXX' );
s.overwrite( 3, 9, 'DEFGHI' );
assert.equal( s.toString(), 'abcDEFGHIjkl' );
});
@@ -759,7 +772,7 @@ describe( 'MagicString', () => {
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/ );
+ assert.throws( () => s.overwrite( 0, 0, 'anything' ), /Cannot overwrite a zero-length range – use appendLeft or prependRight instead/ );
});
it( 'should throw when given non-string content', () => {
@@ -767,16 +780,34 @@ describe( 'MagicString', () => {
assert.throws( () => s.overwrite( 0, 1, [] ), TypeError );
});
- it ( 'replaces interior inserts', () => {
+ it( 'replaces interior inserts', () => {
const s = new MagicString( 'abcdefghijkl' );
- s.insertLeft( 1, '&' );
- s.insertRight( 1, '^' );
- s.insertLeft( 3, '!' );
- s.insertRight( 3, '?' );
+ s.appendLeft( 1, '&' );
+ s.prependRight( 1, '^' );
+ s.appendLeft( 3, '!' );
+ s.prependRight( 3, '?' );
s.overwrite( 1, 3, '...' );
assert.equal( s.toString(), 'a&...?defghijkl' );
});
+
+ it( 'preserves interior inserts with `contentOnly: true`', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.appendLeft( 1, '&' );
+ s.prependRight( 1, '^' );
+ s.appendLeft( 3, '!' );
+ s.prependRight( 3, '?' );
+ s.overwrite( 1, 3, '...', { contentOnly: true });
+ assert.equal( s.toString(), 'a&^...!?defghijkl' );
+ });
+
+ it( 'disallows overwriting across moved content', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.move( 6, 9, 3 );
+ assert.throws( () => s.overwrite( 5, 7, 'XX' ), /Cannot overwrite across a split point/ );
+ });
});
describe( 'prepend', () => {
@@ -874,8 +905,8 @@ describe( 'MagicString', () => {
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.prependRight( 0, '(' );
+ s.prependRight( 4, ')' );
s.remove( 2, 4 );
assert.equal( s.toString(), '(ab);' );
});
@@ -883,10 +914,10 @@ describe( 'MagicString', () => {
it( 'should remove interior inserts', () => {
const s = new MagicString( 'abc;' );
- s.insertLeft( 1, '[' );
- s.insertRight( 1, '(' );
- s.insertLeft( 2, ')' );
- s.insertRight( 2, ']' );
+ s.appendLeft( 1, '[' );
+ s.prependRight( 1, '(' );
+ s.appendLeft( 2, ')' );
+ s.prependRight( 2, ']' );
s.remove( 1, 2 );
assert.equal( s.toString(), 'a[]c;' );
});
@@ -903,6 +934,15 @@ describe( 'MagicString', () => {
const s = new MagicString( 'abcdefghijkl' );
assert.strictEqual( s.remove( 3, 4 ), s );
});
+
+ it( 'removes across moved content', () => {
+ const s = new MagicString( 'abcdefghijkl' );
+
+ s.move( 6, 9, 3 );
+ s.remove( 5, 7 );
+
+ assert.equal( s.toString(), 'abchidejkl' );
+ });
});
describe( 'slice', () => {
@@ -933,8 +973,8 @@ describe( 'MagicString', () => {
it( 'includes inserted characters, respecting insertion direction', () => {
const s = new MagicString( 'abefij' );
- s.insertRight( 2, 'cd' );
- s.insertLeft( 4, 'gh' );
+ s.prependRight( 2, 'cd' );
+ s.appendLeft( 4, 'gh' );
assert.equal( s.slice(), 'abcdefghij' );
assert.equal( s.slice( 1, 5 ), 'bcdefghi' );
--
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