Add @babel/plugin-transform-named-capturing-groups-regex (#7105)
When the `runtime` flag is on (by default), this plugin adds a new helper which wraps the native `RegExp` class to provide groups support. People nees to use a polyfill (I implemented it in core-js) for browsers that don't support ES6 regexps.
This commit is contained in:
parent
aaec2cd51d
commit
a27b9b4299
@ -1783,3 +1783,75 @@ helpers.classPrivateMethodSet = helper("7.1.6")`
|
||||
throw new TypeError("attempted to reassign private method");
|
||||
}
|
||||
`;
|
||||
|
||||
helpers.wrapRegExp = helper("7.2.6")`
|
||||
import wrapNativeSuper from "wrapNativeSuper";
|
||||
import getPrototypeOf from "getPrototypeOf";
|
||||
import possibleConstructorReturn from "possibleConstructorReturn";
|
||||
import inherits from "inherits";
|
||||
|
||||
export default function _wrapRegExp(re, groups) {
|
||||
_wrapRegExp = function(re, groups) {
|
||||
return new BabelRegExp(re, groups);
|
||||
};
|
||||
|
||||
var _RegExp = wrapNativeSuper(RegExp);
|
||||
var _super = RegExp.prototype;
|
||||
var _groups = new WeakMap();
|
||||
|
||||
function BabelRegExp(re, groups) {
|
||||
var _this = _RegExp.call(this, re);
|
||||
_groups.set(_this, groups);
|
||||
return _this;
|
||||
}
|
||||
inherits(BabelRegExp, _RegExp);
|
||||
|
||||
BabelRegExp.prototype.exec = function(str) {
|
||||
var result = _super.exec.call(this, str);
|
||||
if (result) result.groups = buildGroups(result, this);
|
||||
return result;
|
||||
};
|
||||
BabelRegExp.prototype[Symbol.replace] = function(str, substitution) {
|
||||
if (typeof substitution === "string") {
|
||||
var groups = _groups.get(this);
|
||||
return _super[Symbol.replace].call(
|
||||
this,
|
||||
str,
|
||||
substitution.replace(/\\$<([^>]+)>/g, function(_, name) {
|
||||
return "$" + groups[name];
|
||||
})
|
||||
);
|
||||
} else if (typeof substitution === "function") {
|
||||
var _this = this;
|
||||
return _super[Symbol.replace].call(
|
||||
this,
|
||||
str,
|
||||
function() {
|
||||
var args = [];
|
||||
args.push.apply(args, arguments);
|
||||
if (typeof args[args.length - 1] !== "object") {
|
||||
// Modern engines already pass result.groups as the last arg.
|
||||
args.push(buildGroups(args, _this));
|
||||
}
|
||||
return substitution.apply(this, args);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
return _super[Symbol.replace].call(this, str, substitution);
|
||||
}
|
||||
}
|
||||
|
||||
function buildGroups(result, re) {
|
||||
// NOTE: This function should return undefined if there are no groups,
|
||||
// but in that case Babel doesn't add the wrapper anyway.
|
||||
|
||||
var g = _groups.get(re);
|
||||
return Object.keys(groups).reduce(function(groups, name) {
|
||||
groups[name] = result[g[name]];
|
||||
return groups;
|
||||
}, Object.create(null));
|
||||
}
|
||||
|
||||
return _wrapRegExp.apply(this, arguments);
|
||||
}
|
||||
`;
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
src
|
||||
test
|
||||
*.log
|
||||
@ -0,0 +1,19 @@
|
||||
# @babel/plugin-transform-named-capturing-groups-regex
|
||||
|
||||
> Compile regular expressions using named groups to ES5.
|
||||
|
||||
See our website [@babel/plugin-transform-named-capturing-groups-regex](https://babeljs.io/docs/en/next/babel-plugin-transform-named-capturing-groups-regex.html) for more information.
|
||||
|
||||
## Install
|
||||
|
||||
Using npm:
|
||||
|
||||
```sh
|
||||
npm install --save-dev @babel/plugin-transform-named-capturing-groups-regex
|
||||
```
|
||||
|
||||
or using yarn:
|
||||
|
||||
```sh
|
||||
yarn add @babel/plugin-transform-named-capturing-groups-regex --dev
|
||||
```
|
||||
@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "@babel/plugin-transform-named-capturing-groups-regex",
|
||||
"version": "7.2.5",
|
||||
"description": "Compile regular expressions using named groups to ES5.",
|
||||
"homepage": "https://babeljs.io/",
|
||||
"license": "MIT",
|
||||
"main": "lib/index.js",
|
||||
"keywords": [
|
||||
"babel-plugin",
|
||||
"regex",
|
||||
"regexp",
|
||||
"regular expressions"
|
||||
],
|
||||
"repository": "https://github.com/babel/babel/tree/master/packages/babel-plugin-transform-named-capturing-groups-regex",
|
||||
"bugs": "https://github.com/babel/babel/issues",
|
||||
"dependencies": {
|
||||
"regexp-tree": "^0.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/helper-plugin-test-runner": "^7.0.0"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
import regexpTree from "regexp-tree";
|
||||
|
||||
export default function({ types: t }, options) {
|
||||
const { runtime = true } = options;
|
||||
if (typeof runtime !== "boolean") {
|
||||
throw new Error("The 'runtime' option must be boolean");
|
||||
}
|
||||
|
||||
return {
|
||||
name: "transform-named-capturing-groups-regex",
|
||||
|
||||
visitor: {
|
||||
RegExpLiteral(path) {
|
||||
const node = path.node;
|
||||
if (node.pattern.indexOf("(?<") === -1) {
|
||||
// Return early if there are no named groups.
|
||||
// The .indexOf check may have false positives (e.g. /\(?</); in
|
||||
// this case we parse the regex and regexp-tree won't transform it.
|
||||
return;
|
||||
}
|
||||
|
||||
const result = regexpTree.compatTranspile(node.extra.raw, [
|
||||
"namedCapturingGroups",
|
||||
]);
|
||||
const { namedCapturingGroups } = result.getExtra();
|
||||
|
||||
if (
|
||||
namedCapturingGroups &&
|
||||
Object.keys(namedCapturingGroups).length > 0
|
||||
) {
|
||||
node.pattern = result.getSource();
|
||||
|
||||
if (runtime && !isRegExpTest(path)) {
|
||||
path.replaceWith(
|
||||
t.callExpression(this.addHelper("wrapRegExp"), [
|
||||
node,
|
||||
t.valueToNode(namedCapturingGroups),
|
||||
]),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function isRegExpTest(path) {
|
||||
return (
|
||||
path.parentPath.isMemberExpression({
|
||||
object: path.node,
|
||||
computed: false,
|
||||
}) && path.parentPath.get("property").isIdentifier({ name: "test" })
|
||||
);
|
||||
}
|
||||
15
packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/exec.js
vendored
Normal file
15
packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/exec.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
var re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
|
||||
|
||||
var result = re.exec("2017-12-23");
|
||||
|
||||
expect(result.groups).toEqual({
|
||||
year: "2017",
|
||||
month: "12",
|
||||
day: "23",
|
||||
});
|
||||
|
||||
expect(result.groups).toEqual({
|
||||
year: result[1],
|
||||
month: result[2],
|
||||
day: result[3],
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
var re = /(?<group>)/;
|
||||
|
||||
expect(re instanceof RegExp).toBeTruthy();
|
||||
15
packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/match.js
vendored
Normal file
15
packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/match.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
var re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
|
||||
|
||||
var result = "1999-09-29".match(re);
|
||||
|
||||
expect(result.groups).toEqual({
|
||||
year: "1999",
|
||||
month: "09",
|
||||
day: "29",
|
||||
});
|
||||
|
||||
expect(result.groups).toEqual({
|
||||
year: result[1],
|
||||
month: result[2],
|
||||
day: result[3],
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
var re = /no-groups-\(?<looks-like-a-group>looks\)/;
|
||||
var result = re.exec("no-groups-(<looks-like-a-group>looks)")
|
||||
|
||||
expect(result.groups).toBeUndefined();
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"plugins": ["transform-named-capturing-groups-regex"]
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
var re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
|
||||
|
||||
var result = "2015-10-31".replace(re, "$<day>/$<month>/$<year>")
|
||||
|
||||
expect(result).toBe("31/10/2015");
|
||||
@ -0,0 +1 @@
|
||||
/(?<name>)\k<name>/;
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
"external-helpers",
|
||||
["transform-named-capturing-groups-regex", { "runtime": false }]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
/()\1/;
|
||||
@ -0,0 +1 @@
|
||||
/(?<\u{41}>)\k<\u{41}>/;
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"throws": "invalid group Unicode name \"\\u{41}\", use `u` flag."
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
/(?<\u{41}>)\k<\u{41}>/u;
|
||||
@ -0,0 +1 @@
|
||||
/()\1/u;
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
["external-helpers", { "helperVersion": "7.1000.0" }],
|
||||
["transform-named-capturing-groups-regex", { "runtime": false }]
|
||||
]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
"foo".match(/(?<double>.)\k<double>/);
|
||||
@ -0,0 +1,3 @@
|
||||
"foo".match(babelHelpers.wrapRegExp(/(.)\1/, {
|
||||
double: 1
|
||||
}));
|
||||
@ -0,0 +1 @@
|
||||
/no-groups-\(?<looks-like-a-group>looks\)/;
|
||||
@ -0,0 +1 @@
|
||||
/no-groups-\(?<looks-like-a-group>looks\)/;
|
||||
@ -0,0 +1 @@
|
||||
"foo".match(/(.)\1/);
|
||||
@ -0,0 +1 @@
|
||||
"foo".match(/(.)\1/);
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"plugins": [
|
||||
["external-helpers", { "helperVersion": "7.1000.0" }],
|
||||
"transform-named-capturing-groups-regex"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
"abba".match(/(.)(?<n>.)\k<n>\1/);
|
||||
@ -0,0 +1,3 @@
|
||||
"abba".match(babelHelpers.wrapRegExp(/(.)(.)\2\1/, {
|
||||
n: 2
|
||||
}));
|
||||
@ -0,0 +1 @@
|
||||
/^(?<x>.)\k<x>$/.test("aa");
|
||||
@ -0,0 +1 @@
|
||||
/^(.)\1$/.test("aa");
|
||||
@ -0,0 +1,3 @@
|
||||
import runner from "@babel/helper-plugin-test-runner";
|
||||
|
||||
runner(__dirname);
|
||||
Loading…
x
Reference in New Issue
Block a user