@@ -4,6 +4,7 @@ type MappingSegment =
4
4
| [ number , number , number , number , number ] ;
5
5
6
6
type SourceMappings = {
7
+ version : number ;
7
8
sources : string [ ] ;
8
9
names : string [ ] ;
9
10
mappings : MappingSegment [ ] [ ] ;
@@ -18,30 +19,28 @@ function last_line_length(s: string) {
18
19
return s . length - s . lastIndexOf ( '\n' ) - 1 ;
19
20
}
20
21
22
+ // mutate map in-place
21
23
export function sourcemap_add_offset (
22
24
map : SourceMappings , offset : SourceLocation
23
- ) : SourceMappings {
24
- return {
25
- sources : map . sources . slice ( ) ,
26
- mappings : map . mappings . map ( ( line , line_idx ) =>
27
- line . map ( seg => {
28
- const new_seg = seg . slice ( ) as MappingSegment ;
29
- if ( seg . length >= 4 ) {
30
- new_seg [ 2 ] = new_seg [ 2 ] + offset . line ;
31
- if ( line_idx == 0 )
32
- new_seg [ 3 ] = new_seg [ 3 ] + offset . column ;
33
- }
34
- return new_seg ;
35
- } )
36
- )
37
- } as SourceMappings ;
25
+ ) {
26
+ // shift columns in first line
27
+ const m = map . mappings as any ;
28
+ m [ 0 ] . forEach ( seg => {
29
+ if ( seg [ 3 ] ) seg [ 3 ] += offset . column ;
30
+ } ) ;
31
+ // shift lines
32
+ m . forEach ( line => {
33
+ line . forEach ( seg => {
34
+ if ( seg [ 2 ] ) seg [ 2 ] += offset . line ;
35
+ } ) ;
36
+ } ) ;
38
37
}
39
38
40
- function merge_tables < T > ( this_table : T [ ] , other_table ) : [ T [ ] , number [ ] , boolean ] {
39
+ function merge_tables < T > ( this_table : T [ ] , other_table ) : [ T [ ] , number [ ] , boolean , boolean ] {
41
40
const new_table = this_table . slice ( ) ;
42
41
const idx_map = [ ] ;
43
42
other_table = other_table || [ ] ;
44
- let has_changed = false ;
43
+ let val_changed = false ;
45
44
for ( const [ other_idx , other_val ] of other_table . entries ( ) ) {
46
45
const this_idx = this_table . indexOf ( other_val ) ;
47
46
if ( this_idx >= 0 ) {
@@ -50,47 +49,85 @@ function merge_tables<T>(this_table: T[], other_table): [T[], number[], boolean]
50
49
const new_idx = new_table . length ;
51
50
new_table [ new_idx ] = other_val ;
52
51
idx_map [ other_idx ] = new_idx ;
53
- has_changed = true ;
52
+ val_changed = true ;
54
53
}
55
54
}
56
- if ( has_changed ) {
55
+ let idx_changed = val_changed ;
56
+ if ( val_changed ) {
57
57
if ( idx_map . find ( ( val , idx ) => val != idx ) === undefined ) {
58
58
// idx_map is identity map [0, 1, 2, 3, 4, ....]
59
- has_changed = false ;
59
+ idx_changed = false ;
60
60
}
61
61
}
62
- return [ new_table , idx_map , has_changed ] ;
62
+ return [ new_table , idx_map , val_changed , idx_changed ] ;
63
+ }
64
+
65
+ function pushArray < T > ( _this : T [ ] , other : T [ ] ) {
66
+ for ( let i = 0 ; i < other . length ; i ++ )
67
+ _this . push ( other [ i ] ) ;
63
68
}
64
69
65
70
export class StringWithSourcemap {
66
- readonly string : string ;
67
- readonly map : SourceMappings ;
71
+ string : string ;
72
+ map : SourceMappings ;
68
73
69
- constructor ( string : string , map : SourceMappings ) {
74
+ constructor ( string = '' , map = null ) {
70
75
this . string = string ;
71
- this . map = map ;
76
+ if ( map )
77
+ this . map = map as SourceMappings ;
78
+ else
79
+ this . map = {
80
+ version : 3 ,
81
+ mappings : [ ] ,
82
+ sources : [ ] ,
83
+ names : [ ]
84
+ } ;
72
85
}
73
86
87
+ // concat in-place (mutable), return this (chainable)
88
+ // will also mutate the `other` object
74
89
concat ( other : StringWithSourcemap ) : StringWithSourcemap {
75
90
// noop: if one is empty, return the other
76
- if ( this . string == '' ) return other ;
77
91
if ( other . string == '' ) return this ;
92
+ if ( this . string == '' ) {
93
+ this . string = other . string ;
94
+ this . map = other . map ;
95
+ return this ;
96
+ }
97
+
98
+ this . string += other . string ;
99
+
100
+ const m1 = this . map as any ;
101
+ const m2 = other . map as any ;
78
102
79
103
// combine sources and names
80
- const [ sources , new_source_idx , sources_changed ] = merge_tables ( this . map . sources , other . map . sources ) ;
81
- const [ names , new_name_idx , names_changed ] = merge_tables ( this . map . names , other . map . names ) ;
82
-
83
- // update source refs and name refs
84
- const other_mappings =
85
- ( sources_changed || names_changed )
86
- ? other . map . mappings . slice ( ) . map ( line =>
87
- line . map ( seg => {
88
- if ( seg [ 1 ] ) seg [ 1 ] = new_source_idx [ seg [ 1 ] ] ;
89
- if ( seg [ 4 ] ) seg [ 4 ] = new_name_idx [ seg [ 4 ] ] ;
90
- return seg ;
91
- } )
92
- )
93
- : other . map . mappings ;
104
+ const [ sources , new_source_idx , sources_changed , sources_idx_changed ] = merge_tables ( m1 . sources , m2 . sources ) ;
105
+ const [ names , new_name_idx , names_changed , names_idx_changed ] = merge_tables ( m1 . names , m2 . names ) ;
106
+
107
+ if ( sources_changed ) m1 . sources = sources ;
108
+ if ( names_changed ) m1 . names = names ;
109
+
110
+ // unswitched loops are faster
111
+ if ( sources_idx_changed && names_idx_changed ) {
112
+ m2 . forEach ( line => {
113
+ line . forEach ( seg => {
114
+ if ( seg [ 1 ] ) seg [ 1 ] = new_source_idx [ seg [ 1 ] ] ;
115
+ if ( seg [ 4 ] ) seg [ 4 ] = new_name_idx [ seg [ 4 ] ] ;
116
+ } ) ;
117
+ } ) ;
118
+ } else if ( sources_idx_changed ) {
119
+ m2 . forEach ( line => {
120
+ line . forEach ( seg => {
121
+ if ( seg [ 1 ] ) seg [ 1 ] = new_source_idx [ seg [ 1 ] ] ;
122
+ } ) ;
123
+ } ) ;
124
+ } else if ( names_idx_changed ) {
125
+ m2 . forEach ( line => {
126
+ line . forEach ( seg => {
127
+ if ( seg [ 4 ] ) seg [ 4 ] = new_name_idx [ seg [ 4 ] ] ;
128
+ } ) ;
129
+ } ) ;
130
+ }
94
131
95
132
// combine the mappings
96
133
@@ -100,35 +137,25 @@ export class StringWithSourcemap {
100
137
// columns of 2 must be shifted
101
138
102
139
const column_offset = last_line_length ( this . string ) ;
140
+ if ( m2 . length > 0 && column_offset > 0 ) {
141
+ // shift columns in first line
142
+ m2 [ 0 ] . forEach ( seg => {
143
+ seg [ 0 ] += column_offset ;
144
+ } ) ;
145
+ }
146
+
147
+ // combine last line + first line
148
+ pushArray ( m1 . mappings [ m1 . mappings . length - 1 ] , m2 . mappings . shift ( ) ) ;
149
+
150
+ // append other lines
151
+ pushArray ( m1 . mappings , m2 . mappings ) ;
103
152
104
- const first_line : MappingSegment [ ] =
105
- other_mappings . length == 0
106
- ? [ ]
107
- : column_offset == 0
108
- ? other_mappings [ 0 ] . slice ( ) as MappingSegment [ ]
109
- : other_mappings [ 0 ] . slice ( ) . map ( seg => {
110
- // shift column
111
- seg [ 0 ] += column_offset ;
112
- return seg ;
113
- } ) ;
114
-
115
- const mappings : MappingSegment [ ] [ ] =
116
- this . map . mappings . slice ( 0 , - 1 )
117
- . concat ( [
118
- this . map . mappings . slice ( - 1 ) [ 0 ] // last line
119
- . concat ( first_line )
120
- ] )
121
- . concat ( other_mappings . slice ( 1 ) as MappingSegment [ ] [ ] ) ;
122
-
123
- return new StringWithSourcemap (
124
- this . string + other . string ,
125
- { sources, names, mappings }
126
- ) ;
153
+ return this ;
127
154
}
128
155
129
156
static from_processed ( string : string , map ?: SourceMappings ) : StringWithSourcemap {
130
157
if ( map ) return new StringWithSourcemap ( string , map ) ;
131
- map = { names : [ ] , sources : [ ] , mappings : [ ] } ;
158
+ map = { version : 3 , names : [ ] , sources : [ ] , mappings : [ ] } ;
132
159
if ( string == '' ) return new StringWithSourcemap ( string , map ) ;
133
160
// add empty MappingSegment[] for every line
134
161
const lineCount = string . split ( '\n' ) . length ;
@@ -140,7 +167,7 @@ export class StringWithSourcemap {
140
167
source_file : string , source : string , offset_in_source ?: SourceLocation
141
168
) : StringWithSourcemap {
142
169
const offset = offset_in_source || { line : 0 , column : 0 } ;
143
- const map : SourceMappings = { names : [ ] , sources : [ source_file ] , mappings : [ ] } ;
170
+ const map : SourceMappings = { version : 3 , names : [ ] , sources : [ source_file ] , mappings : [ ] } ;
144
171
if ( source . length == 0 ) return new StringWithSourcemap ( source , map ) ;
145
172
146
173
// we create a high resolution identity map here,
0 commit comments