diff --git a/lib/regexpu-core.d.ts b/lib/regexpu-core.d.ts index e6cb4eb325f5..832817bd2753 100644 --- a/lib/regexpu-core.d.ts +++ b/lib/regexpu-core.d.ts @@ -6,6 +6,8 @@ declare module "regexpu-core" { unicodePropertyEscapes?: "transform" | false; namedGroups?: "transform" | false; onNamedGroup?: (name: string, index: number) => void; + modifiers?: "transform" | false; + onNewFlags?: (name: string) => void; }; function rewritePattern( pattern: string, diff --git a/packages/babel-helper-create-regexp-features-plugin/package.json b/packages/babel-helper-create-regexp-features-plugin/package.json index 7cced99ab2ac..bc688e22d15c 100644 --- a/packages/babel-helper-create-regexp-features-plugin/package.json +++ b/packages/babel-helper-create-regexp-features-plugin/package.json @@ -19,7 +19,7 @@ ], "dependencies": { "@babel/helper-annotate-as-pure": "workspace:^", - "regexpu-core": "^5.2.1" + "regexpu-core": "^5.3.1" }, "peerDependencies": { "@babel/core": "^7.0.0" 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 ed9b6f4ed60a..46c94af9d39b 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/features.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/features.ts @@ -6,6 +6,7 @@ export const FEATURES = Object.freeze({ unicodeSetsFlag_syntax: 1 << 4, unicodeSetsFlag: 1 << 5, duplicateNamedCaptureGroups: 1 << 6, + modifiers: 1 << 7, }); // 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 52b9e2188d28..eb32f2a07ce3 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/index.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/index.ts @@ -116,6 +116,13 @@ export function createRegExpFeaturePlugin({ }; } + let newFlags; + if (regexpuOptions.modifiers === "transform") { + regexpuOptions.onNewFlags = flags => { + newFlags = flags; + }; + } + node.pattern = rewritePattern(node.pattern, node.flags, regexpuOptions); if ( @@ -133,7 +140,7 @@ export function createRegExpFeaturePlugin({ path.replaceWith(call); } - node.flags = transformFlags(regexpuOptions, node.flags); + node.flags = transformFlags(regexpuOptions, newFlags ?? node.flags); }, }, }; 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 d879f0a14e70..7396baf3219a 100644 --- a/packages/babel-helper-create-regexp-features-plugin/src/util.ts +++ b/packages/babel-helper-create-regexp-features-plugin/src/util.ts @@ -40,6 +40,7 @@ export function generateRegexpuOptions( unicodePropertyEscapes: feat("unicodePropertyEscape"), namedGroups: feat("namedCaptureGroups") || featDuplicateNamedGroups(), onNamedGroup: () => {}, + modifiers: feat("modifiers"), }; } @@ -71,6 +72,10 @@ export function canSkipRegexpu( return false; } + if (options.modifiers === "transform" && /\(\?[\w-]+:/.test(pattern)) { + return false; + } + return true; } diff --git a/packages/babel-plugin-proposal-regexp-modifiers/.npmignore b/packages/babel-plugin-proposal-regexp-modifiers/.npmignore new file mode 100644 index 000000000000..f9806945836e --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/.npmignore @@ -0,0 +1,3 @@ +src +test +*.log diff --git a/packages/babel-plugin-proposal-regexp-modifiers/README.md b/packages/babel-plugin-proposal-regexp-modifiers/README.md new file mode 100644 index 000000000000..5faf5dafa7b5 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/README.md @@ -0,0 +1,19 @@ +# @babel/plugin-proposal-regexp-modifiers + +> Compile regular expressions using duplicate named groups to index-based groups. + +See our website [@babel/plugin-proposal-regexp-modifiers](https://babeljs.io/docs/en/babel-plugin-proposal-regexp-modifiers) for more information. + +## Install + +Using npm: + +```sh +npm install --save-dev @babel/plugin-proposal-regexp-modifiers +``` + +or using yarn: + +```sh +yarn add @babel/plugin-proposal-regexp-modifiers --dev +``` diff --git a/packages/babel-plugin-proposal-regexp-modifiers/package.json b/packages/babel-plugin-proposal-regexp-modifiers/package.json new file mode 100644 index 000000000000..dc2c1f444141 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/package.json @@ -0,0 +1,50 @@ +{ + "name": "@babel/plugin-proposal-regexp-modifiers", + "version": "7.19.1", + "description": "Compile inline regular expression modifiers", + "homepage": "https://babel.dev/docs/en/next/babel-plugin-proposal-regexp-modifiers", + "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-regexp-modifiers" + }, + "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:^" + }, + "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-regexp-modifiers/src/index.ts b/packages/babel-plugin-proposal-regexp-modifiers/src/index.ts new file mode 100644 index 000000000000..790db43479aa --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/src/index.ts @@ -0,0 +1,12 @@ +/* eslint-disable @babel/development/plugin-name */ +import { createRegExpFeaturePlugin } from "@babel/helper-create-regexp-features-plugin"; +import { declare } from "@babel/helper-plugin-utils"; + +export default declare(api => { + api.assertVersion("^7.19.0"); + + return createRegExpFeaturePlugin({ + name: "proposal-regexp-modifiers", + feature: "modifiers", + }); +}); diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/exec.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/exec.js new file mode 100644 index 000000000000..58229379f405 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/exec.js @@ -0,0 +1,3 @@ +let regex = /(?ims:^[a-z])/u; + +expect(regex.test("\u017F")).toBeTruthy(); diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/input.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/input.js new file mode 100644 index 000000000000..c210b35d3fdc --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/input.js @@ -0,0 +1,2 @@ +/(?ims:^[a-z])/u; +/(?-ims:^[a-z].)(^[a-z].)/uims; diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/options.json b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/options.json new file mode 100644 index 000000000000..24af1c1a4663 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["proposal-regexp-modifiers", "transform-unicode-regex"], + "minNodeVersion": "8.0.0" +} diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/output.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/output.js new file mode 100644 index 000000000000..3eab42554df0 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/with-u-flag/output.js @@ -0,0 +1,2 @@ +/(?:(?:^|(?<=[\n\r\u2028\u2029]))[A-Za-z\u017F\u212A])/; +/(?:^[a-z](?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))((?:^|(?<=[\n\r\u2028\u2029]))[A-Za-z\u017F\u212A](?:[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]))/; diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/exec.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/exec.js new file mode 100644 index 000000000000..abe988c55898 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/exec.js @@ -0,0 +1,11 @@ +let regex = /(?ims:^[a-z].1$)/ + +expect(regex.test("Ax1")).toBeTruthy(); +expect(regex.test("ax1")).toBeTruthy(); +expect(regex.test("ax2")).toBeFalsy(); +expect(regex.test("1ax1")).toBeFalsy(); + +expect(regex.test("a\r1")).toBeTruthy(); +expect(regex.test("a\r\n1")).toBeFalsy(); + +expect(regex.test("\nax1\n\n\n\n\n\n\n\n\n")).toBeTruthy(); diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/options.json b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/options.json new file mode 100644 index 000000000000..d2a834e3d767 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag-exec/options.json @@ -0,0 +1,4 @@ +{ + "plugins": ["proposal-regexp-modifiers"], + "minNodeVersion": "8.0.0" +} diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/input.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/input.js new file mode 100644 index 000000000000..3041280a69a3 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/input.js @@ -0,0 +1,2 @@ +/(?ims:^[a-z]1$)/; +/(?-ims:^[a-z].1$)(^[a-z].)/ims; diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/options.json b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/options.json new file mode 100644 index 000000000000..da996fb6c95b --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["proposal-regexp-modifiers"] +} diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/output.js b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/output.js new file mode 100644 index 000000000000..8b31dd64bfe1 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/fixtures/syntax/without-u-flag/output.js @@ -0,0 +1,2 @@ +/(?:(?:^|(?<=[\n\r\u2028\u2029]))[A-Za-z]1(?:$|(?=[\n\r\u2028\u2029])))/; +/(?:^[a-z].1$)((?:^|(?<=[\n\r\u2028\u2029]))[A-Za-z][\s\S])/; diff --git a/packages/babel-plugin-proposal-regexp-modifiers/test/index.js b/packages/babel-plugin-proposal-regexp-modifiers/test/index.js new file mode 100644 index 000000000000..21a55ce6b5e7 --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/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-regexp-modifiers/test/package.json b/packages/babel-plugin-proposal-regexp-modifiers/test/package.json new file mode 100644 index 000000000000..5ffd9800b97c --- /dev/null +++ b/packages/babel-plugin-proposal-regexp-modifiers/test/package.json @@ -0,0 +1 @@ +{ "type": "module" } diff --git a/packages/babel-standalone/package.json b/packages/babel-standalone/package.json index 51c2f37a144b..650350ca770e 100644 --- a/packages/babel-standalone/package.json +++ b/packages/babel-standalone/package.json @@ -35,6 +35,7 @@ "@babel/plugin-proposal-private-methods": "workspace:^", "@babel/plugin-proposal-private-property-in-object": "workspace:^", "@babel/plugin-proposal-record-and-tuple": "workspace:^", + "@babel/plugin-proposal-regexp-modifiers": "workspace:^", "@babel/plugin-proposal-throw-expressions": "workspace:^", "@babel/plugin-proposal-unicode-property-regex": "workspace:^", "@babel/plugin-proposal-unicode-sets-regex": "workspace:^", diff --git a/packages/babel-standalone/scripts/pluginConfig.json b/packages/babel-standalone/scripts/pluginConfig.json index 1d03b194f388..815d03edd31c 100644 --- a/packages/babel-standalone/scripts/pluginConfig.json +++ b/packages/babel-standalone/scripts/pluginConfig.json @@ -59,6 +59,7 @@ "proposal-private-methods", "proposal-private-property-in-object", "proposal-record-and-tuple", + "proposal-regexp-modifiers", "proposal-throw-expressions", "proposal-unicode-property-regex", "proposal-unicode-sets-regex", diff --git a/packages/babel-standalone/src/generated/plugins.ts b/packages/babel-standalone/src/generated/plugins.ts index 925ecaae8d69..285f8946c40c 100644 --- a/packages/babel-standalone/src/generated/plugins.ts +++ b/packages/babel-standalone/src/generated/plugins.ts @@ -43,6 +43,7 @@ import proposalPipelineOperator from "@babel/plugin-proposal-pipeline-operator"; import proposalPrivateMethods from "@babel/plugin-proposal-private-methods"; import proposalPrivatePropertyInObject from "@babel/plugin-proposal-private-property-in-object"; import proposalRecordAndTuple from "@babel/plugin-proposal-record-and-tuple"; +import proposalRegexpModifiers from "@babel/plugin-proposal-regexp-modifiers"; import proposalThrowExpressions from "@babel/plugin-proposal-throw-expressions"; import proposalUnicodePropertyRegex from "@babel/plugin-proposal-unicode-property-regex"; import proposalUnicodeSetsRegex from "@babel/plugin-proposal-unicode-sets-regex"; @@ -145,6 +146,7 @@ export { proposalPrivateMethods, proposalPrivatePropertyInObject, proposalRecordAndTuple, + proposalRegexpModifiers, proposalThrowExpressions, proposalUnicodePropertyRegex, proposalUnicodeSetsRegex, @@ -249,6 +251,7 @@ export const all: { [k: string]: any } = { "proposal-private-methods": proposalPrivateMethods, "proposal-private-property-in-object": proposalPrivatePropertyInObject, "proposal-record-and-tuple": proposalRecordAndTuple, + "proposal-regexp-modifiers": proposalRegexpModifiers, "proposal-throw-expressions": proposalThrowExpressions, "proposal-unicode-property-regex": proposalUnicodePropertyRegex, "proposal-unicode-sets-regex": proposalUnicodeSetsRegex, diff --git a/packages/babel-standalone/src/preset-stage-3.ts b/packages/babel-standalone/src/preset-stage-3.ts index 56749b82e9e1..69b45b30efd9 100644 --- a/packages/babel-standalone/src/preset-stage-3.ts +++ b/packages/babel-standalone/src/preset-stage-3.ts @@ -8,8 +8,7 @@ export default (_: any, opts: any = {}) => { decoratorsBeforeExport, } = opts; - // todo(flow->ts) improve types - const plugins: any[] = [ + const plugins = [ babelPlugins.syntaxImportAssertions, babelPlugins.proposalUnicodeSetsRegex, babelPlugins.proposalDuplicateNamedCapturingGroupsRegex, @@ -20,23 +19,23 @@ export default (_: any, opts: any = {}) => { decoratorsBeforeExport, }, ], - ]; - - if (!process.env.BABEL_8_BREAKING) { + babelPlugins.proposalRegexpModifiers, // These are Stage 4 - plugins.push( - babelPlugins.proposalExportNamespaceFrom, - babelPlugins.proposalLogicalAssignmentOperators, - [babelPlugins.proposalOptionalChaining, { loose }], - [babelPlugins.proposalNullishCoalescingOperator, { loose }], - [babelPlugins.proposalClassProperties, { loose }], - babelPlugins.proposalJsonStrings, - babelPlugins.proposalNumericSeparator, - [babelPlugins.proposalPrivateMethods, { loose }], - babelPlugins.proposalPrivatePropertyInObject, - babelPlugins.proposalClassStaticBlock, - ); - } + ...(process.env.BABEL_8_BREAKING + ? [] + : [ + babelPlugins.proposalExportNamespaceFrom, + babelPlugins.proposalLogicalAssignmentOperators, + [babelPlugins.proposalOptionalChaining, { loose }], + [babelPlugins.proposalNullishCoalescingOperator, { loose }], + [babelPlugins.proposalClassProperties, { loose }], + babelPlugins.proposalJsonStrings, + babelPlugins.proposalNumericSeparator, + [babelPlugins.proposalPrivateMethods, { loose }], + babelPlugins.proposalPrivatePropertyInObject, + babelPlugins.proposalClassStaticBlock, + ]), + ]; return { plugins }; }; diff --git a/tsconfig.json b/tsconfig.json index f0b50ceeaf81..59a3478a1cb5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -67,6 +67,7 @@ "./packages/babel-plugin-proposal-private-methods/src/**/*.ts", "./packages/babel-plugin-proposal-private-property-in-object/src/**/*.ts", "./packages/babel-plugin-proposal-record-and-tuple/src/**/*.ts", + "./packages/babel-plugin-proposal-regexp-modifiers/src/**/*.ts", "./packages/babel-plugin-proposal-throw-expressions/src/**/*.ts", "./packages/babel-plugin-proposal-unicode-property-regex/src/**/*.ts", "./packages/babel-plugin-proposal-unicode-sets-regex/src/**/*.ts", @@ -367,6 +368,9 @@ "@babel/plugin-proposal-record-and-tuple": [ "./packages/babel-plugin-proposal-record-and-tuple/src" ], + "@babel/plugin-proposal-regexp-modifiers": [ + "./packages/babel-plugin-proposal-regexp-modifiers/src" + ], "@babel/plugin-proposal-throw-expressions": [ "./packages/babel-plugin-proposal-throw-expressions/src" ], diff --git a/yarn.lock b/yarn.lock index cc7549473d09..bfab5e433f31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -636,7 +636,7 @@ __metadata: "@babel/core": "workspace:^" "@babel/helper-annotate-as-pure": "workspace:^" "@babel/helper-plugin-test-runner": "workspace:^" - regexpu-core: ^5.2.1 + regexpu-core: ^5.3.1 peerDependencies: "@babel/core": ^7.0.0 languageName: unknown @@ -1764,6 +1764,18 @@ __metadata: languageName: unknown linkType: soft +"@babel/plugin-proposal-regexp-modifiers@workspace:^, @babel/plugin-proposal-regexp-modifiers@workspace:packages/babel-plugin-proposal-regexp-modifiers": + version: 0.0.0-use.local + resolution: "@babel/plugin-proposal-regexp-modifiers@workspace:packages/babel-plugin-proposal-regexp-modifiers" + dependencies: + "@babel/core": "workspace:^" + "@babel/helper-create-regexp-features-plugin": "workspace:^" + "@babel/helper-plugin-utils": "workspace:^" + peerDependencies: + "@babel/core": ^7.0.0 + languageName: unknown + linkType: soft + "@babel/plugin-proposal-throw-expressions@workspace:^, @babel/plugin-proposal-throw-expressions@workspace:packages/babel-plugin-proposal-throw-expressions": version: 0.0.0-use.local resolution: "@babel/plugin-proposal-throw-expressions@workspace:packages/babel-plugin-proposal-throw-expressions" @@ -3683,6 +3695,7 @@ __metadata: "@babel/plugin-proposal-private-methods": "workspace:^" "@babel/plugin-proposal-private-property-in-object": "workspace:^" "@babel/plugin-proposal-record-and-tuple": "workspace:^" + "@babel/plugin-proposal-regexp-modifiers": "workspace:^" "@babel/plugin-proposal-throw-expressions": "workspace:^" "@babel/plugin-proposal-unicode-property-regex": "workspace:^" "@babel/plugin-proposal-unicode-sets-regex": "workspace:^" @@ -13121,9 +13134,9 @@ fsevents@^1.2.7: languageName: node linkType: hard -"regexpu-core@npm:^5.2.1": - version: 5.3.0 - resolution: "regexpu-core@npm:5.3.0" +"regexpu-core@npm:^5.2.1, regexpu-core@npm:^5.3.1": + version: 5.3.1 + resolution: "regexpu-core@npm:5.3.1" dependencies: "@babel/regjsgen": ^0.8.0 regenerate: ^1.4.2 @@ -13131,7 +13144,7 @@ fsevents@^1.2.7: regjsparser: ^0.9.1 unicode-match-property-ecmascript: ^2.0.0 unicode-match-property-value-ecmascript: ^2.1.0 - checksum: f3c7921543ebda919c53fdbbf3a9cebbecbf8ad65b30e423d7eaef35484e08cbc919f9e8334f4693a72206f583d4f2b48d4415483f6e6e8c81f0046e3a23c66f + checksum: 446fbbb79059afcd64d11ea573276e2df97ee7ad45aa452834d3b2aef7edf7bfe206c310f57f9345d8c95bfedbf9c16a9529f9219a05ae6a6b0d6f0dbe523b33 languageName: node linkType: hard