@@ -95,6 +95,23 @@ function getMarkAsUsedOption(options: unknown): Set<string> {
95
95
return markAsUsedClassNames ;
96
96
}
97
97
98
+ function getPrettyUnusedClassNames ( unusedClassNames : string [ ] ) : string {
99
+ const base = unusedClassNames
100
+ . slice ( 0 , 3 )
101
+ . map ( ( className ) => `'${ className } '` )
102
+ . join ( ", " ) ;
103
+
104
+ if ( unusedClassNames . length <= 3 ) {
105
+ return base ;
106
+ }
107
+
108
+ return `${ base } ... (+${ Math . max ( 0 , unusedClassNames . length - 3 ) } more)` ;
109
+ }
110
+
111
+ function getFixPayloadClassNames ( unusedClassNames : string [ ] ) : string {
112
+ return unusedClassNames . map ( ( className ) => `'${ className } '` ) . join ( ", " ) ;
113
+ }
114
+
98
115
const rule = createRule ( {
99
116
name : "no-unused-classes" ,
100
117
defaultOptions : [
@@ -117,28 +134,28 @@ const rule = createRule({
117
134
docs : {
118
135
description : "Check for any unused classes in imported CSS modules" ,
119
136
recommended : "error" ,
137
+ suggestion : true ,
120
138
} ,
121
139
messages : {
122
- unusedCssClass :
123
- "Unused CSS class '{{ className }}' in '{{ cssFilePath }}'" ,
140
+ unusedCssClasses :
141
+ "Unused CSS classes {{ classNames }} in '{{ cssFilePath }}'" ,
142
+ markAsUsed : "Mark {{ classNames }} as used" ,
124
143
} ,
144
+ hasSuggestions : true ,
145
+ fixable : "code" ,
125
146
} ,
126
147
create ( context ) {
127
148
type SourceFilePath = string ;
128
- type ClassName = string ;
129
149
130
150
const files : Map <
131
151
SourceFilePath ,
132
152
{
133
153
availableClassNames : Set < string > ;
134
154
usedClassNames : Set < string > ;
135
- meta ?: Map <
136
- ClassName ,
137
- {
138
- importNode : TSESTree . ImportDeclaration ;
139
- cssFilePath : string ;
140
- }
141
- > ;
155
+ meta ?: {
156
+ importNode : TSESTree . ImportDeclaration ;
157
+ cssFilePath : string ;
158
+ } ;
142
159
}
143
160
> = new Map ( ) ;
144
161
@@ -166,7 +183,11 @@ const rule = createRule({
166
183
if ( classNames . size > 0 ) {
167
184
const file = files . get ( sourceFilePath ) ;
168
185
169
- const meta = file ?. meta ?? new Map ( ) ;
186
+ const meta = file ?. meta ?? {
187
+ importNode : node ,
188
+ cssFilePath,
189
+ } ;
190
+
170
191
const availableClassNames =
171
192
file ?. availableClassNames ?? new Set ( ) ;
172
193
@@ -182,12 +203,6 @@ const rule = createRule({
182
203
classNames . forEach ( ( className ) => {
183
204
// Add the class to available classes
184
205
availableClassNames . add ( className ) ;
185
-
186
- // Save the AST node and CSS file path for ESLint error use
187
- meta . set ( className , {
188
- importNode : node ,
189
- cssFilePath,
190
- } ) ;
191
206
} ) ;
192
207
}
193
208
}
@@ -238,22 +253,38 @@ const rule = createRule({
238
253
! markAsUsedClassNames . has ( className )
239
254
) ;
240
255
241
- // Iterate over the unused class names and create an error report for each of them
242
- unusedClassNames . forEach ( ( className ) => {
243
- const { importNode, cssFilePath } = meta ?. get ( className ) ?? { } ;
256
+ if ( unusedClassNames . length > 0 ) {
257
+ const { importNode, cssFilePath } = meta ?? { } ;
258
+
259
+ if ( importNode && cssFilePath ) {
260
+ const prettyClassNames : string =
261
+ getPrettyUnusedClassNames ( unusedClassNames ) ;
262
+ const fixPayloadClassNames : string =
263
+ getFixPayloadClassNames ( unusedClassNames ) ;
244
264
245
- if ( importNode ) {
246
265
context . report ( {
247
266
node : importNode , // The AST node
248
- messageId : "unusedCssClass " , // The error message ID
267
+ messageId : "unusedCssClasses " , // The error message ID
249
268
// Used for string interpolation in the error message
250
269
data : {
251
- className , // The unused CSS class name
270
+ classNames : prettyClassNames , // The unused CSS class name
252
271
cssFilePath, // The CSS file path
253
272
} ,
273
+ suggest : [
274
+ {
275
+ fix : ( val ) => {
276
+ return val . insertTextBeforeRange (
277
+ importNode . range ,
278
+ `/* eslint @jespers/css-modules/no-unused-classes: [2, { markAsUsed: [${ fixPayloadClassNames } ] }] */\n`
279
+ ) ;
280
+ } ,
281
+ messageId : "markAsUsed" ,
282
+ data : { classNames : fixPayloadClassNames } ,
283
+ } ,
284
+ ] ,
254
285
} ) ;
255
286
}
256
- } ) ;
287
+ }
257
288
} ) ;
258
289
} ,
259
290
} ;
0 commit comments