Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transform support for the "regexp unicode sets" proposal #14125

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -19,7 +19,7 @@
],
"dependencies": {
"@babel/helper-annotate-as-pure": "workspace:^",
"regexpu-core": "^4.7.1"
"regexpu-core": "^5.0.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
Expand Down
44 changes: 11 additions & 33 deletions packages/babel-helper-create-regexp-features-plugin/src/index.ts
@@ -1,29 +1,10 @@
import rewritePattern from "regexpu-core";
import {
featuresKey,
FEATURES,
enableFeature,
runtimeKey,
hasFeature,
} from "./features";
import { generateRegexpuOptions } from "./util";
import { featuresKey, FEATURES, enableFeature, runtimeKey } from "./features";
import { generateRegexpuOptions, canSkipRegexpu, transformFlags } from "./util";

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

type RegExpFlags = "i" | "g" | "m" | "s" | "u" | "y";

/**
* Remove given flag from given RegExpLiteral node
*
* @param {RegExpLiteral} node
* @param {RegExpFlags} flag
* @returns {void}
*/
function pullFlag(node, flag: RegExpFlags): void {
node.flags = node.flags.replace(flag, "");
}

declare const PACKAGE_JSON: { name: string; version: string };

// Note: Versions are represented as an integer. e.g. 7.1.5 is represented
Expand Down Expand Up @@ -70,20 +51,21 @@ export function createRegExpFeaturePlugin({
const { file } = this;
const features = file.get(featuresKey);
const runtime = file.get(runtimeKey) ?? true;
const regexpuOptions = generateRegexpuOptions(node, features);
if (regexpuOptions === null) {
return;
}

const regexpuOptions = generateRegexpuOptions(features);
if (canSkipRegexpu(node, regexpuOptions)) return;

const namedCaptureGroups = {};
if (regexpuOptions.namedGroup) {
if (regexpuOptions.namedGroups === "transform") {
regexpuOptions.onNamedGroup = (name, index) => {
namedCaptureGroups[name] = index;
};
}

node.pattern = rewritePattern(node.pattern, node.flags, regexpuOptions);

if (
regexpuOptions.namedGroup &&
regexpuOptions.namedGroups === "transform" &&
Object.keys(namedCaptureGroups).length > 0 &&
runtime &&
!isRegExpTest(path)
Expand All @@ -96,12 +78,8 @@ export function createRegExpFeaturePlugin({

path.replaceWith(call);
}
if (hasFeature(features, FEATURES.unicodeFlag)) {
pullFlag(node, "u");
}
if (hasFeature(features, FEATURES.dotAllFlag)) {
pullFlag(node, "s");
}

node.flags = transformFlags(regexpuOptions, node.flags);
},
},
};
Expand Down
88 changes: 42 additions & 46 deletions packages/babel-helper-create-regexp-features-plugin/src/util.ts
@@ -1,65 +1,61 @@
import type { types as t } from "@babel/core";
import { FEATURES, hasFeature } from "./features";

type RegexpuOptions = {
useUnicodeFlag: boolean;
unicodeFlag: "transform" | false;
dotAllFlag: "transform" | false;
unicodePropertyEscapes: "transform" | false;
namedGroups: "transform" | false;
onNamedGroup: (name: string, index: number) => void;
namedGroup: boolean;
unicodePropertyEscape: boolean;
dotAllFlag: boolean;
lookbehind: boolean;
};

export function generateRegexpuOptions(node, features): RegexpuOptions | null {
let useUnicodeFlag = false,
dotAllFlag = false,
unicodePropertyEscape = false,
namedGroup = false;
export function generateRegexpuOptions(toTransform: number): RegexpuOptions {
const feat = (name: keyof typeof FEATURES) => {
return hasFeature(toTransform, FEATURES[name]) ? "transform" : false;
};

return {
unicodeFlag: feat("unicodeFlag"),
dotAllFlag: feat("dotAllFlag"),
unicodePropertyEscapes: feat("unicodePropertyEscape"),
namedGroups: feat("namedCaptureGroups"),
onNamedGroup: () => {},
};
}

export function canSkipRegexpu(
node: t.RegExpLiteral,
options: RegexpuOptions,
): boolean {
const { flags, pattern } = node;
const flagsIncludesU = flags.includes("u");

if (flagsIncludesU) {
if (!hasFeature(features, FEATURES.unicodeFlag)) {
useUnicodeFlag = true;
}
if (flags.includes("u")) {
if (options.unicodeFlag === "transform") return false;
if (
hasFeature(features, FEATURES.unicodePropertyEscape) &&
options.unicodePropertyEscapes === "transform" &&
/\\[pP]{/.test(pattern)
) {
unicodePropertyEscape = true;
return false;
}
}

if (hasFeature(features, FEATURES.dotAllFlag) && flags.indexOf("s") >= 0) {
dotAllFlag = true;
if (flags.includes("s")) {
if (options.dotAllFlag === "transform") return false;
}
if (
hasFeature(features, FEATURES.namedCaptureGroups) &&
/\(\?<(?![=!])/.test(pattern)
) {
namedGroup = true;

if (options.namedGroups === "transform" && /\(\?<(?![=!])/.test(pattern)) {
return false;
}
if (
!namedGroup &&
!unicodePropertyEscape &&
!dotAllFlag &&
(!flagsIncludesU || useUnicodeFlag)
) {
return null;

return true;
}

export function transformFlags(regexpuOptions: RegexpuOptions, flags: string) {
if (regexpuOptions.unicodeFlag === "transform") {
flags = flags.replace("u", "");
}
// Now we have to feed regexpu-core the regex
if (flagsIncludesU && flags.indexOf("s") >= 0) {
// When flags includes u, `config.unicode` will be enabled even if `u` is supported natively.
// In this case we have to enable dotAllFlag, otherwise `rewritePattern(/./su)` will return
// incorrect result
// https://github.com/mathiasbynens/regexpu-core/blob/v4.6.0/rewrite-pattern.js#L191
dotAllFlag = true;
if (regexpuOptions.dotAllFlag === "transform") {
flags = flags.replace("s", "");
}
return {
useUnicodeFlag,
onNamedGroup: () => {},
namedGroup,
unicodePropertyEscape,
dotAllFlag,
lookbehind: true,
};
return flags;
}
@@ -1 +1 @@
/([0-9]{4})/;
/(\d{4})/;
@@ -1,2 +1,2 @@
var a = /./u;
var b = /[\0-\u{10FFFF}]/u;
var b = /[\s\S]/u;

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 42 additions & 1 deletion yarn.lock
Expand Up @@ -586,7 +586,7 @@ __metadata:
"@babel/core": "workspace:^"
"@babel/helper-annotate-as-pure": "workspace:^"
"@babel/helper-plugin-test-runner": "workspace:^"
regexpu-core: ^4.7.1
regexpu-core: ^5.0.0
peerDependencies:
"@babel/core": ^7.0.0
languageName: unknown
Expand Down Expand Up @@ -13251,6 +13251,15 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"regenerate-unicode-properties@npm:^10.0.1":
version: 10.0.1
resolution: "regenerate-unicode-properties@npm:10.0.1"
dependencies:
regenerate: ^1.4.2
checksum: 1b638b7087d8143e5be3e20e2cda197ea0440fa0bc2cc49646b2f50c5a2b1acdc54b21e4215805a5a2dd487c686b2291accd5ad00619534098d2667e76247754
languageName: node
linkType: hard

"regenerate-unicode-properties@npm:^9.0.0":
version: 9.0.0
resolution: "regenerate-unicode-properties@npm:9.0.0"
Expand Down Expand Up @@ -13315,13 +13324,34 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"regexpu-core@npm:^5.0.0":
version: 5.0.0
resolution: "regexpu-core@npm:5.0.0"
dependencies:
regenerate: ^1.4.2
regenerate-unicode-properties: ^10.0.1
regjsgen: ^0.6.0
regjsparser: ^0.8.2
unicode-match-property-ecmascript: ^2.0.0
unicode-match-property-value-ecmascript: ^2.0.0
checksum: e03ca9d9d17e2179d92bfc37baca23143058ee978dddf986a37842fe2866a41a6ed1b847d707df743287da99d1370220a5468633dcc1aea2d3934a3f3a055516
languageName: node
linkType: hard

"regjsgen@npm:^0.5.2":
version: 0.5.2
resolution: "regjsgen@npm:0.5.2"
checksum: 87c83d8488affae2493a823904de1a29a1867a07433c5e1142ad749b5606c5589b305fe35bfcc0972cf5a3b0d66b1f7999009e541be39a5d42c6041c59e2fb52
languageName: node
linkType: hard

"regjsgen@npm:^0.6.0":
version: 0.6.0
resolution: "regjsgen@npm:0.6.0"
checksum: c5158ebd735e75074e41292ade1ff05d85566d205426cc61501e360c450a63baced8512ee3ae238e5c0a0e42969563c7875b08fa69d6f0402daf36bcb3e4d348
languageName: node
linkType: hard

"regjsparser@npm:^0.7.0":
version: 0.7.0
resolution: "regjsparser@npm:0.7.0"
Expand All @@ -13333,6 +13363,17 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"regjsparser@npm:^0.8.2":
version: 0.8.3
resolution: "regjsparser@npm:0.8.3"
dependencies:
jsesc: ~0.5.0
bin:
regjsparser: bin/parser
checksum: b113328c3bb9c5f607e1fb42120b686f5ab3b7efc687fef1ce593effc36dfee091929ba6d7180d501d84137d977dd3b029b9129d07f76490d6fd035afdcbff5b
languageName: node
linkType: hard

"remove-bom-buffer@npm:^3.0.0":
version: 3.0.0
resolution: "remove-bom-buffer@npm:3.0.0"
Expand Down