diff --git a/CHANGELOG.md b/CHANGELOG.md index e4166c9a5e9f..b207fa80326d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Changelog ##### Unreleased +- Added `RegExp` named capture groups polyfill, [#521](https://github.com/zloirock/core-js/issues/521) +- Added `RegExp` `dotAll` flag polyfill, [#792](https://github.com/zloirock/core-js/issues/792) - Added missed polyfills of [Annex B](https://tc39.es/ecma262/#sec-additional-built-in-properties) features (required mainly for some non-browser engines), [#336](https://github.com/zloirock/core-js/issues/336): - `escape` - `unescape` @@ -7,7 +9,7 @@ - `Date.prototype.getYear` - `Date.prototype.setYear` - `Date.prototype.toGMTString` -- Fixed detection of forbidden host code points in URL polyfill +- Fixed detection of forbidden host code points in `URL` polyfill - Allowed `rhino` target in `core-js-compat` / `core-js-builder`, added compat data for `rhino` 1.7.13, [#942](https://github.com/zloirock/core-js/issues/942), thanks [@gausie](https://github.com/gausie) - `.at` marked as supported from FF90 diff --git a/README.md b/README.md index 962bbea2e92e..2c5a79908412 100644 --- a/README.md +++ b/README.md @@ -669,7 +669,7 @@ Adding support of well-known [symbols](#ecmascript-symbol) `@@match`, `@@replace Annex B methods. Modules [`es.string.anchor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.anchor.js), [`es.string.big`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.big.js), [`es.string.blink`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.blink.js), [`es.string.bold`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.bold.js), [`es.string.fixed`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.fixed.js), [`es.string.fontcolor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.fontcolor.js), [`es.string.fontsize`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.fontsize.js), [`es.string.italics`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.italics.js), [`es.string.link`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.link.js), [`es.string.small`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.small.js), [`es.string.strike`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.strike.js), [`es.string.sub`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.sub.js), [`es.string.sup`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.sup.js), [`es.string.substr`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.string.substr.js), [`es.escape`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.escape.js) and [`es.unescape`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.unescape.js). -`RegExp` features: modules [`es.regexp.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.constructor.js), [`es.regexp.flags`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.flags.js), [`es.regexp.sticky`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.sticky.js) and [`es.regexp.test`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.test.js). +`RegExp` features: modules [`es.regexp.constructor`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.constructor.js), [`es.regexp.dot-all`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.dot-all.js), [`es.regexp.flags`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.flags.js), [`es.regexp.sticky`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.sticky.js) and [`es.regexp.test`](https://github.com/zloirock/core-js/blob/master/packages/core-js/modules/es.regexp.test.js). ```js class String { static fromCodePoint(...codePoints: Array): string; @@ -710,7 +710,8 @@ class String { } class RegExp { - constructor(pattern: RegExp | string, flags?: string): RegExp; // support of sticky (`y`) flag; can alter flags + // support of sticky (`y`) flag, dotAll (`s`) flag, named capture groups, can alter flags + constructor(pattern: RegExp | string, flags?: string): RegExp; exec(): Array | null; // IE8 fixes test(string: string): boolean; // delegation to `.exec` toString(): string; // ES2015+ fix - generic @@ -719,8 +720,9 @@ class RegExp { @@replace(string: string, replaceValue: Function | string): string; @@search(string: string): number; @@split(string: string, limit: number): Array; - readonly attribute flags: string; // IE9+ - readonly attribute sticky: boolean; + readonly attribute dotAll: boolean; // IE9+ + readonly attribute flags: string; // IE9+ + readonly attribute sticky: boolean; // IE9+ } function escape(string: string): string; @@ -766,6 +768,7 @@ core-js(-pure)/es|stable|features/string(/virtual)/sup core-js(-pure)/es|stable|features/string(/virtual)/iterator core-js/es|stable|features/regexp core-js/es|stable|features/regexp/constructor +core-js/es|stable|features/regexp/dot-all core-js(-pure)/es|stable|features/regexp/flags core-js/es|stable|features/regexp/sticky core-js/es|stable|features/regexp/test @@ -773,7 +776,7 @@ core-js/es|stable|features/regexp/to-string core-js/es|stable|features/escape core-js/es|stable|features/unescape ``` -[*Examples*](http://es6.zloirock.ru/#for(var%20val%20of%20'a%F0%A0%AE%B7b')%7B%0A%20%20log(val)%3B%20%2F%2F%20%3D%3E%20'a'%2C%20'%F0%A0%AE%B7'%2C%20'b'%0A%7D%0A%0Alog('foobarbaz'.includes('bar'))%3B%20%20%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.includes('bar'%2C%204))%3B%20%20%20%2F%2F%20%3D%3E%20false%0Alog('foobarbaz'.startsWith('foo'))%3B%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.startsWith('bar'%2C%203))%3B%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.endsWith('baz'))%3B%20%20%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.endsWith('bar'%2C%206))%3B%20%20%20%2F%2F%20%3D%3E%20true%0A%0Alog('string'.repeat(3))%3B%20%2F%2F%20%3D%3E%20'stringstringstring'%0A%0Alog('hello'.padStart(10))%3B%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%20%20%20%20%20hello'%0Alog('hello'.padStart(10%2C%20'1234'))%3B%20%2F%2F%20%3D%3E%20'12341hello'%0Alog('hello'.padEnd(10))%3B%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'hello%20%20%20%20%20'%0Alog('hello'.padEnd(10%2C%20'1234'))%3B%20%20%20%2F%2F%20%3D%3E%20'hello12341'%0A%0Alog('%F0%A0%AE%B7'.codePointAt(0))%3B%20%2F%2F%20%3D%3E%20134071%0A%0Avar%20name%20%3D%20'Bob'%3B%0Alog(String.raw%60Hi%5Cn%24%7Bname%7D!%60)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'Hi%5C%5CnBob!'%20(ES6%20template%20string%20syntax)%0Alog(String.raw(%7B%20raw%3A%20'test'%20%7D%2C%200%2C%201%2C%202))%3B%20%2F%2F%20%3D%3E%20%2F%2F%20't0e1s2t'%0A%0Alog('foo'.bold())%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%3Cb%3Efoo%3C%2Fb%3E'%0Alog('bar'.anchor('a%22b'))%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%3Ca%20name%3D%22a%26quot%3Bb%22%3Ebar%3C%2Fa%3E'%0Alog('baz'.link('http%3A%2F%2Fexample.com'))%3B%20%2F%2F%20%3D%3E%20'%3Ca%20href%3D%22http%3A%2F%2Fexample.com%22%3Ebaz%3C%2Fa%3E'%0A%0Alog(RegExp(%2F.%2Fg%2C%20'm'))%3B%20%2F%2F%20%3D%3E%20%2F.%2Fm%0A%0Alog(%2Ffoo%2F.flags)%3B%20%20%20%20%2F%2F%20%3D%3E%20''%0Alog(%2Ffoo%2Fgim.flags)%3B%20%2F%2F%20%3D%3E%20'gim'%0A%0Alog(RegExp('foo'%2C%20'y').sticky)%3B%20%2F%2F%20%3D%3E%20true%0A%0Aconst%20text%20%3D%20'First%20line%5CnSecond%20line'%3B%0Aconst%20regex%20%3D%20RegExp('(%5C%5CS%2B)%20line%5C%5Cn%3F'%2C%20'y')%3B%0A%0Alog(regex.exec(text)%5B1%5D)%3B%20%2F%2F%20%3D%3E%20'First'%0Alog(regex.exec(text)%5B1%5D)%3B%20%2F%2F%20%3D%3E%20'Second'%0Alog(regex.exec(text))%3B%20%20%20%20%2F%2F%20%3D%3E%20null%0A%0Alog('foo'.match(%7B%5BSymbol.match%5D%3A%20_%20%3D%3E%201%7D))%3B%20%20%20%20%20%2F%2F%20%3D%3E%201%0Alog('foo'.replace(%7B%5BSymbol.replace%5D%3A%20_%20%3D%3E%202%7D))%3B%20%2F%2F%20%3D%3E%202%0Alog('foo'.search(%7B%5BSymbol.search%5D%3A%20_%20%3D%3E%203%7D))%3B%20%20%20%2F%2F%20%3D%3E%203%0Alog('foo'.split(%7B%5BSymbol.split%5D%3A%20_%20%3D%3E%204%7D))%3B%20%20%20%20%20%2F%2F%20%3D%3E%204%0A%0Alog(RegExp.prototype.toString.call(%7Bsource%3A%20'foo'%2C%20flags%3A%20'bar'%7D))%3B%0A%0Alog('%20%20%20hello%20%20%20'.trimLeft())%3B%20%20%2F%2F%20%3D%3E%20'hello%20%20%20'%0Alog('%20%20%20hello%20%20%20'.trimRight())%3B%20%2F%2F%20%3D%3E%20'%20%20%20hello'%0Alog('%20%20%20hello%20%20%20'.trimStart())%3B%20%2F%2F%20%3D%3E%20'hello%20%20%20'%0Alog('%20%20%20hello%20%20%20'.trimEnd())%3B%20%20%20%2F%2F%20%3D%3E%20'%20%20%20hello'%0A%0Afor%20(let%20%5B_%2C%20d%2C%20D%5D%20of%20'1111a2b3cccc'.matchAll(%2F(%5Cd)(%5CD)%2Fg))%20%7B%0A%20%20log(d%2C%20D)%3B%20%2F%2F%20%3D%3E%201%20a%2C%202%20b%2C%203%20c%0A%7D%0A%0Alog('Test%20abc%20test%20test%20abc%20test.'.replaceAll('abc'%2C%20'foo'))%3B%20%2F%2F%20-%3E%20'Test%20foo%20test%20test%20foo%20test.'): +[*Examples*](http://es6.zloirock.ru/#for(var%20val%20of%20'a%F0%A0%AE%B7b')%7B%0A%20%20log(val)%3B%20%2F%2F%20%3D%3E%20'a'%2C%20'%F0%A0%AE%B7'%2C%20'b'%0A%7D%0A%0Alog('foobarbaz'.includes('bar'))%3B%20%20%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.includes('bar'%2C%204))%3B%20%20%20%2F%2F%20%3D%3E%20false%0Alog('foobarbaz'.startsWith('foo'))%3B%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.startsWith('bar'%2C%203))%3B%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.endsWith('baz'))%3B%20%20%20%20%20%20%2F%2F%20%3D%3E%20true%0Alog('foobarbaz'.endsWith('bar'%2C%206))%3B%20%20%20%2F%2F%20%3D%3E%20true%0A%0Alog('string'.repeat(3))%3B%20%2F%2F%20%3D%3E%20'stringstringstring'%0A%0Alog('hello'.padStart(10))%3B%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%20%20%20%20%20hello'%0Alog('hello'.padStart(10%2C%20'1234'))%3B%20%2F%2F%20%3D%3E%20'12341hello'%0Alog('hello'.padEnd(10))%3B%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'hello%20%20%20%20%20'%0Alog('hello'.padEnd(10%2C%20'1234'))%3B%20%20%20%2F%2F%20%3D%3E%20'hello12341'%0A%0Alog('%F0%A0%AE%B7'.codePointAt(0))%3B%20%2F%2F%20%3D%3E%20134071%0A%0Avar%20name%20%3D%20'Bob'%3B%0Alog(String.raw%60Hi%5Cn%24%7Bname%7D!%60)%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'Hi%5C%5CnBob!'%20(ES6%20template%20string%20syntax)%0Alog(String.raw(%7B%20raw%3A%20'test'%20%7D%2C%200%2C%201%2C%202))%3B%20%2F%2F%20%3D%3E%20%2F%2F%20't0e1s2t'%0A%0Alog('foo'.bold())%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%3Cb%3Efoo%3C%2Fb%3E'%0Alog('bar'.anchor('a%22b'))%3B%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20%3D%3E%20'%3Ca%20name%3D%22a%26quot%3Bb%22%3Ebar%3C%2Fa%3E'%0Alog('baz'.link('http%3A%2F%2Fexample.com'))%3B%20%2F%2F%20%3D%3E%20'%3Ca%20href%3D%22http%3A%2F%2Fexample.com%22%3Ebaz%3C%2Fa%3E'%0A%0Alog(RegExp('.'%2C%20's').test('%5Cn'))%3B%20%2F%2F%20%3D%3E%20true%0Alog(RegExp('.'%2C%20's').dotAll)%3B%20%20%20%20%20%2F%2F%20%3D%3E%20true%0A%0Alog(RegExp('foo%3A(%3F%3Cfoo%3E%5C%5Cw%2B)%2Cbar%3A(%3F%3Cbar%3E%5C%5Cw%2B)').exec('foo%3Aabc%2Cbar%3Adef').groups.bar)%3B%20%2F%2F%20%3D%3E%20'def'%0A%0Alog('foo%3Aabc%2Cbar%3Adef'.replace(RegExp('foo%3A(%3F%3Cfoo%3E%5C%5Cw%2B)%2Cbar%3A(%3F%3Cbar%3E%5C%5Cw%2B)')%2C%20'%24%3Cbar%3E%2C%24%3Cfoo%3E'))%3B%20%2F%2F%20%3D%3E%20'def%2Cabc'%0A%0Alog(RegExp(%2F.%2Fg%2C%20'm'))%3B%20%2F%2F%20%3D%3E%20%2F.%2Fm%0A%0Alog(%2Ffoo%2F.flags)%3B%20%20%20%20%2F%2F%20%3D%3E%20''%0Alog(%2Ffoo%2Fgim.flags)%3B%20%2F%2F%20%3D%3E%20'gim'%0A%0Alog(RegExp('foo'%2C%20'y').sticky)%3B%20%2F%2F%20%3D%3E%20true%0A%0Aconst%20text%20%3D%20'First%20line%5CnSecond%20line'%3B%0Aconst%20regex%20%3D%20RegExp('(%5C%5CS%2B)%20line%5C%5Cn%3F'%2C%20'y')%3B%0A%0Alog(regex.exec(text)%5B1%5D)%3B%20%2F%2F%20%3D%3E%20'First'%0Alog(regex.exec(text)%5B1%5D)%3B%20%2F%2F%20%3D%3E%20'Second'%0Alog(regex.exec(text))%3B%20%20%20%20%2F%2F%20%3D%3E%20null%0A%0Alog('foo'.match(%7B%5BSymbol.match%5D%3A%20_%20%3D%3E%201%7D))%3B%20%20%20%20%20%2F%2F%20%3D%3E%201%0Alog('foo'.replace(%7B%5BSymbol.replace%5D%3A%20_%20%3D%3E%202%7D))%3B%20%2F%2F%20%3D%3E%202%0Alog('foo'.search(%7B%5BSymbol.search%5D%3A%20_%20%3D%3E%203%7D))%3B%20%20%20%2F%2F%20%3D%3E%203%0Alog('foo'.split(%7B%5BSymbol.split%5D%3A%20_%20%3D%3E%204%7D))%3B%20%20%20%20%20%2F%2F%20%3D%3E%204%0A%0Alog(RegExp.prototype.toString.call(%7Bsource%3A%20'foo'%2C%20flags%3A%20'bar'%7D))%3B%0A%0Alog('%20%20%20hello%20%20%20'.trimLeft())%3B%20%20%2F%2F%20%3D%3E%20'hello%20%20%20'%0Alog('%20%20%20hello%20%20%20'.trimRight())%3B%20%2F%2F%20%3D%3E%20'%20%20%20hello'%0Alog('%20%20%20hello%20%20%20'.trimStart())%3B%20%2F%2F%20%3D%3E%20'hello%20%20%20'%0Alog('%20%20%20hello%20%20%20'.trimEnd())%3B%20%20%20%2F%2F%20%3D%3E%20'%20%20%20hello'%0A%0Afor%20(let%20%5B_%2C%20d%2C%20D%5D%20of%20'1111a2b3cccc'.matchAll(%2F(%5Cd)(%5CD)%2Fg))%20%7B%0A%20%20log(d%2C%20D)%3B%20%2F%2F%20%3D%3E%201%20a%2C%202%20b%2C%203%20c%0A%7D%0A%0Alog('Test%20abc%20test%20test%20abc%20test.'.replaceAll('abc'%2C%20'foo'))%3B%20%2F%2F%20-%3E%20'Test%20foo%20test%20test%20foo%20test.'): ```js for (let value of 'a𠮷b') { console.log(value); // => 'a', '𠮷', 'b' @@ -804,6 +807,13 @@ String.raw({ raw: 'test' }, 0, 1, 2); // => 't0e1s2t' 'bar'.anchor('a"b'); // => 'bar' 'baz'.link('http://example.com'); // => 'baz' +RegExp('.', 's').test('\n'); // => true +RegExp('.', 's').dotAll; // => true + +RegExp('foo:(?\\w+),bar:(?\\w+)').exec('foo:abc,bar:def').groups.bar; // => 'def' + +'foo:abc,bar:def'.replace(RegExp('foo:(?\\w+),bar:(?\\w+)'), '$,$'); // => 'def,abc' + RegExp(/./g, 'm'); // => /./m /foo/.flags; // => '' diff --git a/packages/core-js-compat/src/data.js b/packages/core-js-compat/src/data.js index c40ba76ecc86..33537e6740ae 100644 --- a/packages/core-js-compat/src/data.js +++ b/packages/core-js-compat/src/data.js @@ -929,20 +929,24 @@ const data = { safari: '14.0', }, 'es.regexp.constructor': { - chrome: '51', - firefox: '49', - safari: '10.0', + chrome: '64', + firefox: '78', + safari: '11.1', + }, + 'es.regexp.dot-all': { + chrome: '62', + firefox: '78', + safari: '11.1', }, 'es.regexp.exec': { - chrome: '26', - edge: '13', - firefox: '44', - safari: '10.0', + chrome: '64', + firefox: '78', + safari: '11.1', }, 'es.regexp.flags': { - chrome: '49', - firefox: '37', - safari: '9.0', + chrome: '62', + firefox: '78', + safari: '11.1', }, 'es.regexp.sticky': { chrome: '49', diff --git a/packages/core-js-compat/src/modules-by-versions.js b/packages/core-js-compat/src/modules-by-versions.js index 5ce868c70267..ae28b542f8d9 100644 --- a/packages/core-js-compat/src/modules-by-versions.js +++ b/packages/core-js-compat/src/modules-by-versions.js @@ -92,6 +92,7 @@ module.exports = { 'es.date.set-year', 'es.date.to-gmt-string', 'es.escape', + 'es.regexp.dot-all', 'es.string.substr', 'es.unescape', ], diff --git a/packages/core-js-pure/override/internals/regexp-unsupported-dot-all.js b/packages/core-js-pure/override/internals/regexp-unsupported-dot-all.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/internals/regexp-unsupported-dot-all.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js-pure/override/modules/es.regexp.dot-all.js b/packages/core-js-pure/override/modules/es.regexp.dot-all.js new file mode 100644 index 000000000000..8b1a393741c9 --- /dev/null +++ b/packages/core-js-pure/override/modules/es.regexp.dot-all.js @@ -0,0 +1 @@ +// empty diff --git a/packages/core-js/es/regexp/dot-all.js b/packages/core-js/es/regexp/dot-all.js new file mode 100644 index 000000000000..ed9239c79c19 --- /dev/null +++ b/packages/core-js/es/regexp/dot-all.js @@ -0,0 +1,7 @@ +require('../../modules/es.regexp.constructor'); +require('../../modules/es.regexp.dot-all'); +require('../../modules/es.regexp.exec'); + +module.exports = function (it) { + return it.sticky; +}; diff --git a/packages/core-js/es/regexp/index.js b/packages/core-js/es/regexp/index.js index 92c170a026c9..4a932f79b724 100644 --- a/packages/core-js/es/regexp/index.js +++ b/packages/core-js/es/regexp/index.js @@ -1,5 +1,6 @@ require('../../modules/es.regexp.constructor'); require('../../modules/es.regexp.to-string'); +require('../../modules/es.regexp.dot-all'); require('../../modules/es.regexp.exec'); require('../../modules/es.regexp.flags'); require('../../modules/es.regexp.sticky'); diff --git a/packages/core-js/es/regexp/sticky.js b/packages/core-js/es/regexp/sticky.js index eb33fb10565b..4d09255b1808 100644 --- a/packages/core-js/es/regexp/sticky.js +++ b/packages/core-js/es/regexp/sticky.js @@ -1,3 +1,5 @@ +require('../../modules/es.regexp.constructor'); +require('../../modules/es.regexp.exec'); require('../../modules/es.regexp.sticky'); module.exports = function (it) { diff --git a/packages/core-js/features/regexp/dot-all.js b/packages/core-js/features/regexp/dot-all.js new file mode 100644 index 000000000000..1a235fe4865f --- /dev/null +++ b/packages/core-js/features/regexp/dot-all.js @@ -0,0 +1,3 @@ +var parent = require('../../stable/regexp/dot-all'); + +module.exports = parent; diff --git a/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js b/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js index 43b1e4257b4e..781d2f24bb45 100644 --- a/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js +++ b/packages/core-js/internals/fix-regexp-well-known-symbol-logic.js @@ -10,47 +10,7 @@ var createNonEnumerableProperty = require('../internals/create-non-enumerable-pr var SPECIES = wellKnownSymbol('species'); var RegExpPrototype = RegExp.prototype; -var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () { - // #replace needs built-in support for named groups. - // #match works fine because it just return the exec results, even if it has - // a "grops" property. - var re = /./; - re.exec = function () { - var result = []; - result.groups = { a: '7' }; - return result; - }; - return ''.replace(re, '$') !== '7'; -}); - -// IE <= 11 replaces $0 with the whole match, as if it was $& -// https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0 -var REPLACE_KEEPS_$0 = (function () { - // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing - return 'a'.replace(/./, '$0') === '$0'; -})(); - -var REPLACE = wellKnownSymbol('replace'); -// Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string -var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () { - if (/./[REPLACE]) { - return /./[REPLACE]('a', '$0') === ''; - } - return false; -})(); - -// Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec -// Weex JS has frozen built-in prototypes, so use try / catch wrapper -var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () { - // eslint-disable-next-line regexp/no-empty-group -- required for testing - var re = /(?:)/; - var originalExec = re.exec; - re.exec = function () { return originalExec.apply(this, arguments); }; - var result = 'ab'.split(re); - return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b'; -}); - -module.exports = function (KEY, length, exec, sham) { +module.exports = function (KEY, exec, FORCED, SHAM) { var SYMBOL = wellKnownSymbol(KEY); var DELEGATES_TO_SYMBOL = !fails(function () { @@ -87,12 +47,7 @@ module.exports = function (KEY, length, exec, sham) { if ( !DELEGATES_TO_SYMBOL || !DELEGATES_TO_EXEC || - (KEY === 'replace' && !( - REPLACE_SUPPORTS_NAMED_GROUPS && - REPLACE_KEEPS_$0 && - !REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE - )) || - (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC) + FORCED ) { var nativeRegExpMethod = /./[SYMBOL]; var methods = exec(SYMBOL, ''[KEY], function (nativeMethod, regexp, str, arg2, forceStringMethod) { @@ -107,23 +62,11 @@ module.exports = function (KEY, length, exec, sham) { return { done: true, value: nativeMethod.call(str, regexp, arg2) }; } return { done: false }; - }, { - REPLACE_KEEPS_$0: REPLACE_KEEPS_$0, - REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE: REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE }); - var stringMethod = methods[0]; - var regexMethod = methods[1]; - redefine(String.prototype, KEY, stringMethod); - redefine(RegExpPrototype, SYMBOL, length == 2 - // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue) - // 21.2.5.11 RegExp.prototype[@@split](string, limit) - ? function (string, arg) { return regexMethod.call(string, this, arg); } - // 21.2.5.6 RegExp.prototype[@@match](string) - // 21.2.5.9 RegExp.prototype[@@search](string) - : function (string) { return regexMethod.call(string, this); } - ); + redefine(String.prototype, KEY, methods[0]); + redefine(RegExpPrototype, SYMBOL, methods[1]); } - if (sham) createNonEnumerableProperty(RegExpPrototype[SYMBOL], 'sham', true); + if (SHAM) createNonEnumerableProperty(RegExpPrototype[SYMBOL], 'sham', true); }; diff --git a/packages/core-js/internals/regexp-exec.js b/packages/core-js/internals/regexp-exec.js index 2841e29e08a0..099e9b35ceb0 100644 --- a/packages/core-js/internals/regexp-exec.js +++ b/packages/core-js/internals/regexp-exec.js @@ -1,9 +1,13 @@ 'use strict'; /* eslint-disable regexp/no-assertion-capturing-group, regexp/no-empty-group, regexp/no-lazy-ends -- testing */ /* eslint-disable regexp/no-useless-quantifier -- testing */ -var regexpFlags = require('./regexp-flags'); -var stickyHelpers = require('./regexp-sticky-helpers'); -var shared = require('./shared'); +var regexpFlags = require('../internals/regexp-flags'); +var stickyHelpers = require('../internals/regexp-sticky-helpers'); +var shared = require('../internals/shared'); +var create = require('../internals/object-create'); +var getInternalState = require('../internals/internal-state').get; +var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all'); +var UNSUPPORTED_NCG = require('../internals/regexp-unsupported-ncg'); var nativeExec = RegExp.prototype.exec; var nativeReplace = shared('native-string-replace', String.prototype.replace); @@ -23,12 +27,24 @@ var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y || stickyHelpers.BROKEN_CARET; // nonparticipating capturing group, copied from es5-shim's String#split patch. var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined; -var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y; +var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y || UNSUPPORTED_DOT_ALL || UNSUPPORTED_NCG; if (PATCH) { + // eslint-disable-next-line max-statements -- TODO patchedExec = function exec(str) { var re = this; - var lastIndex, reCopy, match, i; + var state = getInternalState(re); + var raw = state.raw; + var result, reCopy, lastIndex, match, i, object, group; + + if (raw) { + raw.lastIndex = re.lastIndex; + result = patchedExec.call(raw, str); + re.lastIndex = raw.lastIndex; + return result; + } + + var groups = state.groups; var sticky = UNSUPPORTED_Y && re.sticky; var flags = regexpFlags.call(re); var source = re.source; @@ -80,6 +96,14 @@ if (PATCH) { }); } + if (match && groups) { + match.groups = object = create(null); + for (i = 0; i < groups.length; i++) { + group = groups[i]; + object[group[0]] = match[group[1]]; + } + } + return match; }; } diff --git a/packages/core-js/internals/regexp-sticky-helpers.js b/packages/core-js/internals/regexp-sticky-helpers.js index da7641bb79f7..6fecac12cd62 100644 --- a/packages/core-js/internals/regexp-sticky-helpers.js +++ b/packages/core-js/internals/regexp-sticky-helpers.js @@ -1,15 +1,11 @@ -'use strict'; - -var fails = require('./fails'); +var fails = require('../internals/fails'); // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError, -// so we use an intermediate function. -function RE(s, f) { +var RE = function (s, f) { return RegExp(s, f); -} +}; exports.UNSUPPORTED_Y = fails(function () { - // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError var re = RE('a', 'y'); re.lastIndex = 2; return re.exec('abcd') != null; diff --git a/packages/core-js/internals/regexp-unsupported-dot-all.js b/packages/core-js/internals/regexp-unsupported-dot-all.js new file mode 100644 index 000000000000..a48532f832e7 --- /dev/null +++ b/packages/core-js/internals/regexp-unsupported-dot-all.js @@ -0,0 +1,7 @@ +var fails = require('./fails'); + +module.exports = fails(function () { + // babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError + var re = RegExp('.', (typeof '').charAt(0)); + return !(re.dotAll && re.exec('\n') && re.flags === 's'); +}); diff --git a/packages/core-js/internals/regexp-unsupported-ncg.js b/packages/core-js/internals/regexp-unsupported-ncg.js new file mode 100644 index 000000000000..064c9e51a6dd --- /dev/null +++ b/packages/core-js/internals/regexp-unsupported-ncg.js @@ -0,0 +1,8 @@ +var fails = require('./fails'); + +module.exports = fails(function () { + // babel-minify transpiles RegExp('.', 'g') -> /./g and it causes SyntaxError + var re = RegExp('(?b)', (typeof '').charAt(5)); + return re.exec('b').groups.a !== 'b' || + 'b'.replace(re, '$c') !== 'bc'; +}); diff --git a/packages/core-js/modules/es.regexp.constructor.js b/packages/core-js/modules/es.regexp.constructor.js index 03389c5bad24..8619347bed89 100644 --- a/packages/core-js/modules/es.regexp.constructor.js +++ b/packages/core-js/modules/es.regexp.constructor.js @@ -2,6 +2,7 @@ var DESCRIPTORS = require('../internals/descriptors'); var global = require('../internals/global'); var isForced = require('../internals/is-forced'); var inheritIfRequired = require('../internals/inherit-if-required'); +var createNonEnumerableProperty = require('../internals/create-non-enumerable-property'); var defineProperty = require('../internals/object-define-property').f; var getOwnPropertyNames = require('../internals/object-get-own-property-names').f; var isRegExp = require('../internals/is-regexp'); @@ -9,13 +10,18 @@ var getFlags = require('../internals/regexp-flags'); var stickyHelpers = require('../internals/regexp-sticky-helpers'); var redefine = require('../internals/redefine'); var fails = require('../internals/fails'); +var has = require('../internals/has'); var enforceInternalState = require('../internals/internal-state').enforce; var setSpecies = require('../internals/set-species'); var wellKnownSymbol = require('../internals/well-known-symbol'); +var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all'); +var UNSUPPORTED_NCG = require('../internals/regexp-unsupported-ncg'); var MATCH = wellKnownSymbol('match'); var NativeRegExp = global.RegExp; var RegExpPrototype = NativeRegExp.prototype; +// TODO: Use only propper RegExpIdentifierName +var IS_NCG = /^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/; var re1 = /a/g; var re2 = /a/g; @@ -24,20 +30,90 @@ var CORRECT_NEW = new NativeRegExp(re1) !== re1; var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y; -var FORCED = DESCRIPTORS && isForced('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y || fails(function () { - re2[MATCH] = false; - // RegExp constructor can alter flags and IsRegExp works correct with @@match - return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i'; -}))); +var BASE_FORCED = DESCRIPTORS && + (!CORRECT_NEW || UNSUPPORTED_Y || UNSUPPORTED_DOT_ALL || UNSUPPORTED_NCG || fails(function () { + re2[MATCH] = false; + // RegExp constructor can alter flags and IsRegExp works correct with @@match + return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i'; + })); + +var handleDotAll = function (string) { + var length = string.length; + var index = 0; + var result = ''; + var brackets = false; + var chr; + for (; index <= length; index++) { + chr = string.charAt(index); + if (chr === '\\') { + result += chr + string.charAt(++index); + continue; + } + if (!brackets && chr === '.') { + result += '[\\s\\S]'; + } else { + if (chr === '[') { + brackets = true; + } else if (chr === ']') { + brackets = false; + } result += chr; + } + } return result; +}; + +var handleNCG = function (string) { + var length = string.length; + var index = 0; + var result = ''; + var named = []; + var names = {}; + var brackets = false; + var ncg = false; + var groupid = 0; + var groupname = ''; + var chr; + for (; index <= length; index++) { + chr = string.charAt(index); + if (chr === '\\') { + chr = chr + string.charAt(++index); + } else if (chr === ']') { + brackets = false; + } else if (!brackets) switch (true) { + case chr === '[': + brackets = true; + break; + case chr === '(': + if (IS_NCG.test(string.slice(index + 1))) { + index += 2; + ncg = true; + } + result += chr; + groupid++; + continue; + case chr === '>' && ncg: + if (groupname === '' || has(names, groupname)) { + throw new SyntaxError('Invalid capture group name'); + } + names[groupname] = true; + named.push([groupname, groupid]); + ncg = false; + groupname = ''; + continue; + } + if (ncg) groupname += chr; + else result += chr; + } return [result, named]; +}; // `RegExp` constructor // https://tc39.es/ecma262/#sec-regexp-constructor -if (FORCED) { +if (isForced('RegExp', BASE_FORCED)) { var RegExpWrapper = function RegExp(pattern, flags) { var thisIsRegExp = this instanceof RegExpWrapper; var patternIsRegExp = isRegExp(pattern); var flagsAreUndefined = flags === undefined; - var sticky; + var groups = []; + var rawPattern, rawFlags, dotAll, sticky, handled, result, state; if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) { return pattern; @@ -50,24 +126,52 @@ if (FORCED) { pattern = pattern.source; } - if (UNSUPPORTED_Y) { + pattern = pattern === undefined ? '' : String(pattern); + flags = flags === undefined ? '' : String(flags); + rawPattern = pattern; + + if (UNSUPPORTED_DOT_ALL && 'dotAll' in re1) { + dotAll = !!flags && flags.indexOf('s') > -1; + if (dotAll) flags = flags.replace(/s/g, ''); + } + + rawFlags = flags; + + if (UNSUPPORTED_Y && 'sticky' in re1) { sticky = !!flags && flags.indexOf('y') > -1; if (sticky) flags = flags.replace(/y/g, ''); } - var result = inheritIfRequired( + if (UNSUPPORTED_NCG) { + handled = handleNCG(pattern); + pattern = handled[0]; + groups = handled[1]; + } + + result = inheritIfRequired( CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags), thisIsRegExp ? this : RegExpPrototype, RegExpWrapper ); - if (UNSUPPORTED_Y && sticky) { - var state = enforceInternalState(result); - state.sticky = true; + if (dotAll || sticky || groups.length) { + state = enforceInternalState(result); + if (dotAll) { + state.dotAll = true; + state.raw = RegExpWrapper(handleDotAll(pattern), rawFlags); + } + if (sticky) state.sticky = true; + if (groups.length) state.groups = groups; } + if (pattern !== rawPattern) try { + // fails in old engines, but we have no alternatives for unsupported regex syntax + createNonEnumerableProperty(result, 'source', rawPattern === '' ? '(?:)' : rawPattern); + } catch (error) { /* empty */ } + return result; }; + var proxy = function (key) { key in RegExpWrapper || defineProperty(RegExpWrapper, key, { configurable: true, @@ -75,9 +179,11 @@ if (FORCED) { set: function (it) { NativeRegExp[key] = it; } }); }; - var keys = getOwnPropertyNames(NativeRegExp); - var index = 0; - while (keys.length > index) proxy(keys[index++]); + + for (var keys = getOwnPropertyNames(NativeRegExp), index = 0; keys.length > index;) { + proxy(keys[index++]); + } + RegExpPrototype.constructor = RegExpWrapper; RegExpWrapper.prototype = RegExpPrototype; redefine(global, 'RegExp', RegExpWrapper); diff --git a/packages/core-js/modules/es.regexp.dot-all.js b/packages/core-js/modules/es.regexp.dot-all.js new file mode 100644 index 000000000000..11f02b8c6b78 --- /dev/null +++ b/packages/core-js/modules/es.regexp.dot-all.js @@ -0,0 +1,22 @@ +var DESCRIPTORS = require('../internals/descriptors'); +var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all'); +var defineProperty = require('../internals/object-define-property').f; +var getInternalState = require('../internals/internal-state').get; +var RegExpPrototype = RegExp.prototype; + +// `RegExp.prototype.dotAll` getter +// https://tc39.es/ecma262/#sec-get-regexp.prototype.dotall +if (DESCRIPTORS && UNSUPPORTED_DOT_ALL) { + defineProperty(RegExpPrototype, 'dotAll', { + configurable: true, + get: function () { + if (this === RegExpPrototype) return undefined; + // We can't use InternalStateModule.getterFor because + // we don't add metadata for regexps created by a literal. + if (this instanceof RegExp) { + return !!getInternalState(this).dotAll; + } + throw TypeError('Incompatible receiver, RegExp required'); + } + }); +} diff --git a/packages/core-js/modules/es.regexp.flags.js b/packages/core-js/modules/es.regexp.flags.js index 2270ba3140fe..7d9583714e10 100644 --- a/packages/core-js/modules/es.regexp.flags.js +++ b/packages/core-js/modules/es.regexp.flags.js @@ -1,14 +1,16 @@ var DESCRIPTORS = require('../internals/descriptors'); var objectDefinePropertyModule = require('../internals/object-define-property'); var regExpFlags = require('../internals/regexp-flags'); -var UNSUPPORTED_Y = require('../internals/regexp-sticky-helpers').UNSUPPORTED_Y; +var fails = require('../internals/fails'); + +var FORCED = DESCRIPTORS && fails(function () { + // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe + return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy'; +}); // `RegExp.prototype.flags` getter // https://tc39.es/ecma262/#sec-get-regexp.prototype.flags -// eslint-disable-next-line es/no-regexp-prototype-flags -- required for testing -if (DESCRIPTORS && (/./g.flags != 'g' || UNSUPPORTED_Y)) { - objectDefinePropertyModule.f(RegExp.prototype, 'flags', { - configurable: true, - get: regExpFlags - }); -} +if (FORCED) objectDefinePropertyModule.f(RegExp.prototype, 'flags', { + configurable: true, + get: regExpFlags +}); diff --git a/packages/core-js/modules/es.regexp.sticky.js b/packages/core-js/modules/es.regexp.sticky.js index 9ce2717eb03c..b63125b05e33 100644 --- a/packages/core-js/modules/es.regexp.sticky.js +++ b/packages/core-js/modules/es.regexp.sticky.js @@ -7,7 +7,7 @@ var RegExpPrototype = RegExp.prototype; // `RegExp.prototype.sticky` getter // https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky if (DESCRIPTORS && UNSUPPORTED_Y) { - defineProperty(RegExp.prototype, 'sticky', { + defineProperty(RegExpPrototype, 'sticky', { configurable: true, get: function () { if (this === RegExpPrototype) return undefined; diff --git a/packages/core-js/modules/es.string.match.js b/packages/core-js/modules/es.string.match.js index 0461fd0ed595..e294d15682d4 100644 --- a/packages/core-js/modules/es.string.match.js +++ b/packages/core-js/modules/es.string.match.js @@ -7,7 +7,7 @@ var advanceStringIndex = require('../internals/advance-string-index'); var regExpExec = require('../internals/regexp-exec-abstract'); // @@match logic -fixRegExpWellKnownSymbolLogic('match', 1, function (MATCH, nativeMatch, maybeCallNative) { +fixRegExpWellKnownSymbolLogic('match', function (MATCH, nativeMatch, maybeCallNative) { return [ // `String.prototype.match` method // https://tc39.es/ecma262/#sec-string.prototype.match @@ -18,12 +18,12 @@ fixRegExpWellKnownSymbolLogic('match', 1, function (MATCH, nativeMatch, maybeCal }, // `RegExp.prototype[@@match]` method // https://tc39.es/ecma262/#sec-regexp.prototype-@@match - function (regexp) { - var res = maybeCallNative(nativeMatch, regexp, this); + function (string) { + var res = maybeCallNative(nativeMatch, this, string); if (res.done) return res.value; - var rx = anObject(regexp); - var S = String(this); + var rx = anObject(this); + var S = String(string); if (!rx.global) return regExpExec(rx, S); diff --git a/packages/core-js/modules/es.string.replace.js b/packages/core-js/modules/es.string.replace.js index 7f4f2f24bd15..892268118e1b 100644 --- a/packages/core-js/modules/es.string.replace.js +++ b/packages/core-js/modules/es.string.replace.js @@ -1,5 +1,6 @@ 'use strict'; var fixRegExpWellKnownSymbolLogic = require('../internals/fix-regexp-well-known-symbol-logic'); +var fails = require('../internals/fails'); var anObject = require('../internals/an-object'); var toLength = require('../internals/to-length'); var toInteger = require('../internals/to-integer'); @@ -7,7 +8,9 @@ var requireObjectCoercible = require('../internals/require-object-coercible'); var advanceStringIndex = require('../internals/advance-string-index'); var getSubstitution = require('../internals/get-substitution'); var regExpExec = require('../internals/regexp-exec-abstract'); +var wellKnownSymbol = require('../internals/well-known-symbol'); +var REPLACE = wellKnownSymbol('replace'); var max = Math.max; var min = Math.min; @@ -15,10 +18,33 @@ var maybeToString = function (it) { return it === undefined ? it : String(it); }; +// IE <= 11 replaces $0 with the whole match, as if it was $& +// https://stackoverflow.com/questions/6024666/getting-ie-to-replace-a-regex-with-the-literal-string-0 +var REPLACE_KEEPS_$0 = (function () { + // eslint-disable-next-line regexp/prefer-escape-replacement-dollar-char -- required for testing + return 'a'.replace(/./, '$0') === '$0'; +})(); + +// Safari <= 13.0.3(?) substitutes nth capture where n>m with an empty string +var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = (function () { + if (/./[REPLACE]) { + return /./[REPLACE]('a', '$0') === ''; + } + return false; +})(); + +var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () { + var re = /./; + re.exec = function () { + var result = []; + result.groups = { a: '7' }; + return result; + }; + return ''.replace(re, '$') !== '7'; +}); + // @@replace logic -fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, maybeCallNative, reason) { - var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE; - var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0; +fixRegExpWellKnownSymbolLogic('replace', function (_, nativeReplace, maybeCallNative) { var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? '$' : '$0'; return [ @@ -33,17 +59,18 @@ fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, ma }, // `RegExp.prototype[@@replace]` method // https://tc39.es/ecma262/#sec-regexp.prototype-@@replace - function (regexp, replaceValue) { + function (string, replaceValue) { if ( - (!REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE && REPLACE_KEEPS_$0) || - (typeof replaceValue === 'string' && replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1) + typeof replaceValue === 'string' && + replaceValue.indexOf(UNSAFE_SUBSTITUTE) === -1 && + replaceValue.indexOf('$<') === -1 ) { - var res = maybeCallNative(nativeReplace, regexp, this, replaceValue); + var res = maybeCallNative(nativeReplace, this, string, replaceValue); if (res.done) return res.value; } - var rx = anObject(regexp); - var S = String(this); + var rx = anObject(this); + var S = String(string); var functionalReplace = typeof replaceValue === 'function'; if (!functionalReplace) replaceValue = String(replaceValue); @@ -95,4 +122,4 @@ fixRegExpWellKnownSymbolLogic('replace', 2, function (REPLACE, nativeReplace, ma return accumulatedResult + S.slice(nextSourcePosition); } ]; -}); +}, !REPLACE_SUPPORTS_NAMED_GROUPS || !REPLACE_KEEPS_$0 || REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE); diff --git a/packages/core-js/modules/es.string.search.js b/packages/core-js/modules/es.string.search.js index ec6fd4f8d6f7..3504bdebaf46 100644 --- a/packages/core-js/modules/es.string.search.js +++ b/packages/core-js/modules/es.string.search.js @@ -6,7 +6,7 @@ var sameValue = require('../internals/same-value'); var regExpExec = require('../internals/regexp-exec-abstract'); // @@search logic -fixRegExpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybeCallNative) { +fixRegExpWellKnownSymbolLogic('search', function (SEARCH, nativeSearch, maybeCallNative) { return [ // `String.prototype.search` method // https://tc39.es/ecma262/#sec-string.prototype.search @@ -17,12 +17,12 @@ fixRegExpWellKnownSymbolLogic('search', 1, function (SEARCH, nativeSearch, maybe }, // `RegExp.prototype[@@search]` method // https://tc39.es/ecma262/#sec-regexp.prototype-@@search - function (regexp) { - var res = maybeCallNative(nativeSearch, regexp, this); + function (string) { + var res = maybeCallNative(nativeSearch, this, string); if (res.done) return res.value; - var rx = anObject(regexp); - var S = String(this); + var rx = anObject(this); + var S = String(string); var previousLastIndex = rx.lastIndex; if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0; diff --git a/packages/core-js/modules/es.string.split.js b/packages/core-js/modules/es.string.split.js index 1277b01dd841..4bc8fb35687f 100644 --- a/packages/core-js/modules/es.string.split.js +++ b/packages/core-js/modules/es.string.split.js @@ -9,14 +9,26 @@ var toLength = require('../internals/to-length'); var callRegExpExec = require('../internals/regexp-exec-abstract'); var regexpExec = require('../internals/regexp-exec'); var stickyHelpers = require('../internals/regexp-sticky-helpers'); +var fails = require('../internals/fails'); var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y; var arrayPush = [].push; var min = Math.min; var MAX_UINT32 = 0xFFFFFFFF; +// Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec +// Weex JS has frozen built-in prototypes, so use try / catch wrapper +var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = !fails(function () { + // eslint-disable-next-line regexp/no-empty-group -- required for testing + var re = /(?:)/; + var originalExec = re.exec; + re.exec = function () { return originalExec.apply(this, arguments); }; + var result = 'ab'.split(re); + return result.length !== 2 || result[0] !== 'a' || result[1] !== 'b'; +}); + // @@split logic -fixRegExpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCallNative) { +fixRegExpWellKnownSymbolLogic('split', function (SPLIT, nativeSplit, maybeCallNative) { var internalSplit; if ( 'abbc'.split(/(b)*/)[1] == 'c' || @@ -85,12 +97,12 @@ fixRegExpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCal // // NOTE: This cannot be properly polyfilled in engines that don't support // the 'y' flag. - function (regexp, limit) { - var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit); + function (string, limit) { + var res = maybeCallNative(internalSplit, this, string, limit, internalSplit !== nativeSplit); if (res.done) return res.value; - var rx = anObject(regexp); - var S = String(this); + var rx = anObject(this); + var S = String(string); var C = speciesConstructor(rx, RegExp); var unicodeMatching = rx.unicode; @@ -131,4 +143,4 @@ fixRegExpWellKnownSymbolLogic('split', 2, function (SPLIT, nativeSplit, maybeCal return A; } ]; -}, UNSUPPORTED_Y); +}, !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC, UNSUPPORTED_Y); diff --git a/packages/core-js/stable/regexp/dot-all.js b/packages/core-js/stable/regexp/dot-all.js new file mode 100644 index 000000000000..9aff75255578 --- /dev/null +++ b/packages/core-js/stable/regexp/dot-all.js @@ -0,0 +1,3 @@ +var parent = require('../../es/regexp/dot-all'); + +module.exports = parent; diff --git a/tests/compat/tests.js b/tests/compat/tests.js index 9938ffed84f2..0ee2e24ccbc3 100644 --- a/tests/compat/tests.js +++ b/tests/compat/tests.js @@ -145,6 +145,12 @@ var TYPED_ARRAY_CONSTRUCTORS_NOT_REQUIRES_WRAPPERS = function () { && new Int8Array(new ArrayBuffer(2), 1, undefined).length == 1; }; +function NCG_SUPPORT() { + var re = RegExp('(?b)'); + return re.exec('b').groups.a === 'b' && + 'b'.replace(re, '$c') === 'bc'; +} + function createIsRegExpLogicTest(name) { return function () { var regexp = /./; @@ -782,7 +788,7 @@ GLOBAL.tests = { 'es.reflect.to-string-tag': function () { return Reflect[Symbol.toStringTag]; }, - 'es.regexp.constructor': function () { + 'es.regexp.constructor': [NCG_SUPPORT, function () { var re1 = /a/g; var re2 = /a/g; re2[Symbol.match] = false; @@ -791,9 +797,13 @@ GLOBAL.tests = { && RegExp(re2) !== re2 && RegExp(re1, 'i') == '/a/i' && new RegExp('a', 'y') // just check that it doesn't throw + && RegExp('.', 's').exec('\n') && RegExp[Symbol.species]; + }], + 'es.regexp.dot-all': function () { + return RegExp('.', 's').dotAll; }, - 'es.regexp.exec': function () { + 'es.regexp.exec': [NCG_SUPPORT, function () { var re1 = /a/; var re2 = /b*/g; var reSticky = new RegExp('a', 'y'); @@ -806,10 +816,11 @@ GLOBAL.tests = { && reSticky.exec('abc')[0] === 'a' && reSticky.exec('abc') === null && (reSticky.lastIndex = 1, reSticky.exec('bac')[0] === 'a') - && (reStickyAnchored.lastIndex = 2, reStickyAnchored.exec('cba') === null); - }, + && (reStickyAnchored.lastIndex = 2, reStickyAnchored.exec('cba') === null) + && RegExp('.', 's').exec('\n'); + }], 'es.regexp.flags': function () { - return /./g.flags === 'g' && new RegExp('a', 'y').flags === 'y'; + return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) === 'sy'; }, 'es.regexp.sticky': function () { return new RegExp('a', 'y').sticky === true; diff --git a/tests/tests/es.regexp.constructor.js b/tests/tests/es.regexp.constructor.js index 423588d61b06..7128d4a7a718 100644 --- a/tests/tests/es.regexp.constructor.js +++ b/tests/tests/es.regexp.constructor.js @@ -1,5 +1,5 @@ /* eslint-disable regexp/order-in-character-class, regexp/no-dupe-characters-character-class -- required for testing */ -/* eslint-disable regexp/no-useless-flag, regexp/sort-flags -- required for testing */ +/* eslint-disable regexp/no-useless-character-class, regexp/no-useless-flag, regexp/sort-flags -- required for testing */ import { DESCRIPTORS, GLOBAL } from '../helpers/constants'; import { nativeSubclass } from '../helpers/helpers'; @@ -43,4 +43,47 @@ if (DESCRIPTORS) { assert.ok(new Subclass('^abc$').test('abc'), 'correct subclassing with native classes #3'); } }); + + QUnit.test('RegExp dotAll', assert => { + assert.same(RegExp('.', '').test('\n'), false, 'dotAll missed'); + assert.same(RegExp('.', 's').test('\n'), true, 'dotAll basic'); + assert.same(RegExp('[.]', 's').test('\n'), false, 'dotAll brackets #1'); + assert.same(RegExp('[.].', '').test('.\n'), false, 'dotAll brackets #2'); + assert.same(RegExp('[.].', 's').test('.\n'), true, 'dotAll brackets #3'); + assert.same(RegExp('[[].', 's').test('[\n'), true, 'dotAll brackets #4'); + assert.same(RegExp('.[.[].\\..', 's').source, '.[.[].\\..', 'dotAll correct source'); + + const string = '123\n456789\n012'; + const re = RegExp('(\\d{3}).\\d{3}', 'sy'); + + let match = re.exec(string); + assert.same(match[1], '123', 's with y #1'); + assert.same(re.lastIndex, 7, 's with y #2'); + + match = re.exec(string); + assert.same(match[1], '789', 's with y #3'); + assert.same(re.lastIndex, 14, 's with y #4'); + }); + + QUnit.test('RegExp NCG', assert => { + assert.same(RegExp('(?b)').exec('b').groups?.a, 'b', 'NCG #1'); + // eslint-disable-next-line regexp/no-unused-capturing-group -- required for testing + assert.same(RegExp('(b)').exec('b').groups, undefined, 'NCG #2'); + const { groups } = RegExp('foo:(?\\w+),bar:(?\\w+)').exec('foo:abc,bar:def'); + assert.deepEqual(groups, { foo: 'abc', bar: 'def' }, 'NCG #3'); + // fails in Safari + // assert.same(Object.getPrototypeOf(groups), null, 'NCG #4'); + assert.same('foo:abc,bar:def'.replace(RegExp('foo:(?\\w+),bar:(?\\w+)'), '$,$'), 'def,abc', 'replace #1'); + assert.same('foo:abc,bar:def'.replace(RegExp('foo:(?\\w+),bar:(?\\w+)'), (...args) => { + const { foo, bar } = args.pop(); + return `${ bar },${ foo }`; + }), 'def,abc', 'replace #2'); + assert.same('12345'.replaceAll(RegExp('(?[2-4])', 'g'), '$$'), '12233445', 'replaceAll'); + // eslint-disable-next-line no-invalid-regexp -- required for testing + assert.throws(() => RegExp('(?<1a>b)'), SyntaxError, 'incorrect group name #1'); + // eslint-disable-next-line no-invalid-regexp -- required for testing + assert.throws(() => RegExp('(?b)'), SyntaxError, 'incorrect group name #2'); + // eslint-disable-next-line no-invalid-regexp -- required for testing + assert.throws(() => RegExp('(?< a >b)'), SyntaxError, 'incorrect group name #3'); + }); } diff --git a/tests/tests/es.regexp.dot-all.js b/tests/tests/es.regexp.dot-all.js new file mode 100644 index 000000000000..ebbcbfcbb730 --- /dev/null +++ b/tests/tests/es.regexp.dot-all.js @@ -0,0 +1,32 @@ +import { DESCRIPTORS } from '../helpers/constants'; + +if (DESCRIPTORS) { + QUnit.test('RegExp#dotAll', assert => { + const re = RegExp('.', 's'); + assert.same(re.dotAll, true, '.dotAll is true'); + assert.same(re.flags, 's', '.flags contains s'); + assert.same(RegExp('.').dotAll, false, 'no'); + assert.same(/a/.dotAll, false, 'no in literal'); + + const dotAllGetter = Object.getOwnPropertyDescriptor(RegExp.prototype, 'dotAll').get; + if (typeof dotAllGetter === 'function') { + assert.throws(() => { + dotAllGetter.call({}); + }, undefined, '.dotAll getter can only be called on RegExp instances'); + try { + dotAllGetter.call(/a/); + assert.ok(true, '.dotAll getter works on literals'); + } catch (error) { + assert.ok(false, '.dotAll getter works on literals'); + } + try { + dotAllGetter.call(new RegExp('a')); + assert.ok(true, '.dotAll getter works on instances'); + } catch (error) { + assert.ok(false, '.dotAll getter works on instances'); + } + + assert.ok(Object.hasOwnProperty.call(RegExp.prototype, 'dotAll'), 'prototype has .dotAll property'); + } + }); +}