diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 6a78ae8397..1b29a1ce6b 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -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); + } +`; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/.npmignore b/packages/babel-plugin-transform-named-capturing-groups-regex/.npmignore new file mode 100644 index 0000000000..f980694583 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/README.md b/packages/babel-plugin-transform-named-capturing-groups-regex/README.md new file mode 100644 index 0000000000..a04992d5c6 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/README.md @@ -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 +``` diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/package.json b/packages/babel-plugin-transform-named-capturing-groups-regex/package.json new file mode 100644 index 0000000000..5012e9e8c7 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/package.json @@ -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" + } +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.js b/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.js new file mode 100644 index 0000000000..042d16de9d --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.js @@ -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. /\(? 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" }) + ); +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/exec.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/exec.js new file mode 100644 index 0000000000..6f6e7eb977 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/exec.js @@ -0,0 +1,15 @@ +var re = /(?\d{4})-(?\d{2})-(?\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], +}); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/instanceof-regexp.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/instanceof-regexp.js new file mode 100644 index 0000000000..7b3eea5664 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/instanceof-regexp.js @@ -0,0 +1,3 @@ +var re = /(?)/; + +expect(re instanceof RegExp).toBeTruthy(); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/match.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/match.js new file mode 100644 index 0000000000..9fbe902285 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/match.js @@ -0,0 +1,15 @@ +var re = /(?\d{4})-(?\d{2})-(?\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], +}); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/no-groups.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/no-groups.js new file mode 100644 index 0000000000..030be5eea3 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/no-groups.js @@ -0,0 +1,4 @@ +var re = /no-groups-\(?looks\)/; +var result = re.exec("no-groups-(looks)") + +expect(result.groups).toBeUndefined(); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/options.json b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/options.json new file mode 100644 index 0000000000..8b8fe93e5d --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-named-capturing-groups-regex"] +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/replace-string.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/replace-string.js new file mode 100644 index 0000000000..2902c419f5 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/runtime/replace-string.js @@ -0,0 +1,5 @@ +var re = /(?\d{4})-(?\d{2})-(?\d{2})/; + +var result = "2015-10-31".replace(re, "$/$/$") + +expect(result).toBe("31/10/2015"); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js new file mode 100644 index 0000000000..8414081fd5 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js @@ -0,0 +1 @@ +/(?)\k/; \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/options.json b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/options.json new file mode 100644 index 0000000000..3b43d3bd5d --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "external-helpers", + ["transform-named-capturing-groups-regex", { "runtime": false }] + ] +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js new file mode 100644 index 0000000000..67c955321e --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js @@ -0,0 +1 @@ +/()\1/; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/input.js new file mode 100644 index 0000000000..2393cc30f5 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/input.js @@ -0,0 +1 @@ +/(?<\u{41}>)\k<\u{41}>/; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/options.json b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/options.json new file mode 100644 index 0000000000..47dc981e75 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-no-unicode-flag/options.json @@ -0,0 +1,3 @@ +{ + "throws": "invalid group Unicode name \"\\u{41}\", use `u` flag." +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/input.js new file mode 100644 index 0000000000..e455e5e672 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/input.js @@ -0,0 +1 @@ +/(?<\u{41}>)\k<\u{41}>/u; \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/output.js new file mode 100644 index 0000000000..5548e73686 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/escape-unicode-flag/output.js @@ -0,0 +1 @@ +/()\1/u; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/options.json b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/options.json new file mode 100644 index 0000000000..2490cf9876 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/syntax/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.1000.0" }], + ["transform-named-capturing-groups-regex", { "runtime": false }] + ] +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/input.js new file mode 100644 index 0000000000..2761445e09 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/input.js @@ -0,0 +1 @@ +"foo".match(/(?.)\k/); \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js new file mode 100644 index 0000000000..2c6eed2f6d --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js @@ -0,0 +1,3 @@ +"foo".match(babelHelpers.wrapRegExp(/(.)\1/, { + double: 1 +})); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/input.js new file mode 100644 index 0000000000..4c55ef0dbe --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/input.js @@ -0,0 +1 @@ +/no-groups-\(?looks\)/; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js new file mode 100644 index 0000000000..4c55ef0dbe --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js @@ -0,0 +1 @@ +/no-groups-\(?looks\)/; diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/input.js new file mode 100644 index 0000000000..581032632a --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/input.js @@ -0,0 +1 @@ +"foo".match(/(.)\1/); \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/output.js new file mode 100644 index 0000000000..c545e42ac5 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/output.js @@ -0,0 +1 @@ +"foo".match(/(.)\1/); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/options.json b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/options.json new file mode 100644 index 0000000000..ce85d0c0f4 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["external-helpers", { "helperVersion": "7.1000.0" }], + "transform-named-capturing-groups-regex" + ] +} diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/input.js new file mode 100644 index 0000000000..86639f6f7e --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/input.js @@ -0,0 +1 @@ +"abba".match(/(.)(?.)\k\1/); \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js new file mode 100644 index 0000000000..f9da17ab47 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js @@ -0,0 +1,3 @@ +"abba".match(babelHelpers.wrapRegExp(/(.)(.)\2\1/, { + n: 2 +})); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/input.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/input.js new file mode 100644 index 0000000000..3e26e4ee49 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/input.js @@ -0,0 +1 @@ +/^(?.)\k$/.test("aa"); \ No newline at end of file diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js new file mode 100644 index 0000000000..808fc64e4f --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js @@ -0,0 +1 @@ +/^(.)\1$/.test("aa"); diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/test/index.js b/packages/babel-plugin-transform-named-capturing-groups-regex/test/index.js new file mode 100644 index 0000000000..1b534b8fc6 --- /dev/null +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/test/index.js @@ -0,0 +1,3 @@ +import runner from "@babel/helper-plugin-test-runner"; + +runner(__dirname);