Skip to content

Commit

Permalink
Adds String.prototype.matchAll polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
mhassan1 authored and JakeChampion committed Oct 26, 2021
1 parent 65b6a73 commit cdf20a6
Show file tree
Hide file tree
Showing 18 changed files with 663 additions and 0 deletions.
35 changes: 35 additions & 0 deletions polyfills/RegExp/prototype/@@matchAll/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
aliases = [ "es2020" ]
dependencies = [
"_ESAbstract.Construct",
"_ESAbstract.CreateMethodProperty",
"_ESAbstract.CreateRegExpStringIterator",
"_ESAbstract.Get",
"_ESAbstract.SpeciesConstructor",
"_ESAbstract.ToLength",
"_ESAbstract.ToString",
"_ESAbstract.Type",
"Object.defineProperty",
"RegExp.prototype.flags",
"Symbol.matchAll",
]
license = "MIT"
spec = "https://tc39.es/ecma262/#sec-regexp-prototype-matchall"
docs = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll"

[browsers]
android = "*"
bb = "*"
chrome = "<73"
edge = "<79"
edge_mob = "*"
firefox = "<67"
firefox_mob = "<67"
ie = "*"
ie_mob = "*"
ios_chr = "<73"
ios_saf = "<13"
op_mini = "*"
op_mob = "*"
opera = "<60"
safari = "<13"
samsung_mob = "*"
1 change: 1 addition & 0 deletions polyfills/RegExp/prototype/@@matchAll/detect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'Symbol' in self && 'matchAll' in self.Symbol && !!RegExp.prototype[self.Symbol.matchAll]
54 changes: 54 additions & 0 deletions polyfills/RegExp/prototype/@@matchAll/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* global Construct, CreateMethodProperty, CreateRegExpStringIterator, Get, SpeciesConstructor, Symbol, ToLength, ToString, Type */

var supportsRegexpLiteralConstructorWithFlags = (function () {
try {
new RegExp(/x/, 'g')
return true
} catch (ignore) {
return false
}
})();

// 22.2.5.8 RegExp.prototype [ @@matchAll ] ( string )
CreateMethodProperty(RegExp.prototype, Symbol.matchAll, function (string) {
'use strict';
// 1. Let R be the this value.
var R = this;
// 2. If Type(R) is not Object, throw a TypeError exception.
if (Type(R) !== 'object') {
throw new TypeError('Method called on incompatible type: must be an object.');
}
// 3. Let S be ? ToString(string).
var S = ToString(string);
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
var C = SpeciesConstructor(R, RegExp);
// 5. Let flags be ? ToString(? Get(R, "flags")).
var flags = ToString(Get(R, 'flags'));
// IE8 doesn't have RegExp.prototype.flags support
if (!('flags' in RegExp.prototype)) {
flags = '';
if (R.global === true) {
flags += 'g';
}
if (R.ignoreCase === true) {
flags += 'i';
}
if (R.multiline === true) {
flags += 'm';
}
}
// 6. Let matcher be ? Construct(C, « R, flags »).
var matcher = Construct(C, [ supportsRegexpLiteralConstructorWithFlags ? R : R.source, flags ]);
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
var lastIndex = ToLength(Get(R, 'lastIndex'));
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
matcher.lastIndex = lastIndex;
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
var global = flags.indexOf('g') > -1;
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
var fullUnicode = flags.indexOf('u') > -1;
// 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
return CreateRegExpStringIterator(matcher, S, global, fullUnicode);
});
109 changes: 109 additions & 0 deletions polyfills/RegExp/prototype/@@matchAll/tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* eslint-env mocha, browser */
/* globals proclaim, Symbol */

var supportsUnicodeRegexpFlag = (function () {
try {
new RegExp('\uD83D', 'u');
return true;
} catch (ignore) {
return false;
}
})();

