@@ -50,21 +50,27 @@ export class SourceFile {
5050 * Render the raw source map generated from the flattened mappings.
5151 */
5252 renderFlattenedSourceMap ( ) : RawSourceMap {
53- const sources : SourceFile [ ] = [ ] ;
54- const names : string [ ] = [ ] ;
55-
53+ const sources = new IndexedMap < string , string > ( ) ;
54+ const names = new IndexedSet < string > ( ) ;
5655 const mappings : SourceMapMappings = [ ] ;
56+ const sourcePathDir = this . fs . dirname ( this . sourcePath ) ;
57+ // Computing the relative path can be expensive, and we are likely to have the same path for
58+ // many (if not all!) mappings.
59+ const relativeSourcePathCache =
60+ new Cache < string , string > ( input => this . fs . relative ( sourcePathDir , input ) ) ;
5761
5862 for ( const mapping of this . flattenedMappings ) {
59- const sourceIndex = findIndexOrAdd ( sources , mapping . originalSource ) ;
63+ const sourceIndex = sources . set (
64+ relativeSourcePathCache . get ( mapping . originalSource . sourcePath ) ,
65+ mapping . originalSource . contents ) ;
6066 const mappingArray : SourceMapSegment = [
6167 mapping . generatedSegment . column ,
6268 sourceIndex ,
6369 mapping . originalSegment . line ,
6470 mapping . originalSegment . column ,
6571 ] ;
6672 if ( mapping . name !== undefined ) {
67- const nameIndex = findIndexOrAdd ( names , mapping . name ) ;
73+ const nameIndex = names . add ( mapping . name ) ;
6874 mappingArray . push ( nameIndex ) ;
6975 }
7076
@@ -77,14 +83,13 @@ export class SourceFile {
7783 mappings [ line ] . push ( mappingArray ) ;
7884 }
7985
80- const sourcePathDir = this . fs . dirname ( this . sourcePath ) ;
8186 const sourceMap : RawSourceMap = {
8287 version : 3 ,
8388 file : this . fs . relative ( sourcePathDir , this . sourcePath ) ,
84- sources : sources . map ( sf => this . fs . relative ( sourcePathDir , sf . sourcePath ) ) ,
85- names,
89+ sources : sources . keys ,
90+ names : names . values ,
8691 mappings : encode ( mappings ) ,
87- sourcesContent : sources . map ( sf => sf . contents ) ,
92+ sourcesContent : sources . values ,
8893 } ;
8994 return sourceMap ;
9095 }
@@ -259,23 +264,6 @@ export interface Mapping {
259264 readonly name ?: string ;
260265}
261266
262- /**
263- * Find the index of `item` in the `items` array.
264- * If it is not found, then push `item` to the end of the array and return its new index.
265- *
266- * @param items the collection in which to look for `item`.
267- * @param item the item to look for.
268- * @returns the index of the `item` in the `items` array.
269- */
270- function findIndexOrAdd < T > ( items : T [ ] , item : T ) : number {
271- const itemIndex = items . indexOf ( item ) ;
272- if ( itemIndex > - 1 ) {
273- return itemIndex ;
274- } else {
275- items . push ( item ) ;
276- return items . length - 1 ;
277- }
278- }
279267
280268
281269/**
@@ -448,3 +436,96 @@ export function computeStartOfLinePositions(str: string) {
448436function computeLineLengths ( str : string ) : number [ ] {
449437 return ( str . split ( / \n / ) ) . map ( s => s . length ) ;
450438}
439+
440+ /**
441+ * A collection of mappings between `keys` and `values` stored in the order in which the keys are
442+ * first seen.
443+ *
444+ * The difference between this and a standard `Map` is that when you add a key-value pair the index
445+ * of the `key` is returned.
446+ */
447+ class IndexedMap < K , V > {
448+ private map = new Map < K , number > ( ) ;
449+
450+ /**
451+ * An array of keys added to this map.
452+ *
453+ * This array is guaranteed to be in the order of the first time the key was added to the map.
454+ */
455+ readonly keys : K [ ] = [ ] ;
456+
457+ /**
458+ * An array of values added to this map.
459+ *
460+ * This array is guaranteed to be in the order of the first time the associated key was added to
461+ * the map.
462+ */
463+ readonly values : V [ ] = [ ] ;
464+
465+ /**
466+ * Associate the `value` with the `key` and return the index of the key in the collection.
467+ *
468+ * If the `key` already exists then the `value` is not set and the index of that `key` is
469+ * returned; otherwise the `key` and `value` are stored and the index of the new `key` is
470+ * returned.
471+ *
472+ * @param key the key to associated with the `value`.
473+ * @param value the value to associated with the `key`.
474+ * @returns the index of the `key` in the `keys` array.
475+ */
476+ set ( key : K , value : V ) : number {
477+ if ( this . map . has ( key ) ) {
478+ return this . map . get ( key ) ! ;
479+ }
480+ const index = this . values . push ( value ) - 1 ;
481+ this . keys . push ( key ) ;
482+ this . map . set ( key , index ) ;
483+ return index ;
484+ }
485+ }
486+
487+ /**
488+ * A collection of `values` stored in the order in which they were added.
489+ *
490+ * The difference between this and a standard `Set` is that when you add a value the index of that
491+ * item is returned.
492+ */
493+ class IndexedSet < V > {
494+ private map = new Map < V , number > ( ) ;
495+
496+ /**
497+ * An array of values added to this set.
498+ * This array is guaranteed to be in the order of the first time the value was added to the set.
499+ */
500+ readonly values : V [ ] = [ ] ;
501+
502+ /**
503+ * Add the `value` to the `values` array, if it doesn't already exist; returning the index of the
504+ * `value` in the `values` array.
505+ *
506+ * If the `value` already exists then the index of that `value` is returned, otherwise the new
507+ * `value` is stored and the new index returned.
508+ *
509+ * @param value the value to add to the set.
510+ * @returns the index of the `value` in the `values` array.
511+ */
512+ add ( value : V ) : number {
513+ if ( this . map . has ( value ) ) {
514+ return this . map . get ( value ) ! ;
515+ }
516+ const index = this . values . push ( value ) - 1 ;
517+ this . map . set ( value , index ) ;
518+ return index ;
519+ }
520+ }
521+
522+ class Cache < Input , Cached > {
523+ private map = new Map < Input , Cached > ( ) ;
524+ constructor ( private computeFn : ( input : Input ) => Cached ) { }
525+ get ( input : Input ) : Cached {
526+ if ( ! this . map . has ( input ) ) {
527+ this . map . set ( input , this . computeFn ( input ) ) ;
528+ }
529+ return this . map . get ( input ) ! ;
530+ }
531+ }
0 commit comments