diff --git a/README.md b/README.md index 531be9f..cacaf3e 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,7 @@ assert.deepEqual([...matchAll(str, globalRegex)], [ Object.assign(['c'], { index: 3, input: str, groups: undefined }), ]); -assert.deepEqual([...matchAll(str, nonGlobalRegex)], [ - Object.assign(['b'], { index: 2, input: str, groups: undefined }), -]); +assert.throws(() => matchAll(str, nonGlobalRegex)); // non-global regexes throw matchAll.shim(); // will be a no-op if not needed @@ -54,9 +52,7 @@ assert.deepEqual([...str.matchAll(globalRegex)], [ Object.assign(['c'], { index: 3, input: str, groups: undefined }), ]); -assert.deepEqual([...str.matchAll(nonGlobalRegex)], [ - Object.assign(['b'], { index: 2, input: str, groups: undefined }), -]); +assert.throws(() => matchAll(str, nonGlobalRegex)); // non-global regexes throw ``` diff --git a/implementation.js b/implementation.js index 78aa822..fb21929 100644 --- a/implementation.js +++ b/implementation.js @@ -1,7 +1,11 @@ 'use strict'; var ES = require('es-abstract/es2019'); +var callBound = require('es-abstract/helpers/callBound'); var hasSymbols = require('has-symbols')(); +var flagsGetter = require('regexp.prototype.flags'); + +var $indexOf = callBound('String.prototype.indexOf'); var regexpMatchAllPolyfill = require('./polyfill-regexp-matchall'); @@ -24,6 +28,16 @@ module.exports = function matchAll(regexp) { var O = ES.RequireObjectCoercible(this); if (typeof regexp !== 'undefined' && regexp !== null) { + var isRegExp = ES.IsRegExp(regexp); + if (isRegExp) { + // workaround for older engines that lack RegExp.prototype.flags + var flags = 'flags' in regexp ? ES.Get(regexp, 'flags') : flagsGetter(regexp); + ES.RequireObjectCoercible(flags); + if ($indexOf(ES.ToString(flags), 'g') < 0) { + throw new TypeError('matchAll requires a non-global regular expression'); + } + } + var matcher = getMatcher(regexp); if (typeof matcher !== 'undefined') { return ES.Call(matcher, regexp, [O]); diff --git a/polyfill.js b/polyfill.js index ad520f5..c9e0df4 100644 --- a/polyfill.js +++ b/polyfill.js @@ -3,5 +3,12 @@ var implementation = require('./implementation'); module.exports = function getPolyfill() { - return String.prototype.matchAll || implementation; + if (String.prototype.matchAll) { + try { + ''.matchAll(RegExp.prototype); + } catch (e) { + return String.prototype.matchAll; + } + } + return implementation; }; diff --git a/test/tests.js b/test/tests.js index 8adc0aa..7214b47 100644 --- a/test/tests.js +++ b/test/tests.js @@ -110,14 +110,13 @@ module.exports = function (matchAll, regexMatchAll, t) { var str = 'aabc'; var regex = /[ac]/g; if (define.supportsDescriptors) { - Object.defineProperty(regex, 'flags', { value: '' }); + Object.defineProperty(regex, 'flags', { value: undefined }); } - s2t.equal(regex.flags, '', 'regex has an empty string "flags" property'); - var expectedResults = [ - { value: assign(['a'], groups({ index: 0, input: str })), done: false }, - { value: undefined, done: true } - ]; - testResults(s2t, matchAll(str, regex), expectedResults); + s2t.equal(regex.flags, undefined, 'regex has an undefined "flags" property'); + s2t['throws']( + function () { matchAll(str, regex); }, + 'undefined flags throws' + ); s2t.end(); }); @@ -156,11 +155,25 @@ module.exports = function (matchAll, regexMatchAll, t) { s2t.end(); }); - st.test('works with a non-global non-sticky regex', function (s2t) { + st.test('throws with a non-global regex', function (s2t) { var str = 'AaBbCc'; var regex = /[bc]/i; + s2t['throws']( + function () { matchAll(str, regex); }, + TypeError, + 'a non-global regex throws' + ); + s2t.end(); + }); + + st.test('works with a global non-sticky regex', function (s2t) { + var str = 'AaBbCc'; + var regex = /[bc]/gi; var expectedResults = [ { value: assign(['B'], groups({ index: 2, input: str })), done: false }, + { value: assign(['b'], groups({ index: 3, input: str })), done: false }, + { value: assign(['C'], groups({ index: 4, input: str })), done: false }, + { value: assign(['c'], groups({ index: 5, input: str })), done: false }, { value: undefined, done: true } ]; testResults(s2t, matchAll(str, regex), expectedResults); @@ -205,17 +218,27 @@ module.exports = function (matchAll, regexMatchAll, t) { var expectedResults = [ { value: undefined, done: true } ]; + + /* eslint no-invalid-regexp: [2, { "allowConstructorFlags": ["y"] }] */ + var regex = new RegExp('\\B', 'y'); + s2t['throws']( + function () { matchAll(str, regex); }, + TypeError, + 'non-global sticky regex throws' + ); + /* eslint no-invalid-regexp: [2, { "allowConstructorFlags": ["y"] }] */ - testResults(s2t, matchAll(str, new RegExp('\\B', 'y')), expectedResults); + testResults(s2t, matchAll(str, new RegExp('\\B', 'gy')), expectedResults); + s2t.end(); }); st.test('unflagged', function (s2t) { - var expectedResults = [ - { value: assign([''], groups({ index: 1, input: str })), done: false }, - { value: undefined, done: true } - ]; - testResults(s2t, matchAll(str, /\B/), expectedResults); + s2t['throws']( + function () { matchAll(str, /\B/); }, + TypeError, + 'unflagged regex throws' + ); s2t.end(); });