diff --git a/Makefile b/Makefile index be55efdde2d3..52eabafc9955 100644 --- a/Makefile +++ b/Makefile @@ -185,15 +185,16 @@ test-test262-update-allowlist: new-version-checklist: - # @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - # @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - # @echo "!!!!!! !!!!!!" - # @echo "!!!!!! Write any message that should !!!!!!" - # @echo "!!!!!! block the release here !!!!!!" - # @echo "!!!!!! !!!!!!" - # @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - # @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" - # @exit 1 + @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo "!!!!!! !!!!!!" + @echo "!!!!!! Update minVersion in the wrapRegExp helper, !!!!!!" + @echo "!!!!!! and the requried core version in the duplicate !!!!!!" + @echo "!!!!!! named groups plugin. !!!!!!" + @echo "!!!!!! !!!!!!" + @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @echo "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" + @exit 1 new-version: $(MAKE) new-version-checklist diff --git a/packages/babel-helper-create-regexp-features-plugin/src/features.ts b/packages/babel-helper-create-regexp-features-plugin/src/features.ts index abe72d0b4729..ed9b6f4ed60a 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/features.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/features.ts @@ -5,6 +5,7 @@ export const FEATURES = Object.freeze({ namedCaptureGroups: 1 << 3, unicodeSetsFlag_syntax: 1 << 4, unicodeSetsFlag: 1 << 5, + duplicateNamedCaptureGroups: 1 << 6, }); // We can't use a symbol because this needs to always be the same, even if diff --git a/packages/babel-helper-create-regexp-features-plugin/src/index.ts b/packages/babel-helper-create-regexp-features-plugin/src/index.ts index 060a3638f94d..52b9e2188d28 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/index.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/index.ts @@ -1,5 +1,11 @@ import rewritePattern from "regexpu-core"; -import { featuresKey, FEATURES, enableFeature, runtimeKey } from "./features"; +import { + featuresKey, + FEATURES, + enableFeature, + runtimeKey, + hasFeature, +} from "./features"; import { generateRegexpuOptions, canSkipRegexpu, transformFlags } from "./util"; import type { NodePath } from "@babel/traverse"; @@ -44,7 +50,7 @@ export function createRegExpFeaturePlugin({ const features = file.get(featuresKey) ?? 0; let newFeatures = enableFeature(features, FEATURES[feature]); - const { useUnicodeFlag, runtime = true } = options; + const { useUnicodeFlag, runtime } = options; if (useUnicodeFlag === false) { newFeatures = enableFeature(newFeatures, FEATURES.unicodeFlag); } @@ -52,8 +58,29 @@ export function createRegExpFeaturePlugin({ file.set(featuresKey, newFeatures); } - if (!runtime) { - file.set(runtimeKey, false); + if (runtime !== undefined) { + if ( + file.has(runtimeKey) && + file.get(runtimeKey) !== runtime && + // TODO(Babel 8): Remove this check. It's necessary because in Babel 7 + // we allow multiple copies of transform-named-capturing-groups-regex + // with conflicting 'runtime' options. + hasFeature(newFeatures, FEATURES.duplicateNamedCaptureGroups) + ) { + throw new Error( + `The 'runtime' option must be the same for ` + + `'@babel/plugin-transform-named-capturing-groups-regex' and ` + + `'@babel/plugin-proposal-duplicate-named-capturing-groups-regex'.`, + ); + } + // TODO(Babel 8): Remove this check and always set it. + // It's necessary because in Babel 7 we allow multiple copies of + // transform-named-capturing-groups-regex with conflicting 'runtime' options. + if (feature === "namedCaptureGroups") { + if (!runtime || !file.has(runtimeKey)) file.set(runtimeKey, runtime); + } else { + file.set(runtimeKey, runtime); + } } if (!file.has(versionKey) || file.get(versionKey) < version) { @@ -68,13 +95,24 @@ export function createRegExpFeaturePlugin({ const features = file.get(featuresKey); const runtime = file.get(runtimeKey) ?? true; - const regexpuOptions = generateRegexpuOptions(features); - if (canSkipRegexpu(node, regexpuOptions)) return; + const regexpuOptions = generateRegexpuOptions(node.pattern, features); + if (canSkipRegexpu(node, regexpuOptions)) { + return; + } - const namedCaptureGroups: Record = {}; + const namedCaptureGroups: Record = { + __proto__: null, + }; if (regexpuOptions.namedGroups === "transform") { regexpuOptions.onNamedGroup = (name, index) => { - namedCaptureGroups[name] = index; + const prev = namedCaptureGroups[name]; + if (typeof prev === "number") { + namedCaptureGroups[name] = [prev, index]; + } else if (Array.isArray(prev)) { + prev.push(index); + } else { + namedCaptureGroups[name] = index; + } }; } diff --git a/packages/babel-helper-create-regexp-features-plugin/src/util.ts b/packages/babel-helper-create-regexp-features-plugin/src/util.ts index b4c7e5251dfc..d879f0a14e70 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/util.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/util.ts @@ -3,7 +3,10 @@ import { FEATURES, hasFeature } from "./features"; import type { RegexpuOptions } from "regexpu-core"; -export function generateRegexpuOptions(toTransform: number): RegexpuOptions { +export function generateRegexpuOptions( + pattern: string, + toTransform: number, +): RegexpuOptions { type Experimental = 1; const feat = ( @@ -13,6 +16,21 @@ export function generateRegexpuOptions(toTransform: number): RegexpuOptions { return hasFeature(toTransform, FEATURES[name]) ? ok : false; }; + const featDuplicateNamedGroups = (): "transform" | false => { + if (!feat("duplicateNamedCaptureGroups")) return false; + + // This can return false positive, for example for /\(?\)/. + // However, it's such a rare occurrence that it's ok to compile + // the regexp even if we only need to compile regexps with + // duplicate named capturing groups. + const regex = /\(\?<([^>]+)>/g; + const seen = new Set(); + for (let match; (match = regex.exec(pattern)); seen.add(match[1])) { + if (seen.has(match[1])) return "transform"; + } + return false; + }; + return { unicodeFlag: feat("unicodeFlag"), unicodeSetsFlag: @@ -20,7 +38,7 @@ export function generateRegexpuOptions(toTransform: number): RegexpuOptions { feat("unicodeSetsFlag_syntax", "parse"), dotAllFlag: feat("dotAllFlag"), unicodePropertyEscapes: feat("unicodePropertyEscape"), - namedGroups: feat("namedCaptureGroups"), + namedGroups: feat("namedCaptureGroups") || featDuplicateNamedGroups(), onNamedGroup: () => {}, }; } diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/input.mjs b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-false-true/input.mjs similarity index 100% rename from packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/input.mjs rename to packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-false-true/input.mjs diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-false-true/options.json b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-false-true/options.json new file mode 100644 index 000000000000..97779c7e163d --- /dev/null +++ b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-false-true/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["transform-named-capturing-groups-regex", { "runtime": true }], + ["proposal-duplicate-named-capturing-groups-regex", { "runtime": false }] + ], + "throws": "The 'runtime' option must be the same for '@babel/plugin-transform-named-capturing-groups-regex' and '@babel/plugin-proposal-duplicate-named-capturing-groups-regex'." +} diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/input.mjs b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/input.mjs new file mode 100644 index 000000000000..3d7f5b1517b5 --- /dev/null +++ b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/input.mjs @@ -0,0 +1 @@ +/(?\d{4})/ diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/options.json b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/options.json new file mode 100644 index 000000000000..d430ab5acc70 --- /dev/null +++ b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/conflict-true-false/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["transform-named-capturing-groups-regex", { "runtime": false }], + ["proposal-duplicate-named-capturing-groups-regex", { "runtime": true }] + ], + "throws": "The 'runtime' option must be the same for '@babel/plugin-transform-named-capturing-groups-regex' and '@babel/plugin-proposal-duplicate-named-capturing-groups-regex'." +} diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/input.mjs b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/input.mjs new file mode 100644 index 000000000000..3d7f5b1517b5 --- /dev/null +++ b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/input.mjs @@ -0,0 +1 @@ +/(?\d{4})/ diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/options.json b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/options.json similarity index 100% rename from packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/options.json rename to packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/options.json diff --git a/packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/output.mjs b/packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/output.mjs similarity index 100% rename from packages/babel-helper-create-regexp-features-plugin/test/fixtures/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/output.mjs rename to packages/babel-helper-create-regexp-features-plugin/test/fixtures/runtime/plugin-transform-named-capturing-groups-regex-runtime-false-not-overwritten/output.mjs diff --git a/packages/babel-helpers/src/helpers-generated.ts b/packages/babel-helpers/src/helpers-generated.ts index 60969d0a4685..4ce1f1a34cc0 100644 --- a/packages/babel-helpers/src/helpers-generated.ts +++ b/packages/babel-helpers/src/helpers-generated.ts @@ -55,6 +55,6 @@ export default Object.freeze({ ), wrapRegExp: helper( "7.2.6", - 'import setPrototypeOf from"setPrototypeOf";import inherits from"inherits";export default function _wrapRegExp(){_wrapRegExp=function(re,groups){return new BabelRegExp(re,void 0,groups)};var _super=RegExp.prototype,_groups=new WeakMap;function BabelRegExp(re,flags,groups){var _this=new RegExp(re,flags);return _groups.set(_this,groups||_groups.get(re)),setPrototypeOf(_this,BabelRegExp.prototype)}function buildGroups(result,re){var g=_groups.get(re);return Object.keys(g).reduce((function(groups,name){return groups[name]=result[g[name]],groups}),Object.create(null))}return inherits(BabelRegExp,RegExp),BabelRegExp.prototype.exec=function(str){var result=_super.exec.call(this,str);return result&&(result.groups=buildGroups(result,this)),result},BabelRegExp.prototype[Symbol.replace]=function(str,substitution){if("string"==typeof substitution){var groups=_groups.get(this);return _super[Symbol.replace].call(this,str,substitution.replace(/\\$<([^>]+)>/g,(function(_,name){return"$"+groups[name]})))}if("function"==typeof substitution){var _this=this;return _super[Symbol.replace].call(this,str,(function(){var args=arguments;return"object"!=typeof args[args.length-1]&&(args=[].slice.call(args)).push(buildGroups(args,_this)),substitution.apply(this,args)}))}return _super[Symbol.replace].call(this,str,substitution)},_wrapRegExp.apply(this,arguments)}', + 'import setPrototypeOf from"setPrototypeOf";import inherits from"inherits";export default function _wrapRegExp(){_wrapRegExp=function(re,groups){return new BabelRegExp(re,void 0,groups)};var _super=RegExp.prototype,_groups=new WeakMap;function BabelRegExp(re,flags,groups){var _this=new RegExp(re,flags);return _groups.set(_this,groups||_groups.get(re)),setPrototypeOf(_this,BabelRegExp.prototype)}function buildGroups(result,re){var g=_groups.get(re);return Object.keys(g).reduce((function(groups,name){var i=g[name];if("number"==typeof i)groups[name]=result[i];else{for(var k=0;void 0===result[i[k]]&&k+1]+)>/g,(function(_,name){return"$"+groups[name]})))}if("function"==typeof substitution){var _this=this;return _super[Symbol.replace].call(this,str,(function(){var args=arguments;return"object"!=typeof args[args.length-1]&&(args=[].slice.call(args)).push(buildGroups(args,_this)),substitution.apply(this,args)}))}return _super[Symbol.replace].call(this,str,substitution)},_wrapRegExp.apply(this,arguments)}', ), }); diff --git a/packages/babel-helpers/src/helpers/wrapRegExp.js b/packages/babel-helpers/src/helpers/wrapRegExp.js index d8e110af2675..b5100b6fec0f 100644 --- a/packages/babel-helpers/src/helpers/wrapRegExp.js +++ b/packages/babel-helpers/src/helpers/wrapRegExp.js @@ -56,7 +56,16 @@ export default function _wrapRegExp() { var g = _groups.get(re); return Object.keys(g).reduce(function (groups, name) { - groups[name] = result[g[name]]; + var i = g[name]; + if (typeof i === "number") groups[name] = result[i]; + else { + // i is an array of indexes + var k = 0; + // if no group matched, we stop at k = i.length - 1 and then + // we store result[i[i.length - 1]] which is undefined. + while (result[i[k]] === undefined && k + 1 < i.length) k++; + groups[name] = result[i[k]]; + } return groups; }, Object.create(null)); } diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/.npmignore b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/.npmignore new file mode 100644 index 000000000000..f9806945836e --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/README.md b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/README.md new file mode 100644 index 000000000000..58fec2daa0d2 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/README.md @@ -0,0 +1,19 @@ +# @babel/plugin-proposal-duplicate-named-capturing-groups-regex + +> Compile regular expressions using duplicate named groups to index-based groups. + +See our website [@babel/plugin-proposal-duplicate-named-capturing-groups-regex](https://babeljs.io/docs/en/babel-plugin-proposal-duplicate-named-capturing-groups-regex) for more information. + +## Install + +Using npm: + +```sh +npm install --save-dev @babel/plugin-proposal-duplicate-named-capturing-groups-regex +``` + +or using yarn: + +```sh +yarn add @babel/plugin-proposal-duplicate-named-capturing-groups-regex --dev +``` diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/package.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/package.json new file mode 100644 index 000000000000..92c1c9fcca92 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/package.json @@ -0,0 +1,52 @@ +{ + "name": "@babel/plugin-proposal-duplicate-named-capturing-groups-regex", + "version": "7.18.6", + "description": "Compile regular expressions using duplicate named groups to index-based groups.", + "homepage": "https://babel.dev/docs/en/next/babel-plugin-proposal-duplicate-named-capturing-groups-regex", + "license": "MIT", + "publishConfig": { + "access": "public" + }, + "main": "./lib/index.js", + "keywords": [ + "babel-plugin", + "regex", + "regexp", + "regular expressions" + ], + "repository": { + "type": "git", + "url": "https://github.com/babel/babel.git", + "directory": "packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex" + }, + "bugs": "https://github.com/babel/babel/issues", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "workspace:^", + "@babel/helper-plugin-utils": "workspace:^" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + }, + "devDependencies": { + "@babel/core": "workspace:^", + "@babel/helper-plugin-test-runner": "workspace:^", + "core-js": "^3.22.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "author": "The Babel Team (https://babel.dev/team)", + "conditions": { + "USE_ESM": [ + { + "type": "module" + }, + null + ] + }, + "exports": { + ".": "./lib/index.js", + "./package.json": "./package.json" + }, + "type": "commonjs" +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src/index.ts b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src/index.ts new file mode 100644 index 000000000000..76d83980f495 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src/index.ts @@ -0,0 +1,20 @@ +/* eslint-disable @babel/development/plugin-name */ +import { createRegExpFeaturePlugin } from "@babel/helper-create-regexp-features-plugin"; +import { declare } from "@babel/helper-plugin-utils"; + +export interface Options { + runtime?: boolean; +} + +export default declare((api, options: Options) => { + const { runtime } = options; + if (runtime !== undefined && typeof runtime !== "boolean") { + throw new Error("The 'runtime' option must be boolean"); + } + + return createRegExpFeaturePlugin({ + name: "proposal-duplicate-named-capturing-groups-regex", + feature: "duplicateNamedCaptureGroups", + options: { runtime }, + }); +}); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/input.js new file mode 100644 index 000000000000..b6b1233b66a9 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/input.js @@ -0,0 +1 @@ +/(?:(?\d\d\d\d)|(?\d\d))-(?\d\d)/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/output.js new file mode 100644 index 000000000000..f53db765b4ae --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/basic/output.js @@ -0,0 +1,5 @@ +/*#__PURE__*/ +babelHelpers.wrapRegExp(/(?:(\d\d\d\d)|(\d\d))\x2D(\d\d)/, { + year: [1, 2], + month: 3 +}); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/exec.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/exec.js new file mode 100644 index 000000000000..1347ef53e9f1 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/exec.js @@ -0,0 +1,5 @@ +let regexp = /(?a)|(?b)|c/; + +expect("a".match(regexp).groups).toEqual({ named: "a" }); +expect("b".match(regexp).groups).toEqual({ named: "b" }); +expect("c".match(regexp).groups).toEqual({ named: undefined }); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/options.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/options.json new file mode 100644 index 000000000000..cacf4d1974df --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/runtime/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["proposal-duplicate-named-capturing-groups-regex"] +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js new file mode 100644 index 000000000000..873e6d6b0572 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/input.js @@ -0,0 +1 @@ +/(?:(?\d\d\d\d)|(?\d\d)) is \k/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js new file mode 100644 index 000000000000..7351dd79a1e4 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/basic/output.js @@ -0,0 +1 @@ +/(?:(\d\d\d\d)|(\d\d)) is \1\2/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/options.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/options.json new file mode 100644 index 000000000000..d254b9b39da5 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/options.json @@ -0,0 +1,5 @@ +{ + "plugins": [ + ["proposal-duplicate-named-capturing-groups-regex", { "runtime": false }] + ] +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/input.js new file mode 100644 index 000000000000..1d1628ac0978 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/input.js @@ -0,0 +1 @@ +/(?\d\d\d\d) is \k/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/output.js new file mode 100644 index 000000000000..1d1628ac0978 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-not-transformed/output.js @@ -0,0 +1 @@ +/(?\d\d\d\d) is \k/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/input.js new file mode 100644 index 000000000000..1d1628ac0978 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/input.js @@ -0,0 +1 @@ +/(?\d\d\d\d) is \k/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/options.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/options.json new file mode 100644 index 000000000000..247156232ea5 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + ["proposal-duplicate-named-capturing-groups-regex", { "runtime": false }], + ["transform-named-capturing-groups-regex", { "runtime": false }] + ] +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/output.js new file mode 100644 index 000000000000..cac0bdaaf258 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/syntax/unique-name-with-named-groups-plugin/output.js @@ -0,0 +1 @@ +/(\d\d\d\d) is \1/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/basic/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/basic/input.js new file mode 100644 index 000000000000..2761445e091b --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js new file mode 100644 index 000000000000..779aac3d8537 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/basic/output.js @@ -0,0 +1,3 @@ +"foo".match( /*#__PURE__*/babelHelpers.wrapRegExp(/(.)\1/, { + double: 1 +})); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/input.js new file mode 100644 index 000000000000..00a7c78b9344 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/input.js @@ -0,0 +1 @@ +/no-groups-\(?looks\)\u{10000}/u; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/options.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/options.json new file mode 100644 index 000000000000..148c4e8cb79c --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/options.json @@ -0,0 +1,6 @@ +{ + "plugins": [ + "transform-named-capturing-groups-regex", + "transform-unicode-regex" + ] +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/output.js new file mode 100644 index 000000000000..af299f8fceaf --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group-unicode/output.js @@ -0,0 +1 @@ +/no\x2Dgroups\x2D\(?looks\)(?:\uD800\uDC00)/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/input.js new file mode 100644 index 000000000000..4c55ef0dbe17 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js new file mode 100644 index 000000000000..6fe7c2ef533c --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/looks-like-a-group/output.js @@ -0,0 +1 @@ +/no\x2Dgroups\x2D\(?looks\)/; diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/input.js new file mode 100644 index 000000000000..581032632ae1 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/only-when-named-groups/output.js new file mode 100644 index 000000000000..c545e42ac5ca --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/options.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/options.json new file mode 100644 index 000000000000..8b8fe93e5deb --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["transform-named-capturing-groups-regex"] +} diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/input.js new file mode 100644 index 000000000000..86639f6f7e17 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js new file mode 100644 index 000000000000..16f5e24236b2 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/skips-anonymous-capturing-groups/output.js @@ -0,0 +1,3 @@ +"abba".match( /*#__PURE__*/babelHelpers.wrapRegExp(/(.)(.)\2\1/, { + n: 2 +})); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/test-method/input.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/test-method/input.js new file mode 100644 index 000000000000..3e26e4ee498a --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-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-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js new file mode 100644 index 000000000000..808fc64e4f90 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/fixtures/wrapper/test-method/output.js @@ -0,0 +1 @@ +/^(.)\1$/.test("aa"); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/index.js b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/index.js new file mode 100644 index 000000000000..21a55ce6b5e7 --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/index.js @@ -0,0 +1,3 @@ +import runner from "@babel/helper-plugin-test-runner"; + +runner(import.meta.url); diff --git a/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/package.json b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/package.json new file mode 100644 index 000000000000..5ffd9800b97c --- /dev/null +++ b/packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/test/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.ts b/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.ts index 0cd52bba1b09..f81e9867cada 100644 --- a/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.ts +++ b/packages/babel-plugin-transform-named-capturing-groups-regex/src/index.ts @@ -7,8 +7,8 @@ export interface Options { } export default declare((api, options: Options) => { - const { runtime = true } = options; - if (typeof runtime !== "boolean") { + const { runtime } = options; + if (runtime !== undefined && typeof runtime !== "boolean") { throw new Error("The 'runtime' option must be boolean"); } diff --git a/packages/babel-standalone/package.json b/packages/babel-standalone/package.json index 7dd19712d77c..095731959980 100644 --- a/packages/babel-standalone/package.json +++ b/packages/babel-standalone/package.json @@ -18,6 +18,7 @@ "@babel/plugin-proposal-decorators": "workspace:^", "@babel/plugin-proposal-destructuring-private": "workspace:^", "@babel/plugin-proposal-do-expressions": "workspace:^", + "@babel/plugin-proposal-duplicate-named-capturing-groups-regex": "workspace:^", "@babel/plugin-proposal-dynamic-import": "workspace:^", "@babel/plugin-proposal-export-default-from": "workspace:^", "@babel/plugin-proposal-export-namespace-from": "workspace:^", diff --git a/packages/babel-standalone/scripts/pluginConfig.json b/packages/babel-standalone/scripts/pluginConfig.json index 363b180479f9..adde8183bce3 100644 --- a/packages/babel-standalone/scripts/pluginConfig.json +++ b/packages/babel-standalone/scripts/pluginConfig.json @@ -27,6 +27,7 @@ "proposal-decorators", "proposal-destructuring-private", "proposal-do-expressions", + "proposal-duplicate-named-capturing-groups-regex", "proposal-dynamic-import", "proposal-export-default-from", "proposal-export-namespace-from", diff --git a/packages/babel-standalone/src/generated/plugins.ts b/packages/babel-standalone/src/generated/plugins.ts index 0e9e6f9c1efb..ff13ad3fd0d8 100644 --- a/packages/babel-standalone/src/generated/plugins.ts +++ b/packages/babel-standalone/src/generated/plugins.ts @@ -30,6 +30,7 @@ import proposalClassStaticBlock from "@babel/plugin-proposal-class-static-block" import proposalDecorators from "@babel/plugin-proposal-decorators"; import proposalDestructuringPrivate from "@babel/plugin-proposal-destructuring-private"; import proposalDoExpressions from "@babel/plugin-proposal-do-expressions"; +import proposalDuplicateNamedCapturingGroupsRegex from "@babel/plugin-proposal-duplicate-named-capturing-groups-regex"; import proposalDynamicImport from "@babel/plugin-proposal-dynamic-import"; import proposalExportDefaultFrom from "@babel/plugin-proposal-export-default-from"; import proposalExportNamespaceFrom from "@babel/plugin-proposal-export-namespace-from"; @@ -128,6 +129,7 @@ export { proposalDecorators, proposalDestructuringPrivate, proposalDoExpressions, + proposalDuplicateNamedCapturingGroupsRegex, proposalDynamicImport, proposalExportDefaultFrom, proposalExportNamespaceFrom, @@ -227,6 +229,8 @@ export const all: { [k: string]: any } = { "proposal-decorators": proposalDecorators, "proposal-destructuring-private": proposalDestructuringPrivate, "proposal-do-expressions": proposalDoExpressions, + "proposal-duplicate-named-capturing-groups-regex": + proposalDuplicateNamedCapturingGroupsRegex, "proposal-dynamic-import": proposalDynamicImport, "proposal-export-default-from": proposalExportDefaultFrom, "proposal-export-namespace-from": proposalExportNamespaceFrom, diff --git a/packages/babel-standalone/src/preset-stage-3.ts b/packages/babel-standalone/src/preset-stage-3.ts index 7c7d73d73862..1a8e257edf5b 100644 --- a/packages/babel-standalone/src/preset-stage-3.ts +++ b/packages/babel-standalone/src/preset-stage-3.ts @@ -5,6 +5,7 @@ export default (_: any, { loose = false } = {}) => { const plugins: any[] = [ babelPlugins.syntaxImportAssertions, babelPlugins.proposalUnicodeSetsRegex, + babelPlugins.proposalDuplicateNamedCapturingGroupsRegex, ]; if (!process.env.BABEL_8_BREAKING) { diff --git a/tsconfig.json b/tsconfig.json index 269001d3a2a7..8869ed393c1e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -50,6 +50,7 @@ "./packages/babel-plugin-proposal-decorators/src/**/*.ts", "./packages/babel-plugin-proposal-destructuring-private/src/**/*.ts", "./packages/babel-plugin-proposal-do-expressions/src/**/*.ts", + "./packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src/**/*.ts", "./packages/babel-plugin-proposal-dynamic-import/src/**/*.ts", "./packages/babel-plugin-proposal-export-default-from/src/**/*.ts", "./packages/babel-plugin-proposal-export-namespace-from/src/**/*.ts", @@ -313,6 +314,9 @@ "@babel/plugin-proposal-do-expressions": [ "./packages/babel-plugin-proposal-do-expressions/src" ], + "@babel/plugin-proposal-duplicate-named-capturing-groups-regex": [ + "./packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex/src" + ], "@babel/plugin-proposal-dynamic-import": [ "./packages/babel-plugin-proposal-dynamic-import/src" ], diff --git a/yarn.lock b/yarn.lock index 91fa744eb662..2f72be31cab9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1359,6 +1359,20 @@ __metadata: languageName: unknown linkType: soft +"@babel/plugin-proposal-duplicate-named-capturing-groups-regex@workspace:^, @babel/plugin-proposal-duplicate-named-capturing-groups-regex@workspace:packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex": + version: 0.0.0-use.local + resolution: "@babel/plugin-proposal-duplicate-named-capturing-groups-regex@workspace:packages/babel-plugin-proposal-duplicate-named-capturing-groups-regex" + dependencies: + "@babel/core": "workspace:^" + "@babel/helper-create-regexp-features-plugin": "workspace:^" + "@babel/helper-plugin-test-runner": "workspace:^" + "@babel/helper-plugin-utils": "workspace:^" + core-js: ^3.22.1 + peerDependencies: + "@babel/core": ^7.0.0 + languageName: unknown + linkType: soft + "@babel/plugin-proposal-dynamic-import@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-proposal-dynamic-import@npm:7.18.6" @@ -3610,6 +3624,7 @@ __metadata: "@babel/plugin-proposal-decorators": "workspace:^" "@babel/plugin-proposal-destructuring-private": "workspace:^" "@babel/plugin-proposal-do-expressions": "workspace:^" + "@babel/plugin-proposal-duplicate-named-capturing-groups-regex": "workspace:^" "@babel/plugin-proposal-dynamic-import": "workspace:^" "@babel/plugin-proposal-export-default-from": "workspace:^" "@babel/plugin-proposal-export-namespace-from": "workspace:^"