1
1
import * as AST from "@eslint-react/ast" ;
2
2
import { useComponentCollector , useComponentCollectorLegacy } from "@eslint-react/core" ;
3
- import type { _ } from "@eslint-react/eff" ;
4
- import { constFalse } from "@eslint-react/eff" ;
3
+ import { _ } from "@eslint-react/eff" ;
5
4
import * as JSX from "@eslint-react/jsx" ;
6
5
import type { RuleFeature } from "@eslint-react/shared" ;
7
6
import { RE_CONSTANT_CASE , RE_PASCAL_CASE } from "@eslint-react/shared" ;
8
7
import type { JSONSchema4 } from "@typescript-eslint/utils/json-schema" ;
9
- import type { CamelCase } from "string-ts" ;
10
8
import { match } from "ts-pattern" ;
11
9
12
10
import { createRule } from "../utils" ;
@@ -18,7 +16,9 @@ export const RULE_FEATURES = [
18
16
"CFG" ,
19
17
] as const satisfies RuleFeature [ ] ;
20
18
21
- export type MessageID = CamelCase < typeof RULE_NAME > ;
19
+ export type MessageID =
20
+ | "usePascalCase"
21
+ | "useConstantCase" ;
22
22
23
23
type Case = "CONSTANT_CASE" | "PascalCase" ;
24
24
@@ -88,32 +88,43 @@ function normalizeOptions(options: Options) {
88
88
} as const ;
89
89
}
90
90
91
- function validate ( name : string | _ , options : ReturnType < typeof normalizeOptions > ) {
92
- if ( name == null ) return false ;
93
- if ( options . excepts . some ( ( regex ) => regex . test ( name ) ) ) {
94
- return true ;
91
+ function getViolationMessage ( name : string | _ , options : ReturnType < typeof normalizeOptions > ) : MessageID | _ {
92
+ if ( name == null ) return _ ;
93
+ const {
94
+ allowAllCaps = false ,
95
+ allowLeadingUnderscore = false ,
96
+ allowNamespace = false ,
97
+ excepts,
98
+ rule,
99
+ } = options ;
100
+ if ( excepts . some ( ( regex ) => regex . test ( name ) ) ) {
101
+ return _ ;
95
102
}
96
103
let normalized = name
97
104
. normalize ( "NFKD" )
98
105
. replace ( / [ \u0300 - \u036F ] / g, "" ) ;
99
106
normalized = normalized . split ( "." ) . at ( - 1 ) ?? normalized ;
100
- const { allowLeadingUnderscore = false , allowNamespace = false } = options ;
101
107
if ( allowNamespace ) {
102
108
normalized = normalized . replace ( ":" , "" ) ;
103
109
}
104
110
if ( allowLeadingUnderscore ) {
105
111
normalized = normalized . replace ( / ^ _ / , "" ) ;
106
112
}
107
- return match ( options . rule )
108
- . with ( "CONSTANT_CASE" , ( ) => RE_CONSTANT_CASE . test ( normalized ) )
113
+ return match ( rule )
114
+ . with ( "CONSTANT_CASE" , ( ) =>
115
+ RE_CONSTANT_CASE . test ( normalized )
116
+ ? _
117
+ : "useConstantCase" )
109
118
. with ( "PascalCase" , ( ) => {
110
119
// Allow all caps if the string is shorter than 4 characters. e.g. UI, CSS, SVG, etc.
111
120
if ( normalized . length > 3 && / ^ [ A - Z ] + $ / u. test ( normalized ) ) {
112
- return options . allowAllCaps ?? false ;
121
+ return allowAllCaps
122
+ ? _
123
+ : "usePascalCase" ;
113
124
}
114
- return RE_PASCAL_CASE . test ( normalized ) ;
125
+ return RE_PASCAL_CASE . test ( normalized ) ? _ : "usePascalCase" ;
115
126
} )
116
- . otherwise ( constFalse ) ;
127
+ . otherwise ( ( ) => _ ) ;
117
128
}
118
129
119
130
export default createRule < Options , MessageID > ( {
@@ -124,7 +135,8 @@ export default createRule<Options, MessageID>({
124
135
description : "enforce component naming convention to 'PascalCase' or 'CONSTANT_CASE'" ,
125
136
} ,
126
137
messages : {
127
- componentName : "A component name must be in {{case}}." ,
138
+ useConstantCase : "Component name '{{name}}' must be in CONSTANT_CASE." ,
139
+ usePascalCase : "Component name '{{name}}' must be in PascalCase." ,
128
140
} ,
129
141
schema,
130
142
} ,
@@ -143,14 +155,13 @@ export default createRule<Options, MessageID>({
143
155
if ( / ^ [ a - z ] / u. test ( name ) ) {
144
156
return ;
145
157
}
146
- if ( validate ( name , options ) ) {
147
- return ;
148
- }
158
+ const violation = getViolationMessage ( name , options ) ;
159
+ if ( violation == null ) return ;
149
160
context . report ( {
150
- messageId : "componentName" ,
161
+ messageId : violation ,
151
162
node,
152
163
data : {
153
- case : options . rule ,
164
+ name ,
154
165
} ,
155
166
} ) ;
156
167
} ,
@@ -160,29 +171,30 @@ export default createRule<Options, MessageID>({
160
171
for ( const { node : component } of functionComponents . values ( ) ) {
161
172
const id = AST . getFunctionIdentifier ( component ) ;
162
173
if ( id ?. name == null ) continue ;
163
- if ( validate ( id . name , options ) ) {
164
- continue ;
165
- }
174
+ const name = id . name ;
175
+ const violation = getViolationMessage ( name , options ) ;
176
+ if ( violation == null ) continue ;
166
177
context . report ( {
167
- messageId : "componentName" ,
178
+ messageId : violation ,
168
179
node : id ,
169
180
data : {
170
- case : options . rule ,
181
+ name ,
171
182
} ,
172
183
} ) ;
173
184
}
174
185
for ( const { node : component } of classComponents . values ( ) ) {
175
186
const id = AST . getClassIdentifier ( component ) ;
176
187
if ( id ?. name == null ) continue ;
177
- if ( ! validate ( id . name , options ) ) {
178
- context . report ( {
179
- messageId : "componentName" ,
180
- node : id ,
181
- data : {
182
- case : options . rule ,
183
- } ,
184
- } ) ;
185
- }
188
+ const name = id . name ;
189
+ const violation = getViolationMessage ( name , options ) ;
190
+ if ( violation == null ) continue ;
191
+ context . report ( {
192
+ messageId : violation ,
193
+ node : id ,
194
+ data : {
195
+ case : options . rule ,
196
+ } ,
197
+ } ) ;
186
198
}
187
199
} ,
188
200
} ;
0 commit comments