magic-string.es.js 32KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297
  1. import { encode } from 'vlq';
  2. function Chunk ( start, end, content ) {
  3. this.start = start;
  4. this.end = end;
  5. this.original = content;
  6. this.intro = '';
  7. this.outro = '';
  8. this.content = content;
  9. this.storeName = false;
  10. this.edited = false;
  11. // we make these non-enumerable, for sanity while debugging
  12. Object.defineProperties( this, {
  13. previous: { writable: true, value: null },
  14. next: { writable: true, value: null }
  15. });
  16. }
  17. Chunk.prototype = {
  18. appendLeft: function appendLeft ( content ) {
  19. this.outro += content;
  20. },
  21. appendRight: function appendRight ( content ) {
  22. this.intro = this.intro + content;
  23. },
  24. clone: function clone () {
  25. var chunk = new Chunk( this.start, this.end, this.original );
  26. chunk.intro = this.intro;
  27. chunk.outro = this.outro;
  28. chunk.content = this.content;
  29. chunk.storeName = this.storeName;
  30. chunk.edited = this.edited;
  31. return chunk;
  32. },
  33. contains: function contains ( index ) {
  34. return this.start < index && index < this.end;
  35. },
  36. eachNext: function eachNext ( fn ) {
  37. var chunk = this;
  38. while ( chunk ) {
  39. fn( chunk );
  40. chunk = chunk.next;
  41. }
  42. },
  43. eachPrevious: function eachPrevious ( fn ) {
  44. var chunk = this;
  45. while ( chunk ) {
  46. fn( chunk );
  47. chunk = chunk.previous;
  48. }
  49. },
  50. edit: function edit ( content, storeName, contentOnly ) {
  51. this.content = content;
  52. if ( !contentOnly ) {
  53. this.intro = '';
  54. this.outro = '';
  55. }
  56. this.storeName = storeName;
  57. this.edited = true;
  58. return this;
  59. },
  60. prependLeft: function prependLeft ( content ) {
  61. this.outro = content + this.outro;
  62. },
  63. prependRight: function prependRight ( content ) {
  64. this.intro = content + this.intro;
  65. },
  66. split: function split ( index ) {
  67. var sliceIndex = index - this.start;
  68. var originalBefore = this.original.slice( 0, sliceIndex );
  69. var originalAfter = this.original.slice( sliceIndex );
  70. this.original = originalBefore;
  71. var newChunk = new Chunk( index, this.end, originalAfter );
  72. newChunk.outro = this.outro;
  73. this.outro = '';
  74. this.end = index;
  75. if ( this.edited ) {
  76. // TODO is this block necessary?...
  77. newChunk.edit( '', false );
  78. this.content = '';
  79. } else {
  80. this.content = originalBefore;
  81. }
  82. newChunk.next = this.next;
  83. if ( newChunk.next ) { newChunk.next.previous = newChunk; }
  84. newChunk.previous = this;
  85. this.next = newChunk;
  86. return newChunk;
  87. },
  88. toString: function toString () {
  89. return this.intro + this.content + this.outro;
  90. },
  91. trimEnd: function trimEnd ( rx ) {
  92. this.outro = this.outro.replace( rx, '' );
  93. if ( this.outro.length ) { return true; }
  94. var trimmed = this.content.replace( rx, '' );
  95. if ( trimmed.length ) {
  96. if ( trimmed !== this.content ) {
  97. this.split( this.start + trimmed.length ).edit( '', false );
  98. }
  99. return true;
  100. } else {
  101. this.edit( '', false );
  102. this.intro = this.intro.replace( rx, '' );
  103. if ( this.intro.length ) { return true; }
  104. }
  105. },
  106. trimStart: function trimStart ( rx ) {
  107. this.intro = this.intro.replace( rx, '' );
  108. if ( this.intro.length ) { return true; }
  109. var trimmed = this.content.replace( rx, '' );
  110. if ( trimmed.length ) {
  111. if ( trimmed !== this.content ) {
  112. this.split( this.end - trimmed.length );
  113. this.edit( '', false );
  114. }
  115. return true;
  116. } else {
  117. this.edit( '', false );
  118. this.outro = this.outro.replace( rx, '' );
  119. if ( this.outro.length ) { return true; }
  120. }
  121. }
  122. };
  123. var _btoa;
  124. if ( typeof window !== 'undefined' && typeof window.btoa === 'function' ) {
  125. _btoa = window.btoa;
  126. } else if ( typeof Buffer === 'function' ) {
  127. _btoa = function (str) { return new Buffer( str ).toString( 'base64' ); };
  128. } else {
  129. _btoa = function () {
  130. throw new Error( 'Unsupported environment: `window.btoa` or `Buffer` should be supported.' );
  131. };
  132. }
  133. var btoa = _btoa;
  134. function SourceMap ( properties ) {
  135. this.version = 3;
  136. this.file = properties.file;
  137. this.sources = properties.sources;
  138. this.sourcesContent = properties.sourcesContent;
  139. this.names = properties.names;
  140. this.mappings = properties.mappings;
  141. }
  142. SourceMap.prototype = {
  143. toString: function toString () {
  144. return JSON.stringify( this );
  145. },
  146. toUrl: function toUrl () {
  147. return 'data:application/json;charset=utf-8;base64,' + btoa( this.toString() );
  148. }
  149. };
  150. function guessIndent ( code ) {
  151. var lines = code.split( '\n' );
  152. var tabbed = lines.filter( function (line) { return /^\t+/.test( line ); } );
  153. var spaced = lines.filter( function (line) { return /^ {2,}/.test( line ); } );
  154. if ( tabbed.length === 0 && spaced.length === 0 ) {
  155. return null;
  156. }
  157. // More lines tabbed than spaced? Assume tabs, and
  158. // default to tabs in the case of a tie (or nothing
  159. // to go on)
  160. if ( tabbed.length >= spaced.length ) {
  161. return '\t';
  162. }
  163. // Otherwise, we need to guess the multiple
  164. var min = spaced.reduce( function ( previous, current ) {
  165. var numSpaces = /^ +/.exec( current )[0].length;
  166. return Math.min( numSpaces, previous );
  167. }, Infinity );
  168. return new Array( min + 1 ).join( ' ' );
  169. }
  170. function getRelativePath ( from, to ) {
  171. var fromParts = from.split( /[\/\\]/ );
  172. var toParts = to.split( /[\/\\]/ );
  173. fromParts.pop(); // get dirname
  174. while ( fromParts[0] === toParts[0] ) {
  175. fromParts.shift();
  176. toParts.shift();
  177. }
  178. if ( fromParts.length ) {
  179. var i = fromParts.length;
  180. while ( i-- ) { fromParts[i] = '..'; }
  181. }
  182. return fromParts.concat( toParts ).join( '/' );
  183. }
  184. var toString = Object.prototype.toString;
  185. function isObject ( thing ) {
  186. return toString.call( thing ) === '[object Object]';
  187. }
  188. function getLocator ( source ) {
  189. var originalLines = source.split( '\n' );
  190. var start = 0;
  191. var lineRanges = originalLines.map( function ( line, i ) {
  192. var end = start + line.length + 1;
  193. var range = { start: start, end: end, line: i };
  194. start = end;
  195. return range;
  196. });
  197. var i = 0;
  198. function rangeContains ( range, index ) {
  199. return range.start <= index && index < range.end;
  200. }
  201. function getLocation ( range, index ) {
  202. return { line: range.line, column: index - range.start };
  203. }
  204. return function locate ( index ) {
  205. var range = lineRanges[i];
  206. var d = index >= range.end ? 1 : -1;
  207. while ( range ) {
  208. if ( rangeContains( range, index ) ) { return getLocation( range, index ); }
  209. i += d;
  210. range = lineRanges[i];
  211. }
  212. };
  213. }
  214. function Mappings ( hires ) {
  215. var this$1 = this;
  216. var offsets = {
  217. generatedCodeColumn: 0,
  218. sourceIndex: 0,
  219. sourceCodeLine: 0,
  220. sourceCodeColumn: 0,
  221. sourceCodeName: 0
  222. };
  223. var generatedCodeLine = 0;
  224. var generatedCodeColumn = 0;
  225. this.raw = [];
  226. var rawSegments = this.raw[ generatedCodeLine ] = [];
  227. var pending = null;
  228. this.addEdit = function ( sourceIndex, content, original, loc, nameIndex ) {
  229. if ( content.length ) {
  230. rawSegments.push([
  231. generatedCodeColumn,
  232. sourceIndex,
  233. loc.line,
  234. loc.column,
  235. nameIndex ]);
  236. } else if ( pending ) {
  237. rawSegments.push( pending );
  238. }
  239. this$1.advance( content );
  240. pending = null;
  241. };
  242. this.addUneditedChunk = function ( sourceIndex, chunk, original, loc, sourcemapLocations ) {
  243. var originalCharIndex = chunk.start;
  244. var first = true;
  245. while ( originalCharIndex < chunk.end ) {
  246. if ( hires || first || sourcemapLocations[ originalCharIndex ] ) {
  247. rawSegments.push([
  248. generatedCodeColumn,
  249. sourceIndex,
  250. loc.line,
  251. loc.column,
  252. -1
  253. ]);
  254. }
  255. if ( original[ originalCharIndex ] === '\n' ) {
  256. loc.line += 1;
  257. loc.column = 0;
  258. generatedCodeLine += 1;
  259. this$1.raw[ generatedCodeLine ] = rawSegments = [];
  260. generatedCodeColumn = 0;
  261. } else {
  262. loc.column += 1;
  263. generatedCodeColumn += 1;
  264. }
  265. originalCharIndex += 1;
  266. first = false;
  267. }
  268. pending = [
  269. generatedCodeColumn,
  270. sourceIndex,
  271. loc.line,
  272. loc.column,
  273. -1 ];
  274. };
  275. this.advance = function (str) {
  276. if ( !str ) { return; }
  277. var lines = str.split( '\n' );
  278. var lastLine = lines.pop();
  279. if ( lines.length ) {
  280. generatedCodeLine += lines.length;
  281. this$1.raw[ generatedCodeLine ] = rawSegments = [];
  282. generatedCodeColumn = lastLine.length;
  283. } else {
  284. generatedCodeColumn += lastLine.length;
  285. }
  286. };
  287. this.encode = function () {
  288. return this$1.raw.map( function (segments) {
  289. var generatedCodeColumn = 0;
  290. return segments.map( function (segment) {
  291. var arr = [
  292. segment[0] - generatedCodeColumn,
  293. segment[1] - offsets.sourceIndex,
  294. segment[2] - offsets.sourceCodeLine,
  295. segment[3] - offsets.sourceCodeColumn
  296. ];
  297. generatedCodeColumn = segment[0];
  298. offsets.sourceIndex = segment[1];
  299. offsets.sourceCodeLine = segment[2];
  300. offsets.sourceCodeColumn = segment[3];
  301. if ( ~segment[4] ) {
  302. arr.push( segment[4] - offsets.sourceCodeName );
  303. offsets.sourceCodeName = segment[4];
  304. }
  305. return encode( arr );
  306. }).join( ',' );
  307. }).join( ';' );
  308. };
  309. }
  310. var Stats = function Stats () {
  311. Object.defineProperties( this, {
  312. startTimes: { value: {} }
  313. });
  314. };
  315. Stats.prototype.time = function time ( label ) {
  316. this.startTimes[ label ] = process.hrtime();
  317. };
  318. Stats.prototype.timeEnd = function timeEnd ( label ) {
  319. var elapsed = process.hrtime( this.startTimes[ label ] );
  320. if ( !this[ label ] ) { this[ label ] = 0; }
  321. this[ label ] += elapsed[0] * 1e3 + elapsed[1] * 1e-6;
  322. };
  323. var warned = {
  324. insertLeft: false,
  325. insertRight: false,
  326. storeName: false
  327. };
  328. function MagicString$1 ( string, options ) {
  329. if ( options === void 0 ) options = {};
  330. var chunk = new Chunk( 0, string.length, string );
  331. Object.defineProperties( this, {
  332. original: { writable: true, value: string },
  333. outro: { writable: true, value: '' },
  334. intro: { writable: true, value: '' },
  335. firstChunk: { writable: true, value: chunk },
  336. lastChunk: { writable: true, value: chunk },
  337. lastSearchedChunk: { writable: true, value: chunk },
  338. byStart: { writable: true, value: {} },
  339. byEnd: { writable: true, value: {} },
  340. filename: { writable: true, value: options.filename },
  341. indentExclusionRanges: { writable: true, value: options.indentExclusionRanges },
  342. sourcemapLocations: { writable: true, value: {} },
  343. storedNames: { writable: true, value: {} },
  344. indentStr: { writable: true, value: guessIndent( string ) }
  345. });
  346. this.byStart[ 0 ] = chunk;
  347. this.byEnd[ string.length ] = chunk;
  348. }
  349. MagicString$1.prototype = {
  350. addSourcemapLocation: function addSourcemapLocation ( char ) {
  351. this.sourcemapLocations[ char ] = true;
  352. },
  353. append: function append ( content ) {
  354. if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); }
  355. this.outro += content;
  356. return this;
  357. },
  358. appendLeft: function appendLeft ( index, content ) {
  359. if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
  360. this._split( index );
  361. var chunk = this.byEnd[ index ];
  362. if ( chunk ) {
  363. chunk.appendLeft( content );
  364. } else {
  365. this.intro += content;
  366. }
  367. return this;
  368. },
  369. appendRight: function appendRight ( index, content ) {
  370. if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
  371. this._split( index );
  372. var chunk = this.byStart[ index ];
  373. if ( chunk ) {
  374. chunk.appendRight( content );
  375. } else {
  376. this.outro += content;
  377. }
  378. return this;
  379. },
  380. clone: function clone () {
  381. var cloned = new MagicString$1( this.original, { filename: this.filename });
  382. var originalChunk = this.firstChunk;
  383. var clonedChunk = cloned.firstChunk = cloned.lastSearchedChunk = originalChunk.clone();
  384. while ( originalChunk ) {
  385. cloned.byStart[ clonedChunk.start ] = clonedChunk;
  386. cloned.byEnd[ clonedChunk.end ] = clonedChunk;
  387. var nextOriginalChunk = originalChunk.next;
  388. var nextClonedChunk = nextOriginalChunk && nextOriginalChunk.clone();
  389. if ( nextClonedChunk ) {
  390. clonedChunk.next = nextClonedChunk;
  391. nextClonedChunk.previous = clonedChunk;
  392. clonedChunk = nextClonedChunk;
  393. }
  394. originalChunk = nextOriginalChunk;
  395. }
  396. cloned.lastChunk = clonedChunk;
  397. if ( this.indentExclusionRanges ) {
  398. cloned.indentExclusionRanges = this.indentExclusionRanges.slice();
  399. }
  400. Object.keys( this.sourcemapLocations ).forEach( function (loc) {
  401. cloned.sourcemapLocations[ loc ] = true;
  402. });
  403. return cloned;
  404. },
  405. generateMap: function generateMap ( options ) {
  406. var this$1 = this;
  407. options = options || {};
  408. var sourceIndex = 0;
  409. var names = Object.keys( this.storedNames );
  410. var mappings = new Mappings( options.hires );
  411. var locate = getLocator( this.original );
  412. if ( this.intro ) {
  413. mappings.advance( this.intro );
  414. }
  415. this.firstChunk.eachNext( function (chunk) {
  416. var loc = locate( chunk.start );
  417. if ( chunk.intro.length ) { mappings.advance( chunk.intro ); }
  418. if ( chunk.edited ) {
  419. mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 );
  420. } else {
  421. mappings.addUneditedChunk( sourceIndex, chunk, this$1.original, loc, this$1.sourcemapLocations );
  422. }
  423. if ( chunk.outro.length ) { mappings.advance( chunk.outro ); }
  424. });
  425. var map = new SourceMap({
  426. file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
  427. sources: [ options.source ? getRelativePath( options.file || '', options.source ) : null ],
  428. sourcesContent: options.includeContent ? [ this.original ] : [ null ],
  429. names: names,
  430. mappings: mappings.encode()
  431. });
  432. return map;
  433. },
  434. getIndentString: function getIndentString () {
  435. return this.indentStr === null ? '\t' : this.indentStr;
  436. },
  437. indent: function indent ( indentStr, options ) {
  438. var this$1 = this;
  439. var pattern = /^[^\r\n]/gm;
  440. if ( isObject( indentStr ) ) {
  441. options = indentStr;
  442. indentStr = undefined;
  443. }
  444. indentStr = indentStr !== undefined ? indentStr : ( this.indentStr || '\t' );
  445. if ( indentStr === '' ) { return this; } // noop
  446. options = options || {};
  447. // Process exclusion ranges
  448. var isExcluded = {};
  449. if ( options.exclude ) {
  450. var exclusions = typeof options.exclude[0] === 'number' ? [ options.exclude ] : options.exclude;
  451. exclusions.forEach( function (exclusion) {
  452. for ( var i = exclusion[0]; i < exclusion[1]; i += 1 ) {
  453. isExcluded[i] = true;
  454. }
  455. });
  456. }
  457. var shouldIndentNextCharacter = options.indentStart !== false;
  458. var replacer = function (match) {
  459. if ( shouldIndentNextCharacter ) { return ("" + indentStr + match); }
  460. shouldIndentNextCharacter = true;
  461. return match;
  462. };
  463. this.intro = this.intro.replace( pattern, replacer );
  464. var charIndex = 0;
  465. var chunk = this.firstChunk;
  466. while ( chunk ) {
  467. var end = chunk.end;
  468. if ( chunk.edited ) {
  469. if ( !isExcluded[ charIndex ] ) {
  470. chunk.content = chunk.content.replace( pattern, replacer );
  471. if ( chunk.content.length ) {
  472. shouldIndentNextCharacter = chunk.content[ chunk.content.length - 1 ] === '\n';
  473. }
  474. }
  475. } else {
  476. charIndex = chunk.start;
  477. while ( charIndex < end ) {
  478. if ( !isExcluded[ charIndex ] ) {
  479. var char = this$1.original[ charIndex ];
  480. if ( char === '\n' ) {
  481. shouldIndentNextCharacter = true;
  482. } else if ( char !== '\r' && shouldIndentNextCharacter ) {
  483. shouldIndentNextCharacter = false;
  484. if ( charIndex === chunk.start ) {
  485. chunk.prependRight( indentStr );
  486. } else {
  487. this$1._splitChunk( chunk, charIndex );
  488. chunk = chunk.next;
  489. chunk.prependRight( indentStr );
  490. }
  491. }
  492. }
  493. charIndex += 1;
  494. }
  495. }
  496. charIndex = chunk.end;
  497. chunk = chunk.next;
  498. }
  499. this.outro = this.outro.replace( pattern, replacer );
  500. return this;
  501. },
  502. insert: function insert () {
  503. throw new Error( 'magicString.insert(...) is deprecated. Use prependRight(...) or appendLeft(...)' );
  504. },
  505. insertLeft: function insertLeft ( index, content ) {
  506. if ( !warned.insertLeft ) {
  507. console.warn( 'magicString.insertLeft(...) is deprecated. Use magicString.appendLeft(...) instead' ); // eslint-disable-line no-console
  508. warned.insertLeft = true;
  509. }
  510. return this.appendLeft( index, content );
  511. },
  512. insertRight: function insertRight ( index, content ) {
  513. if ( !warned.insertRight ) {
  514. console.warn( 'magicString.insertRight(...) is deprecated. Use magicString.prependRight(...) instead' ); // eslint-disable-line no-console
  515. warned.insertRight = true;
  516. }
  517. return this.prependRight( index, content );
  518. },
  519. move: function move ( start, end, index ) {
  520. if ( index >= start && index <= end ) { throw new Error( 'Cannot move a selection inside itself' ); }
  521. this._split( start );
  522. this._split( end );
  523. this._split( index );
  524. var first = this.byStart[ start ];
  525. var last = this.byEnd[ end ];
  526. var oldLeft = first.previous;
  527. var oldRight = last.next;
  528. var newRight = this.byStart[ index ];
  529. if ( !newRight && last === this.lastChunk ) { return this; }
  530. var newLeft = newRight ? newRight.previous : this.lastChunk;
  531. if ( oldLeft ) { oldLeft.next = oldRight; }
  532. if ( oldRight ) { oldRight.previous = oldLeft; }
  533. if ( newLeft ) { newLeft.next = first; }
  534. if ( newRight ) { newRight.previous = last; }
  535. if ( !first.previous ) { this.firstChunk = last.next; }
  536. if ( !last.next ) {
  537. this.lastChunk = first.previous;
  538. this.lastChunk.next = null;
  539. }
  540. first.previous = newLeft;
  541. last.next = newRight || null;
  542. if ( !newLeft ) { this.firstChunk = first; }
  543. if ( !newRight ) { this.lastChunk = last; }
  544. return this;
  545. },
  546. overwrite: function overwrite ( start, end, content, options ) {
  547. var this$1 = this;
  548. if ( typeof content !== 'string' ) { throw new TypeError( 'replacement content must be a string' ); }
  549. while ( start < 0 ) { start += this$1.original.length; }
  550. while ( end < 0 ) { end += this$1.original.length; }
  551. if ( end > this.original.length ) { throw new Error( 'end is out of bounds' ); }
  552. if ( start === end ) { throw new Error( 'Cannot overwrite a zero-length range – use appendLeft or prependRight instead' ); }
  553. this._split( start );
  554. this._split( end );
  555. if ( options === true ) {
  556. if ( !warned.storeName ) {
  557. 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
  558. warned.storeName = true;
  559. }
  560. options = { storeName: true };
  561. }
  562. var storeName = options !== undefined ? options.storeName : false;
  563. var contentOnly = options !== undefined ? options.contentOnly : false;
  564. if ( storeName ) {
  565. var original = this.original.slice( start, end );
  566. this.storedNames[ original ] = true;
  567. }
  568. var first = this.byStart[ start ];
  569. var last = this.byEnd[ end ];
  570. if ( first ) {
  571. if ( end > first.end && first.next !== this.byStart[ first.end ] ) {
  572. throw new Error( 'Cannot overwrite across a split point' );
  573. }
  574. first.edit( content, storeName, contentOnly );
  575. if ( first !== last ) {
  576. var chunk = first.next;
  577. while ( chunk !== last ) {
  578. chunk.edit( '', false );
  579. chunk = chunk.next;
  580. }
  581. chunk.edit( '', false );
  582. }
  583. }
  584. else {
  585. // must be inserting at the end
  586. var newChunk = new Chunk( start, end, '' ).edit( content, storeName );
  587. // TODO last chunk in the array may not be the last chunk, if it's moved...
  588. last.next = newChunk;
  589. newChunk.previous = last;
  590. }
  591. return this;
  592. },
  593. prepend: function prepend ( content ) {
  594. if ( typeof content !== 'string' ) { throw new TypeError( 'outro content must be a string' ); }
  595. this.intro = content + this.intro;
  596. return this;
  597. },
  598. prependLeft: function prependLeft ( index, content ) {
  599. if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
  600. this._split( index );
  601. var chunk = this.byEnd[ index ];
  602. if ( chunk ) {
  603. chunk.prependLeft( content );
  604. } else {
  605. this.intro = content + this.intro;
  606. }
  607. return this;
  608. },
  609. prependRight: function prependRight ( index, content ) {
  610. if ( typeof content !== 'string' ) { throw new TypeError( 'inserted content must be a string' ); }
  611. this._split( index );
  612. var chunk = this.byStart[ index ];
  613. if ( chunk ) {
  614. chunk.prependRight( content );
  615. } else {
  616. this.outro = content + this.outro;
  617. }
  618. return this;
  619. },
  620. remove: function remove ( start, end ) {
  621. var this$1 = this;
  622. while ( start < 0 ) { start += this$1.original.length; }
  623. while ( end < 0 ) { end += this$1.original.length; }
  624. if ( start === end ) { return this; }
  625. if ( start < 0 || end > this.original.length ) { throw new Error( 'Character is out of bounds' ); }
  626. if ( start > end ) { throw new Error( 'end must be greater than start' ); }
  627. this._split( start );
  628. this._split( end );
  629. var chunk = this.byStart[ start ];
  630. while ( chunk ) {
  631. chunk.intro = '';
  632. chunk.outro = '';
  633. chunk.edit( '' );
  634. chunk = end > chunk.end ? this$1.byStart[ chunk.end ] : null;
  635. }
  636. return this;
  637. },
  638. slice: function slice ( start, end ) {
  639. var this$1 = this;
  640. if ( start === void 0 ) start = 0;
  641. if ( end === void 0 ) end = this.original.length;
  642. while ( start < 0 ) { start += this$1.original.length; }
  643. while ( end < 0 ) { end += this$1.original.length; }
  644. var result = '';
  645. // find start chunk
  646. var chunk = this.firstChunk;
  647. while ( chunk && ( chunk.start > start || chunk.end <= start ) ) {
  648. // found end chunk before start
  649. if ( chunk.start < end && chunk.end >= end ) {
  650. return result;
  651. }
  652. chunk = chunk.next;
  653. }
  654. if ( chunk && chunk.edited && chunk.start !== start ) { throw new Error(("Cannot use replaced character " + start + " as slice start anchor.")); }
  655. var startChunk = chunk;
  656. while ( chunk ) {
  657. if ( chunk.intro && ( startChunk !== chunk || chunk.start === start ) ) {
  658. result += chunk.intro;
  659. }
  660. var containsEnd = chunk.start < end && chunk.end >= end;
  661. if ( containsEnd && chunk.edited && chunk.end !== end ) { throw new Error(("Cannot use replaced character " + end + " as slice end anchor.")); }
  662. var sliceStart = startChunk === chunk ? start - chunk.start : 0;
  663. var sliceEnd = containsEnd ? chunk.content.length + end - chunk.end : chunk.content.length;
  664. result += chunk.content.slice( sliceStart, sliceEnd );
  665. if ( chunk.outro && ( !containsEnd || chunk.end === end ) ) {
  666. result += chunk.outro;
  667. }
  668. if ( containsEnd ) {
  669. break;
  670. }
  671. chunk = chunk.next;
  672. }
  673. return result;
  674. },
  675. // TODO deprecate this? not really very useful
  676. snip: function snip ( start, end ) {
  677. var clone = this.clone();
  678. clone.remove( 0, start );
  679. clone.remove( end, clone.original.length );
  680. return clone;
  681. },
  682. _split: function _split ( index ) {
  683. var this$1 = this;
  684. if ( this.byStart[ index ] || this.byEnd[ index ] ) { return; }
  685. var chunk = this.lastSearchedChunk;
  686. var searchForward = index > chunk.end;
  687. while ( true ) {
  688. if ( chunk.contains( index ) ) { return this$1._splitChunk( chunk, index ); }
  689. chunk = searchForward ?
  690. this$1.byStart[ chunk.end ] :
  691. this$1.byEnd[ chunk.start ];
  692. }
  693. },
  694. _splitChunk: function _splitChunk ( chunk, index ) {
  695. if ( chunk.edited && chunk.content.length ) { // zero-length edited chunks are a special case (overlapping replacements)
  696. var loc = getLocator( this.original )( index );
  697. throw new Error( ("Cannot split a chunk that has already been edited (" + (loc.line) + ":" + (loc.column) + " – \"" + (chunk.original) + "\")") );
  698. }
  699. var newChunk = chunk.split( index );
  700. this.byEnd[ index ] = chunk;
  701. this.byStart[ index ] = newChunk;
  702. this.byEnd[ newChunk.end ] = newChunk;
  703. if ( chunk === this.lastChunk ) { this.lastChunk = newChunk; }
  704. this.lastSearchedChunk = chunk;
  705. return true;
  706. },
  707. toString: function toString () {
  708. var str = this.intro;
  709. var chunk = this.firstChunk;
  710. while ( chunk ) {
  711. str += chunk.toString();
  712. chunk = chunk.next;
  713. }
  714. return str + this.outro;
  715. },
  716. trimLines: function trimLines () {
  717. return this.trim('[\\r\\n]');
  718. },
  719. trim: function trim ( charType ) {
  720. return this.trimStart( charType ).trimEnd( charType );
  721. },
  722. trimEnd: function trimEnd ( charType ) {
  723. var this$1 = this;
  724. var rx = new RegExp( ( charType || '\\s' ) + '+$' );
  725. this.outro = this.outro.replace( rx, '' );
  726. if ( this.outro.length ) { return this; }
  727. var chunk = this.lastChunk;
  728. do {
  729. var end = chunk.end;
  730. var aborted = chunk.trimEnd( rx );
  731. // if chunk was trimmed, we have a new lastChunk
  732. if ( chunk.end !== end ) {
  733. if ( this$1.lastChunk === chunk ) {
  734. this$1.lastChunk = chunk.next;
  735. }
  736. this$1.byEnd[ chunk.end ] = chunk;
  737. this$1.byStart[ chunk.next.start ] = chunk.next;
  738. this$1.byEnd[ chunk.next.end ] = chunk.next;
  739. }
  740. if ( aborted ) { return this$1; }
  741. chunk = chunk.previous;
  742. } while ( chunk );
  743. return this;
  744. },
  745. trimStart: function trimStart ( charType ) {
  746. var this$1 = this;
  747. var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' );
  748. this.intro = this.intro.replace( rx, '' );
  749. if ( this.intro.length ) { return this; }
  750. var chunk = this.firstChunk;
  751. do {
  752. var end = chunk.end;
  753. var aborted = chunk.trimStart( rx );
  754. if ( chunk.end !== end ) {
  755. // special case...
  756. if ( chunk === this$1.lastChunk ) { this$1.lastChunk = chunk.next; }
  757. this$1.byEnd[ chunk.end ] = chunk;
  758. this$1.byStart[ chunk.next.start ] = chunk.next;
  759. this$1.byEnd[ chunk.next.end ] = chunk.next;
  760. }
  761. if ( aborted ) { return this$1; }
  762. chunk = chunk.next;
  763. } while ( chunk );
  764. return this;
  765. }
  766. };
  767. var hasOwnProp = Object.prototype.hasOwnProperty;
  768. function Bundle ( options ) {
  769. if ( options === void 0 ) options = {};
  770. this.intro = options.intro || '';
  771. this.separator = options.separator !== undefined ? options.separator : '\n';
  772. this.sources = [];
  773. this.uniqueSources = [];
  774. this.uniqueSourceIndexByFilename = {};
  775. }
  776. Bundle.prototype = {
  777. addSource: function addSource ( source ) {
  778. if ( source instanceof MagicString$1 ) {
  779. return this.addSource({
  780. content: source,
  781. filename: source.filename,
  782. separator: this.separator
  783. });
  784. }
  785. if ( !isObject( source ) || !source.content ) {
  786. throw new Error( 'bundle.addSource() takes an object with a `content` property, which should be an instance of MagicString, and an optional `filename`' );
  787. }
  788. [ 'filename', 'indentExclusionRanges', 'separator' ].forEach( function (option) {
  789. if ( !hasOwnProp.call( source, option ) ) { source[ option ] = source.content[ option ]; }
  790. });
  791. if ( source.separator === undefined ) { // TODO there's a bunch of this sort of thing, needs cleaning up
  792. source.separator = this.separator;
  793. }
  794. if ( source.filename ) {
  795. if ( !hasOwnProp.call( this.uniqueSourceIndexByFilename, source.filename ) ) {
  796. this.uniqueSourceIndexByFilename[ source.filename ] = this.uniqueSources.length;
  797. this.uniqueSources.push({ filename: source.filename, content: source.content.original });
  798. } else {
  799. var uniqueSource = this.uniqueSources[ this.uniqueSourceIndexByFilename[ source.filename ] ];
  800. if ( source.content.original !== uniqueSource.content ) {
  801. throw new Error( ("Illegal source: same filename (" + (source.filename) + "), different contents") );
  802. }
  803. }
  804. }
  805. this.sources.push( source );
  806. return this;
  807. },
  808. append: function append ( str, options ) {
  809. this.addSource({
  810. content: new MagicString$1( str ),
  811. separator: ( options && options.separator ) || ''
  812. });
  813. return this;
  814. },
  815. clone: function clone () {
  816. var bundle = new Bundle({
  817. intro: this.intro,
  818. separator: this.separator
  819. });
  820. this.sources.forEach( function (source) {
  821. bundle.addSource({
  822. filename: source.filename,
  823. content: source.content.clone(),
  824. separator: source.separator
  825. });
  826. });
  827. return bundle;
  828. },
  829. generateMap: function generateMap ( options ) {
  830. var this$1 = this;
  831. if ( options === void 0 ) options = {};
  832. var names = [];
  833. this.sources.forEach( function (source) {
  834. Object.keys( source.content.storedNames ).forEach( function (name) {
  835. if ( !~names.indexOf( name ) ) { names.push( name ); }
  836. });
  837. });
  838. var mappings = new Mappings( options.hires );
  839. if ( this.intro ) {
  840. mappings.advance( this.intro );
  841. }
  842. this.sources.forEach( function ( source, i ) {
  843. if ( i > 0 ) {
  844. mappings.advance( this$1.separator );
  845. }
  846. var sourceIndex = source.filename ? this$1.uniqueSourceIndexByFilename[ source.filename ] : -1;
  847. var magicString = source.content;
  848. var locate = getLocator( magicString.original );
  849. if ( magicString.intro ) {
  850. mappings.advance( magicString.intro );
  851. }
  852. magicString.firstChunk.eachNext( function (chunk) {
  853. var loc = locate( chunk.start );
  854. if ( chunk.intro.length ) { mappings.advance( chunk.intro ); }
  855. if ( source.filename ) {
  856. if ( chunk.edited ) {
  857. mappings.addEdit( sourceIndex, chunk.content, chunk.original, loc, chunk.storeName ? names.indexOf( chunk.original ) : -1 );
  858. } else {
  859. mappings.addUneditedChunk( sourceIndex, chunk, magicString.original, loc, magicString.sourcemapLocations );
  860. }
  861. }
  862. else {
  863. mappings.advance( chunk.content );
  864. }
  865. if ( chunk.outro.length ) { mappings.advance( chunk.outro ); }
  866. });
  867. if ( magicString.outro ) {
  868. mappings.advance( magicString.outro );
  869. }
  870. });
  871. return new SourceMap({
  872. file: ( options.file ? options.file.split( /[\/\\]/ ).pop() : null ),
  873. sources: this.uniqueSources.map( function (source) {
  874. return options.file ? getRelativePath( options.file, source.filename ) : source.filename;
  875. }),
  876. sourcesContent: this.uniqueSources.map( function (source) {
  877. return options.includeContent ? source.content : null;
  878. }),
  879. names: names,
  880. mappings: mappings.encode()
  881. });
  882. },
  883. getIndentString: function getIndentString () {
  884. var indentStringCounts = {};
  885. this.sources.forEach( function (source) {
  886. var indentStr = source.content.indentStr;
  887. if ( indentStr === null ) { return; }
  888. if ( !indentStringCounts[ indentStr ] ) { indentStringCounts[ indentStr ] = 0; }
  889. indentStringCounts[ indentStr ] += 1;
  890. });
  891. return ( Object.keys( indentStringCounts ).sort( function ( a, b ) {
  892. return indentStringCounts[a] - indentStringCounts[b];
  893. })[0] ) || '\t';
  894. },
  895. indent: function indent ( indentStr ) {
  896. var this$1 = this;
  897. if ( !arguments.length ) {
  898. indentStr = this.getIndentString();
  899. }
  900. if ( indentStr === '' ) { return this; } // noop
  901. var trailingNewline = !this.intro || this.intro.slice( -1 ) === '\n';
  902. this.sources.forEach( function ( source, i ) {
  903. var separator = source.separator !== undefined ? source.separator : this$1.separator;
  904. var indentStart = trailingNewline || ( i > 0 && /\r?\n$/.test( separator ) );
  905. source.content.indent( indentStr, {
  906. exclude: source.indentExclusionRanges,
  907. indentStart: indentStart//: trailingNewline || /\r?\n$/.test( separator ) //true///\r?\n/.test( separator )
  908. });
  909. // TODO this is a very slow way to determine this
  910. trailingNewline = source.content.toString().slice( 0, -1 ) === '\n';
  911. });
  912. if ( this.intro ) {
  913. this.intro = indentStr + this.intro.replace( /^[^\n]/gm, function ( match, index ) {
  914. return index > 0 ? indentStr + match : match;
  915. });
  916. }
  917. return this;
  918. },
  919. prepend: function prepend ( str ) {
  920. this.intro = str + this.intro;
  921. return this;
  922. },
  923. toString: function toString () {
  924. var this$1 = this;
  925. var body = this.sources.map( function ( source, i ) {
  926. var separator = source.separator !== undefined ? source.separator : this$1.separator;
  927. var str = ( i > 0 ? separator : '' ) + source.content.toString();
  928. return str;
  929. }).join( '' );
  930. return this.intro + body;
  931. },
  932. trimLines: function trimLines () {
  933. return this.trim('[\\r\\n]');
  934. },
  935. trim: function trim ( charType ) {
  936. return this.trimStart( charType ).trimEnd( charType );
  937. },
  938. trimStart: function trimStart ( charType ) {
  939. var this$1 = this;
  940. var rx = new RegExp( '^' + ( charType || '\\s' ) + '+' );
  941. this.intro = this.intro.replace( rx, '' );
  942. if ( !this.intro ) {
  943. var source;
  944. var i = 0;
  945. do {
  946. source = this$1.sources[i];
  947. if ( !source ) {
  948. break;
  949. }
  950. source.content.trimStart( charType );
  951. i += 1;
  952. } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source?
  953. }
  954. return this;
  955. },
  956. trimEnd: function trimEnd ( charType ) {
  957. var this$1 = this;
  958. var rx = new RegExp( ( charType || '\\s' ) + '+$' );
  959. var source;
  960. var i = this.sources.length - 1;
  961. do {
  962. source = this$1.sources[i];
  963. if ( !source ) {
  964. this$1.intro = this$1.intro.replace( rx, '' );
  965. break;
  966. }
  967. source.content.trimEnd( charType );
  968. i -= 1;
  969. } while ( source.content.toString() === '' ); // TODO faster way to determine non-empty source?
  970. return this;
  971. }
  972. };
  973. export { Bundle };
  974. export default MagicString$1;
  975. //# sourceMappingURL=magic-string.es.js.map