@@ -23,71 +23,99 @@ const legacy = {
23
23
react : legacyReact ,
24
24
}
25
25
26
- // See https://www.npmjs.com/package/eslint-plugin-html#user-content-settings
27
- const htmlExtensions = htmlSettings . getSettings ( { } ) . htmlExtensions
26
+ // WARNING: eslint rules from `typescript-eslint`, even the "untyped" rules, assume that your code will be run through
27
+ // `tsc` or equivalent for type checking. Using any rule set from `typescript-eslint` will, for example, turn off the
28
+ // `no-undef` rule. That makes sense if you'll use TypeScript to catch undefined globals, but it could be dangerous
29
+ // for plain JavaScript.
30
+ // More information here: https://github.com/typescript-eslint/typescript-eslint/issues/8825#issuecomment-2033315610
28
31
29
- // '.html' => '**/*.html'
30
- const htmlGlobs = htmlExtensions . map ( ext => `**/*${ ext } ` )
32
+ /**
33
+ * Convert an array of file extensions to an array of globs
34
+ * @param {string[] } extArray - an array of file extensions, like `.foo`
35
+ * @returns {string[] } an array of globs, like `** /*.foo` (without the space)
36
+ */
37
+ const extArrayToGlobArray = extArray => extArray . map ( ext => `**/*${ ext } ` )
31
38
32
- const typeScriptExtensions = [ '.ts' , '.tsx' , '.mts' , '.cts' ]
39
+ // See https://www.npmjs.com/package/eslint-plugin-html#user-content-settings
40
+ const htmlSettingsDefault = htmlSettings . getSettings ( { } )
41
+
42
+ const fileExtensions = ( x => {
43
+ x . allScript = [ ...x . javaScript , ...x . typeScript ]
44
+ return x
45
+ } ) ( {
46
+ html : /** @type {string[] } */ ( htmlSettingsDefault . htmlExtensions ) ,
47
+ javaScript : [ '.js' , '.jsx' , '.mjs' , '.cjs' ] ,
48
+ markdown : [ '.md' ] ,
49
+ typeScript : [ '.ts' , '.tsx' , '.mts' , '.cts' ] ,
50
+ react : [ '.jsx' , '.tsx' ] ,
51
+ xml : /** @type {string[] } */ ( htmlSettingsDefault . xmlExtensions ) ,
52
+ } )
53
+
54
+ // This explicitly lists each entry so that we can get unused warnings
55
+ const fileGlobs = {
56
+ allScript : extArrayToGlobArray ( fileExtensions . allScript ) ,
57
+ html : extArrayToGlobArray ( fileExtensions . html ) ,
58
+ javaScript : extArrayToGlobArray ( fileExtensions . javaScript ) ,
59
+ markdown : extArrayToGlobArray ( fileExtensions . markdown ) ,
60
+ react : extArrayToGlobArray ( fileExtensions . react ) ,
61
+ typeScript : extArrayToGlobArray ( fileExtensions . typeScript ) ,
62
+ xml : extArrayToGlobArray ( fileExtensions . xml ) ,
63
+ }
33
64
34
65
/**
35
- * Base rules recommended when type information is not available.
36
- * These rules are also safe to use when type information is available.
66
+ * Rules for specific file types outside of the core JS/TS rule sets.
37
67
*/
38
- const typeFreeRules = tseslint . config (
39
- eslint . configs . recommended ,
40
- tseslint . configs . recommended ,
41
- tseslint . configs . stylistic ,
68
+ const miscFileRules = tseslint . config ( [
69
+ // eslint-plugin-html
70
+ {
71
+ name : 'scratch/miscFileRules[eslint-plugin-html]' ,
72
+ files : [ ...fileGlobs . html , ...fileGlobs . xml ] ,
73
+ plugins : { html } ,
74
+ settings : {
75
+ 'html/html-extensions' : fileExtensions . html ,
76
+ 'xml/xml-extensions' : fileExtensions . xml ,
77
+ } ,
78
+ } ,
79
+ // eslint-plugin-markdown
80
+ {
81
+ name : 'scratch/miscFileRules[eslint-plugin-markdown]' ,
82
+ files : fileGlobs . markdown ,
83
+ extends : [ markdown . configs . recommended ] ,
84
+ } ,
85
+ ] )
42
86
87
+ /**
88
+ * Rules recommended for all script files, whether or not type information is available or checked.
89
+ */
90
+ const allScriptRules = tseslint . config ( [
43
91
// eslint-plugin-formatjs
44
92
{
93
+ name : 'scratch/allScriptRules[eslint-plugin-formatjs]' ,
45
94
plugins : {
46
95
formatjs,
47
96
} ,
48
97
rules : {
49
98
'formatjs/no-offset' : [ 'error' ] ,
50
99
} ,
51
100
} ,
52
- // eslint-plugin-html
53
- {
54
- files : htmlGlobs ,
55
- plugins : { html } ,
56
- settings : {
57
- 'html/html-extensions' : htmlExtensions ,
58
- } ,
59
- } ,
60
101
// eslint-plugin-import
61
102
{
103
+ name : 'scratch/allScriptRules[eslint-plugin-import]' ,
62
104
plugins : importPlugin . flatConfigs . recommended . plugins ,
63
105
rules : {
64
106
'import/no-duplicates' : 'error' , // Forbid duplicate imports
65
107
} ,
66
108
} ,
67
- // eslint-plugin-jsdoc
68
- jsdoc . configs [ 'flat/recommended-error' ] ,
69
- {
70
- files : [ '**/*.ts' , '**/*.tsx' , '**/*.mts' , '**/*.cts' ] ,
71
- extends : [ jsdoc . configs [ 'flat/recommended-typescript-error' ] ] ,
72
- } ,
109
+ // eslint-plugin-jsx-a11y
73
110
{
74
- rules : {
75
- // If JSDoc comments are present, they must be informative (non-trivial).
76
- // For example, the description "The foo." on a variable called "foo" is not informative.
77
- // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
78
- 'jsdoc/informative-docs' : [ 'error' ] ,
79
-
80
- // Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
81
- // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
82
- 'jsdoc/require-jsdoc' : [ 'off' ] ,
83
- } ,
111
+ name : 'scratch/allScriptRules[eslint-plugin-jsx-a11y]' ,
112
+ files : fileGlobs . react ,
113
+ extends : [ jsxA11y . flatConfigs . recommended ] ,
84
114
} ,
85
- // eslint-plugin-jsx-a11y
86
- jsxA11y . flatConfigs . recommended ,
87
- // eslint-plugin-markdown
88
- markdown . configs . recommended ,
89
115
// eslint-plugin-react
90
116
{
117
+ name : 'scratch/allScriptRules[eslint-plugin-react]' ,
118
+ files : fileGlobs . react ,
91
119
plugins : {
92
120
react,
93
121
} ,
@@ -142,27 +170,17 @@ const typeFreeRules = tseslint.config(
142
170
} ,
143
171
// eslint-plugin-react-hooks
144
172
{
173
+ name : 'scratch/allScriptRules[eslint-plugin-react-hooks]' ,
174
+ files : fileGlobs . react ,
145
175
extends : [ reactHooks . configs [ 'recommended-latest' ] ] ,
146
176
rules : {
147
177
// https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#advanced-configuration
148
178
'react-hooks/exhaustive-deps' : [ 'error' , { additionalHooks : '^useAsync$' } ] ,
149
179
} ,
150
180
} ,
151
- // typescript-eslint
152
- {
153
- rules : {
154
- // https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
155
- '@typescript-eslint/no-non-null-asserted-nullish-coalescing' : [ 'error' ] ,
156
-
157
- // https://typescript-eslint.io/rules/no-useless-constructor/
158
- '@typescript-eslint/no-useless-constructor' : [ 'error' ] ,
159
-
160
- // https://typescript-eslint.io/rules/no-non-null-assertion
161
- '@typescript-eslint/no-non-null-assertion' : [ 'error' ] ,
162
- } ,
163
- } ,
164
181
// @eslint -community/eslint-plugin-eslint-comments
165
182
{
183
+ name : 'scratch/allScriptRules[eslint-plugin-eslint-comments]' ,
166
184
extends : [
167
185
// @ts -expect-error This plugin's recommended rules don't quite match the type `tseslint.config` expects.
168
186
eslintComments . recommended ,
@@ -174,6 +192,7 @@ const typeFreeRules = tseslint.config(
174
192
} ,
175
193
// @eslint /js
176
194
{
195
+ name : 'scratch/allScriptRules[@eslint/js]' ,
177
196
rules : {
178
197
// https://eslint.org/docs/latest/rules/arrow-body-style
179
198
'arrow-body-style' : [ 'error' , 'as-needed' ] ,
@@ -206,17 +225,48 @@ const typeFreeRules = tseslint.config(
206
225
'symbol-description' : [ 'error' ] ,
207
226
} ,
208
227
} ,
209
- )
228
+ ] )
229
+
230
+ /**
231
+ * Additional rules recommended when type information is not available or checked.
232
+ */
233
+ const typeFreeRules = tseslint . config ( [
234
+ {
235
+ name : 'scratch/typeFreeRules[base]' ,
236
+ extends : [ eslint . configs . recommended ] ,
237
+ } ,
238
+ ...allScriptRules ,
239
+ {
240
+ name : 'scratch/typeFreeRules[eslint-plugin-jsdoc]' ,
241
+ extends : [ jsdoc . configs [ 'flat/recommended-error' ] ] ,
242
+ rules : {
243
+ // If JSDoc comments are present, they must be informative (non-trivial).
244
+ // For example, the description "The foo." on a variable called "foo" is not informative.
245
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
246
+ 'jsdoc/informative-docs' : [ 'error' ] ,
247
+
248
+ // Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
249
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
250
+ 'jsdoc/require-jsdoc' : [ 'off' ] ,
251
+ } ,
252
+ } ,
253
+ ] )
210
254
211
255
/**
212
- * Additional rules recommended when information is available.
256
+ * Rules recommended when type information is available and checked. This configuration turns off some rules with the
257
+ * assumption that other software, such as TypeScript, will flag those problems. For example, the `no-undef` rule is
258
+ * disabled in this configuration. These rules include `allScriptRules`.
213
259
* These rules require additional configuration.
214
260
* @see https://typescript-eslint.io/getting-started/typed-linting/
215
261
*/
216
- const typeCheckedRules = tseslint . config (
217
- tseslint . configs . recommendedTypeChecked ,
218
- tseslint . configs . stylisticTypeChecked ,
262
+ const typeCheckedRules = tseslint . config ( [
219
263
{
264
+ name : 'scratch/typeCheckedRules[base]' ,
265
+ extends : [
266
+ eslint . configs . recommended ,
267
+ tseslint . configs . recommendedTypeChecked ,
268
+ tseslint . configs . stylisticTypeChecked ,
269
+ ] ,
220
270
rules : {
221
271
// https://typescript-eslint.io/rules/no-unnecessary-condition/
222
272
'@typescript-eslint/no-unnecessary-condition' : [ 'error' ] ,
@@ -225,7 +275,43 @@ const typeCheckedRules = tseslint.config(
225
275
'@typescript-eslint/require-await' : [ 'error' ] ,
226
276
} ,
227
277
} ,
228
- )
278
+ ...allScriptRules ,
279
+ {
280
+ name : 'scratch/typeCheckedRules[eslint-plugin-jsdoc][1]' ,
281
+ extends : [ jsdoc . configs [ 'flat/recommended-error' ] ] ,
282
+ } ,
283
+ {
284
+ name : 'scratch/typeCheckedRules[eslint-plugin-jsdoc][2]' ,
285
+ extends : [ jsdoc . configs [ 'flat/recommended-typescript-error' ] ] ,
286
+ } ,
287
+ {
288
+ name : 'scratch/typeCheckedRules[eslint-plugin-jsdoc][3]' ,
289
+ rules : {
290
+ // If JSDoc comments are present, they must be informative (non-trivial).
291
+ // For example, the description "The foo." on a variable called "foo" is not informative.
292
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
293
+ 'jsdoc/informative-docs' : [ 'error' ] ,
294
+
295
+ // Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
296
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
297
+ 'jsdoc/require-jsdoc' : [ 'off' ] ,
298
+ } ,
299
+ } ,
300
+ // typescript-eslint
301
+ {
302
+ name : 'scratch/typeCheckedRules[typescript-eslint]' ,
303
+ rules : {
304
+ // https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
305
+ '@typescript-eslint/no-non-null-asserted-nullish-coalescing' : [ 'error' ] ,
306
+
307
+ // https://typescript-eslint.io/rules/no-useless-constructor/
308
+ '@typescript-eslint/no-useless-constructor' : [ 'error' ] ,
309
+
310
+ // https://typescript-eslint.io/rules/no-non-null-assertion
311
+ '@typescript-eslint/no-non-null-assertion' : [ 'error' ] ,
312
+ } ,
313
+ } ,
314
+ ] )
229
315
230
316
/**
231
317
* Scratch's recommended configuration when type information is not available.
@@ -235,9 +321,10 @@ const recommendedTypeFree = tseslint.config(typeFreeRules, eslintConfigPrettier)
235
321
/**
236
322
* Scratch's recommended configuration when type information is available.
237
323
* These rules require additional configuration.
324
+ * WARNING: These rules do not specify the `files` property.
238
325
* @see https://typescript-eslint.io/getting-started/typed-linting/
239
326
*/
240
- const recommendedTypeChecked = tseslint . config ( typeFreeRules , typeCheckedRules , eslintConfigPrettier )
327
+ const recommendedTypeChecked = tseslint . config ( typeCheckedRules , eslintConfigPrettier )
241
328
242
329
/**
243
330
* Scratch's recommended configuration for general use.
@@ -246,10 +333,22 @@ const recommendedTypeChecked = tseslint.config(typeFreeRules, typeCheckedRules,
246
333
* @see https://typescript-eslint.io/getting-started/typed-linting/
247
334
*/
248
335
const recommended = tseslint . config (
249
- typeFreeRules ,
250
336
{
251
- files : typeScriptExtensions ,
337
+ name : 'scratch/recommended' ,
338
+ } ,
339
+ miscFileRules ,
340
+ {
341
+ files : fileGlobs . allScript ,
342
+ extends : [ typeFreeRules ] ,
343
+ } ,
344
+ {
345
+ files : fileGlobs . typeScript ,
252
346
extends : [ typeCheckedRules ] ,
347
+ languageOptions : {
348
+ parserOptions : {
349
+ projectService : true ,
350
+ } ,
351
+ } ,
253
352
} ,
254
353
eslintConfigPrettier ,
255
354
)
@@ -258,4 +357,4 @@ const recommended = tseslint.config(
258
357
export { config } from 'typescript-eslint'
259
358
260
359
// Our exported configurations
261
- export { recommended , recommendedTypeChecked , recommendedTypeFree , legacy }
360
+ export { recommended , recommendedTypeChecked , recommendedTypeFree , miscFileRules , legacy }
0 commit comments