Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge multiple regex transform plugin (#10447)
* feat: implement create-regexp-features-plugin * fix: test input is not effective * refactor: leverage create-regexp-features-plugin * test: add more test cases * test: update test fixture * chore: add type annotation to features * test: add regression test for issue 9892 * add regression test for issue 9199 * address review comments from Nicolò * address review comments from Brian * small tweaks * Enable dotAllFlag when flags includes u
- Loading branch information
1 parent
ec3345b
commit 8ffca04
Showing
38 changed files
with
333 additions
and
113 deletions.
There are no files selected for viewing
3 changes: 3 additions & 0 deletions
3
packages/babel-helper-create-regexp-features-plugin/.npmignore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
src | ||
test | ||
*.log |
19 changes: 19 additions & 0 deletions
19
packages/babel-helper-create-regexp-features-plugin/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
31 changes: 31 additions & 0 deletions
31
packages/babel-helper-create-regexp-features-plugin/package.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
packages/babel-helper-create-regexp-features-plugin/src/features.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
96 changes: 96 additions & 0 deletions
96
packages/babel-helper-create-regexp-features-plugin/src/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
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" }) | ||
); | ||
} |
56 changes: 56 additions & 0 deletions
56
packages/babel-helper-create-regexp-features-plugin/src/util.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}; | ||
} |
1 change: 1 addition & 0 deletions
1
...s/plugin-proposal-unicode-property-regex/use-unicode-flag-false-not-overwritten/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/\p{Script_Extensions=Wancho}/u |
6 changes: 6 additions & 0 deletions
6
...lugin-proposal-unicode-property-regex/use-unicode-flag-false-not-overwritten/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
] | ||
} |
1 change: 1 addition & 0 deletions
1
.../plugin-proposal-unicode-property-regex/use-unicode-flag-false-not-overwritten/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/(?:\uD838[\uDEC0-\uDEF9\uDEFF])/; |
1 change: 1 addition & 0 deletions
1
...res/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/input.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/(?<year>\d{4})/ |
6 changes: 6 additions & 0 deletions
6
.../plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
] | ||
} |
1 change: 1 addition & 0 deletions
1
...es/plugin-transform-named-capturing-groups-regex/runtime-false-not-overwritten/output.mjs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
/([0-9]{4})/; |
3 changes: 3 additions & 0 deletions
3
packages/babel-helper-create-regexp-features-plugin/test/index.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import runner from "@babel/helper-plugin-test-runner"; | ||
|
||
runner(__dirname); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...lugin-proposal-unicode-property-regex/test/fixtures/with-unicode-flag/unicode-12/input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
}); | ||
}); |
2 changes: 2 additions & 0 deletions
2
...n-transform-dotall-regex/test/fixtures/dotall-regex/with-unicode-property-escape/input.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
3 changes: 3 additions & 0 deletions
3
...ansform-dotall-regex/test/fixtures/dotall-regex/with-unicode-property-escape/options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"plugins": ["transform-dotall-regex", "proposal-unicode-property-regex"] | ||
} |
2 changes: 2 additions & 0 deletions
2
...-transform-dotall-regex/test/fixtures/dotall-regex/with-unicode-property-escape/output.js
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.