UNPKG

33.7 kBJavaScriptView Raw
1function Chunk ( start, end, content ) {
2 this.start = start;
3 this.end = end;
4 this.original = content;
5
6 this.intro = '';
7 this.outro = '';
8
9 this.content = content;
10 this.storeName = false;
11 this.edited = false;
12
13 // we make these non-enumerable, for sanity while debugging
14 Object.defineProperties( this, {
15 previous: { writable: true, value: null },
16 next: { writable: true, value: null }
17 });
18}
19
20Chunk.prototype = {
21 appendLeft: function appendLeft ( content ) {
22 this.outro += content;
23 },
24
25 appendRight: function appendRight ( content ) {
26 this.intro = this.intro + content;
27 },
28
29 clone: function clone () {
30 var chunk = new Chunk( this.start, this.end, this.original );
31
32 chunk.intro = this.intro;
33 chunk.outro = this.outro;
34 chunk.content = this.content;
35 chunk.storeName = this.storeName;
36 chunk.edited = this.edited;
37
38 return chunk;
39 },
40
41 contains: function contains ( index ) {
42 return this.start < index && index < this.end;
43 },
44
45 eachNext: function eachNext ( fn ) {
46 var chunk = this;
47 while ( chunk ) {
48 fn( chunk );
49 chunk = chunk.next;
50 }
51 },
52
53 eachPrevious: function eachPrevious ( fn ) {
54 var chunk = this;
55 while ( chunk ) {
56 fn( chunk );
57 chunk = chunk.previous;
58 }
59 },
60
61 edit: function edit ( content, storeName, contentOnly ) {
62 this.content = content;
63 if ( !contentOnly ) {
64 this.intro = '';
65 this.outro = '';
66 }
67 this.storeName = storeName;
68
69 this.edited = true;
70
71 return this;
72 },
73
74 prependLeft: function prependLeft ( content ) {
75 this.outro = content + this.outro;
76 },
77
78 prependRight: function prependRight ( content ) {
79 this.intro = content + this.intro;
80 },
81
82 split: function split ( index ) {
83 var sliceIndex = index - this.start;
84
85 var originalBefore = this.original.slice( 0, sliceIndex );
86 var originalAfter = this.original.slice( sliceIndex );
87
88 this.original = originalBefore;
89
90 var newChunk = new Chunk( index, this.end, originalAfter );
91 newChunk.outro = this.outro;
92 this.outro = '';
93
94 this.end = index;
95
96 if ( this.edited ) {
97 // TODO is this block necessary?...
98 newChunk.edit( '', false );
99 this.content = '';
100 } else {
101 this.content = originalBefore;
102 }
103
104 newChunk.next = this.next;
105 if ( newChunk.next ) { newChunk.next.previous = newChunk; }
106 newChunk.previous = this;
107 this.next = newChunk;
108
109 return newChunk;
110 },
111
112 toString: function toString () {
113 return this.intro + this.content + this.outro;
114 },
115
116 trimEnd: function trimEnd ( rx ) {
117 this.outro = this.outro.replace( rx, '' );
118 if ( this.outro.length ) { return true; }
119
120 var trimmed = this.content.replace( rx, '' );
121
122 if ( trimmed.length ) {
123 if ( trimmed !== this.content ) {
124 this.split( this.start + trimmed.length ).edit( '', false );
125 }
126
127 return true;
128 } else {
129 this.edit( '', false );
130
131 this.intro = this.intro.replace( rx, '' );
132 if ( this.intro.length ) { return true; }
133 }
134 },
135
136 trimStart: function trimStart ( rx ) {
137 this.intro = this.intro.replace( rx, '' );
138 if ( this.intro.length ) { return true; }
139
140 var trimmed = this.content.replace( rx, '' );
141
142 if ( trimmed.length ) {
143 if ( trimmed !== this.content ) {
144 this.split( this.end - trimmed.length );
145 this.edit( '', false );
146 }
147
148 return true;
149 } else {
150 this.edit( '', false );
151
152 this.outro = this.outro.replace( rx, '' );
153 if ( this.outro.length ) { return true; }
154 }
155 }
156};
157
158var _btoa;
159
160if ( typeof window !== 'undefined' && typeof window.btoa === 'function' ) {
161 _btoa = window.btoa;
162} else if ( typeof Buffer === 'function' ) {
163 _btoa = function (str) { return new Buffer( str ).toString( 'base64' ); };
164} else {
165 _btoa = function () {
166 throw new Error( 'Unsupported environment: `window.btoa` or `Buffer` should be supported.' );
167 };
168}
169
170var btoa = _btoa;
171
172function SourceMap ( properties ) {
173 this.version = 3;
174
175 this.file = properties.file;
176 this.sources = properties.sources;
177 this.sourcesContent = properties.sourcesContent;
178 this.names = properties.names;
179 this.mappings = properties.mappings;
180}
181
182SourceMap.prototype = {
183 toString: function toString () {
184 return JSON.stringify( this );
185 },
186
187 toUrl: function toUrl () {
188 return 'data:application/json;charset=utf-8;base64,' + btoa( this.toString() );
189 }
190};
191
192function guessIndent ( code ) {
193 var lines = code.split( '\n' );
194
195 var tabbed = lines.filter( function (line) { return /^\t+/.test( line ); } );
196 var spaced = lines.filter( function (line) { return /^ {2,}/.test( line ); } );
197
198 if ( tabbed.length === 0 && spaced.length === 0 ) {
199 return null;
200 }
201
202 // More lines tabbed than spaced? Assume tabs, and
203 // default to tabs in the case of a tie (or nothing
204 // to go on)
205 if ( tabbed.length >= spaced.length ) {
206 return '\t';
207 }
208
209 // Otherwise, we need to guess the multiple
210 var min = spaced.reduce( function ( previous, current ) {
211 var numSpaces = /^ +/.exec( current )[0].length;
212 return Math.min( numSpaces, previous );
213 }, Infinity );
214
215 return new Array( min + 1 ).join( ' ' );
216}
217
218function getRelativePath ( from, to ) {
219 var fromParts = from.split( /[\/\\]/ );
220 var toParts = to.split( /[\/\\]/ );
221
222 fromParts.pop(); // get dirname
223
224 while ( fromParts[0] === toParts[0] ) {
225 fromParts.shift();
226 toParts.shift();
227 }
228
229 if ( fromParts.length ) {
230 var i = fromParts.length;
231 while ( i-- ) { fromParts[i] = '..'; }
232 }
233
234 return fromParts.concat( toParts ).join( '/' );
235}
236
237var toString = Object.prototype.toString;
238
239function isObject ( thing ) {
240 return toString.call( thing ) === '[object Object]';
241}
242
243function getLocator ( source ) {
244 var originalLines = source.split( '\n' );
245 var lineOffsets = [];
246
247 for ( var i = 0, pos = 0; i < originalLines.length; i++ ) {
248 lineOffsets.push( pos );
249 pos += originalLines[i].length + 1;
250 }
251
252 return function locate ( index ) {
253 var i = 0;
254 var j = lineOffsets.length;
255 while ( i < j ) {
256 var m = ( i + j ) >> 1;
257 if ( index < lineOffsets[m] ) {
258 j = m;
259 } else {
260 i = m + 1;
261 }
262 }
263 var line = i - 1;
264 var column = index - lineOffsets[line];
265 return { line: line, column: column };
266 };
267}
268
269var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
270function encode(decoded) {
271 var sourceFileIndex = 0; // second field
272 var sourceCodeLine = 0; // third field
273 var sourceCodeColumn = 0; // fourth field
274 var nameIndex = 0; // fifth field
275 var mappings = '';
276 for (var i = 0; i < decoded.length; i++) {
277 var line = decoded[i];
278 if (i > 0)
279 mappings += ';';
280 if (line.length === 0)
281 continue;
282 var generatedCodeColumn = 0; // first field
283 var lineMappings = [];
284 for (var _i = 0, line_1 = line; _i < line_1.length; _i++) {
285 var segment = line_1[_i];
286 var segmentMappings = encodeInteger(segment[0] - generatedCodeColumn);
287 generatedCodeColumn = segment[0];
288 if (segment.length > 1) {
289 segmentMappings +=
290 encodeInteger(segment[1] - sourceFileIndex) +
291 encodeInteger(segment[2] - sourceCodeLine) +
292 encodeInteger(segment[3] - sourceCodeColumn);
293 sourceFileIndex = segment[1];
294 sourceCodeLine = segment[2];
295 sourceCodeColumn = segment[3];
296 }
297 if (segment.length === 5) {
298 segmentMappings += encodeInteger(segment[4] - nameIndex);
299 nameIndex = segment[4];
300 }
301 lineMappings.push(segmentMappings);
302 }
303 mappings += lineMappings.join(',');
304 }
305 return mappings;
306}
307function encodeInteger(num) {
308 var result = '';
309 num = num < 0 ? (-num << 1) | 1 : num << 1;
310 do {
311 var clamped = num & 31;
312 num >>= 5;
313 if (num > 0) {
314 clamped |= 32;
315 }
316 result += chars[clamped];
317 } while (num > 0);
318 return result;
319}
320
321function Mappings ( hires ) {
322 var this$1 = this;
323
324 var generatedCodeLine = 0;
325 var generatedCodeColumn = 0;
326
327 this.raw = [];
328 var rawSegments = this.raw[ generatedCodeLine ] = [];
329
330 var pending = null;
331
332 this.addEdit = function ( sourceIndex, content, original, loc, nameIndex ) {
333 if ( content.length ) {
334 var segment = [
335 generatedCodeColumn,
336 sourceIndex,
337 loc.line,
338 loc.column
339 ];
340 if ( nameIndex >= 0 ) {
341 segment.push( nameIndex );
342 }
343 rawSegments.push( segment );
344 } else if ( pending ) {
345 rawSegments.push( pending );
346 }
347
348 this$1.advance( content );
349 pending = null;
350 };
351
352 this.addUneditedChunk = function ( sourceIndex, chunk, original, loc, sourcemapLocations ) {
353 var originalCharIndex = chunk.start;
354 var first = true;
355
356 while ( originalCharIndex < chunk.end ) {
357 if ( hires || first || sourcemapLocations[ originalCharIndex ] ) {
358 rawSegments.push([
359 generatedCodeColumn,
360 sourceIndex,
361 loc.line,
362 loc.column
363 ]);
364 }
365
366 if ( original[ originalCharIndex ] === '\n' ) {
367 loc.line += 1;
368 loc.column = 0;
369 generatedCodeLine += 1;
370 this$1.raw[ generatedCodeLine ] = rawSegments = [];
371 generatedCodeColumn = 0;
372 } else {
373 loc.column += 1;
374 generatedCodeColumn += 1;
375 }
376
377 originalCharIndex += 1;
378 first = false;
379 }
380
381 pending = [
382 generatedCodeColumn,
383 sourceIndex,
384 loc.line,
385 loc.column
386 ];
387 };
388
389 this.advance = function (str) {
390 if ( !str ) { return; }
391
392 var lines = str.split( '\n' );
393
394 if ( lines.length > 1 ) {
395 for ( var i = 0; i < lines.length - 1; i++ ) {
396 generatedCodeLine++;
397 this$1.raw[generatedCodeLine] = rawSegments = [];
398 }
399 generatedCodeColumn = 0;
400 }
401
402 generatedCodeColumn += lines[lines.length - 1].length;
403 };
404
405 this.encode = function () {
406 return encode(this$1.raw);
407 };
408}
409
410var Stats = function Stats () {
411 Object.defineProperties( this, {
412 startTimes: { value: {} }
413 });
414};
415
416Stats.prototype.time = function time ( label ) {
417 this.startTimes[ label ] = process.hrtime();
418};
419
420Stats.prototype.timeEnd = function timeEnd ( label ) {
421 var elapsed = process.hrtime( this.startTimes[ label ] );
422
423 if ( !this[ label ] ) { this[ label ] = 0; }
424 this[ label ] += elapsed[0] * 1e3 + elapsed[1] * 1e-6;
425};
426
427var warned = {
428 insertLeft: false,
429 insertRight: false,
430 storeName: false
431};
432
433function MagicString$1 ( string, options ) {
434 if ( options === void 0 ) options = {};
435
436 var chunk = new Chunk( 0, string.length, string );
437
438 Object.defineProperties( this, {
439 original: { writable: true, value: string },
440 outro: { writable: true, value: '' },
441 intro: { writable: true, value: '' },
442 firstChunk: { writable: true, value: chunk },
443 lastChunk: { writable: true, value: chunk },
444 lastSearchedChunk: { writable: true, value: chunk },
445 byStart: { writable: true, value: {} },
446 byEnd: { writable: true, value: {} },
447 filename: { writable: true, value: options.filename },
448 indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },
449 sourcemapLocations: { writable: true, value: {} },
450 storedNames: { writable: true, value: {} },
451 indentStr: { writable: true, value: guessIndent( string ) }
452 });
453
454 this.byStart[ 0 ] = chunk;
455 this.byEnd[ string.length ] = chunk;
456}
457
458MagicString$1.prototype = {
459 addSourcemapLocation: function addSourcemapLocation ( char ) {
460 this.sourcemapLocations[ char ] = true;
461 },
462
463 append: function append ( content ) {
464 if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); }
465
466 this.outro += content;
467 return this;
468 },
469
470 appendLeft: function appendLeft ( index, content ) {
471 if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
472
473 this._split( index );
474
475 var chunk = this.byEnd[ index ];
476
477 if ( chunk ) {
478 chunk.appendLeft( content );
479 } else {
480 this.intro += content;
481 }
482
483 return this;
484 },
485
486 appendRight: function appendRight ( index, content ) {
487 if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
488
489 this._split( index );
490
491 var chunk = this.byStart[ index ];
492
493 if ( chunk ) {
494 chunk.appendRight( content );
495 } else {
496 this.outro += content;
497 }
498
499 return this;
500 },
501
502 clone: function clone () {
503 var cloned = new MagicString$1( this.original, { filename: this.filename });
504
505 var originalChunk = this.firstChunk;
506 var clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
507
508 while ( originalChunk ) {
509 cloned.byStart[ clonedChunk.start ] = clonedChunk;
510 cloned.byEnd[ clonedChunk.end ] = clonedChunk;
511
512 var nextOriginalChunk = originalChunk.next;
513 var nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
514
515 if ( nextClonedChunk ) {
516 clonedChunk.next = nextClonedChunk;
517 nextClonedChunk.previous = clonedChunk;
518
519 clonedChunk = nextClonedChunk;
520 }
521
522 originalChunk = nextOriginalChunk;
523 }
524
525 cloned.lastChunk = clonedChunk;
526
527 if ( this.indentExclusionRanges ) {
528 cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
529 }
530
531 Object.keys( this.sourcemapLocations ).forEach( function (loc) {
532 cloned.sourcemapLocations[ loc ] = true;
533 });
534
535 return cloned;
536 },
537
538 generateMap: function generateMap ( options ) {
539 var this$1 = this;
540
541 options = options || {};
542
543 var sourceIndex = 0;
544 var names = Object.keys( this.storedNames );
545 var mappings = new Mappings( options.hires );
546
547 var locate = getLocator( this.original );
548
549 if ( this.intro ) {
550 mappings.advance( this.intro );
551 }
552
553 this.firstChunk.eachNext( function (chunk) {
554 var loc = locate( chunk.start );
555
556 if ( chunk.intro.length ) { mappings.advance( chunk.intro ); }
557
558 if ( chunk.edited ) {
559 mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 );
560 } else {
561 mappings.addUneditedChunk( sourceIndex, chunk, this$1.original, loc, this$1.sourcemapLocations );
562 }
563
564 if ( chunk.outro.length ) { mappings.advance( chunk.outro ); }
565 });
566
567 var map = new SourceMap({
568 file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
569 sources: [ options.source ? getRelativePath( options.file || '', options.source ) : null ],
570 sourcesContent: options.includeContent ? [ this.original ] : [ null ],
571 names: names,
572 mappings: mappings.encode()
573 });
574 return map;
575 },
576
577 getIndentString: function getIndentString () {
578 return this.indentStr === null ? '\t' : this.indentStr;
579 },
580
581 indent: function indent ( indentStr, options ) {
582 var this$1 = this;
583
584 var pattern = /^[^\r\n]/gm;
585
586 if ( isObject( indentStr ) ) {
587 options = indentStr;
588 indentStr = undefined;
589 }
590
591 indentStr = indentStr !== undefined ? indentStr : ( this.indentStr || '\t' );
592
593 if ( indentStr === '' ) { return this; } // noop
594
595 options = options || {};
596
597 // Process exclusion ranges
598 var isExcluded = {};
599
600 if ( options.exclude ) {
601 var exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude;
602 exclusions.forEach( function (exclusion) {
603 for ( var i = exclusion[0]; i < exclusion[1]; i += 1 ) {
604 isExcluded[i] = true;
605 }
606 });
607 }
608
609 var shouldIndentNextCharacter = options.indentStart !== false;
610 var replacer = function (match) {
611 if ( shouldIndentNextCharacter ) { return ("" + indentStr + match); }
612 shouldIndentNextCharacter = true;
613 return match;
614 };
615
616 this.intro = this.intro.replace( pattern, replacer );
617
618 var charIndex = 0;
619
620 var chunk = this.firstChunk;
621
622 while ( chunk ) {
623 var end = chunk.end;
624
625 if ( chunk.edited ) {
626 if ( !isExcluded[ charIndex ] ) {
627 chunk.content = chunk.content.replace( pattern, replacer );
628
629 if ( chunk.content.length ) {
630 shouldIndentNextCharacter = chunk.content[ chunk.content.length - 1 ] === '\n';
631 }
632 }
633 } else {
634 charIndex = chunk.start;
635
636 while ( charIndex < end ) {
637 if ( !isExcluded[ charIndex ] ) {
638 var char = this$1.original[ charIndex ];
639
640 if ( char === '\n' ) {
641 shouldIndentNextCharacter = true;
642 } else if ( char !== '\r' && shouldIndentNextCharacter ) {
643 shouldIndentNextCharacter = false;
644
645 if ( charIndex === chunk.start ) {
646 chunk.prependRight( indentStr );
647 } else {
648 this$1._splitChunk( chunk, charIndex );
649 chunk = chunk.next;
650 chunk.prependRight( indentStr );
651 }
652 }
653 }
654
655 charIndex += 1;
656 }
657 }
658
659 charIndex = chunk.end;
660 chunk = chunk.next;
661 }
662
663 this.outro = this.outro.replace( pattern, replacer );
664
665 return this;
666 },
667
668 insert: function insert () {
669 throw new Error( 'magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)' );
670 },
671
672 insertLeft: function insertLeft ( index, content ) {
673 if ( !warned.insertLeft ) {
674 console.warn( 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead' ); // eslint-disable-line no-console
675 warned.insertLeft = true;
676 }
677
678 return this.appendLeft( index, content );
679 },
680
681 insertRight: function insertRight ( index, content ) {
682 if ( !warned.insertRight ) {
683 console.warn( 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead' ); // eslint-disable-line no-console
684 warned.insertRight = true;
685 }
686
687 return this.prependRight( index, content );
688 },
689
690 move: function move ( start, end, index ) {
691 if ( index >= start && index <= end ) { throw new Error( 'Cannot move a selection inside itself' ); }
692
693 this._split( start );
694 this._split( end );
695 this._split( index );
696
697 var first = this.byStart[ start ];
698 var last = this.byEnd[ end ];
699
700 var oldLeft = first.previous;
701 var oldRight = last.next;
702
703 var newRight = this.byStart[ index ];
704 if ( !newRight && last === this.lastChunk ) { return this; }
705 var newLeft = newRight ? newRight.previous : this.lastChunk;
706
707 if ( oldLeft ) { oldLeft.next = oldRight; }
708 if ( oldRight ) { oldRight.previous = oldLeft; }
709
710 if ( newLeft ) { newLeft.next = first; }
711 if ( newRight ) { newRight.previous = last; }
712
713 if ( !first.previous ) { this.firstChunk = last.next; }
714 if ( !last.next ) {
715 this.lastChunk = first.previous;
716 this.lastChunk.next = null;
717 }
718
719 first.previous = newLeft;
720 last.next = newRight || null;
721
722 if ( !newLeft ) { this.firstChunk = first; }
723 if ( !newRight ) { this.lastChunk = last; }
724
725 return this;
726 },
727
728 overwrite: function overwrite ( start, end, content, options ) {
729 var this$1 = this;
730
731 if ( typeof content !== 'string' ) { throw new TypeError( 'replacement content must be a string' ); }
732
733 while ( start < 0 ) { start += this$1.original.length; }
734 while ( end < 0 ) { end += this$1.original.length; }
735
736 if ( end > this.original.length ) { throw new Error( 'end is out of bounds' ); }
737 if ( start === end ) { throw new Error( 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead' ); }
738
739 this._split( start );
740 this._split( end );
741
742 if ( options === true ) {
743 if ( !warned.storeName ) {
744 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
745 warned.storeName = true;
746 }
747
748 options = { storeName: true };
749 }
750 var storeName = options !== undefined ? options.storeName : false;
751 var contentOnly = options !== undefined ? options.contentOnly : false;
752
753 if ( storeName ) {
754 var original = this.original.slice( start, end );
755 this.storedNames[ original ] = true;
756 }
757
758 var first = this.byStart[ start ];
759 var last = this.byEnd[ end ];
760
761 if ( first ) {
762 if ( end > first.end && first.next !== this.byStart[ first.end ] ) {
763 throw new Error( 'Cannot overwrite across a split point' );
764 }
765
766 first.edit( content, storeName, contentOnly );
767
768 if ( first !== last ) {
769 var chunk = first.next;
770 while ( chunk !== last ) {
771 chunk.edit( '', false );
772 chunk = chunk.next;
773 }
774
775 chunk.edit( '', false );
776 }
777 }
778
779 else {
780 // must be inserting at the end
781 var newChunk = new Chunk( start, end, '' ).edit( content, storeName );
782
783 // TODO last chunk in the array may not be the last chunk, if it's moved...
784 last.next = newChunk;
785 newChunk.previous = last;
786 }
787
788 return this;
789 },
790
791 prepend: function prepend ( content ) {
792 if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); }
793
794 this.intro = content + this.intro;
795 return this;
796 },
797
798 prependLeft: function prependLeft ( index, content ) {
799 if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
800
801 this._split( index );
802
803 var chunk = this.byEnd[ index ];
804
805 if ( chunk ) {
806 chunk.prependLeft( content );
807 } else {
808 this.intro = content + this.intro;
809 }
810
811 return this;
812 },
813
814 prependRight: function prependRight ( index, content ) {
815 if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
816
817 this._split( index );
818
819 var chunk = this.byStart[ index ];
820
821 if ( chunk ) {
822 chunk.prependRight( content );
823 } else {
824 this.outro = content + this.outro;
825 }
826
827 return this;
828 },
829
830 remove: function remove ( start, end ) {
831 var this$1 = this;
832
833 while ( start < 0 ) { start += this$1.original.length; }
834 while ( end < 0 ) { end += this$1.original.length; }
835
836 if ( start === end ) { return this; }
837
838 if ( start < 0 || end > this.original.length ) { throw new Error( 'Character is out of bounds' ); }
839 if ( start > end ) { throw new Error( 'end must be greater than start' ); }
840
841 this._split( start );
842 this._split( end );
843
844 var chunk = this.byStart[ start ];
845
846 while ( chunk ) {
847 chunk.intro = '';
848 chunk.outro = '';
849 chunk.edit( '' );
850
851 chunk = end > chunk.end ? this$1.byStart[ chunk.end ] : null;
852 }
853
854 return this;
855 },
856
857 slice: function slice ( start, end ) {
858 var this$1 = this;
859 if ( start === void 0 ) start = 0;
860 if ( end === void 0 ) end = this.original.length;
861
862 while ( start < 0 ) { start += this$1.original.length; }
863 while ( end < 0 ) { end += this$1.original.length; }
864
865 var result = '';
866
867 // find start chunk
868 var chunk = this.firstChunk;
869 while ( chunk && ( chunk.start > start || chunk.end <= start ) ) {
870
871 // found end chunk before start
872 if ( chunk.start < end && chunk.end >= end ) {
873 return result;
874 }
875
876 chunk = chunk.next;
877 }
878
879 if ( chunk && chunk.edited && chunk.start !== start ) { throw new Error(("Cannot use replaced character " + start + " as slice start anchor.")); }
880
881 var startChunk = chunk;
882 while ( chunk ) {
883 if ( chunk.intro && ( startChunk !== chunk || chunk.start === start ) ) {
884 result += chunk.intro;
885 }
886
887 var containsEnd = chunk.start < end && chunk.end >= end;
888 if ( containsEnd && chunk.edited && chunk.end !== end ) { throw new Error(("Cannot use replaced character " + end + " as slice end anchor.")); }
889
890 var sliceStart = startChunk === chunk ? start - chunk.start : 0;
891 var sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
892
893 result += chunk.content.slice( sliceStart, sliceEnd );
894
895 if ( chunk.outro && ( !containsEnd || chunk.end === end ) ) {
896 result += chunk.outro;
897 }
898
899 if ( containsEnd ) {
900 break;
901 }
902
903 chunk = chunk.next;
904 }
905
906 return result;
907 },
908
909 // TODO deprecate this? not really very useful
910 snip: function snip ( start, end ) {
911 var clone = this.clone();
912 clone.remove( 0, start );
913 clone.remove( end, clone.original.length );
914
915 return clone;
916 },
917
918 _split: function _split ( index ) {
919 var this$1 = this;
920
921 if ( this.byStart[ index ] || this.byEnd[ index ] ) { return; }
922
923 var chunk = this.lastSearchedChunk;
924 var searchForward = index > chunk.end;
925
926 while ( true ) {
927 if ( chunk.contains( index ) ) { return this$1._splitChunk( chunk, index ); }
928
929 chunk = searchForward ?
930 this$1.byStart[ chunk.end ] :
931 this$1.byEnd[ chunk.start ];
932 }
933 },
934
935 _splitChunk: function _splitChunk ( chunk, index ) {
936 if ( chunk.edited && chunk.content.length ) { // zero-length edited chunks are a special case (overlapping replacements)
937 var loc = getLocator( this.original )( index );
938 throw new Error( ("Cannot split a chunk that has already been edited (" + (loc.line) + ":" + (loc.column) + " – \"" + (chunk.original) + "\")") );
939 }
940
941 var newChunk = chunk.split( index );
942
943 this.byEnd[ index ] = chunk;
944 this.byStart[ index ] = newChunk;
945 this.byEnd[ newChunk.end ] = newChunk;
946
947 if ( chunk === this.lastChunk ) { this.lastChunk = newChunk; }
948
949 this.lastSearchedChunk = chunk;
950 return true;
951 },
952
953 toString: function toString () {
954 var str = this.intro;
955
956 var chunk = this.firstChunk;
957 while ( chunk ) {
958 str += chunk.toString();
959 chunk = chunk.next;
960 }
961
962 return str + this.outro;
963 },
964
965 trimLines: function trimLines () {
966 return this.trim('[\\r\\n]');
967 },
968
969 trim: function trim ( charType ) {
970 return this.trimStart( charType ).trimEnd( charType );
971 },
972
973 trimEnd: function trimEnd ( charType ) {
974 var this$1 = this;
975
976 var rx = new RegExp( ( charType || '\\s' ) + '+$' );
977
978 this.outro = this.outro.replace( rx, '' );
979 if ( this.outro.length ) { return this; }
980
981 var chunk = this.lastChunk;
982
983 do {
984 var end = chunk.end;
985 var aborted = chunk.trimEnd( rx );
986
987 // if chunk was trimmed, we have a new lastChunk
988 if ( chunk.end !== end ) {
989 if ( this$1.lastChunk === chunk ) {
990 this$1.lastChunk = chunk.next;
991 }
992
993 this$1.byEnd[ chunk.end ] = chunk;
994 this$1.byStart[ chunk.next.start ] = chunk.next;
995 this$1.byEnd[ chunk.next.end ] = chunk.next;
996 }
997
998 if ( aborted ) { return this$1; }
999 chunk = chunk.previous;
1000 } while ( chunk );
1001
1002 return this;
1003 },
1004
1005 trimStart: function trimStart ( charType ) {
1006 var this$1 = this;
1007
1008 var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' );
1009
1010 this.intro = this.intro.replace( rx, '' );
1011 if ( this.intro.length ) { return this; }
1012
1013 var chunk = this.firstChunk;
1014
1015 do {
1016 var end = chunk.end;
1017 var aborted = chunk.trimStart( rx );
1018
1019 if ( chunk.end !== end ) {
1020 // special case...
1021 if ( chunk === this$1.lastChunk ) { this$1.lastChunk = chunk.next; }
1022
1023 this$1.byEnd[ chunk.end ] = chunk;
1024 this$1.byStart[ chunk.next.start ] = chunk.next;
1025 this$1.byEnd[ chunk.next.end ] = chunk.next;
1026 }
1027
1028 if ( aborted ) { return this$1; }
1029 chunk = chunk.next;
1030 } while ( chunk );
1031
1032 return this;
1033 }
1034};
1035
1036var hasOwnProp = Object.prototype.hasOwnProperty;
1037
1038function Bundle ( options ) {
1039 if ( options === void 0 ) options = {};
1040
1041 this.intro = options.intro || '';
1042 this.separator = options.separator !== undefined ? options.separator : '\n';
1043
1044 this.sources = [];
1045
1046 this.uniqueSources = [];
1047 this.uniqueSourceIndexByFilename = {};
1048}
1049
1050Bundle.prototype = {
1051 addSource: function addSource ( source ) {
1052 if ( source instanceof MagicString$1 ) {
1053 return this.addSource({
1054 content: source,
1055 filename: source.filename,
1056 separator: this.separator
1057 });
1058 }
1059
1060 if ( !isObject( source ) || !source.content ) {
1061 throw new Error( 'bundle.addSource() takes an object with a `content` property, which should be an instance of MagicString, and an optional `filename`' );
1062 }
1063
1064 [ 'filename', 'indentExclusionRanges', 'separator' ].forEach( function (option) {
1065 if ( !hasOwnProp.call( source, option ) ) { source[ option ] = source.content[ option ]; }
1066 });
1067
1068 if ( source.separator === undefined ) { // TODO there's a bunch of this sort of thing, needs cleaning up
1069 source.separator = this.separator;
1070 }
1071
1072 if ( source.filename ) {
1073 if ( !hasOwnProp.call( this.uniqueSourceIndexByFilename, source.filename ) ) {
1074 this.uniqueSourceIndexByFilename[ source.filename ] = this.uniqueSources.length;
1075 this.uniqueSources.push({ filename: source.filename, content: source.content.original });
1076 } else {
1077 var uniqueSource = this.uniqueSources[ this.uniqueSourceIndexByFilename[ source.filename ] ];
1078 if ( source.content.original !== uniqueSource.content ) {
1079 throw new Error( ("Illegal source: same filename (" + (source.filename) + "), different contents") );
1080 }
1081 }
1082 }
1083
1084 this.sources.push( source );
1085 return this;
1086 },
1087
1088 append: function append ( str, options ) {
1089 this.addSource({
1090 content: new MagicString$1( str ),
1091 separator: ( options && options.separator ) || ''
1092 });
1093
1094 return this;
1095 },
1096
1097 clone: function clone () {
1098 var bundle = new Bundle({
1099 intro: this.intro,
1100 separator: this.separator
1101 });
1102
1103 this.sources.forEach( function (source) {
1104 bundle.addSource({
1105 filename: source.filename,
1106 content: source.content.clone(),
1107 separator: source.separator
1108 });
1109 });
1110
1111 return bundle;
1112 },
1113
1114 generateMap: function generateMap ( options ) {
1115 var this$1 = this;
1116 if ( options === void 0 ) options = {};
1117
1118 var names = [];
1119 this.sources.forEach( function (source) {
1120 Object.keys( source.content.storedNames ).forEach( function (name) {
1121 if ( !~names.indexOf( name ) ) { names.push( name ); }
1122 });
1123 });
1124
1125 var mappings = new Mappings( options.hires );
1126
1127 if ( this.intro ) {
1128 mappings.advance( this.intro );
1129 }
1130
1131 this.sources.forEach( function ( source, i ) {
1132 if ( i > 0 ) {
1133 mappings.advance( this$1.separator );
1134 }
1135
1136 var sourceIndex = source.filename ? this$1.uniqueSourceIndexByFilename[ source.filename ] : -1;
1137 var magicString = source.content;
1138 var locate = getLocator( magicString.original );
1139
1140 if ( magicString.intro ) {
1141 mappings.advance( magicString.intro );
1142 }
1143
1144 magicString.firstChunk.eachNext( function (chunk) {
1145 var loc = locate( chunk.start );
1146
1147 if ( chunk.intro.length ) { mappings.advance( chunk.intro ); }
1148
1149 if ( source.filename ) {
1150 if ( chunk.edited ) {
1151 mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 );
1152 } else {
1153 mappings.addUneditedChunk( sourceIndex, chunk, magicString.original, loc, magicString.sourcemapLocations );
1154 }
1155 }
1156
1157 else {
1158 mappings.advance( chunk.content );
1159 }
1160
1161 if ( chunk.outro.length ) { mappings.advance( chunk.outro ); }
1162 });
1163
1164 if ( magicString.outro ) {
1165 mappings.advance( magicString.outro );
1166 }
1167 });
1168
1169 return new SourceMap({
1170 file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
1171 sources: this.uniqueSources.map( function (source) {
1172 return options.file ? getRelativePath( options.file, source.filename ) : source.filename;
1173 }),
1174 sourcesContent: this.uniqueSources.map( function (source) {
1175 return options.includeContent ? source.content : null;
1176 }),
1177 names: names,
1178 mappings: mappings.encode()
1179 });
1180 },
1181
1182 getIndentString: function getIndentString () {
1183 var indentStringCounts = {};
1184
1185 this.sources.forEach( function (source) {
1186 var indentStr = source.content.indentStr;
1187
1188 if ( indentStr === null ) { return; }
1189
1190 if ( !indentStringCounts[ indentStr ] ) { indentStringCounts[ indentStr ] = 0; }
1191 indentStringCounts[ indentStr ] += 1;
1192 });
1193
1194 return ( Object.keys( indentStringCounts ).sort( function ( a, b ) {
1195 return indentStringCounts[a] - indentStringCounts[b];
1196 })[0] ) || '\t';
1197 },
1198
1199 indent: function indent ( indentStr ) {
1200 var this$1 = this;
1201
1202 if ( !arguments.length ) {
1203 indentStr = this.getIndentString();
1204 }
1205
1206 if ( indentStr === '' ) { return this; } // noop
1207
1208 var trailingNewline = !this.intro || this.intro.slice( -1 ) === '\n';
1209
1210 this.sources.forEach( function ( source, i ) {
1211 var separator = source.separator !== undefined ? source.separator : this$1.separator;
1212 var indentStart = trailingNewline || ( i > 0 && /\r?\n$/.test( separator ) );
1213
1214 source.content.indent( indentStr, {
1215 exclude: source.indentExclusionRanges,
1216 indentStart: indentStart//: trailingNewline || /\r?\n$/.test( separator ) //true///\r?\n/.test( separator )
1217 });
1218
1219 // TODO this is a very slow way to determine this
1220 trailingNewline = source.content.toString().slice( 0, -1 ) === '\n';
1221 });
1222
1223 if ( this.intro ) {
1224 this.intro = indentStr + this.intro.replace( /^[^\n]/gm, function ( match, index ) {
1225 return index > 0 ? indentStr + match : match;
1226 });
1227 }
1228
1229 return this;
1230 },
1231
1232 prepend: function prepend ( str ) {
1233 this.intro = str + this.intro;
1234 return this;
1235 },
1236
1237 toString: function toString () {
1238 var this$1 = this;
1239
1240 var body = this.sources.map( function ( source, i ) {
1241 var separator = source.separator !== undefined ? source.separator : this$1.separator;
1242 var str = ( i > 0 ? separator : '' ) + source.content.toString();
1243
1244 return str;
1245 }).join( '' );
1246
1247 return this.intro + body;
1248 },
1249
1250 trimLines: function trimLines () {
1251 return this.trim('[\\r\\n]');
1252 },
1253
1254 trim: function trim ( charType ) {
1255 return this.trimStart( charType ).trimEnd( charType );
1256 },
1257
1258 trimStart: function trimStart ( charType ) {
1259 var this$1 = this;
1260
1261 var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' );
1262 this.intro = this.intro.replace( rx, '' );
1263
1264 if ( !this.intro ) {
1265 var source;
1266 var i = 0;
1267
1268 do {
1269 source = this$1.sources[i];
1270
1271 if ( !source ) {
1272 break;
1273 }
1274
1275 source.content.trimStart( charType );
1276 i += 1;
1277 } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source?
1278 }
1279
1280 return this;
1281 },
1282
1283 trimEnd: function trimEnd ( charType ) {
1284 var this$1 = this;
1285
1286 var rx = new RegExp( ( charType || '\\s' ) + '+$' );
1287
1288 var source;
1289 var i = this.sources.length - 1;
1290
1291 do {
1292 source = this$1.sources[i];
1293
1294 if ( !source ) {
1295 this$1.intro = this$1.intro.replace( rx, '' );
1296 break;
1297 }
1298
1299 source.content.trimEnd( charType );
1300 i -= 1;
1301 } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source?
1302
1303 return this;
1304 }
1305};
1306
1307export { Bundle };
1308export default MagicString$1;
1309//# sourceMappingURL=magic-string.es.js.map