UNPKG

5.67 kBJavaScriptView Raw
1'use strict';
2var apply = require('../internals/function-apply');
3var call = require('../internals/function-call');
4var uncurryThis = require('../internals/function-uncurry-this');
5var fixRegExpWellKnownSymbolLogic = require('../internals/fix-regexp-well-known-symbol-logic');
6var fails = require('../internals/fails');
7var anObject = require('../internals/an-object');
8var isCallable = require('../internals/is-callable');
9var isNullOrUndefined = require('../internals/is-null-or-undefined');
10var toIntegerOrInfinity = require('../internals/to-integer-or-infinity');
11var toLength = require('../internals/to-length');
12var toString = require('../internals/to-string');
13var requireObjectCoercible = require('../internals/require-object-coercible');
14var advanceStringIndex = require('../internals/advance-string-index');
15var getMethod = require('../internals/get-method');
16var getSubstitution = require('../internals/get-substitution');
17var regExpExec = require('../internals/regexp-exec-abstract');
18var wellKnownSymbol = require('../internals/well-known-symbol');
19
20var REPLACE = wellKnownSymbol('replace');
21var max = Math.max;
22var min = Math.min;
23var concat = uncurryThis([].concat);
24var push = uncurryThis([].push);
25var stringIndexOf = uncurryThis(''.indexOf);
26var stringSlice = uncurryThis(''.slice);
27
28var maybeToString = function (it) {
29 return it === undefined ? it : String(it);
30};
31
32// IE <= 11 replaces $0 with the whole match, as if it was $&
33// https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0
34var REPLACE_KEEPS_$0 = (function () {
35 // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing
36 return 'a'.replace(/./, '$0') === '$0';
37})();
38
39// Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string
40var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () {
41 if (/./[REPLACE]) {
42 return /./[REPLACE]('a', '$0') === '';
43 }
44 return false;
45})();
46
47var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
48 var re = /./;
49 re.exec = function () {
50 var result = [];
51 result.groups = { a: '7' };
52 return result;
53 };
54 // eslint-disable-next-line regexp/no-useless-dollar-replacements -- false positive
55 return ''.replace(re, '$<a>') !== '7';
56});
57
58// @@replace logic
59fixRegExpWellKnownSymbolLogic('replace', function (_, nativeReplace, maybeCallNative) {
60 var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0';
61
62 return [
63 // `String.prototype.replace` method
64 // https://tc39.es/ecma262/#sec-string.prototype.replace
65 function replace(searchValue, replaceValue) {
66 var O = requireObjectCoercible(this);
67 var replacer = isNullOrUndefined(searchValue) ? undefined : getMethod(searchValue, REPLACE);
68 return replacer
69 ? call(replacer, searchValue, O, replaceValue)
70 : call(nativeReplace, toString(O), searchValue, replaceValue);
71 },
72 // `RegExp.prototype[@@replace]` method
73 // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
74 function (string, replaceValue) {
75 var rx = anObject(this);
76 var S = toString(string);
77
78 if (
79 typeof replaceValue == 'string' &&
80 stringIndexOf(replaceValue, UNSAFE_SUBSTITUTE) === -1 &&
81 stringIndexOf(replaceValue, '$<') === -1
82 ) {
83 var res = maybeCallNative(nativeReplace, rx, S, replaceValue);
84 if (res.done) return res.value;
85 }
86
87 var functionalReplace = isCallable(replaceValue);
88 if (!functionalReplace) replaceValue = toString(replaceValue);
89
90 var global = rx.global;
91 var fullUnicode;
92 if (global) {
93 fullUnicode = rx.unicode;
94 rx.lastIndex = 0;
95 }
96
97 var results = [];
98 var result;
99 while (true) {
100 result = regExpExec(rx, S);
101 if (result === null) break;
102
103 push(results, result);
104 if (!global) break;
105
106 var matchStr = toString(result[0]);
107 if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
108 }
109
110 var accumulatedResult = '';
111 var nextSourcePosition = 0;
112 for (var i = 0; i < results.length; i++) {
113 result = results[i];
114
115 var matched = toString(result[0]);
116 var position = max(min(toIntegerOrInfinity(result.index), S.length), 0);
117 var captures = [];
118 var replacement;
119 // NOTE: This is equivalent to
120 // captures = result.slice(1).map(maybeToString)
121 // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
122 // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
123 // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
124 for (var j = 1; j < result.length; j++) push(captures, maybeToString(result[j]));
125 var namedCaptures = result.groups;
126 if (functionalReplace) {
127 var replacerArgs = concat([matched], captures, position, S);
128 if (namedCaptures !== undefined) push(replacerArgs, namedCaptures);
129 replacement = toString(apply(replaceValue, undefined, replacerArgs));
130 } else {
131 replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
132 }
133 if (position >= nextSourcePosition) {
134 accumulatedResult += stringSlice(S, nextSourcePosition, position) + replacement;
135 nextSourcePosition = position + matched.length;
136 }
137 }
138
139 return accumulatedResult + stringSlice(S, nextSourcePosition);
140 }
141 ];
142}, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE);