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
Merge multiple regex transform plugin #10447
Changes from all commits
00e0312
04b48fe
0812de6
9a714cf
5a2415f
a1e0413
c019c03
60f9179
6e8f1bd
bcc46c7
c65a129
7fbd168
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
src | ||
test | ||
*.log |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# @babel/helper-create-regexp-features-plugin | ||
|
||
> Compile ESNext Regular Expressions to ES5 | ||
|
||
See our website [@babel/helper-create-regexp-features-plugin](https://babeljs.io/docs/en/next/babel-helper-create-regexp-features-plugin.html) for more information. | ||
|
||
## Install | ||
|
||
Using npm: | ||
|
||
```sh | ||
npm install --save-dev @babel/helper-create-regexp-features-plugin | ||
``` | ||
|
||
or using yarn: | ||
|
||
```sh | ||
yarn add @babel/helper-create-regexp-features-plugin --dev | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "@babel/helper-create-regexp-features-plugin", | ||
"version": "7.6.0", | ||
"author": "The Babel Team (https://babeljs.io/team)", | ||
"license": "MIT", | ||
"description": "Compile ESNext Regular Expressions to ES5", | ||
"repository": { | ||
"type": "git", | ||
"repository": "https://github.com/babel/babel", | ||
"directory": "packages/babel-helper-create-regexp-features-plugin" | ||
}, | ||
"main": "lib/index.js", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"keywords": [ | ||
"babel", | ||
"babel-plugin" | ||
], | ||
"dependencies": { | ||
"@babel/helper-regex": "^7.4.4", | ||
"regexpu-core": "^4.6.0" | ||
}, | ||
"peerDependencies": { | ||
"@babel/core": "^7.0.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.6.0", | ||
"@babel/helper-plugin-test-runner": "^7.0.0" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// @flow | ||
export const FEATURES = Object.freeze({ | ||
unicodeFlag: 1 << 0, | ||
dotAllFlag: 1 << 1, | ||
unicodePropertyEscape: 1 << 2, | ||
namedCaptureGroups: 1 << 3, | ||
}); | ||
|
||
// We can't use a symbol because this needs to always be the same, even if | ||
// this package isn't deduped by npm. e.g. | ||
// - node_modules/ | ||
// - @babel/plugin-regexp-features | ||
// - @babel/plugin-proposal-unicode-property-regex | ||
// - node_modules | ||
// - @babel-plugin-regexp-features | ||
export const featuresKey = "@babel/plugin-regexp-features/featuresKey"; | ||
export const runtimeKey = "@babel/plugin-regexp-features/runtimeKey"; | ||
|
||
type FeatureType = $Values<typeof FEATURES>; | ||
|
||
export function enableFeature(features: number, feature: FeatureType): number { | ||
return features | feature; | ||
} | ||
|
||
export function hasFeature(features: number, feature: FeatureType) { | ||
return !!(features & feature); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import rewritePattern from "regexpu-core"; | ||
import { | ||
featuresKey, | ||
FEATURES, | ||
enableFeature, | ||
runtimeKey, | ||
hasFeature, | ||
} from "./features"; | ||
import { generateRegexpuOptions } from "./util"; | ||
|
||
import pkg from "../package.json"; | ||
import { types as t } from "@babel/core"; | ||
import { pullFlag } from "@babel/helper-regex"; | ||
|
||
// Note: Versions are represented as an integer. e.g. 7.1.5 is represented | ||
// as 70000100005. This method is easier than using a semver-parsing | ||
// package, but it breaks if we release x.y.z where x, y or z are | ||
// greater than 99_999. | ||
const version = pkg.version.split(".").reduce((v, x) => v * 1e5 + +x, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW, I think it's perfectly fine using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's my fault 😛 I used this logic for class features There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is okay to leave it as is because all we need is a version sorter and we are not using |
||
const versionKey = "@babel/plugin-regexp-features/version"; | ||
|
||
export function createRegExpFeaturePlugin({ name, feature, options = {} }) { | ||
return { | ||
name, | ||
pre() { | ||
const { file } = this; | ||
const features = file.get(featuresKey) ?? 0; | ||
let newFeatures = enableFeature(features, FEATURES[feature]); | ||
|
||
const { useUnicodeFlag, runtime = true } = options; | ||
if (useUnicodeFlag === false) { | ||
newFeatures = enableFeature(newFeatures, FEATURES.unicodeFlag); | ||
} | ||
if (newFeatures !== features) { | ||
file.set(featuresKey, newFeatures); | ||
} | ||
|
||
if (!runtime) { | ||
file.set(runtimeKey, false); | ||
} | ||
|
||
if (!file.has(versionKey) || file.get(versionKey) < version) { | ||
file.set(versionKey, version); | ||
} | ||
}, | ||
|
||
visitor: { | ||
RegExpLiteral(path) { | ||
const { node } = path; | ||
const { file } = this; | ||
const features = file.get(featuresKey); | ||
const runtime = file.get(runtimeKey) ?? true; | ||
const regexpuOptions = generateRegexpuOptions(node, features); | ||
if (regexpuOptions === null) { | ||
return; | ||
} | ||
const namedCaptureGroups = {}; | ||
if (regexpuOptions.namedGroup) { | ||
regexpuOptions.onNamedGroup = (name, index) => { | ||
namedCaptureGroups[name] = index; | ||
}; | ||
} | ||
node.pattern = rewritePattern(node.pattern, node.flags, regexpuOptions); | ||
|
||
if ( | ||
regexpuOptions.namedGroup && | ||
Object.keys(namedCaptureGroups).length > 0 && | ||
runtime && | ||
!isRegExpTest(path) | ||
) { | ||
path.replaceWith( | ||
t.callExpression(this.addHelper("wrapRegExp"), [ | ||
node, | ||
t.valueToNode(namedCaptureGroups), | ||
]), | ||
); | ||
} | ||
if (hasFeature(features, FEATURES.unicodeFlag)) { | ||
pullFlag(node, "u"); | ||
} | ||
if (hasFeature(features, FEATURES.dotAllFlag)) { | ||
pullFlag(node, "s"); | ||
} | ||
}, | ||
}, | ||
}; | ||
} | ||
|
||
function isRegExpTest(path) { | ||
return ( | ||
path.parentPath.isMemberExpression({ | ||
object: path.node, | ||
computed: false, | ||
}) && path.parentPath.get("property").isIdentifier({ name: "test" }) | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { FEATURES, hasFeature } from "./features"; | ||
|
||
export function generateRegexpuOptions(node, features) { | ||
let useUnicodeFlag = false, | ||
dotAllFlag = false, | ||
unicodePropertyEscape = false, | ||
namedGroup = false; | ||
const { flags, pattern } = node; | ||
const flagsIncludesU = flags.includes("u"); | ||
|
||
if (flagsIncludesU) { | ||
if (!hasFeature(features, FEATURES.unicodeFlag)) { | ||
useUnicodeFlag = true; | ||
} | ||
if ( | ||
hasFeature(features, FEATURES.unicodePropertyEscape) && | ||
/\\[pP]{/.test(pattern) | ||
) { | ||
unicodePropertyEscape = true; | ||
} | ||
} | ||
|
||
if (hasFeature(features, FEATURES.dotAllFlag) && flags.indexOf("s") >= 0) { | ||
dotAllFlag = true; | ||
} | ||
if ( | ||
hasFeature(features, FEATURES.namedCaptureGroups) && | ||
/\(\?<(?![=!])/.test(pattern) | ||
) { | ||
namedGroup = true; | ||
} | ||
if ( | ||
!namedGroup && | ||
!unicodePropertyEscape && | ||
!dotAllFlag && | ||
(!flagsIncludesU || useUnicodeFlag) | ||
) { | ||
return null; | ||
} | ||
// 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; | ||
} | ||
return { | ||
useUnicodeFlag, | ||
onNamedGroup: () => {}, | ||
namedGroup, | ||
unicodePropertyEscape, | ||
dotAllFlag, | ||
lookbehind: true, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/\p{Script_Extensions=Wancho}/u |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"plugins": [ | ||
["proposal-unicode-property-regex", { "useUnicodeFlag": false }, "name 1"], | ||
["proposal-unicode-property-regex", { "useUnicodeFlag": true }, "name 2"] | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/(?:\uD838[\uDEC0-\uDEF9\uDEFF])/; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/(?<year>\d{4})/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"plugins": [ | ||
["transform-named-capturing-groups-regex", { "runtime": false }, "name 1"], | ||
["transform-named-capturing-groups-regex", { "runtime": true }, "name 2"] | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/([0-9]{4})/; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import runner from "@babel/helper-plugin-test-runner"; | ||
|
||
runner(__dirname); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
/* eslint-disable @babel/development/plugin-name */ | ||
import { createRegExpFeaturePlugin } from "@babel/helper-create-regexp-features-plugin"; | ||
import { declare } from "@babel/helper-plugin-utils"; | ||
import rewritePattern from "regexpu-core"; | ||
import * as regex from "@babel/helper-regex"; | ||
|
||
export default declare((api, options) => { | ||
api.assertVersion(7); | ||
|
@@ -10,23 +10,9 @@ export default declare((api, options) => { | |
throw new Error(".useUnicodeFlag must be a boolean, or undefined"); | ||
} | ||
|
||
return { | ||
return createRegExpFeaturePlugin({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯, clean! |
||
name: "proposal-unicode-property-regex", | ||
|
||
visitor: { | ||
RegExpLiteral(path) { | ||
const node = path.node; | ||
if (!regex.is(node, "u")) { | ||
return; | ||
} | ||
node.pattern = rewritePattern(node.pattern, node.flags, { | ||
unicodePropertyEscape: true, | ||
useUnicodeFlag, | ||
}); | ||
if (!useUnicodeFlag) { | ||
regex.pullFlag(node, "u"); | ||
} | ||
}, | ||
}, | ||
}; | ||
feature: "unicodePropertyEscape", | ||
options: { useUnicodeFlag }, | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
var regex = /[\u{1E2C0}-\u{1E2F9}\u{1E2FF}]/u; | ||
var regex = /[\p{Script_Extensions=Wancho}]/u; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,12 @@ | ||
/* eslint-disable @babel/development/plugin-name */ | ||
import { createRegExpFeaturePlugin } from "@babel/helper-create-regexp-features-plugin"; | ||
import { declare } from "@babel/helper-plugin-utils"; | ||
import rewritePattern from "regexpu-core"; | ||
import * as regex from "@babel/helper-regex"; | ||
|
||
export default declare(api => { | ||
api.assertVersion(7); | ||
|
||
return { | ||
return createRegExpFeaturePlugin({ | ||
name: "transform-dotall-regex", | ||
|
||
visitor: { | ||
RegExpLiteral(path) { | ||
const node = path.node; | ||
if (!regex.is(node, "s")) { | ||
return; | ||
} | ||
node.pattern = rewritePattern(node.pattern, node.flags, { | ||
dotAllFlag: true, | ||
useUnicodeFlag: regex.is(node, "u"), | ||
}); | ||
regex.pullFlag(node, "s"); | ||
}, | ||
}, | ||
}; | ||
feature: "dotAllFlag", | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
var a = /\p{Unified_Ideograph}./u; | ||
var b = /\p{Unified_Ideograph}./su; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"plugins": ["transform-dotall-regex", "proposal-unicode-property-regex"] | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This link is dead. Is there supposed to be anything here?