Skip to content

Commit 10fe2f3

Browse files
Add transform support for the "regexp unicode sets" proposal (#14125)
1 parent 96a8251 commit 10fe2f3

File tree

35 files changed

+368
-85
lines changed

35 files changed

+368
-85
lines changed

packages/babel-core/src/parser/util/missing-plugin-helper.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ const pluginNameMap = {
201201
url: "https://git.io/JvKp3",
202202
},
203203
},
204+
regexpUnicodeSets: {
205+
syntax: {
206+
name: "@babel/plugin-syntax-unicode-sets-regex",
207+
url: "https://git.io/J9GTd",
208+
},
209+
transform: {
210+
name: "@babel/plugin-proposal-unicode-sets-regex",
211+
url: "https://git.io/J9GTQ",
212+
},
213+
},
204214
throwExpressions: {
205215
syntax: {
206216
name: "@babel/plugin-syntax-throw-expressions",

packages/babel-helper-create-regexp-features-plugin/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
],
2020
"dependencies": {
2121
"@babel/helper-annotate-as-pure": "workspace:^",
22-
"regexpu-core": "^4.7.1"
22+
"regexpu-core": "^5.0.1"
2323
},
2424
"peerDependencies": {
2525
"@babel/core": "^7.0.0"

packages/babel-helper-create-regexp-features-plugin/src/features.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ export const FEATURES = Object.freeze({
33
dotAllFlag: 1 << 1,
44
unicodePropertyEscape: 1 << 2,
55
namedCaptureGroups: 1 << 3,
6+
unicodeSetsFlag_syntax: 1 << 4,
7+
unicodeSetsFlag: 1 << 5,
68
});
79

810
// We can't use a symbol because this needs to always be the same, even if

packages/babel-helper-create-regexp-features-plugin/src/index.ts

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,10 @@
11
import rewritePattern from "regexpu-core";
2-
import {
3-
featuresKey,
4-
FEATURES,
5-
enableFeature,
6-
runtimeKey,
7-
hasFeature,
8-
} from "./features";
9-
import { generateRegexpuOptions } from "./util";
2+
import { featuresKey, FEATURES, enableFeature, runtimeKey } from "./features";
3+
import { generateRegexpuOptions, canSkipRegexpu, transformFlags } from "./util";
104

115
import { types as t } from "@babel/core";
126
import annotateAsPure from "@babel/helper-annotate-as-pure";
137

14-
type RegExpFlags = "i" | "g" | "m" | "s" | "u" | "y";
15-
16-
/**
17-
* Remove given flag from given RegExpLiteral node
18-
*
19-
* @param {RegExpLiteral} node
20-
* @param {RegExpFlags} flag
21-
* @returns {void}
22-
*/
23-
function pullFlag(node, flag: RegExpFlags): void {
24-
node.flags = node.flags.replace(flag, "");
25-
}
26-
278
declare const PACKAGE_JSON: { name: string; version: string };
289

2910
// Note: Versions are represented as an integer. e.g. 7.1.5 is represented
@@ -39,9 +20,13 @@ export function createRegExpFeaturePlugin({
3920
name,
4021
feature,
4122
options = {} as any,
23+
manipulateOptions = (() => {}) as (opts: any, parserOpts: any) => void,
4224
}) {
4325
return {
4426
name,
27+
28+
manipulateOptions,
29+
4530
pre() {
4631
const { file } = this;
4732
const features = file.get(featuresKey) ?? 0;
@@ -70,20 +55,21 @@ export function createRegExpFeaturePlugin({
7055
const { file } = this;
7156
const features = file.get(featuresKey);
7257
const runtime = file.get(runtimeKey) ?? true;
73-
const regexpuOptions = generateRegexpuOptions(node, features);
74-
if (regexpuOptions === null) {
75-
return;
76-
}
58+
59+
const regexpuOptions = generateRegexpuOptions(features);
60+
if (canSkipRegexpu(node, regexpuOptions)) return;
61+
7762
const namedCaptureGroups = {};
78-
if (regexpuOptions.namedGroup) {
63+
if (regexpuOptions.namedGroups === "transform") {
7964
regexpuOptions.onNamedGroup = (name, index) => {
8065
namedCaptureGroups[name] = index;
8166
};
8267
}
68+
8369
node.pattern = rewritePattern(node.pattern, node.flags, regexpuOptions);
8470

8571
if (
86-
regexpuOptions.namedGroup &&
72+
regexpuOptions.namedGroups === "transform" &&
8773
Object.keys(namedCaptureGroups).length > 0 &&
8874
runtime &&
8975
!isRegExpTest(path)
@@ -96,12 +82,8 @@ export function createRegExpFeaturePlugin({
9682

9783
path.replaceWith(call);
9884
}
99-
if (hasFeature(features, FEATURES.unicodeFlag)) {
100-
pullFlag(node, "u");
101-
}
102-
if (hasFeature(features, FEATURES.dotAllFlag)) {
103-
pullFlag(node, "s");
104-
}
85+
86+
node.flags = transformFlags(regexpuOptions, node.flags);
10587
},
10688
},
10789
};
Lines changed: 58 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,77 @@
1+
import type { types as t } from "@babel/core";
12
import { FEATURES, hasFeature } from "./features";
23

34
type RegexpuOptions = {
4-
useUnicodeFlag: boolean;
5+
unicodeFlag: "transform" | false;
6+
unicodeSetsFlag: "transform" | "parse" | false;
7+
dotAllFlag: "transform" | false;
8+
unicodePropertyEscapes: "transform" | false;
9+
namedGroups: "transform" | false;
510
onNamedGroup: (name: string, index: number) => void;
6-
namedGroup: boolean;
7-
unicodePropertyEscape: boolean;
8-
dotAllFlag: boolean;
9-
lookbehind: boolean;
1011
};
1112

12-
export function generateRegexpuOptions(node, features): RegexpuOptions | null {
13-
let useUnicodeFlag = false,
14-
dotAllFlag = false,
15-
unicodePropertyEscape = false,
16-
namedGroup = false;
13+
export function generateRegexpuOptions(toTransform: number): RegexpuOptions {
14+
type Experimental = 1;
15+
16+
const feat = <Stability extends 0 | 1 = 0>(
17+
name: keyof typeof FEATURES,
18+
ok: "transform" | (Stability extends 0 ? never : "parse") = "transform",
19+
) => {
20+
return hasFeature(toTransform, FEATURES[name]) ? ok : false;
21+
};
22+
23+
return {
24+
unicodeFlag: feat("unicodeFlag"),
25+
unicodeSetsFlag:
26+
feat<Experimental>("unicodeSetsFlag") ||
27+
feat<Experimental>("unicodeSetsFlag_syntax", "parse"),
28+
dotAllFlag: feat("dotAllFlag"),
29+
unicodePropertyEscapes: feat("unicodePropertyEscape"),
30+
namedGroups: feat("namedCaptureGroups"),
31+
onNamedGroup: () => {},
32+
};
33+
}
34+
35+
export function canSkipRegexpu(
36+
node: t.RegExpLiteral,
37+
options: RegexpuOptions,
38+
): boolean {
1739
const { flags, pattern } = node;
18-
const flagsIncludesU = flags.includes("u");
1940

20-
if (flagsIncludesU) {
21-
if (!hasFeature(features, FEATURES.unicodeFlag)) {
22-
useUnicodeFlag = true;
23-
}
41+
if (flags.includes("v")) {
42+
if (options.unicodeSetsFlag === "transform") return false;
43+
}
44+
45+
if (flags.includes("u")) {
46+
if (options.unicodeFlag === "transform") return false;
2447
if (
25-
hasFeature(features, FEATURES.unicodePropertyEscape) &&
48+
options.unicodePropertyEscapes === "transform" &&
2649
/\\[pP]{/.test(pattern)
2750
) {
28-
unicodePropertyEscape = true;
51+
return false;
2952
}
3053
}
3154

32-
if (hasFeature(features, FEATURES.dotAllFlag) && flags.indexOf("s") >= 0) {
33-
dotAllFlag = true;
55+
if (flags.includes("s")) {
56+
if (options.dotAllFlag === "transform") return false;
3457
}
35-
if (
36-
hasFeature(features, FEATURES.namedCaptureGroups) &&
37-
/\(\?<(?![=!])/.test(pattern)
38-
) {
39-
namedGroup = true;
58+
59+
if (options.namedGroups === "transform" && /\(\?<(?![=!])/.test(pattern)) {
60+
return false;
4061
}
41-
if (
42-
!namedGroup &&
43-
!unicodePropertyEscape &&
44-
!dotAllFlag &&
45-
(!flagsIncludesU || useUnicodeFlag)
46-
) {
47-
return null;
62+
63+
return true;
64+
}
65+
66+
export function transformFlags(regexpuOptions: RegexpuOptions, flags: string) {
67+
if (regexpuOptions.unicodeSetsFlag === "transform") {
68+
flags = flags.replace("v", "u");
4869
}
49-
// Now we have to feed regexpu-core the regex
50-
if (flagsIncludesU && flags.indexOf("s") >= 0) {
51-
// When flags includes u, `config.unicode` will be enabled even if `u` is supported natively.
52-
// In this case we have to enable dotAllFlag, otherwise `rewritePattern(/./su)` will return
53-
// incorrect result
54-
// https://github.com/mathiasbynens/regexpu-core/blob/v4.6.0/rewrite-pattern.js#L191
55-
dotAllFlag = true;
70+
if (regexpuOptions.unicodeFlag === "transform") {
71+
flags = flags.replace("u", "");
5672
}
57-
return {
58-
useUnicodeFlag,
59-
onNamedGroup: () => {},
60-
namedGroup,
61-
unicodePropertyEscape,
62-
dotAllFlag,
63-
lookbehind: true,
64-
};
73+
if (regexpuOptions.dotAllFlag === "transform") {
74+
flags = flags.replace("s", "");
75+
}
76+
return flags;
6577
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/([0-9]{4})/;
1+
/(\d{4})/;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
src
2+
test
3+
*.log
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# @babel/plugin-proposal-unicode-sets-regex
2+
3+
> Compile regular expressions' unicodeSets (v) flag.
4+
5+
See our website [@babel/plugin-proposal-unicode-sets-regex](https://babeljs.io/docs/en/babel-plugin-proposal-unicode-sets-regex) for more information.
6+
7+
## Install
8+
9+
Using npm:
10+
11+
```sh
12+
npm install --save-dev @babel/plugin-proposal-unicode-sets-regex
13+
```
14+
15+
or using yarn:
16+
17+
```sh
18+
yarn add @babel/plugin-proposal-unicode-sets-regex --dev
19+
```
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"name": "@babel/plugin-proposal-unicode-sets-regex",
3+
"version": "7.16.7",
4+
"description": "Compile regular expressions' unicodeSets (v) flag.",
5+
"homepage": "https://babel.dev/docs/en/next/babel-plugin-proposal-unicode-sets-regex",
6+
"license": "MIT",
7+
"publishConfig": {
8+
"access": "public"
9+
},
10+
"main": "./lib/index.js",
11+
"keywords": [
12+
"babel-plugin",
13+
"regex",
14+
"regexp",
15+
"unicode",
16+
"sets",
17+
"properties",
18+
"property",
19+
"string",
20+
"strings",
21+
"regular expressions"
22+
],
23+
"repository": {
24+
"type": "git",
25+
"url": "https://github.com/babel/babel.git",
26+
"directory": "packages/babel-plugin-proposal-unicode-sets-regex"
27+
},
28+
"bugs": "https://github.com/babel/babel/issues",
29+
"dependencies": {
30+
"@babel/helper-create-regexp-features-plugin": "workspace:^",
31+
"@babel/helper-plugin-utils": "workspace:^"
32+
},
33+
"peerDependencies": {
34+
"@babel/core": "^7.0.0"
35+
},
36+
"devDependencies": {
37+
"@babel/core": "workspace:^",
38+
"@babel/helper-plugin-test-runner": "workspace:^"
39+
},
40+
"author": "The Babel Team (https://babel.dev/team)",
41+
"exports": {
42+
".": "./lib/index.js",
43+
"./package.json": "./package.json"
44+
},
45+
"engines": {
46+
"node": ">=6.9.0"
47+
}
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* eslint-disable @babel/development/plugin-name */
2+
import { createRegExpFeaturePlugin } from "@babel/helper-create-regexp-features-plugin";
3+
import { declare } from "@babel/helper-plugin-utils";
4+
5+
export default declare(api => {
6+
api.assertVersion(7);
7+
8+
return createRegExpFeaturePlugin({
9+
name: "transform-unicode-sets-regex",
10+
feature: "unicodeSetsFlag",
11+
manipulateOptions(opts, parserOpts) {
12+
parserOpts.plugins.push("regexpUnicodeSets");
13+
},
14+
});
15+
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy