diff --git a/src/MagicString.js b/src/MagicString.js index 9a5168b..8c92bb3 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -660,12 +660,18 @@ export default class MagicString { if (DEBUG) this.stats.time('_split'); let chunk = this.lastSearchedChunk; + let previousChunk = chunk; const searchForward = index > chunk.end; while (chunk) { if (chunk.contains(index)) return this._splitChunk(chunk, index); chunk = searchForward ? this.byStart[chunk.end] : this.byEnd[chunk.start]; + + // Prevent infinite loop (e.g. via empty chunks, where start === end) + if (chunk === previousChunk) return; + + previousChunk = chunk; } } diff --git a/test/MagicString.test.js b/test/MagicString.test.js index 9cf7b12..1ae90b3 100644 --- a/test/MagicString.test.js +++ b/test/MagicString.test.js @@ -98,6 +98,26 @@ describe('MagicString', () => { assert.equal(s.toString(), 'x4213'); }); + + it('should append/prepend at end of string when index is out of upper bound', () => { + const s = new MagicString('x'); + s.prependLeft(6, 'A'); + s.appendLeft(6, 'B'); + s.prependRight(6, 'C'); + s.appendRight(6, 'D'); + + assert.equal(s.toString(), 'ABxCD'); + }); + + it('should append/prepend on empty string when index is out of upper bound', () => { + const s = new MagicString(''); + s.prependLeft(6, 'A'); + s.appendLeft(6, 'B'); + s.prependRight(6, 'C'); + s.appendRight(6, 'D'); + + assert.equal(s.toString(), 'ABCD'); + }); }); describe('appendLeft', () => {