describe('RegExp.prototype[@@matchAll]', function () {
it('is a function', function () {
proclaim.isFunction(RegExp.prototype[Symbol.matchAll]);
});

it('has correct arity', function () {
proclaim.arity(RegExp.prototype[Symbol.matchAll], 1);
});

it('has correct name', function () {
try {
proclaim.hasName(RegExp.prototype[Symbol.matchAll], '[Symbol.matchAll]');
} catch (ignore) {
// older browsers do not set `name` for symbols
proclaim.hasName(RegExp.prototype[Symbol.matchAll], '');
}
});

it('is not enumerable', function () {
proclaim.isNotEnumerable(RegExp.prototype, Symbol.matchAll);
});

it("matches for a regex", function () {
var iterator = (/t(e)(st(\d?))/)[Symbol.matchAll]('test1test2');
proclaim.isInstanceOf(iterator.next, Function);

var expected = ['test1', 'e', 'st1', '1'];
expected.groups = undefined;
expected.index = 0;
expected.input = 'test1test2';
expected.length = 4;
proclaim.deepStrictEqual(iterator.next(), {
value: expected,
done: false
});

proclaim.deepStrictEqual(iterator.next(), {
value: undefined,
done: true
});

proclaim.deepStrictEqual(iterator.next(), {
value: undefined,
done: true
});
});

it("matches for a global regex", function () {
var iterator = (/t(e)(st(\d?))/g)[Symbol.matchAll]('test1test2');
var expected = ['test1', 'e', 'st1', '1'];
expected.groups = undefined;
expected.index = 0;
expected.input = 'test1test2';
expected.length = 4;
proclaim.deepStrictEqual(iterator.next(), {
value: expected,
done: false
});

expected = ['test2', 'e', 'st2', '2'];
expected.groups = undefined;
expected.index = 5;
expected.input = 'test1test2';
expected.length = 4;
proclaim.deepStrictEqual(iterator.next(), {
value: expected,
done: false
});

proclaim.deepStrictEqual(iterator.next(), {
value: undefined,
done: true
});
});

if (supportsUnicodeRegexpFlag) {
it("matches for a unicode regex", function () {
var regexp = new RegExp('\uD83D', 'ug');
var iterator = regexp[Symbol.matchAll]('\uD83D \uDC2B');

var expected = ['\uD83D'];
expected.groups = undefined;
expected.index = 0;
expected.input = '\uD83D \uDC2B';
expected.length = 1;
proclaim.deepStrictEqual(iterator.next(), {
value: expected,
done: false
});

proclaim.deepStrictEqual(iterator.next(), {
value: undefined,
done: true
});
});
}
});
34 changes: 34 additions & 0 deletions polyfills/String/prototype/matchAll/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
aliases = [ "es2020" ]
dependencies = [
"_ESAbstract.Call",
"_ESAbstract.CreateMethodProperty",
"_ESAbstract.Get",
"_ESAbstract.GetMethod",
"_ESAbstract.Invoke",
"_ESAbstract.IsRegExp",
"_ESAbstract.RequireObjectCoercible",
"_ESAbstract.ToString",
"RegExp.prototype.@@matchAll",
"RegExp.prototype.flags",
"Symbol.matchAll",
]
license = "MIT"
spec = "https://tc39.es/ecma262/#sec-string.prototype.matchall"
docs = "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll"

[browsers]
android = "*"
bb = "*"
chrome = "<73"
edge = "<79"
edge_mob = "<79"
firefox = "<67"
ios_chr = "<13"
ios_saf = "<13"
ie = "*"
ie_mob = "*"
opera = "<60"
op_mini = "*"
safari = "<13"
firefox_mob = "<67"
samsung_mob = "*"
1 change: 1 addition & 0 deletions polyfills/String/prototype/matchAll/detect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
'matchAll' in String.prototype
44 changes: 44 additions & 0 deletions polyfills/String/prototype/matchAll/polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* global Call, CreateMethodProperty, Get, GetMethod, Invoke, IsRegExp, RequireObjectCoercible, ToString */

// 22.1.3.13 String.prototype.matchAll ( regexp )
CreateMethodProperty(String.prototype, 'matchAll', function matchAll(regexp) {
'use strict';
// 1. Let O be ? RequireObjectCoercible(this value).
var O = RequireObjectCoercible(this);
// 2. If regexp is neither undefined nor null, then
if (regexp !== undefined && regexp !== null) {
// 2.a. Let isRegExp be ? IsRegExp(regexp).
var isRegExp = IsRegExp(regexp);
// 2.b. If isRegExp is true, then
if (isRegExp) {
// 2.b.i. Let flags be ? Get(regexp, "flags").
var flags = Get(regexp, "flags");

// IE8 doesn't have RegExp.prototype.flags support, it does have RegExp.prototype.global
// 2.b.iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
if (!('flags' in RegExp.prototype) && regexp.global !== true) {
throw TypeError('');
} else if ('flags' in RegExp.prototype) {
// 2.b.ii. Perform ? RequireObjectCoercible(flags).
RequireObjectCoercible(flags)
// 2.b.iii. If ? ToString(flags) does not contain "g", throw a TypeError exception.
if (ToString(flags).indexOf('g') === -1) {
throw TypeError('');
}
}
}
// 2.c. Let matcher be ? GetMethod(regexp, @@matchAll).
var matcher = 'Symbol' in self && 'matchAll' in self.Symbol ? GetMethod(regexp, self.Symbol.matchAll) : undefined;
// 2.d. If matcher is not undefined, then
if (matcher !== undefined) {
// 2.d.i. Return ? Call(matcher, regexp, « O »).
return Call(matcher, regexp, [ O ]);
}
}
// 3. Let S be ? ToString(O).
var S = ToString(O);
// 4. Let rx be ? RegExpCreate(regexp, "g").
var rx = new RegExp(regexp, 'g');
// 5. Return ? Invoke(rx, @@matchAll, « S »).
return Invoke(rx, 'Symbol' in self && 'matchAll' in self.Symbol && self.Symbol.matchAll, [ S ]);
});

0 comments on commit cdf20a6

Please sign in to comment.