Skip to content

Commit 0efc25a

Browse files
committed
add .dotAll support
1 parent fad1d44 commit 0efc25a

18 files changed

+185
-30
lines changed

packages/core-js-compat/src/data.js

+2
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,8 @@ const data = {
933933
firefox: '49',
934934
safari: '10.0',
935935
},
936+
'es.regexp.dot-all': {
937+
},
936938
'es.regexp.exec': {
937939
chrome: '26',
938940
edge: '13',

packages/core-js-compat/src/modules-by-versions.js

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ module.exports = {
9292
'es.date.set-year',
9393
'es.date.to-gmt-string',
9494
'es.escape',
95+
'es.regexp.dot-all',
9596
'es.string.substr',
9697
'es.unescape',
9798
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// empty
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// empty

packages/core-js/es/regexp/dot-all.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
require('../../modules/es.regexp.constructor');
2+
require('../../modules/es.regexp.dot-all');
3+
require('../../modules/es.regexp.exec');
4+
5+
module.exports = function (it) {
6+
return it.sticky;
7+
};

packages/core-js/es/regexp/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require('../../modules/es.regexp.constructor');
22
require('../../modules/es.regexp.to-string');
3+
require('../../modules/es.regexp.dot-all');
34
require('../../modules/es.regexp.exec');
45
require('../../modules/es.regexp.flags');
56
require('../../modules/es.regexp.sticky');

packages/core-js/es/regexp/sticky.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require('../../modules/es.regexp.constructor');
2+
require('../../modules/es.regexp.exec');
13
require('../../modules/es.regexp.sticky');
24

35
module.exports = function (it) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var parent = require('../../stable/regexp/dot-all');
2+
3+
module.exports = parent;

packages/core-js/internals/regexp-exec.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
var regexpFlags = require('./regexp-flags');
55
var stickyHelpers = require('./regexp-sticky-helpers');
66
var shared = require('./shared');
7+
var getInternalState = require('../internals/internal-state').get;
8+
var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all');
79

810
var nativeExec = RegExp.prototype.exec;
911
var nativeReplace = shared('native-string-replace', String.prototype.replace);
@@ -23,12 +25,21 @@ var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y || stickyHelpers.BROKEN_CARET;
2325
// nonparticipating capturing group, copied from es5-shim's String#split patch.
2426
var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
2527

26-
var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y;
28+
var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y || UNSUPPORTED_DOT_ALL;
2729

2830
if (PATCH) {
2931
patchedExec = function exec(str) {
3032
var re = this;
31-
var lastIndex, reCopy, match, i;
33+
var lastIndex, reCopy, match, i, result;
34+
var raw = UNSUPPORTED_DOT_ALL && getInternalState(re).raw;
35+
36+
if (raw) {
37+
raw.lastIndex = re.lastIndex;
38+
result = patchedExec.call(raw, str);
39+
re.lastIndex = raw.lastIndex;
40+
return result;
41+
}
42+
3243
var sticky = UNSUPPORTED_Y && re.sticky;
3344
var flags = regexpFlags.call(re);
3445
var source = re.source;

packages/core-js/internals/regexp-sticky-helpers.js

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
1-
'use strict';
2-
31
var fails = require('./fails');
42

53
// babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,
6-
// so we use an intermediate function.
7-
function RE(s, f) {
4+
var RE = function RE(s, f) {
85
return RegExp(s, f);
9-
}
6+
};
107

118
exports.UNSUPPORTED_Y = fails(function () {
12-
// babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError
139
var re = RE('a', 'y');
1410
re.lastIndex = 2;
1511
return re.exec('abcd') != null;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var fails = require('./fails');
2+
3+
module.exports = fails(function () {
4+
// babel-minify transpiles RegExp('.', 's') -> /./s and it causes SyntaxError
5+
var re = RegExp('.', (typeof '').charAt(0));
6+
return !(re.dotAll && re.exec('\n') && re.flags === 's');
7+
});

packages/core-js/modules/es.regexp.constructor.js

+50-11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ var fails = require('../internals/fails');
1212
var enforceInternalState = require('../internals/internal-state').enforce;
1313
var setSpecies = require('../internals/set-species');
1414
var wellKnownSymbol = require('../internals/well-known-symbol');
15+
var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all');
1516

1617
var MATCH = wellKnownSymbol('match');
1718
var NativeRegExp = global.RegExp;
@@ -24,20 +25,44 @@ var CORRECT_NEW = new NativeRegExp(re1) !== re1;
2425

2526
var UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y;
2627

27-
var FORCED = DESCRIPTORS && isForced('RegExp', (!CORRECT_NEW || UNSUPPORTED_Y || fails(function () {
28+
var BASE_FORCED = DESCRIPTORS && !CORRECT_NEW || UNSUPPORTED_Y || UNSUPPORTED_DOT_ALL || fails(function () {
2829
re2[MATCH] = false;
2930
// RegExp constructor can alter flags and IsRegExp works correct with @@match
3031
return NativeRegExp(re1) != re1 || NativeRegExp(re2) == re2 || NativeRegExp(re1, 'i') != '/a/i';
31-
})));
32+
});
33+
34+
var deDotAll = function (string) {
35+
var result = '';
36+
var index = 0;
37+
var length = string.length;
38+
var brackets = false;
39+
var chr;
40+
for (; index <= length; index++) {
41+
chr = string.charAt(index);
42+
if (chr === '\\') {
43+
result += chr + string.charAt(++index);
44+
continue;
45+
}
46+
if (!brackets && chr === '.') {
47+
result += '[\\s\\S]';
48+
} else {
49+
if (chr === '[') {
50+
brackets = true;
51+
} else if (chr === ']') {
52+
brackets = false;
53+
} result += chr;
54+
}
55+
} return result;
56+
};
3257

3358
// `RegExp` constructor
3459
// https://tc39.es/ecma262/#sec-regexp-constructor
35-
if (FORCED) {
60+
if (isForced('RegExp', BASE_FORCED)) {
3661
var RegExpWrapper = function RegExp(pattern, flags) {
3762
var thisIsRegExp = this instanceof RegExpWrapper;
3863
var patternIsRegExp = isRegExp(pattern);
3964
var flagsAreUndefined = flags === undefined;
40-
var sticky;
65+
var rawFlags, dotAll, sticky, result, state;
4166

4267
if (!thisIsRegExp && patternIsRegExp && pattern.constructor === RegExpWrapper && flagsAreUndefined) {
4368
return pattern;
@@ -50,34 +75,48 @@ if (FORCED) {
5075
pattern = pattern.source;
5176
}
5277

78+
if (UNSUPPORTED_DOT_ALL) {
79+
dotAll = !!flags && flags.indexOf('s') > -1;
80+
if (dotAll) flags = flags.replace(/s/g, '');
81+
}
82+
83+
rawFlags = flags;
84+
5385
if (UNSUPPORTED_Y) {
5486
sticky = !!flags && flags.indexOf('y') > -1;
5587
if (sticky) flags = flags.replace(/y/g, '');
5688
}
5789

58-
var result = inheritIfRequired(
90+
result = inheritIfRequired(
5991
CORRECT_NEW ? new NativeRegExp(pattern, flags) : NativeRegExp(pattern, flags),
6092
thisIsRegExp ? this : RegExpPrototype,
6193
RegExpWrapper
6294
);
6395

64-
if (UNSUPPORTED_Y && sticky) {
65-
var state = enforceInternalState(result);
66-
state.sticky = true;
96+
if (dotAll || sticky) {
97+
state = enforceInternalState(result);
98+
if (dotAll) {
99+
state.dotAll = true;
100+
state.raw = RegExpWrapper(deDotAll(pattern), rawFlags);
101+
}
102+
if (sticky) state.sticky = true;
67103
}
68104

69105
return result;
70106
};
107+
71108
var proxy = function (key) {
72109
key in RegExpWrapper || defineProperty(RegExpWrapper, key, {
73110
configurable: true,
74111
get: function () { return NativeRegExp[key]; },
75112
set: function (it) { NativeRegExp[key] = it; }
76113
});
77114
};
78-
var keys = getOwnPropertyNames(NativeRegExp);
79-
var index = 0;
80-
while (keys.length > index) proxy(keys[index++]);
115+
116+
for (var keys = getOwnPropertyNames(NativeRegExp), index = 0; keys.length > index;) {
117+
proxy(keys[index++]);
118+
}
119+
81120
RegExpPrototype.constructor = RegExpWrapper;
82121
RegExpWrapper.prototype = RegExpPrototype;
83122
redefine(global, 'RegExp', RegExpWrapper);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
var DESCRIPTORS = require('../internals/descriptors');
2+
var UNSUPPORTED_DOT_ALL = require('../internals/regexp-unsupported-dot-all');
3+
var defineProperty = require('../internals/object-define-property').f;
4+
var getInternalState = require('../internals/internal-state').get;
5+
var RegExpPrototype = RegExp.prototype;
6+
7+
// `RegExp.prototype.dotAll` getter
8+
// https://tc39.es/ecma262/#sec-get-regexp.prototype.dotall
9+
if (DESCRIPTORS && UNSUPPORTED_DOT_ALL) {
10+
defineProperty(RegExpPrototype, 'dotAll', {
11+
configurable: true,
12+
get: function () {
13+
if (this === RegExpPrototype) return undefined;
14+
// We can't use InternalStateModule.getterFor because
15+
// we don't add metadata for regexps created by a literal.
16+
if (this instanceof RegExp) {
17+
return !!getInternalState(this).dotAll;
18+
}
19+
throw TypeError('Incompatible receiver, RegExp required');
20+
}
21+
});
22+
}
+10-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
var DESCRIPTORS = require('../internals/descriptors');
22
var objectDefinePropertyModule = require('../internals/object-define-property');
33
var regExpFlags = require('../internals/regexp-flags');
4-
var UNSUPPORTED_Y = require('../internals/regexp-sticky-helpers').UNSUPPORTED_Y;
4+
var fails = require('../internals/fails');
5+
6+
var FORCED = DESCRIPTORS && fails(function () {
7+
// eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
8+
return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) !== 'sy';
9+
});
510

611
// `RegExp.prototype.flags` getter
712
// https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
8-
// eslint-disable-next-line es/no-regexp-prototype-flags -- required for testing
9-
if (DESCRIPTORS && (/./g.flags != 'g' || UNSUPPORTED_Y)) {
10-
objectDefinePropertyModule.f(RegExp.prototype, 'flags', {
11-
configurable: true,
12-
get: regExpFlags
13-
});
14-
}
13+
if (FORCED) objectDefinePropertyModule.f(RegExp.prototype, 'flags', {
14+
configurable: true,
15+
get: regExpFlags
16+
});

packages/core-js/modules/es.regexp.sticky.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ var RegExpPrototype = RegExp.prototype;
77
// `RegExp.prototype.sticky` getter
88
// https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
99
if (DESCRIPTORS && UNSUPPORTED_Y) {
10-
defineProperty(RegExp.prototype, 'sticky', {
10+
defineProperty(RegExpPrototype, 'sticky', {
1111
configurable: true,
1212
get: function () {
1313
if (this === RegExpPrototype) return undefined;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var parent = require('../../es/regexp/dot-all');
2+
3+
module.exports = parent;

tests/compat/tests.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -791,8 +791,12 @@ GLOBAL.tests = {
791791
&& RegExp(re2) !== re2
792792
&& RegExp(re1, 'i') == '/a/i'
793793
&& new RegExp('a', 'y') // just check that it doesn't throw
794+
&& RegExp('.', 's').exec('\n')
794795
&& RegExp[Symbol.species];
795796
},
797+
'es.regexp.dot-all': function () {
798+
return RegExp('.', 's').dotAll;
799+
},
796800
'es.regexp.exec': function () {
797801
var re1 = /a/;
798802
var re2 = /b*/g;
@@ -806,10 +810,11 @@ GLOBAL.tests = {
806810
&& reSticky.exec('abc')[0] === 'a'
807811
&& reSticky.exec('abc') === null
808812
&& (reSticky.lastIndex = 1, reSticky.exec('bac')[0] === 'a')
809-
&& (reStickyAnchored.lastIndex = 2, reStickyAnchored.exec('cba') === null);
813+
&& (reStickyAnchored.lastIndex = 2, reStickyAnchored.exec('cba') === null)
814+
&& RegExp('.', 's').exec('\n');
810815
},
811816
'es.regexp.flags': function () {
812-
return /./g.flags === 'g' && new RegExp('a', 'y').flags === 'y';
817+
return Object.getOwnPropertyDescriptor(RegExp.prototype, 'flags').get.call({ dotAll: true, sticky: true }) === 'sy';
813818
},
814819
'es.regexp.sticky': function () {
815820
return new RegExp('a', 'y').sticky === true;

tests/tests/es.regexp.dot-all.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/* eslint-disable regexp/no-useless-character-class, regexp/no-useless-flag -- required for testing */
2+
import { DESCRIPTORS } from '../helpers/constants';
3+
4+
if (DESCRIPTORS) {
5+
QUnit.test('RegExp#dotAll', assert => {
6+
let re = RegExp('.', 's');
7+
assert.same(re.dotAll, true, '.dotAll is true');
8+
assert.same(re.flags, 's', '.flags contains s');
9+
assert.same(RegExp('.').dotAll, false, 'no');
10+
assert.same(/a/.dotAll, false, 'no in literal');
11+
12+
assert.same(RegExp('.', '').test('\n'), false, 'dotAll missed');
13+
assert.same(RegExp('.', 's').test('\n'), true, 'dotAll basic');
14+
assert.same(RegExp('[.]', 's').test('\n'), false, 'dotAll brackets #1');
15+
assert.same(RegExp('[.].', '').test('.\n'), false, 'dotAll brackets #2');
16+
assert.same(RegExp('[.].', 's').test('.\n'), true, 'dotAll brackets #3');
17+
assert.same(RegExp('[[].', 's').test('[\n'), true, 'dotAll brackets #4');
18+
assert.same(RegExp('.[.[].\\..', 's').source, '.[.[].\\..', 'dotAll correct source');
19+
20+
const string = '123\n456789\n012';
21+
re = RegExp('(\\d{3}).\\d{3}', 'sy');
22+
23+
let match = re.exec(string);
24+
assert.same(match[1], '123', 's with y #1');
25+
assert.same(re.lastIndex, 7, 's with y #2');
26+
27+
match = re.exec(string);
28+
assert.same(match[1], '789', 's with y #3');
29+
assert.same(re.lastIndex, 14, 's with y #4');
30+
31+
const dotAllGetter = Object.getOwnPropertyDescriptor(RegExp.prototype, 'dotAll').get;
32+
if (typeof dotAllGetter === 'function') {
33+
assert.throws(() => {
34+
dotAllGetter.call({});
35+
}, undefined, '.dotAll getter can only be called on RegExp instances');
36+
try {
37+
dotAllGetter.call(/a/);
38+
assert.ok(true, '.dotAll getter works on literals');
39+
} catch (error) {
40+
assert.ok(false, '.dotAll getter works on literals');
41+
}
42+
try {
43+
dotAllGetter.call(new RegExp('a'));
44+
assert.ok(true, '.dotAll getter works on instances');
45+
} catch (error) {
46+
assert.ok(false, '.dotAll getter works on instances');
47+
}
48+
49+
assert.ok(Object.hasOwnProperty.call(RegExp.prototype, 'dotAll'), 'prototype has .dotAll property');
50+
}
51+
});
52+
}

0 commit comments

Comments
 (0)