@@ -31,6 +31,35 @@ const hasStableSort =
3131// This is larger than max string length
3232const MAX_SOURCE_POSITION = 0x20000000 ;
3333
34+ /**
35+ * Stable comparator hoisted to module scope so each `_sortReplacements()`
36+ * call doesn't allocate a fresh closure.
37+ * @param {Replacement } a a
38+ * @param {Replacement } b b
39+ * @returns {number } order
40+ */
41+ const compareStable = ( a , b ) => {
42+ const diff1 = a . start - b . start ;
43+ if ( diff1 !== 0 ) return diff1 ;
44+ const diff2 = a . end - b . end ;
45+ if ( diff2 !== 0 ) return diff2 ;
46+ return 0 ;
47+ } ;
48+
49+ /**
50+ * Index-stabilising comparator for v8 < 7.0 (pre-stable Array.prototype.sort).
51+ * @param {Replacement } a a
52+ * @param {Replacement } b b
53+ * @returns {number } order
54+ */
55+ const compareUnstableFallback = ( a , b ) => {
56+ const diff1 = a . start - b . start ;
57+ if ( diff1 !== 0 ) return diff1 ;
58+ const diff2 = a . end - b . end ;
59+ if ( diff2 !== 0 ) return diff2 ;
60+ return /** @type {number } */ ( a . index ) - /** @type {number } */ ( b . index ) ;
61+ } ;
62+
3463class Replacement {
3564 /**
3665 * @param {number } start start
@@ -124,7 +153,7 @@ class ReplaceSource extends Source {
124153 if ( this . _replacements . length === 0 ) {
125154 return this . _source . source ( ) ;
126155 }
127- let current = this . _source . source ( ) ;
156+ const current = /** @type { string } */ ( this . _source . source ( ) ) ;
128157 let pos = 0 ;
129158 const result = [ ] ;
130159
@@ -133,19 +162,19 @@ class ReplaceSource extends Source {
133162 const start = Math . floor ( replacement . start ) ;
134163 const end = Math . floor ( replacement . end + 1 ) ;
135164 if ( pos < start ) {
136- const offset = start - pos ;
137- result . push ( current . slice ( 0 , offset ) ) ;
138- current = current . slice ( offset ) ;
165+ // slice directly from the original string rather than repeatedly
166+ // producing smaller intermediate strings, which avoids O(n) copies.
167+ result . push ( current . slice ( pos , start ) ) ;
139168 pos = start ;
140169 }
141170 result . push ( replacement . content ) ;
142171 if ( pos < end ) {
143- const offset = end - pos ;
144- current = current . slice ( offset ) ;
145172 pos = end ;
146173 }
147174 }
148- result . push ( current ) ;
175+ if ( pos < current . length ) {
176+ result . push ( pos === 0 ? current : current . slice ( pos ) ) ;
177+ }
149178 return result . join ( "" ) ;
150179 }
151180
@@ -178,24 +207,10 @@ class ReplaceSource extends Source {
178207 _sortReplacements ( ) {
179208 if ( this . _isSorted ) return ;
180209 if ( hasStableSort ) {
181- this . _replacements . sort ( ( a , b ) => {
182- const diff1 = a . start - b . start ;
183- if ( diff1 !== 0 ) return diff1 ;
184- const diff2 = a . end - b . end ;
185- if ( diff2 !== 0 ) return diff2 ;
186- return 0 ;
187- } ) ;
210+ this . _replacements . sort ( compareStable ) ;
188211 } else {
189212 for ( const [ i , repl ] of this . _replacements . entries ( ) ) repl . index = i ;
190- this . _replacements . sort ( ( a , b ) => {
191- const diff1 = a . start - b . start ;
192- if ( diff1 !== 0 ) return diff1 ;
193- const diff2 = a . end - b . end ;
194- if ( diff2 !== 0 ) return diff2 ;
195- return (
196- /** @type {number } */ ( a . index ) - /** @type {number } */ ( b . index )
197- ) ;
198- } ) ;
213+ this . _replacements . sort ( compareUnstableFallback ) ;
199214 }
200215 this . _isSorted = true ;
201216 }
@@ -545,10 +560,14 @@ class ReplaceSource extends Source {
545560 hash . update ( "ReplaceSource" ) ;
546561 this . _source . updateHash ( hash ) ;
547562 hash . update ( this . _name || "" ) ;
563+ // Feed each replacement as multiple updates instead of building one
564+ // combined template literal per replacement. The resulting digest is
565+ // identical (hash.update is additive over bytes), but we avoid
566+ // allocating a new string per replacement.
548567 for ( const repl of this . _replacements ) {
549- hash . update (
550- ` ${ repl . start } ${ repl . end } ${ repl . content } ${ repl . name ? repl . name : "" } ` ,
551- ) ;
568+ hash . update ( ` ${ repl . start } ${ repl . end } ` ) ;
569+ hash . update ( repl . content ) ;
570+ if ( repl . name ) hash . update ( repl . name ) ;
552571 }
553572 }
554573}
0 commit comments