source: node_modules/magic-string/dist/magic-string.cjs.js@ 57e58a3

Last change on this file since 57e58a3 was 57e58a3, checked in by ste08 <sjovanoska@…>, 4 months ago

Initial commit

  • Property mode set to 100644
File size: 37.5 KB
Line 
1'use strict';
2
3var sourcemapCodec = require('@jridgewell/sourcemap-codec');
4
5class BitSet {
6 constructor(arg) {
7 this.bits = arg instanceof BitSet ? arg.bits.slice() : [];
8 }
9
10 add(n) {
11 this.bits[n >> 5] |= 1 << (n & 31);
12 }
13
14 has(n) {
15 return !!(this.bits[n >> 5] & (1 << (n & 31)));
16 }
17}
18
19class Chunk {
20 constructor(start, end, content) {
21 this.start = start;
22 this.end = end;
23 this.original = content;
24
25 this.intro = '';
26 this.outro = '';
27
28 this.content = content;
29 this.storeName = false;
30 this.edited = false;
31
32 {
33 this.previous = null;
34 this.next = null;
35 }
36 }
37
38 appendLeft(content) {
39 this.outro += content;
40 }
41
42 appendRight(content) {
43 this.intro = this.intro + content;
44 }
45
46 clone() {
47 const chunk = new Chunk(this.start, this.end, this.original);
48
49 chunk.intro = this.intro;
50 chunk.outro = this.outro;
51 chunk.content = this.content;
52 chunk.storeName = this.storeName;
53 chunk.edited = this.edited;
54
55 return chunk;
56 }
57
58 contains(index) {
59 return this.start < index && index < this.end;
60 }
61
62 eachNext(fn) {
63 let chunk = this;
64 while (chunk) {
65 fn(chunk);
66 chunk = chunk.next;
67 }
68 }
69
70 eachPrevious(fn) {
71 let chunk = this;
72 while (chunk) {
73 fn(chunk);
74 chunk = chunk.previous;
75 }
76 }
77
78 edit(content, storeName, contentOnly) {
79 this.content = content;
80 if (!contentOnly) {
81 this.intro = '';
82 this.outro = '';
83 }
84 this.storeName = storeName;
85
86 this.edited = true;
87
88 return this;
89 }
90
91 prependLeft(content) {
92 this.outro = content + this.outro;
93 }
94
95 prependRight(content) {
96 this.intro = content + this.intro;
97 }
98
99 reset() {
100 this.intro = '';
101 this.outro = '';
102 if (this.edited) {
103 this.content = this.original;
104 this.storeName = false;
105 this.edited = false;
106 }
107 }
108
109 split(index) {
110 const sliceIndex = index - this.start;
111
112 const originalBefore = this.original.slice(0, sliceIndex);
113 const originalAfter = this.original.slice(sliceIndex);
114
115 this.original = originalBefore;
116
117 const newChunk = new Chunk(index, this.end, originalAfter);
118 newChunk.outro = this.outro;
119 this.outro = '';
120
121 this.end = index;
122
123 if (this.edited) {
124 // after split we should save the edit content record into the correct chunk
125 // to make sure sourcemap correct
126 // For example:
127 // ' test'.trim()
128 // split -> ' ' + 'test'
129 // ✔️ edit -> '' + 'test'
130 // ✖️ edit -> 'test' + ''
131 // TODO is this block necessary?...
132 newChunk.edit('', false);
133 this.content = '';
134 } else {
135 this.content = originalBefore;
136 }
137
138 newChunk.next = this.next;
139 if (newChunk.next) newChunk.next.previous = newChunk;
140 newChunk.previous = this;
141 this.next = newChunk;
142
143 return newChunk;
144 }
145
146 toString() {
147 return this.intro + this.content + this.outro;
148 }
149
150 trimEnd(rx) {
151 this.outro = this.outro.replace(rx, '');
152 if (this.outro.length) return true;
153
154 const trimmed = this.content.replace(rx, '');
155
156 if (trimmed.length) {
157 if (trimmed !== this.content) {
158 this.split(this.start + trimmed.length).edit('', undefined, true);
159 if (this.edited) {
160 // save the change, if it has been edited
161 this.edit(trimmed, this.storeName, true);
162 }
163 }
164 return true;
165 } else {
166 this.edit('', undefined, true);
167
168 this.intro = this.intro.replace(rx, '');
169 if (this.intro.length) return true;
170 }
171 }
172
173 trimStart(rx) {
174 this.intro = this.intro.replace(rx, '');
175 if (this.intro.length) return true;
176
177 const trimmed = this.content.replace(rx, '');
178
179 if (trimmed.length) {
180 if (trimmed !== this.content) {
181 const newChunk = this.split(this.end - trimmed.length);
182 if (this.edited) {
183 // save the change, if it has been edited
184 newChunk.edit(trimmed, this.storeName, true);
185 }
186 this.edit('', undefined, true);
187 }
188 return true;
189 } else {
190 this.edit('', undefined, true);
191
192 this.outro = this.outro.replace(rx, '');
193 if (this.outro.length) return true;
194 }
195 }
196}
197
198function getBtoa() {
199 if (typeof globalThis !== 'undefined' && typeof globalThis.btoa === 'function') {
200 return (str) => globalThis.btoa(unescape(encodeURIComponent(str)));
201 } else if (typeof Buffer === 'function') {
202 return (str) => Buffer.from(str, 'utf-8').toString('base64');
203 } else {
204 return () => {
205 throw new Error('Unsupported environment: `window.btoa` or `Buffer` should be supported.');
206 };
207 }
208}
209
210const btoa = /*#__PURE__*/ getBtoa();
211
212class SourceMap {
213 constructor(properties) {
214 this.version = 3;
215 this.file = properties.file;
216 this.sources = properties.sources;
217 this.sourcesContent = properties.sourcesContent;
218 this.names = properties.names;
219 this.mappings = sourcemapCodec.encode(properties.mappings);
220 if (typeof properties.x_google_ignoreList !== 'undefined') {
221 this.x_google_ignoreList = properties.x_google_ignoreList;
222 }
223 if (typeof properties.debugId !== 'undefined') {
224 this.debugId = properties.debugId;
225 }
226 }
227
228 toString() {
229 return JSON.stringify(this);
230 }
231
232 toUrl() {
233 return 'data:application/json;charset=utf-8;base64,' + btoa(this.toString());
234 }
235}
236
237function guessIndent(code) {
238 const lines = code.split('\n');
239
240 const tabbed = lines.filter((line) => /^\t+/.test(line));
241 const spaced = lines.filter((line) => /^ {2,}/.test(line));
242
243 if (tabbed.length === 0 && spaced.length === 0) {
244 return null;
245 }
246
247 // More lines tabbed than spaced? Assume tabs, and
248 // default to tabs in the case of a tie (or nothing
249 // to go on)
250 if (tabbed.length >= spaced.length) {
251 return '\t';
252 }
253
254 // Otherwise, we need to guess the multiple
255 const min = spaced.reduce((previous, current) => {
256 const numSpaces = /^ +/.exec(current)[0].length;
257 return Math.min(numSpaces, previous);
258 }, Infinity);
259
260 return new Array(min + 1).join(' ');
261}
262
263function getRelativePath(from, to) {
264 const fromParts = from.split(/[/\\]/);
265 const toParts = to.split(/[/\\]/);
266
267 fromParts.pop(); // get dirname
268
269 while (fromParts[0] === toParts[0]) {
270 fromParts.shift();
271 toParts.shift();
272 }
273
274 if (fromParts.length) {
275 let i = fromParts.length;
276 while (i--) fromParts[i] = '..';
277 }
278
279 return fromParts.concat(toParts).join('/');
280}
281
282const toString = Object.prototype.toString;
283
284function isObject(thing) {
285 return toString.call(thing) === '[object Object]';
286}
287
288function getLocator(source) {
289 const originalLines = source.split('\n');
290 const lineOffsets = [];
291
292 for (let i = 0, pos = 0; i < originalLines.length; i++) {
293 lineOffsets.push(pos);
294 pos += originalLines[i].length + 1;
295 }
296
297 return function locate(index) {
298 let i = 0;
299 let j = lineOffsets.length;
300 while (i < j) {
301 const m = (i + j) >> 1;
302 if (index < lineOffsets[m]) {
303 j = m;
304 } else {
305 i = m + 1;
306 }
307 }
308 const line = i - 1;
309 const column = index - lineOffsets[line];
310 return { line, column };
311 };
312}
313
314const wordRegex = /\w/;
315
316class Mappings {
317 constructor(hires) {
318 this.hires = hires;
319 this.generatedCodeLine = 0;
320 this.generatedCodeColumn = 0;
321 this.raw = [];
322 this.rawSegments = this.raw[this.generatedCodeLine] = [];
323 this.pending = null;
324 }
325
326 addEdit(sourceIndex, content, loc, nameIndex) {
327 if (content.length) {
328 const contentLengthMinusOne = content.length - 1;
329 let contentLineEnd = content.indexOf('\n', 0);
330 let previousContentLineEnd = -1;
331 // Loop through each line in the content and add a segment, but stop if the last line is empty,
332 // else code afterwards would fill one line too many
333 while (contentLineEnd >= 0 && contentLengthMinusOne > contentLineEnd) {
334 const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];
335 if (nameIndex >= 0) {
336 segment.push(nameIndex);
337 }
338 this.rawSegments.push(segment);
339
340 this.generatedCodeLine += 1;
341 this.raw[this.generatedCodeLine] = this.rawSegments = [];
342 this.generatedCodeColumn = 0;
343
344 previousContentLineEnd = contentLineEnd;
345 contentLineEnd = content.indexOf('\n', contentLineEnd + 1);
346 }
347
348 const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];
349 if (nameIndex >= 0) {
350 segment.push(nameIndex);
351 }
352 this.rawSegments.push(segment);
353
354 this.advance(content.slice(previousContentLineEnd + 1));
355 } else if (this.pending) {
356 this.rawSegments.push(this.pending);
357 this.advance(content);
358 }
359
360 this.pending = null;
361 }
362
363 addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
364 let originalCharIndex = chunk.start;
365 let first = true;
366 // when iterating each char, check if it's in a word boundary
367 let charInHiresBoundary = false;
368
369 while (originalCharIndex < chunk.end) {
370 if (original[originalCharIndex] === '\n') {
371 loc.line += 1;
372 loc.column = 0;
373 this.generatedCodeLine += 1;
374 this.raw[this.generatedCodeLine] = this.rawSegments = [];
375 this.generatedCodeColumn = 0;
376 first = true;
377 charInHiresBoundary = false;
378 } else {
379 if (this.hires || first || sourcemapLocations.has(originalCharIndex)) {
380 const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];
381
382 if (this.hires === 'boundary') {
383 // in hires "boundary", group segments per word boundary than per char
384 if (wordRegex.test(original[originalCharIndex])) {
385 // for first char in the boundary found, start the boundary by pushing a segment
386 if (!charInHiresBoundary) {
387 this.rawSegments.push(segment);
388 charInHiresBoundary = true;
389 }
390 } else {
391 // for non-word char, end the boundary by pushing a segment
392 this.rawSegments.push(segment);
393 charInHiresBoundary = false;
394 }
395 } else {
396 this.rawSegments.push(segment);
397 }
398 }
399
400 loc.column += 1;
401 this.generatedCodeColumn += 1;
402 first = false;
403 }
404
405 originalCharIndex += 1;
406 }
407
408 this.pending = null;
409 }
410
411 advance(str) {
412 if (!str) return;
413
414 const lines = str.split('\n');
415
416 if (lines.length > 1) {
417 for (let i = 0; i < lines.length - 1; i++) {
418 this.generatedCodeLine++;
419 this.raw[this.generatedCodeLine] = this.rawSegments = [];
420 }
421 this.generatedCodeColumn = 0;
422 }
423
424 this.generatedCodeColumn += lines[lines.length - 1].length;
425 }
426}
427
428const n = '\n';
429
430const warned = {
431 insertLeft: false,
432 insertRight: false,
433 storeName: false,
434};
435
436class MagicString {
437 constructor(string, options = {}) {
438 const chunk = new Chunk(0, string.length, string);
439
440 Object.defineProperties(this, {
441 original: { writable: true, value: string },
442 outro: { writable: true, value: '' },
443 intro: { writable: true, value: '' },
444 firstChunk: { writable: true, value: chunk },
445 lastChunk: { writable: true, value: chunk },
446 lastSearchedChunk: { writable: true, value: chunk },
447 byStart: { writable: true, value: {} },
448 byEnd: { writable: true, value: {} },
449 filename: { writable: true, value: options.filename },
450 indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },
451 sourcemapLocations: { writable: true, value: new BitSet() },
452 storedNames: { writable: true, value: {} },
453 indentStr: { writable: true, value: undefined },
454 ignoreList: { writable: true, value: options.ignoreList },
455 offset: { writable: true, value: options.offset || 0 },
456 });
457
458 this.byStart[0] = chunk;
459 this.byEnd[string.length] = chunk;
460 }
461
462 addSourcemapLocation(char) {
463 this.sourcemapLocations.add(char);
464 }
465
466 append(content) {
467 if (typeof content !== 'string') throw new TypeError('outro content must be a string');
468
469 this.outro += content;
470 return this;
471 }
472
473 appendLeft(index, content) {
474 index = index + this.offset;
475
476 if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
477
478 this._split(index);
479
480 const chunk = this.byEnd[index];
481
482 if (chunk) {
483 chunk.appendLeft(content);
484 } else {
485 this.intro += content;
486 }
487 return this;
488 }
489
490 appendRight(index, content) {
491 index = index + this.offset;
492
493 if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
494
495 this._split(index);
496
497 const chunk = this.byStart[index];
498
499 if (chunk) {
500 chunk.appendRight(content);
501 } else {
502 this.outro += content;
503 }
504 return this;
505 }
506
507 clone() {
508 const cloned = new MagicString(this.original, { filename: this.filename, offset: this.offset });
509
510 let originalChunk = this.firstChunk;
511 let clonedChunk = (cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone());
512
513 while (originalChunk) {
514 cloned.byStart[clonedChunk.start] = clonedChunk;
515 cloned.byEnd[clonedChunk.end] = clonedChunk;
516
517 const nextOriginalChunk = originalChunk.next;
518 const nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
519
520 if (nextClonedChunk) {
521 clonedChunk.next = nextClonedChunk;
522 nextClonedChunk.previous = clonedChunk;
523
524 clonedChunk = nextClonedChunk;
525 }
526
527 originalChunk = nextOriginalChunk;
528 }
529
530 cloned.lastChunk = clonedChunk;
531
532 if (this.indentExclusionRanges) {
533 cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
534 }
535
536 cloned.sourcemapLocations = new BitSet(this.sourcemapLocations);
537
538 cloned.intro = this.intro;
539 cloned.outro = this.outro;
540
541 return cloned;
542 }
543
544 generateDecodedMap(options) {
545 options = options || {};
546
547 const sourceIndex = 0;
548 const names = Object.keys(this.storedNames);
549 const mappings = new Mappings(options.hires);
550
551 const locate = getLocator(this.original);
552
553 if (this.intro) {
554 mappings.advance(this.intro);
555 }
556
557 this.firstChunk.eachNext((chunk) => {
558 const loc = locate(chunk.start);
559
560 if (chunk.intro.length) mappings.advance(chunk.intro);
561
562 if (chunk.edited) {
563 mappings.addEdit(
564 sourceIndex,
565 chunk.content,
566 loc,
567 chunk.storeName ? names.indexOf(chunk.original) : -1,
568 );
569 } else {
570 mappings.addUneditedChunk(sourceIndex, chunk, this.original, loc, this.sourcemapLocations);
571 }
572
573 if (chunk.outro.length) mappings.advance(chunk.outro);
574 });
575
576 return {
577 file: options.file ? options.file.split(/[/\\]/).pop() : undefined,
578 sources: [
579 options.source ? getRelativePath(options.file || '', options.source) : options.file || '',
580 ],
581 sourcesContent: options.includeContent ? [this.original] : undefined,
582 names,
583 mappings: mappings.raw,
584 x_google_ignoreList: this.ignoreList ? [sourceIndex] : undefined,
585 };
586 }
587
588 generateMap(options) {
589 return new SourceMap(this.generateDecodedMap(options));
590 }
591
592 _ensureindentStr() {
593 if (this.indentStr === undefined) {
594 this.indentStr = guessIndent(this.original);
595 }
596 }
597
598 _getRawIndentString() {
599 this._ensureindentStr();
600 return this.indentStr;
601 }
602
603 getIndentString() {
604 this._ensureindentStr();
605 return this.indentStr === null ? '\t' : this.indentStr;
606 }
607
608 indent(indentStr, options) {
609 const pattern = /^[^\r\n]/gm;
610
611 if (isObject(indentStr)) {
612 options = indentStr;
613 indentStr = undefined;
614 }
615
616 if (indentStr === undefined) {
617 this._ensureindentStr();
618 indentStr = this.indentStr || '\t';
619 }
620
621 if (indentStr === '') return this; // noop
622
623 options = options || {};
624
625 // Process exclusion ranges
626 const isExcluded = {};
627
628 if (options.exclude) {
629 const exclusions =
630 typeof options.exclude[0] === 'number' ? [options.exclude] : options.exclude;
631 exclusions.forEach((exclusion) => {
632 for (let i = exclusion[0]; i < exclusion[1]; i += 1) {
633 isExcluded[i] = true;
634 }
635 });
636 }
637
638 let shouldIndentNextCharacter = options.indentStart !== false;
639 const replacer = (match) => {
640 if (shouldIndentNextCharacter) return `${indentStr}${match}`;
641 shouldIndentNextCharacter = true;
642 return match;
643 };
644
645 this.intro = this.intro.replace(pattern, replacer);
646
647 let charIndex = 0;
648 let chunk = this.firstChunk;
649
650 while (chunk) {
651 const end = chunk.end;
652
653 if (chunk.edited) {
654 if (!isExcluded[charIndex]) {
655 chunk.content = chunk.content.replace(pattern, replacer);
656
657 if (chunk.content.length) {
658 shouldIndentNextCharacter = chunk.content[chunk.content.length - 1] === '\n';
659 }
660 }
661 } else {
662 charIndex = chunk.start;
663
664 while (charIndex < end) {
665 if (!isExcluded[charIndex]) {
666 const char = this.original[charIndex];
667
668 if (char === '\n') {
669 shouldIndentNextCharacter = true;
670 } else if (char !== '\r' && shouldIndentNextCharacter) {
671 shouldIndentNextCharacter = false;
672
673 if (charIndex === chunk.start) {
674 chunk.prependRight(indentStr);
675 } else {
676 this._splitChunk(chunk, charIndex);
677 chunk = chunk.next;
678 chunk.prependRight(indentStr);
679 }
680 }
681 }
682
683 charIndex += 1;
684 }
685 }
686
687 charIndex = chunk.end;
688 chunk = chunk.next;
689 }
690
691 this.outro = this.outro.replace(pattern, replacer);
692
693 return this;
694 }
695
696 insert() {
697 throw new Error(
698 'magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)',
699 );
700 }
701
702 insertLeft(index, content) {
703 if (!warned.insertLeft) {
704 console.warn(
705 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead',
706 );
707 warned.insertLeft = true;
708 }
709
710 return this.appendLeft(index, content);
711 }
712
713 insertRight(index, content) {
714 if (!warned.insertRight) {
715 console.warn(
716 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead',
717 );
718 warned.insertRight = true;
719 }
720
721 return this.prependRight(index, content);
722 }
723
724 move(start, end, index) {
725 start = start + this.offset;
726 end = end + this.offset;
727 index = index + this.offset;
728
729 if (index >= start && index <= end) throw new Error('Cannot move a selection inside itself');
730
731 this._split(start);
732 this._split(end);
733 this._split(index);
734
735 const first = this.byStart[start];
736 const last = this.byEnd[end];
737
738 const oldLeft = first.previous;
739 const oldRight = last.next;
740
741 const newRight = this.byStart[index];
742 if (!newRight && last === this.lastChunk) return this;
743 const newLeft = newRight ? newRight.previous : this.lastChunk;
744
745 if (oldLeft) oldLeft.next = oldRight;
746 if (oldRight) oldRight.previous = oldLeft;
747
748 if (newLeft) newLeft.next = first;
749 if (newRight) newRight.previous = last;
750
751 if (!first.previous) this.firstChunk = last.next;
752 if (!last.next) {
753 this.lastChunk = first.previous;
754 this.lastChunk.next = null;
755 }
756
757 first.previous = newLeft;
758 last.next = newRight || null;
759
760 if (!newLeft) this.firstChunk = first;
761 if (!newRight) this.lastChunk = last;
762 return this;
763 }
764
765 overwrite(start, end, content, options) {
766 options = options || {};
767 return this.update(start, end, content, { ...options, overwrite: !options.contentOnly });
768 }
769
770 update(start, end, content, options) {
771 start = start + this.offset;
772 end = end + this.offset;
773
774 if (typeof content !== 'string') throw new TypeError('replacement content must be a string');
775
776 if (this.original.length !== 0) {
777 while (start < 0) start += this.original.length;
778 while (end < 0) end += this.original.length;
779 }
780
781 if (end > this.original.length) throw new Error('end is out of bounds');
782 if (start === end)
783 throw new Error(
784 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead',
785 );
786
787 this._split(start);
788 this._split(end);
789
790 if (options === true) {
791 if (!warned.storeName) {
792 console.warn(
793 'The final argument to magicString.overwrite(...) should be an options object. See https://github.com/rich-harris/magic-string',
794 );
795 warned.storeName = true;
796 }
797
798 options = { storeName: true };
799 }
800 const storeName = options !== undefined ? options.storeName : false;
801 const overwrite = options !== undefined ? options.overwrite : false;
802
803 if (storeName) {
804 const original = this.original.slice(start, end);
805 Object.defineProperty(this.storedNames, original, {
806 writable: true,
807 value: true,
808 enumerable: true,
809 });
810 }
811
812 const first = this.byStart[start];
813 const last = this.byEnd[end];
814
815 if (first) {
816 let chunk = first;
817 while (chunk !== last) {
818 if (chunk.next !== this.byStart[chunk.end]) {
819 throw new Error('Cannot overwrite across a split point');
820 }
821 chunk = chunk.next;
822 chunk.edit('', false);
823 }
824
825 first.edit(content, storeName, !overwrite);
826 } else {
827 // must be inserting at the end
828 const newChunk = new Chunk(start, end, '').edit(content, storeName);
829
830 // TODO last chunk in the array may not be the last chunk, if it's moved...
831 last.next = newChunk;
832 newChunk.previous = last;
833 }
834 return this;
835 }
836
837 prepend(content) {
838 if (typeof content !== 'string') throw new TypeError('outro content must be a string');
839
840 this.intro = content + this.intro;
841 return this;
842 }
843
844 prependLeft(index, content) {
845 index = index + this.offset;
846
847 if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
848
849 this._split(index);
850
851 const chunk = this.byEnd[index];
852
853 if (chunk) {
854 chunk.prependLeft(content);
855 } else {
856 this.intro = content + this.intro;
857 }
858 return this;
859 }
860
861 prependRight(index, content) {
862 index = index + this.offset;
863
864 if (typeof content !== 'string') throw new TypeError('inserted content must be a string');
865
866 this._split(index);
867
868 const chunk = this.byStart[index];
869
870 if (chunk) {
871 chunk.prependRight(content);
872 } else {
873 this.outro = content + this.outro;
874 }
875 return this;
876 }
877
878 remove(start, end) {
879 start = start + this.offset;
880 end = end + this.offset;
881
882 if (this.original.length !== 0) {
883 while (start < 0) start += this.original.length;
884 while (end < 0) end += this.original.length;
885 }
886
887 if (start === end) return this;
888
889 if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds');
890 if (start > end) throw new Error('end must be greater than start');
891
892 this._split(start);
893 this._split(end);
894
895 let chunk = this.byStart[start];
896
897 while (chunk) {
898 chunk.intro = '';
899 chunk.outro = '';
900 chunk.edit('');
901
902 chunk = end > chunk.end ? this.byStart[chunk.end] : null;
903 }
904 return this;
905 }
906
907 reset(start, end) {
908 start = start + this.offset;
909 end = end + this.offset;
910
911 if (this.original.length !== 0) {
912 while (start < 0) start += this.original.length;
913 while (end < 0) end += this.original.length;
914 }
915
916 if (start === end) return this;
917
918 if (start < 0 || end > this.original.length) throw new Error('Character is out of bounds');
919 if (start > end) throw new Error('end must be greater than start');
920
921 this._split(start);
922 this._split(end);
923
924 let chunk = this.byStart[start];
925
926 while (chunk) {
927 chunk.reset();
928
929 chunk = end > chunk.end ? this.byStart[chunk.end] : null;
930 }
931 return this;
932 }
933
934 lastChar() {
935 if (this.outro.length) return this.outro[this.outro.length - 1];
936 let chunk = this.lastChunk;
937 do {
938 if (chunk.outro.length) return chunk.outro[chunk.outro.length - 1];
939 if (chunk.content.length) return chunk.content[chunk.content.length - 1];
940 if (chunk.intro.length) return chunk.intro[chunk.intro.length - 1];
941 } while ((chunk = chunk.previous));
942 if (this.intro.length) return this.intro[this.intro.length - 1];
943 return '';
944 }
945
946 lastLine() {
947 let lineIndex = this.outro.lastIndexOf(n);
948 if (lineIndex !== -1) return this.outro.substr(lineIndex + 1);
949 let lineStr = this.outro;
950 let chunk = this.lastChunk;
951 do {
952 if (chunk.outro.length > 0) {
953 lineIndex = chunk.outro.lastIndexOf(n);
954 if (lineIndex !== -1) return chunk.outro.substr(lineIndex + 1) + lineStr;
955 lineStr = chunk.outro + lineStr;
956 }
957
958 if (chunk.content.length > 0) {
959 lineIndex = chunk.content.lastIndexOf(n);
960 if (lineIndex !== -1) return chunk.content.substr(lineIndex + 1) + lineStr;
961 lineStr = chunk.content + lineStr;
962 }
963
964 if (chunk.intro.length > 0) {
965 lineIndex = chunk.intro.lastIndexOf(n);
966 if (lineIndex !== -1) return chunk.intro.substr(lineIndex + 1) + lineStr;
967 lineStr = chunk.intro + lineStr;
968 }
969 } while ((chunk = chunk.previous));
970 lineIndex = this.intro.lastIndexOf(n);
971 if (lineIndex !== -1) return this.intro.substr(lineIndex + 1) + lineStr;
972 return this.intro + lineStr;
973 }
974
975 slice(start = 0, end = this.original.length - this.offset) {
976 start = start + this.offset;
977 end = end + this.offset;
978
979 if (this.original.length !== 0) {
980 while (start < 0) start += this.original.length;
981 while (end < 0) end += this.original.length;
982 }
983
984 let result = '';
985
986 // find start chunk
987 let chunk = this.firstChunk;
988 while (chunk && (chunk.start > start || chunk.end <= start)) {
989 // found end chunk before start
990 if (chunk.start < end && chunk.end >= end) {
991 return result;
992 }
993
994 chunk = chunk.next;
995 }
996
997 if (chunk && chunk.edited && chunk.start !== start)
998 throw new Error(`Cannot use replaced character ${start} as slice start anchor.`);
999
1000 const startChunk = chunk;
1001 while (chunk) {
1002 if (chunk.intro && (startChunk !== chunk || chunk.start === start)) {
1003 result += chunk.intro;
1004 }
1005
1006 const containsEnd = chunk.start < end && chunk.end >= end;
1007 if (containsEnd && chunk.edited && chunk.end !== end)
1008 throw new Error(`Cannot use replaced character ${end} as slice end anchor.`);
1009
1010 const sliceStart = startChunk === chunk ? start - chunk.start : 0;
1011 const sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
1012
1013 result += chunk.content.slice(sliceStart, sliceEnd);
1014
1015 if (chunk.outro && (!containsEnd || chunk.end === end)) {
1016 result += chunk.outro;
1017 }
1018
1019 if (containsEnd) {
1020 break;
1021 }
1022
1023 chunk = chunk.next;
1024 }
1025
1026 return result;
1027 }
1028
1029 // TODO deprecate this? not really very useful
1030 snip(start, end) {
1031 const clone = this.clone();
1032 clone.remove(0, start);
1033 clone.remove(end, clone.original.length);
1034
1035 return clone;
1036 }
1037
1038 _split(index) {
1039 if (this.byStart[index] || this.byEnd[index]) return;
1040
1041 let chunk = this.lastSearchedChunk;
1042 const searchForward = index > chunk.end;
1043
1044 while (chunk) {
1045 if (chunk.contains(index)) return this._splitChunk(chunk, index);
1046
1047 chunk = searchForward ? this.byStart[chunk.end] : this.byEnd[chunk.start];
1048 }
1049 }
1050
1051 _splitChunk(chunk, index) {
1052 if (chunk.edited && chunk.content.length) {
1053 // zero-length edited chunks are a special case (overlapping replacements)
1054 const loc = getLocator(this.original)(index);
1055 throw new Error(
1056 `Cannot split a chunk that has already been edited (${loc.line}:${loc.column} – "${chunk.original}")`,
1057 );
1058 }
1059
1060 const newChunk = chunk.split(index);
1061
1062 this.byEnd[index] = chunk;
1063 this.byStart[index] = newChunk;
1064 this.byEnd[newChunk.end] = newChunk;
1065
1066 if (chunk === this.lastChunk) this.lastChunk = newChunk;
1067
1068 this.lastSearchedChunk = chunk;
1069 return true;
1070 }
1071
1072 toString() {
1073 let str = this.intro;
1074
1075 let chunk = this.firstChunk;
1076 while (chunk) {
1077 str += chunk.toString();
1078 chunk = chunk.next;
1079 }
1080
1081 return str + this.outro;
1082 }
1083
1084 isEmpty() {
1085 let chunk = this.firstChunk;
1086 do {
1087 if (
1088 (chunk.intro.length && chunk.intro.trim()) ||
1089 (chunk.content.length && chunk.content.trim()) ||
1090 (chunk.outro.length && chunk.outro.trim())
1091 )
1092 return false;
1093 } while ((chunk = chunk.next));
1094 return true;
1095 }
1096
1097 length() {
1098 let chunk = this.firstChunk;
1099 let length = 0;
1100 do {
1101 length += chunk.intro.length + chunk.content.length + chunk.outro.length;
1102 } while ((chunk = chunk.next));
1103 return length;
1104 }
1105
1106 trimLines() {
1107 return this.trim('[\\r\\n]');
1108 }
1109
1110 trim(charType) {
1111 return this.trimStart(charType).trimEnd(charType);
1112 }
1113
1114 trimEndAborted(charType) {
1115 const rx = new RegExp((charType || '\\s') + '+$');
1116
1117 this.outro = this.outro.replace(rx, '');
1118 if (this.outro.length) return true;
1119
1120 let chunk = this.lastChunk;
1121
1122 do {
1123 const end = chunk.end;
1124 const aborted = chunk.trimEnd(rx);
1125
1126 // if chunk was trimmed, we have a new lastChunk
1127 if (chunk.end !== end) {
1128 if (this.lastChunk === chunk) {
1129 this.lastChunk = chunk.next;
1130 }
1131
1132 this.byEnd[chunk.end] = chunk;
1133 this.byStart[chunk.next.start] = chunk.next;
1134 this.byEnd[chunk.next.end] = chunk.next;
1135 }
1136
1137 if (aborted) return true;
1138 chunk = chunk.previous;
1139 } while (chunk);
1140
1141 return false;
1142 }
1143
1144 trimEnd(charType) {
1145 this.trimEndAborted(charType);
1146 return this;
1147 }
1148 trimStartAborted(charType) {
1149 const rx = new RegExp('^' + (charType || '\\s') + '+');
1150
1151 this.intro = this.intro.replace(rx, '');
1152 if (this.intro.length) return true;
1153
1154 let chunk = this.firstChunk;
1155
1156 do {
1157 const end = chunk.end;
1158 const aborted = chunk.trimStart(rx);
1159
1160 if (chunk.end !== end) {
1161 // special case...
1162 if (chunk === this.lastChunk) this.lastChunk = chunk.next;
1163
1164 this.byEnd[chunk.end] = chunk;
1165 this.byStart[chunk.next.start] = chunk.next;
1166 this.byEnd[chunk.next.end] = chunk.next;
1167 }
1168
1169 if (aborted) return true;
1170 chunk = chunk.next;
1171 } while (chunk);
1172
1173 return false;
1174 }
1175
1176 trimStart(charType) {
1177 this.trimStartAborted(charType);
1178 return this;
1179 }
1180
1181 hasChanged() {
1182 return this.original !== this.toString();
1183 }
1184
1185 _replaceRegexp(searchValue, replacement) {
1186 function getReplacement(match, str) {
1187 if (typeof replacement === 'string') {
1188 return replacement.replace(/\$(\$|&|\d+)/g, (_, i) => {
1189 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter
1190 if (i === '$') return '$';
1191 if (i === '&') return match[0];
1192 const num = +i;
1193 if (num < match.length) return match[+i];
1194 return `$${i}`;
1195 });
1196 } else {
1197 return replacement(...match, match.index, str, match.groups);
1198 }
1199 }
1200 function matchAll(re, str) {
1201 let match;
1202 const matches = [];
1203 while ((match = re.exec(str))) {
1204 matches.push(match);
1205 }
1206 return matches;
1207 }
1208 if (searchValue.global) {
1209 const matches = matchAll(searchValue, this.original);
1210 matches.forEach((match) => {
1211 if (match.index != null) {
1212 const replacement = getReplacement(match, this.original);
1213 if (replacement !== match[0]) {
1214 this.overwrite(match.index, match.index + match[0].length, replacement);
1215 }
1216 }
1217 });
1218 } else {
1219 const match = this.original.match(searchValue);
1220 if (match && match.index != null) {
1221 const replacement = getReplacement(match, this.original);
1222 if (replacement !== match[0]) {
1223 this.overwrite(match.index, match.index + match[0].length, replacement);
1224 }
1225 }
1226 }
1227 return this;
1228 }
1229
1230 _replaceString(string, replacement) {
1231 const { original } = this;
1232 const index = original.indexOf(string);
1233
1234 if (index !== -1) {
1235 this.overwrite(index, index + string.length, replacement);
1236 }
1237
1238 return this;
1239 }
1240
1241 replace(searchValue, replacement) {
1242 if (typeof searchValue === 'string') {
1243 return this._replaceString(searchValue, replacement);
1244 }
1245
1246 return this._replaceRegexp(searchValue, replacement);
1247 }
1248
1249 _replaceAllString(string, replacement) {
1250 const { original } = this;
1251 const stringLength = string.length;
1252 for (
1253 let index = original.indexOf(string);
1254 index !== -1;
1255 index = original.indexOf(string, index + stringLength)
1256 ) {
1257 const previous = original.slice(index, index + stringLength);
1258 if (previous !== replacement) this.overwrite(index, index + stringLength, replacement);
1259 }
1260
1261 return this;
1262 }
1263
1264 replaceAll(searchValue, replacement) {
1265 if (typeof searchValue === 'string') {
1266 return this._replaceAllString(searchValue, replacement);
1267 }
1268
1269 if (!searchValue.global) {
1270 throw new TypeError(
1271 'MagicString.prototype.replaceAll called with a non-global RegExp argument',
1272 );
1273 }
1274
1275 return this._replaceRegexp(searchValue, replacement);
1276 }
1277}
1278
1279const hasOwnProp = Object.prototype.hasOwnProperty;
1280
1281class Bundle {
1282 constructor(options = {}) {
1283 this.intro = options.intro || '';
1284 this.separator = options.separator !== undefined ? options.separator : '\n';
1285 this.sources = [];
1286 this.uniqueSources = [];
1287 this.uniqueSourceIndexByFilename = {};
1288 }
1289
1290 addSource(source) {
1291 if (source instanceof MagicString) {
1292 return this.addSource({
1293 content: source,
1294 filename: source.filename,
1295 separator: this.separator,
1296 });
1297 }
1298
1299 if (!isObject(source) || !source.content) {
1300 throw new Error(
1301 'bundle.addSource() takes an object with a `content` property, which should be an instance of MagicString, and an optional `filename`',
1302 );
1303 }
1304
1305 ['filename', 'ignoreList', 'indentExclusionRanges', 'separator'].forEach((option) => {
1306 if (!hasOwnProp.call(source, option)) source[option] = source.content[option];
1307 });
1308
1309 if (source.separator === undefined) {
1310 // TODO there's a bunch of this sort of thing, needs cleaning up
1311 source.separator = this.separator;
1312 }
1313
1314 if (source.filename) {
1315 if (!hasOwnProp.call(this.uniqueSourceIndexByFilename, source.filename)) {
1316 this.uniqueSourceIndexByFilename[source.filename] = this.uniqueSources.length;
1317 this.uniqueSources.push({ filename: source.filename, content: source.content.original });
1318 } else {
1319 const uniqueSource = this.uniqueSources[this.uniqueSourceIndexByFilename[source.filename]];
1320 if (source.content.original !== uniqueSource.content) {
1321 throw new Error(`Illegal source: same filename (${source.filename}), different contents`);
1322 }
1323 }
1324 }
1325
1326 this.sources.push(source);
1327 return this;
1328 }
1329
1330 append(str, options) {
1331 this.addSource({
1332 content: new MagicString(str),
1333 separator: (options && options.separator) || '',
1334 });
1335
1336 return this;
1337 }
1338
1339 clone() {
1340 const bundle = new Bundle({
1341 intro: this.intro,
1342 separator: this.separator,
1343 });
1344
1345 this.sources.forEach((source) => {
1346 bundle.addSource({
1347 filename: source.filename,
1348 content: source.content.clone(),
1349 separator: source.separator,
1350 });
1351 });
1352
1353 return bundle;
1354 }
1355
1356 generateDecodedMap(options = {}) {
1357 const names = [];
1358 let x_google_ignoreList = undefined;
1359 this.sources.forEach((source) => {
1360 Object.keys(source.content.storedNames).forEach((name) => {
1361 if (!~names.indexOf(name)) names.push(name);
1362 });
1363 });
1364
1365 const mappings = new Mappings(options.hires);
1366
1367 if (this.intro) {
1368 mappings.advance(this.intro);
1369 }
1370
1371 this.sources.forEach((source, i) => {
1372 if (i > 0) {
1373 mappings.advance(this.separator);
1374 }
1375
1376 const sourceIndex = source.filename ? this.uniqueSourceIndexByFilename[source.filename] : -1;
1377 const magicString = source.content;
1378 const locate = getLocator(magicString.original);
1379
1380 if (magicString.intro) {
1381 mappings.advance(magicString.intro);
1382 }
1383
1384 magicString.firstChunk.eachNext((chunk) => {
1385 const loc = locate(chunk.start);
1386
1387 if (chunk.intro.length) mappings.advance(chunk.intro);
1388
1389 if (source.filename) {
1390 if (chunk.edited) {
1391 mappings.addEdit(
1392 sourceIndex,
1393 chunk.content,
1394 loc,
1395 chunk.storeName ? names.indexOf(chunk.original) : -1,
1396 );
1397 } else {
1398 mappings.addUneditedChunk(
1399 sourceIndex,
1400 chunk,
1401 magicString.original,
1402 loc,
1403 magicString.sourcemapLocations,
1404 );
1405 }
1406 } else {
1407 mappings.advance(chunk.content);
1408 }
1409
1410 if (chunk.outro.length) mappings.advance(chunk.outro);
1411 });
1412
1413 if (magicString.outro) {
1414 mappings.advance(magicString.outro);
1415 }
1416
1417 if (source.ignoreList && sourceIndex !== -1) {
1418 if (x_google_ignoreList === undefined) {
1419 x_google_ignoreList = [];
1420 }
1421 x_google_ignoreList.push(sourceIndex);
1422 }
1423 });
1424
1425 return {
1426 file: options.file ? options.file.split(/[/\\]/).pop() : undefined,
1427 sources: this.uniqueSources.map((source) => {
1428 return options.file ? getRelativePath(options.file, source.filename) : source.filename;
1429 }),
1430 sourcesContent: this.uniqueSources.map((source) => {
1431 return options.includeContent ? source.content : null;
1432 }),
1433 names,
1434 mappings: mappings.raw,
1435 x_google_ignoreList,
1436 };
1437 }
1438
1439 generateMap(options) {
1440 return new SourceMap(this.generateDecodedMap(options));
1441 }
1442
1443 getIndentString() {
1444 const indentStringCounts = {};
1445
1446 this.sources.forEach((source) => {
1447 const indentStr = source.content._getRawIndentString();
1448
1449 if (indentStr === null) return;
1450
1451 if (!indentStringCounts[indentStr]) indentStringCounts[indentStr] = 0;
1452 indentStringCounts[indentStr] += 1;
1453 });
1454
1455 return (
1456 Object.keys(indentStringCounts).sort((a, b) => {
1457 return indentStringCounts[a] - indentStringCounts[b];
1458 })[0] || '\t'
1459 );
1460 }
1461
1462 indent(indentStr) {
1463 if (!arguments.length) {
1464 indentStr = this.getIndentString();
1465 }
1466
1467 if (indentStr === '') return this; // noop
1468
1469 let trailingNewline = !this.intro || this.intro.slice(-1) === '\n';
1470
1471 this.sources.forEach((source, i) => {
1472 const separator = source.separator !== undefined ? source.separator : this.separator;
1473 const indentStart = trailingNewline || (i > 0 && /\r?\n$/.test(separator));
1474
1475 source.content.indent(indentStr, {
1476 exclude: source.indentExclusionRanges,
1477 indentStart, //: trailingNewline || /\r?\n$/.test( separator ) //true///\r?\n/.test( separator )
1478 });
1479
1480 trailingNewline = source.content.lastChar() === '\n';
1481 });
1482
1483 if (this.intro) {
1484 this.intro =
1485 indentStr +
1486 this.intro.replace(/^[^\n]/gm, (match, index) => {
1487 return index > 0 ? indentStr + match : match;
1488 });
1489 }
1490
1491 return this;
1492 }
1493
1494 prepend(str) {
1495 this.intro = str + this.intro;
1496 return this;
1497 }
1498
1499 toString() {
1500 const body = this.sources
1501 .map((source, i) => {
1502 const separator = source.separator !== undefined ? source.separator : this.separator;
1503 const str = (i > 0 ? separator : '') + source.content.toString();
1504
1505 return str;
1506 })
1507 .join('');
1508
1509 return this.intro + body;
1510 }
1511
1512 isEmpty() {
1513 if (this.intro.length && this.intro.trim()) return false;
1514 if (this.sources.some((source) => !source.content.isEmpty())) return false;
1515 return true;
1516 }
1517
1518 length() {
1519 return this.sources.reduce(
1520 (length, source) => length + source.content.length(),
1521 this.intro.length,
1522 );
1523 }
1524
1525 trimLines() {
1526 return this.trim('[\\r\\n]');
1527 }
1528
1529 trim(charType) {
1530 return this.trimStart(charType).trimEnd(charType);
1531 }
1532
1533 trimStart(charType) {
1534 const rx = new RegExp('^' + (charType || '\\s') + '+');
1535 this.intro = this.intro.replace(rx, '');
1536
1537 if (!this.intro) {
1538 let source;
1539 let i = 0;
1540
1541 do {
1542 source = this.sources[i++];
1543 if (!source) {
1544 break;
1545 }
1546 } while (!source.content.trimStartAborted(charType));
1547 }
1548
1549 return this;
1550 }
1551
1552 trimEnd(charType) {
1553 const rx = new RegExp((charType || '\\s') + '+$');
1554
1555 let source;
1556 let i = this.sources.length - 1;
1557
1558 do {
1559 source = this.sources[i--];
1560 if (!source) {
1561 this.intro = this.intro.replace(rx, '');
1562 break;
1563 }
1564 } while (!source.content.trimEndAborted(charType));
1565
1566 return this;
1567 }
1568}
1569
1570MagicString.Bundle = Bundle;
1571MagicString.SourceMap = SourceMap;
1572MagicString.default = MagicString; // work around TypeScript bug https://github.com/Rich-Harris/magic-string/pull/121
1573
1574module.exports = MagicString;
1575//# sourceMappingURL=magic-string.cjs.js.map
Note: See TracBrowser for help on using the repository browser.