Skip to content

Commit 331159a

Browse files
committed
update
1 parent 10b3976 commit 331159a

File tree

4 files changed

+48
-20
lines changed

4 files changed

+48
-20
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ The `plugin:regexp/all` config enables all rules. It's meant for testing, not fo
139139
| :----------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :- | :- | :- | :- |
140140
| [confusing-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/confusing-quantifier.html) | disallow confusing quantifiers | || | |
141141
| [control-character-escape](https://ota-meshi.github.io/eslint-plugin-regexp/rules/control-character-escape.html) | enforce consistent escaping of control characters || | 🔧 | |
142-
| [grapheme-string-literal](https://ota-meshi.github.io/eslint-plugin-regexp/rules/grapheme-string-literal.html) | enforce single grapheme in string literal | | | | |
143142
| [negation](https://ota-meshi.github.io/eslint-plugin-regexp/rules/negation.html) | enforce use of escapes on negation || | 🔧 | |
144143
| [no-dupe-characters-character-class](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-dupe-characters-character-class.html) | disallow duplicate characters in the RegExp character class || | 🔧 | |
145144
| [no-empty-string-literal](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-string-literal.html) | disallow empty string literals in character classes || | | |
@@ -181,6 +180,7 @@ The `plugin:regexp/all` config enables all rules. It's meant for testing, not fo
181180

182181
| Name                             | Description | 💼 | ⚠️ | 🔧 | 💡 |
183182
| :------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- | :- | :- | :- | :- |
183+
| [grapheme-string-literal](https://ota-meshi.github.io/eslint-plugin-regexp/rules/grapheme-string-literal.html) | enforce single grapheme in string literal | | | | |
184184
| [hexadecimal-escape](https://ota-meshi.github.io/eslint-plugin-regexp/rules/hexadecimal-escape.html) | enforce consistent usage of hexadecimal escape | | | 🔧 | |
185185
| [letter-case](https://ota-meshi.github.io/eslint-plugin-regexp/rules/letter-case.html) | enforce into your favorite case | | | 🔧 | |
186186
| [match-any](https://ota-meshi.github.io/eslint-plugin-regexp/rules/match-any.html) | enforce match any character style || | 🔧 | |

docs/rules/index.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ sidebarDepth: 0
4545
| :-------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------- | :- | :- | :- | :- |
4646
| [confusing-quantifier](confusing-quantifier.md) | disallow confusing quantifiers | || | |
4747
| [control-character-escape](control-character-escape.md) | enforce consistent escaping of control characters || | 🔧 | |
48-
| [grapheme-string-literal](grapheme-string-literal.md) | enforce single grapheme in string literal | | | | |
4948
| [negation](negation.md) | enforce use of escapes on negation || | 🔧 | |
5049
| [no-dupe-characters-character-class](no-dupe-characters-character-class.md) | disallow duplicate characters in the RegExp character class || | 🔧 | |
5150
| [no-empty-string-literal](no-empty-string-literal.md) | disallow empty string literals in character classes || | | |
@@ -87,6 +86,7 @@ sidebarDepth: 0
8786

8887
| Name                             | Description | 💼 | ⚠️ | 🔧 | 💡 |
8988
| :---------------------------------------------------------------------- | :--------------------------------------------------------------------- | :- | :- | :- | :- |
89+
| [grapheme-string-literal](grapheme-string-literal.md) | enforce single grapheme in string literal | | | | |
9090
| [hexadecimal-escape](hexadecimal-escape.md) | enforce consistent usage of hexadecimal escape | | | 🔧 | |
9191
| [letter-case](letter-case.md) | enforce into your favorite case | | | 🔧 | |
9292
| [match-any](match-any.md) | enforce match any character style || | 🔧 | |

lib/rules/grapheme-string-literal.ts

+31-15
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
import type { RegExpVisitor } from "@eslint-community/regexpp/visitor"
22
import type { RegExpContext } from "../utils"
33
import { createRule, defineRegexpVisitor } from "../utils"
4+
import type { StringAlternative } from "@eslint-community/regexpp/ast"
45

56
const segmenter = new Intl.Segmenter()
67

78
export default createRule("grapheme-string-literal", {
89
meta: {
910
docs: {
1011
description: "enforce single grapheme in string literal",
11-
category: "Best Practices",
12+
category: "Stylistic Issues",
1213
recommended: false,
1314
},
1415
schema: [],
1516
messages: {
16-
useSingleGrapheme: "Use single grapheme in string literal.",
17+
onlySingleCharacters:
18+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `{{alternatives}}`) for strings instead.",
1719
},
1820
type: "suggestion",
1921
},
@@ -23,21 +25,35 @@ export default createRule("grapheme-string-literal", {
2325
): RegExpVisitor.Handlers {
2426
const { node, getRegexpLocation } = regexpContext
2527

28+
function isMultipleGraphemes(saNode: StringAlternative) {
29+
if (saNode.elements.length <= 1) return false
30+
const string = String.fromCodePoint(
31+
...saNode.elements.map((element) => element.value),
32+
)
33+
34+
const segments = [...segmenter.segment(string)]
35+
return segments.length > 1
36+
}
37+
38+
function buildAlternativeExample(saNode: StringAlternative) {
39+
const alternativeRaws = saNode.parent.alternatives
40+
.filter(isMultipleGraphemes)
41+
.map((alt) => alt.raw)
42+
return `(?:${alternativeRaws.join("|")}|[...])`
43+
}
44+
2645
return {
2746
onStringAlternativeEnter(saNode) {
28-
if (saNode.elements.length <= 1) return
29-
const string = String.fromCodePoint(
30-
...saNode.elements.map((element) => element.value),
31-
)
32-
33-
const segments = [...segmenter.segment(string)]
34-
if (segments.length > 1) {
35-
context.report({
36-
node,
37-
loc: getRegexpLocation(saNode),
38-
messageId: "useSingleGrapheme",
39-
})
40-
}
47+
if (!isMultipleGraphemes(saNode)) return
48+
49+
context.report({
50+
node,
51+
loc: getRegexpLocation(saNode),
52+
messageId: "onlySingleCharacters",
53+
data: {
54+
alternatives: buildAlternativeExample(saNode),
55+
},
56+
})
4157
},
4258
}
4359
}

tests/lib/rules/grapheme-string-literal.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ tester.run("grapheme-string-literal", rule as any, {
179179
code: String.raw`/[\q{abc}]/v`,
180180
errors: [
181181
{
182-
message: "Use single grapheme in string literal.",
182+
message:
183+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:abc|[...])`) for strings instead.",
183184
line: 1,
184185
column: 6,
185186
},
@@ -189,7 +190,8 @@ tester.run("grapheme-string-literal", rule as any, {
189190
code: String.raw`/[\q{a|bc|}]/v`,
190191
errors: [
191192
{
192-
message: "Use single grapheme in string literal.",
193+
message:
194+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:bc|[...])`) for strings instead.",
193195
line: 1,
194196
column: 8,
195197
},
@@ -199,11 +201,21 @@ tester.run("grapheme-string-literal", rule as any, {
199201
code: String.raw`/[\q{🇦🇨🇦🇩}]/v`,
200202
errors: [
201203
{
202-
message: "Use single grapheme in string literal.",
204+
message:
205+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:🇦🇨🇦🇩|[...])`) for strings instead.",
203206
line: 1,
204207
column: 6,
205208
},
206209
],
207210
},
211+
{
212+
code: String.raw`/[\q{abc|def|ghi|j|k|lm|n}]/v`,
213+
errors: [
214+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:abc|def|ghi|lm|[...])`) for strings instead.",
215+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:abc|def|ghi|lm|[...])`) for strings instead.",
216+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:abc|def|ghi|lm|[...])`) for strings instead.",
217+
"Only single characters and graphemes are allowed inside character classes. Use regular alternatives (e.g. `(?:abc|def|ghi|lm|[...])`) for strings instead.",
218+
],
219+
},
208220
],
209221
})

0 commit comments

Comments
 (0)