Skip to content

Commit

Permalink
Add support for RegExp constructor with (RegExp, String).
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Dec 9, 2014
1 parent 7077d36 commit d825d40
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 24 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ details.
* `startsWith()` ([a standalone shim is also available](http://mths.be/startswith))
* `endsWith()` ([a standalone shim is also available](http://mths.be/endswith))
* `includes()` ([a standalone shim is also available](http://mths.be/includes))
* `RegExp`:
* `new RegExp`, when given a RegExp as the pattern, will no longer throw when given a "flags" string argument. (requires ES5)
* `RegExp.prototype`:
* `flags` (requires ES5)
* `Number`:
Expand Down
55 changes: 55 additions & 0 deletions es6-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,27 @@
enumerable: false,
get: getter
});
},
proxy: function (originalObject, key, targetObject) {
if (!supportsDescriptors) {
throw new TypeError('getters require true ES5 support');
}
var originalDescriptor = Object.getOwnPropertyDescriptor(originalObject, key);
Object.defineProperty(targetObject, key, {
configurable: originalDescriptor.configurable,
enumerable: originalDescriptor.enumerable,
get: function getKey() { return originalObject[key]; },
set: function setKey(value) { originalObject[key] = value; }
});
},
redefine: function (object, property, newValue) {
if (supportsDescriptors) {
var descriptor = Object.getOwnPropertyDescriptor(object, property);
descriptor.value = newValue;
Object.defineProperty(object, property, descriptor);
} else {
object[property] = newValue;
}
}
};

Expand Down Expand Up @@ -1042,6 +1063,40 @@
Value.getter(RegExp.prototype, 'flags', regExpFlagsGetter);
}

var regExpSupportsFlagsWithRegex = (function () {
try {
return String(new RegExp(/a/g, 'i')) === '/a/i';
} catch (e) {
return false;
}
}());

if (!regExpSupportsFlagsWithRegex && supportsDescriptors) {
var OrigRegExp = RegExp;
var RegExpShim = function RegExp(pattern, flags) {
if (Type.regex(pattern) && Type.string(flags)) {
return new RegExp(pattern.source, flags);
}
return new OrigRegExp(pattern, flags);
};
defineProperty(RegExpShim, 'toString', OrigRegExp.toString.bind(OrigRegExp), true);
if (Object.setPrototypeOf) {
// sets up proper prototype chain where possible
Object.setPrototypeOf(OrigRegExp, RegExpShim);
}
Object.getOwnPropertyNames(OrigRegExp).forEach(function (key) {
if (key === '$input') { return; } // Chrome < v39 & Opera < 26 have a nonstandard "$input" property
if (key in noop) { return; }
Value.proxy(OrigRegExp, key, RegExpShim);
});
RegExpShim.prototype = OrigRegExp.prototype;
Value.redefine(OrigRegExp.prototype, 'constructor', RegExpShim);
/*globals RegExp: true */
RegExp = RegExpShim;
Value.redefine(globals, 'RegExp', RegExpShim);
/*globals RegExp: false */
}

var MathShims = {
acosh: function (value) {
value = Number(value);
Expand Down
79 changes: 55 additions & 24 deletions test/regexp.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*global describe, it, expect, require */
/*global describe, it, xit, expect, require */

var exported = require('../');

Expand Down Expand Up @@ -28,19 +28,30 @@ describe('RegExp', function () {
expect(String(new RegExp('a', 'mgi'))).to.equal('/a/gim');
});

it('allows a regex with flags', function () {
var a = /a/g;
var makeRegex = function () { return new RegExp(a, 'mi'); };
expect(makeRegex).not.to['throw'](TypeError);
expect(makeRegex()).to.eql(/a/mi);
expect(String(makeRegex())).to.equal('/a/im');
});

it('works with instanceof', function () {
expect(/a/g).to.be.an['instanceof'](RegExp);
expect(new RegExp('a', 'im')).to.be.an['instanceof'](RegExp);
expect(/a/g).to.be.an.instanceOf(RegExp);
expect(new RegExp('a', 'im')).to.be.an.instanceOf(RegExp);
expect(new RegExp(/a/g, 'im')).to.be.an.instanceOf(RegExp);
});

it('has the right constructor', function () {
expect(/a/g).to.have.property('constructor', RegExp);
expect(new RegExp('a', 'im')).to.have.property('constructor', RegExp);
expect(new RegExp(/a/g, 'im')).to.have.property('constructor', RegExp);
});

it('toStrings properly', function () {
expect(Object.prototype.toString.call(/a/g)).to.equal('[object RegExp]');
expect(Object.prototype.toString.call(new RegExp('a', 'g'))).to.equal('[object RegExp]');
expect(Object.prototype.toString.call(new RegExp(/a/g, 'im'))).to.equal('[object RegExp]');
});
});

Expand Down Expand Up @@ -102,18 +113,49 @@ describe('RegExp', function () {
});

describe('Object properties', function () {
xit('does not have the nonstandard $input property', function () {
it('does not have the nonstandard $input property', function () {
expect(RegExp).not.to.have.property('$input'); // Chrome < 39, Opera < 26 have this
});

it('has "input" property', function () {
expect(Object.getOwnPropertyNames(RegExp)).to.include('input');
expect(Object.getOwnPropertyNames(RegExp)).to.include('$_');
expect(Object.keys(RegExp)).not.to.include('$_');
});

it('has "last match" property', function () {
expect(Object.getOwnPropertyNames(RegExp)).to.include('lastMatch');
expect(Object.getOwnPropertyNames(RegExp)).to.include('$+');
expect(Object.keys(RegExp)).not.to.include('$+');
});

it('has "last paren" property', function () {
expect(Object.getOwnPropertyNames(RegExp)).to.include('lastParen');
expect(Object.getOwnPropertyNames(RegExp)).to.include('$&');
expect(Object.keys(RegExp)).not.to.include('$&');
});

it('has "leftContext" property', function () {
expect(Object.getOwnPropertyNames(RegExp)).to.include('leftContext');
expect(Object.getOwnPropertyNames(RegExp)).to.include('$`');
expect(Object.keys(RegExp)).not.to.include('$`');
});

it('has "rightContext" property', function () {
expect(Object.getOwnPropertyNames(RegExp)).to.include('rightContext');
expect(Object.getOwnPropertyNames(RegExp)).to.include("$'");
expect(Object.keys(RegExp)).not.to.include("$'");
});

xit('has "multiline" property', function () {
// fails in IE 9, 10, 11
expect(Object.getOwnPropertyNames(RegExp)).to.include('multiline');
expect(Object.getOwnPropertyNames(RegExp)).to.include('$*');
expect(Object.keys(RegExp)).not.to.include('$*');
});

it('has the right globals', function () {
var enumerableKeys = [
'input',
'multiline',
'lastMatch',
'lastParen',
'leftContext',
'rightContext',
var matchVars = [
'$1',
'$2',
'$3',
Expand All @@ -124,20 +166,9 @@ describe('RegExp', function () {
'$8',
'$9'
];
var nonEnumerableKeys = [
'$_',
'$*',
'$&',
'$+',
'$`',
"$'"
];
expect(Object.keys(RegExp)).to.eql(enumerableKeys);
var noop = function () {};
var actualNonEnumerables = Object.getOwnPropertyNames(RegExp).filter(function (key) {
return !(key in noop) && key !== '$input'; // see above test
matchVars.forEach(function (match) {
expect(RegExp).to.have.property(match);
});
expect(actualNonEnumerables).to.have.members(enumerableKeys.concat(nonEnumerableKeys));
});

it('updates RegExp globals', function () {
Expand Down

0 comments on commit d825d40

Please sign in to comment.