Skip to content

Commit a34b908

Browse files
committed
Add no-regexp-unicode-property-escapes-2020 rule
1 parent 61d47aa commit a34b908

17 files changed

+732
-212
lines changed

.github/workflows/cron.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: cron
2+
on:
3+
schedule:
4+
- cron: 0 0 * * 0
5+
6+
jobs:
7+
check-resource-update:
8+
name: check-resource-update
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@v3
13+
- name: Install Node.js
14+
uses: actions/setup-node@v3
15+
with:
16+
node-version: 14
17+
- name: Install Packages
18+
run: npm install
19+
- name: Update
20+
run: npm run resource-update:unicode-properties
21+
- name: Check changes
22+
run: |
23+
git add --all && \
24+
git diff-index --cached HEAD --stat --exit-code

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ There are multiple configs that enable all rules in this category: `plugin:es-x/
5555
| [es-x/no-nullish-coalescing-operators](./no-nullish-coalescing-operators.md) | disallow nullish coalescing operators. | |
5656
| [es-x/no-optional-chaining](./no-optional-chaining.md) | disallow optional chaining. | |
5757
| [es-x/no-promise-all-settled](./no-promise-all-settled.md) | disallow `Promise.allSettled` function. | |
58+
| [es-x/no-regexp-unicode-property-escapes-2020](./no-regexp-unicode-property-escapes-2020.md) | disallow the new values of RegExp Unicode property escape sequences in ES2020. | |
5859
| [es-x/no-string-prototype-matchall](./no-string-prototype-matchall.md) | disallow the `String.prototype.matchAll` method. | |
5960

6061
## ES2019
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "es-x/no-regexp-unicode-property-escapes-2020"
3+
description: "disallow the new values of RegExp Unicode property escape sequences in ES2020"
4+
---
5+
6+
# es-x/no-regexp-unicode-property-escapes-2020
7+
> disallow the new values of RegExp Unicode property escape sequences in ES2020
8+
9+
- ❗ <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
10+
- ✅ The following configurations enable this rule: `plugin:es-x/no-new-in-es2020`, `plugin:es-x/restrict-to-es3`, `plugin:es-x/restrict-to-es5`, `plugin:es-x/restrict-to-es2015`, `plugin:es-x/restrict-to-es2016`, `plugin:es-x/restrict-to-es2017`, `plugin:es-x/restrict-to-es2018`, and `plugin:es-x/restrict-to-es2019`
11+
12+
This rule reports the new values of ES2018 [RegExp Unicode property escape sequences](https://github.com/tc39/proposal-regexp-unicode-property-escapes#readme) which were added in ES2020.
13+
14+
For example, the following patterns are valid in ES2020, but syntax error in ES2019 environments:
15+
16+
- `\p{Script=Elym}`
17+
- `\p{Script=Elymaic}`
18+
- `\p{Script=Hmnp}`
19+
- `\p{Script=Nand}`
20+
- `\p{Script=Nandinagari}`
21+
- `\p{Script=Nyiakeng_Puachue_Hmong}`
22+
- `\p{Script=Wancho}`
23+
- `\p{Script=Wcho}`
24+
25+
## 💡 Examples
26+
27+
⛔ Examples of **incorrect** code for this rule:
28+
29+
<eslint-playground type="bad">
30+
31+
```js
32+
/*eslint es-x/no-regexp-unicode-property-escapes-2020: error */
33+
const r1 = /\p{Script=Elym}/u
34+
const r2 = /\p{Script=Elymaic}/u
35+
```
36+
37+
</eslint-playground>
38+
39+
## 📚 References
40+
41+
- [Rule source](https://github.com/ota-meshi/eslint-plugin-es-x/blob/master/lib/rules/no-regexp-unicode-property-escapes-2020.js)
42+
- [Test source](https://github.com/ota-meshi/eslint-plugin-es-x/blob/master/tests/lib/rules/no-regexp-unicode-property-escapes-2020.js)

lib/configs/no-new-in-es2020.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module.exports = {
1515
"es-x/no-nullish-coalescing-operators": "error",
1616
"es-x/no-optional-chaining": "error",
1717
"es-x/no-promise-all-settled": "error",
18+
"es-x/no-regexp-unicode-property-escapes-2020": "error",
1819
"es-x/no-string-prototype-matchall": "error",
1920
},
2021
}

lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ module.exports = {
189189
"no-regexp-u-flag": require("./rules/no-regexp-u-flag"),
190190
"no-regexp-unicode-property-escapes": require("./rules/no-regexp-unicode-property-escapes"),
191191
"no-regexp-unicode-property-escapes-2019": require("./rules/no-regexp-unicode-property-escapes-2019"),
192+
"no-regexp-unicode-property-escapes-2020": require("./rules/no-regexp-unicode-property-escapes-2020"),
192193
"no-regexp-y-flag": require("./rules/no-regexp-y-flag"),
193194
"no-rest-parameters": require("./rules/no-rest-parameters"),
194195
"no-rest-spread-properties": require("./rules/no-rest-spread-properties"),

lib/rules/no-regexp-lookbehind-assertions.js

Lines changed: 16 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,7 @@
44
*/
55
"use strict"
66

7-
const { RegExpValidator } = require("regexpp")
8-
const { getRegExpCalls } = require("../utils")
9-
10-
/**
11-
* Verify a given regular expression.
12-
* @param {RuleContext} context The rule context to report.
13-
* @param {Node} node The AST node to report.
14-
* @param {string} pattern The pattern part of a RegExp.
15-
* @param {string} flags The flags part of a RegExp.
16-
* @returns {void}
17-
*/
18-
function verify(context, node, pattern, flags) {
19-
try {
20-
let found = false
21-
22-
new RegExpValidator({
23-
onLookaroundAssertionEnter(_start, kind) {
24-
if (kind === "lookbehind") {
25-
found = true
26-
}
27-
},
28-
}).validatePattern(pattern, 0, pattern.length, flags.includes("u"))
29-
30-
if (found) {
31-
context.report({ node, messageId: "forbidden" })
32-
}
33-
} catch (error) {
34-
//istanbul ignore else
35-
if (error.message.startsWith("Invalid regular expression:")) {
36-
return
37-
}
38-
//istanbul ignore next
39-
throw error
40-
}
41-
}
7+
const { defineRegExpHandler } = require("../util/define-regexp-handler")
428

439
module.exports = {
4410
meta: {
@@ -56,18 +22,20 @@ module.exports = {
5622
type: "problem",
5723
},
5824
create(context) {
59-
return {
60-
"Literal[regex]"(node) {
61-
const { pattern, flags } = node.regex
62-
verify(context, node, pattern || "", flags || "")
63-
},
64-
65-
"Program:exit"() {
66-
const scope = context.getScope()
67-
for (const { node, pattern, flags } of getRegExpCalls(scope)) {
68-
verify(context, node, pattern || "", flags || "")
69-
}
70-
},
71-
}
25+
return defineRegExpHandler(context, (node) => {
26+
let found = false
27+
return {
28+
onLookaroundAssertionEnter(_start, kind) {
29+
if (kind === "lookbehind") {
30+
found = true
31+
}
32+
},
33+
onExit() {
34+
if (found) {
35+
context.report({ node, messageId: "forbidden" })
36+
}
37+
},
38+
}
39+
})
7240
},
7341
}

lib/rules/no-regexp-named-capture-groups.js

Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,46 +4,7 @@
44
*/
55
"use strict"
66

7-
const { RegExpValidator } = require("regexpp")
8-
const { getRegExpCalls } = require("../utils")
9-
10-
/**
11-
* Verify a given regular expression.
12-
* @param {RuleContext} context The rule context to report.
13-
* @param {Node} node The AST node to report.
14-
* @param {string} pattern The pattern part of a RegExp.
15-
* @param {string} flags The flags part of a RegExp.
16-
* @returns {void}
17-
*/
18-
function verify(context, node, pattern, flags) {
19-
try {
20-
let found = false
21-
22-
new RegExpValidator({
23-
onCapturingGroupEnter(_start, name) {
24-
if (name) {
25-
found = true
26-
}
27-
},
28-
onBackreference(_start, _end, ref) {
29-
if (typeof ref === "string") {
30-
found = true
31-
}
32-
},
33-
}).validatePattern(pattern, 0, pattern.length, flags.includes("u"))
34-
35-
if (found) {
36-
context.report({ node, messageId: "forbidden" })
37-
}
38-
} catch (error) {
39-
//istanbul ignore else
40-
if (error.message.startsWith("Invalid regular expression:")) {
41-
return
42-
}
43-
//istanbul ignore next
44-
throw error
45-
}
46-
}
7+
const { defineRegExpHandler } = require("../util/define-regexp-handler")
478

489
module.exports = {
4910
meta: {
@@ -61,18 +22,25 @@ module.exports = {
6122
type: "problem",
6223
},
6324
create(context) {
64-
return {
65-
"Literal[regex]"(node) {
66-
const { pattern, flags } = node.regex
67-
verify(context, node, pattern || "", flags || "")
68-
},
69-
70-
"Program:exit"() {
71-
const scope = context.getScope()
72-
for (const { node, pattern, flags } of getRegExpCalls(scope)) {
73-
verify(context, node, pattern || "", flags || "")
74-
}
75-
},
76-
}
25+
return defineRegExpHandler(context, (node) => {
26+
let found = false
27+
return {
28+
onCapturingGroupEnter(_start, name) {
29+
if (name) {
30+
found = true
31+
}
32+
},
33+
onBackreference(_start, _end, ref) {
34+
if (typeof ref === "string") {
35+
found = true
36+
}
37+
},
38+
onExit() {
39+
if (found) {
40+
context.report({ node, messageId: "forbidden" })
41+
}
42+
},
43+
}
44+
})
7745
},
7846
}

lib/rules/no-regexp-unicode-property-escapes-2019.js

Lines changed: 34 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -4,63 +4,19 @@
44
*/
55
"use strict"
66

7-
const { RegExpValidator } = require("regexpp")
8-
const { getRegExpCalls } = require("../utils")
9-
10-
const scNamePattern = /^(?:Script(?:_Extensions)?|scx?)$/u
11-
const scValuePattern =
12-
/^(?:Dogr|Dogra|Gong|Gunjala_Gondi|Hanifi_Rohingya|Maka|Makasar|Medefaidrin|Medf|Old_Sogdian|Rohg|Sogd|Sogdian|Sogo)$/u
7+
const { defineRegExpHandler } = require("../util/define-regexp-handler")
8+
const {
9+
scNameSet,
10+
scValueSets,
11+
binPropertySets,
12+
} = require("../util/unicode-properties")
1313

1414
function isNewUnicodePropertyKeyValuePair(key, value) {
15-
return scNamePattern.test(key) && scValuePattern.test(value)
15+
return scNameSet.has(key) && scValueSets.es2019.has(value)
1616
}
1717

1818
function isNewBinaryUnicodeProperty(key) {
19-
return key === "Extended_Pictographic"
20-
}
21-
22-
/**
23-
* Verify a given regular expression.
24-
* @param {RuleContext} context The rule context to report.
25-
* @param {Node} node The AST node to report.
26-
* @param {string} pattern The pattern part of a RegExp.
27-
* @param {string} flags The flags part of a RegExp.
28-
* @returns {void}
29-
*/
30-
function verify(context, node, pattern, flags) {
31-
try {
32-
let foundValue = ""
33-
34-
new RegExpValidator({
35-
onUnicodePropertyCharacterSet(start, end, _kind, key, value) {
36-
if (foundValue) {
37-
return
38-
}
39-
if (
40-
value
41-
? isNewUnicodePropertyKeyValuePair(key, value)
42-
: isNewBinaryUnicodeProperty(key)
43-
) {
44-
foundValue = pattern.slice(start, end)
45-
}
46-
},
47-
}).validatePattern(pattern, 0, pattern.length, flags.includes("u"))
48-
49-
if (foundValue) {
50-
context.report({
51-
node,
52-
messageId: "forbidden",
53-
data: { value: foundValue },
54-
})
55-
}
56-
} catch (error) {
57-
//istanbul ignore else
58-
if (error.message.startsWith("Invalid regular expression:")) {
59-
return
60-
}
61-
//istanbul ignore next
62-
throw error
63-
}
19+
return binPropertySets.es2019.has(key)
6420
}
6521

6622
module.exports = {
@@ -80,18 +36,31 @@ module.exports = {
8036
type: "problem",
8137
},
8238
create(context) {
83-
return {
84-
"Literal[regex]"(node) {
85-
const { pattern, flags } = node.regex
86-
verify(context, node, pattern || "", flags || "")
87-
},
88-
89-
"Program:exit"() {
90-
const scope = context.getScope()
91-
for (const { node, pattern, flags } of getRegExpCalls(scope)) {
92-
verify(context, node, pattern || "", flags || "")
93-
}
94-
},
95-
}
39+
return defineRegExpHandler(context, (node, { pattern }) => {
40+
let foundValue = ""
41+
return {
42+
onUnicodePropertyCharacterSet(start, end, _kind, key, value) {
43+
if (foundValue) {
44+
return
45+
}
46+
if (
47+
value
48+
? isNewUnicodePropertyKeyValuePair(key, value)
49+
: isNewBinaryUnicodeProperty(key)
50+
) {
51+
foundValue = pattern.slice(start, end)
52+
}
53+
},
54+
onExit() {
55+
if (foundValue) {
56+
context.report({
57+
node,
58+
messageId: "forbidden",
59+
data: { value: foundValue },
60+
})
61+
}
62+
},
63+
}
64+
})
9665
},
9766
}

0 commit comments

Comments
 (0)