@internal
(searchValue: RegExp, replacement: string | ReplacementFunction)
| 1448 | |
| 1449 | /** @internal */ |
| 1450 | _replaceRegexp(searchValue: RegExp, replacement: string | ReplacementFunction): this { |
| 1451 | function getReplacement(match: RegExpMatchArray, str: string): string { |
| 1452 | if (typeof replacement === 'string') { |
| 1453 | return expandReplacement( |
| 1454 | replacement, |
| 1455 | match[0], |
| 1456 | match.index, |
| 1457 | str, |
| 1458 | match.slice(1), |
| 1459 | match.groups, |
| 1460 | ) |
| 1461 | } |
| 1462 | else { |
| 1463 | // `String.prototype.replace` only passes the named-capture-groups object |
| 1464 | // when the pattern actually has named groups - passing an `undefined` |
| 1465 | // there unconditionally shifts the last argument a replacer sees |
| 1466 | return match.groups === undefined |
| 1467 | ? replacement(match[0], ...match.slice(1), match.index, str) |
| 1468 | : replacement(match[0], ...match.slice(1), match.index, str, match.groups) |
| 1469 | } |
| 1470 | } |
| 1471 | // Returns true once a surviving match has been handled, so a non-global |
| 1472 | // search can stop at the first match still present in the output. |
| 1473 | const replaceMatch = (match: RegExpMatchArray): boolean => { |
| 1474 | /* v8 ignore next 2 -- `match.index` is always defined for matches from `matchAll` */ |
| 1475 | if (match.index == null) |
| 1476 | return false |
| 1477 | |
| 1478 | if (this._hasRemovedContent(match.index, match.index + match[0].length)) |
| 1479 | return false |
| 1480 | |
| 1481 | const replacement = getReplacement(match, this.original) |
| 1482 | if (replacement === match[0]) |
| 1483 | return true |
| 1484 | |
| 1485 | if (match[0].length === 0) { |
| 1486 | // a zero-length match spans no characters, so there is no range to |
| 1487 | // overwrite - the replacement is an insertion at the matched position, |
| 1488 | // which is what `String.prototype.replace` does for an empty match |
| 1489 | this.appendRight(match.index, replacement) |
| 1490 | } |
| 1491 | else { |
| 1492 | this.overwrite(match.index, match.index + match[0].length, replacement) |
| 1493 | } |
| 1494 | return true |
| 1495 | } |
| 1496 | |
| 1497 | if (searchValue.global) { |
| 1498 | // `String.prototype.replace` starts a global regexp from the beginning of |
| 1499 | // the string, so reset `lastIndex` - a regexp that has already been used |
| 1500 | // would otherwise resume from wherever it stopped and skip earlier matches. |
| 1501 | // `matchAll` also steps over a zero-length match, where `exec` in a loop |
| 1502 | // would keep rematching it at an unmoving `lastIndex` and never terminate. |
| 1503 | searchValue.lastIndex = 0 |
| 1504 | for (const match of this.original.matchAll(searchValue)) { |
| 1505 | replaceMatch(match) |
| 1506 | } |
| 1507 | } |
no test coverage detected