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