@@ -1222,35 +1222,40 @@ export default class MagicString {
12221222 return replacement ( match [ 0 ] , ...match . slice ( 1 ) , match . index , str , match . groups )
12231223 }
12241224 }
1225- function matchAll ( re : RegExp , str : string ) : RegExpExecArray [ ] {
1226- const matches = [ ]
1227- while ( true ) {
1228- const match = re . exec ( str )
1229- if ( ! match )
1230- break
1231-
1232- matches . push ( match )
1225+ const replaceMatch = ( match : RegExpMatchArray ) : void => {
1226+ if ( match . index == null )
1227+ return
1228+
1229+ const replacement = getReplacement ( match , this . original )
1230+ if ( replacement === match [ 0 ] )
1231+ return
1232+
1233+ if ( match [ 0 ] . length === 0 ) {
1234+ // a zero-length match spans no characters, so there is no range to
1235+ // overwrite - the replacement is an insertion at the matched position,
1236+ // which is what `String.prototype.replace` does for an empty match
1237+ this . appendRight ( match . index , replacement )
1238+ }
1239+ else {
1240+ this . overwrite ( match . index , match . index + match [ 0 ] . length , replacement )
12331241 }
1234- return matches
12351242 }
1243+
12361244 if ( searchValue . global ) {
1237- const matches = matchAll ( searchValue , this . original )
1238- matches . forEach ( ( match ) => {
1239- if ( match . index != null ) {
1240- const replacement = getReplacement ( match , this . original )
1241- if ( replacement !== match [ 0 ] ) {
1242- this . overwrite ( match . index , match . index + match [ 0 ] . length , replacement )
1243- }
1244- }
1245- } )
1245+ // `String.prototype.replace` starts a global regexp from the beginning of
1246+ // the string, so reset `lastIndex` - a regexp that has already been used
1247+ // would otherwise resume from wherever it stopped and skip earlier matches.
1248+ // `matchAll` also steps over a zero-length match, where `exec` in a loop
1249+ // would keep rematching it at an unmoving `lastIndex` and never terminate.
1250+ searchValue . lastIndex = 0
1251+ for ( const match of this . original . matchAll ( searchValue ) ) {
1252+ replaceMatch ( match )
1253+ }
12461254 }
12471255 else {
12481256 const match = this . original . match ( searchValue )
1249- if ( match && match . index != null ) {
1250- const replacement = getReplacement ( match , this . original )
1251- if ( replacement !== match [ 0 ] ) {
1252- this . overwrite ( match . index , match . index + match [ 0 ] . length , replacement )
1253- }
1257+ if ( match ) {
1258+ replaceMatch ( match )
12541259 }
12551260 }
12561261 return this
@@ -1266,7 +1271,15 @@ export default class MagicString {
12661271 replacement = replacement ( string , index , original )
12671272 }
12681273 if ( string !== replacement ) {
1269- this . overwrite ( index , index + string . length , replacement )
1274+ if ( string . length === 0 ) {
1275+ // an empty search string matches the empty range at the start of the
1276+ // string, which has no characters to overwrite - the replacement is an
1277+ // insertion there, as it is for `String.prototype.replace`
1278+ this . appendRight ( index , replacement )
1279+ }
1280+ else {
1281+ this . overwrite ( index , index + string . length , replacement )
1282+ }
12701283 }
12711284 }
12721285
@@ -1288,6 +1301,21 @@ export default class MagicString {
12881301 _replaceAllString ( string : string , replacement : string | ReplacementFunction ) : this {
12891302 const { original } = this
12901303 const stringLength = string . length
1304+
1305+ // an empty search string matches the empty range before every character plus
1306+ // one at the end, and `indexOf` clamps its start index to the string length,
1307+ // so it can neither find those ranges nor ever report -1 - step through them
1308+ if ( stringLength === 0 ) {
1309+ for ( let index = 0 ; index <= original . length ; index += 1 ) {
1310+ const _replacement
1311+ = typeof replacement === 'function' ? replacement ( '' , index , original ) : replacement
1312+ if ( _replacement !== '' )
1313+ this . appendRight ( index , _replacement )
1314+ }
1315+
1316+ return this
1317+ }
1318+
12911319 for (
12921320 let index = original . indexOf ( string ) ;
12931321 index !== - 1 ;
0 commit comments