eslint-config-scratch 10.0.13 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/eslint.mjs CHANGED
@@ -3,272 +3,360 @@ import formatjs from 'eslint-plugin-formatjs'
3
3
  import html from 'eslint-plugin-html'
4
4
  import htmlSettings from 'eslint-plugin-html/src/settings.js'
5
5
  import importPlugin from 'eslint-plugin-import'
6
+ import jsdoc from 'eslint-plugin-jsdoc'
6
7
  import jsxA11y from 'eslint-plugin-jsx-a11y'
7
- import markdown from 'eslint-plugin-markdown'
8
8
  import react from 'eslint-plugin-react'
9
9
  import reactHooks from 'eslint-plugin-react-hooks'
10
- import globals from 'globals'
11
10
  import tseslint from 'typescript-eslint'
12
11
  import eslintComments from '@eslint-community/eslint-plugin-eslint-comments/configs'
13
12
  import eslint from '@eslint/js'
13
+ import markdown from '@eslint/markdown'
14
+ import legacyES6 from './legacy/es6.mjs'
15
+ import legacyBase from './legacy/index.mjs'
16
+ import legacyNode from './legacy/node.mjs'
17
+ import legacyReact from './legacy/react.mjs'
18
+
19
+ const legacy = {
20
+ base: legacyBase,
21
+ es6: legacyES6,
22
+ node: legacyNode,
23
+ react: legacyReact,
24
+ }
14
25
 
15
- // See https://www.npmjs.com/package/eslint-plugin-html#user-content-settings
16
- const htmlExtensions = htmlSettings.getSettings({}).htmlExtensions
17
-
18
- // '.html' => '**/*.html'
19
- const htmlGlobs = htmlExtensions.map(ext => `**/*${ext}`)
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
20
31
 
21
32
  /**
22
- * @typedef {import('eslint').Linter.Globals} Globals
23
- * @typedef {keyof globals} GlobalsKey
24
- * @typedef {Globals | GlobalsKey} GlobalsObjOrKey
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)
25
36
  */
37
+ const extArrayToGlobArray = extArray => extArray.map(ext => `**/*${ext}`)
38
+
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
+ }
26
64
 
27
65
  /**
28
- * Flatten the globals passed to `makeScratchConfig` into an object suitable for ESLint's `globals` option.
29
- * @param {GlobalsObjOrKey | GlobalsObjOrKey[]} [globalsIn] The globals to flatten.
30
- * @returns {Globals|undefined} Flattened globals object for ESLint.
66
+ * Rules for specific file types outside of the core JS/TS rule sets.
31
67
  */
32
- const flattenGlobals = globalsIn => {
33
- if (!globalsIn) return
34
-
35
- /**
36
- *
37
- * @param {Globals} globalsAcc Globals accumulator.
38
- * @param {GlobalsObjOrKey} objOrKey A globals object or key to add to the accumulator.
39
- * @returns {Globals} The accumulator after adding the current globals object or key.
40
- */
41
- const globalsReducer = (globalsAcc, objOrKey) => {
42
- if (typeof objOrKey === 'string') {
43
- const globalsForKey = globals[objOrKey]
44
- if (!globalsForKey) {
45
- throw new Error(`Invalid globals name. Not a key from the globals package: ${objOrKey}`)
46
- }
47
- Object.assign(globalsAcc, globalsForKey)
48
- } else {
49
- Object.assign(globalsAcc, objOrKey)
50
- }
51
-
52
- return globalsAcc
53
- }
54
-
55
- if (Array.isArray(globalsIn)) {
56
- return globalsIn.reduce(globalsReducer, {})
57
- }
58
-
59
- return globalsReducer({}, globalsIn)
60
- }
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
+ language: 'markdown/gfm', // Github Flavored Markdown
85
+ },
86
+ markdown.configs.processor, // Process script blocks inside Markdown files
87
+ ])
61
88
 
62
89
  /**
63
- * Create an ESLint configuration for Scratch style.
64
- * Supports JavaScript, TypeScript, and React (JSX/TSX) files.
65
- * Setting `tsconfigRootDir` enables type-aware rules, some of which apply even in JavaScript files.
66
- * @param {object} options Configuration options
67
- * @param {string} [options.tsconfigRootDir] Enable type checking by setting the root TypeScript config directory.
68
- * @param {GlobalsObjOrKey | GlobalsObjOrKey[]} [options.globals] Globals to provide to ESLint.
69
- * This can be expressed as:
70
- * - a single string, such as `'browser'`, corresponding to a key in the `globals` package.
71
- * - a single object as described in the "Specifying Globals" section of the ESLint documentation:
72
- * https://eslint.org/docs/latest/use/configure/language-options#using-configuration-files
73
- * - an array of zero or more elements, each of which can be either of the above
74
- * @example
75
- * // eslint.config.mjs
76
- * export default makeScratchConfig({tsconfigRootDir: import.meta.dirname, globals: 'node'})
77
- * @example
78
- * // eslint.config.mjs
79
- * export default [
80
- * ...makeScratchConfig({tsconfigRootDir: import.meta.dirname, globals: 'browser'}),
81
- * {
82
- * // customization
83
- * }
84
- * ]
85
- * @returns {import('typescript-eslint').ConfigArray} An ESLint configuration array.
90
+ * Rules recommended for all script files, whether or not type information is available or checked.
86
91
  */
87
- const makeEslintConfig = ({ tsconfigRootDir, globals: globalsIn } = {}) => {
88
- const flattenedGlobals = flattenGlobals(globalsIn)
89
-
90
- return tseslint.config(
91
- // Start with recommended rules from ESLint and TypeScript ESLint.
92
- {
93
- extends: [
94
- eslint.configs.recommended,
95
- tsconfigRootDir ? tseslint.configs.recommendedTypeChecked : tseslint.configs.recommended,
96
- tsconfigRootDir ? tseslint.configs.stylisticTypeChecked : tseslint.configs.stylistic,
97
- ],
98
- languageOptions: {
99
- parserOptions: {
100
- ...(tsconfigRootDir
101
- ? {
102
- projectService: true,
103
- tsconfigRootDir,
104
- }
105
- : {}),
106
- },
107
- ...(globalsIn ? { globals: flattenedGlobals } : {}),
108
- },
92
+ const allScriptRules = tseslint.config([
93
+ // eslint-plugin-formatjs
94
+ {
95
+ name: 'scratch/allScriptRules[eslint-plugin-formatjs]',
96
+ plugins: {
97
+ formatjs,
109
98
  },
110
- // eslint-plugin-formatjs
111
- {
112
- plugins: {
113
- formatjs,
114
- },
115
- rules: {
116
- 'formatjs/no-offset': ['error'],
117
- },
99
+ rules: {
100
+ 'formatjs/no-offset': ['error'],
118
101
  },
119
- // eslint-plugin-html
120
- {
121
- files: htmlGlobs,
122
- plugins: { html },
123
- settings: {
124
- 'html/html-extensions': htmlExtensions,
125
- },
102
+ },
103
+ // eslint-plugin-import
104
+ {
105
+ name: 'scratch/allScriptRules[eslint-plugin-import]',
106
+ plugins: importPlugin.flatConfigs.recommended.plugins,
107
+ rules: {
108
+ 'import/no-duplicates': 'error', // Forbid duplicate imports
126
109
  },
127
- // eslint-plugin-import
128
- {
129
- plugins: importPlugin.flatConfigs.recommended.plugins,
130
- rules: {
131
- 'import/no-duplicates': 'error', // Forbid duplicate imports
132
- },
110
+ },
111
+ // eslint-plugin-jsx-a11y
112
+ {
113
+ name: 'scratch/allScriptRules[eslint-plugin-jsx-a11y]',
114
+ files: fileGlobs.react,
115
+ extends: [jsxA11y.flatConfigs.recommended],
116
+ },
117
+ // eslint-plugin-react
118
+ {
119
+ name: 'scratch/allScriptRules[eslint-plugin-react]',
120
+ files: fileGlobs.react,
121
+ plugins: {
122
+ react,
133
123
  },
134
- // eslint-plugin-jsx-a11y
135
- jsxA11y.flatConfigs.recommended,
136
- // eslint-plugin-markdown
137
- markdown.configs.recommended,
138
- // eslint-plugin-react
139
- {
140
- plugins: {
141
- react,
142
- },
143
- rules: {
144
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger.md
145
- 'react/no-danger': ['error'],
124
+ rules: {
125
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/no-danger.md
126
+ 'react/no-danger': ['error'],
146
127
 
147
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
148
- 'react/self-closing-comp': ['error'],
128
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md
129
+ 'react/self-closing-comp': ['error'],
149
130
 
150
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
151
- 'react/jsx-boolean-value': ['error', 'never'],
131
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md
132
+ 'react/jsx-boolean-value': ['error', 'never'],
152
133
 
153
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
154
- 'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
134
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md
135
+ 'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
155
136
 
156
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
157
- 'react/jsx-curly-spacing': ['error'],
137
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md
138
+ 'react/jsx-curly-spacing': ['error'],
158
139
 
159
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md
160
- 'react/jsx-equals-spacing': ['error'],
140
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-equals-spacing.md
141
+ 'react/jsx-equals-spacing': ['error'],
161
142
 
162
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md
163
- 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }],
143
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md
144
+ 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx'] }],
164
145
 
165
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md
166
- 'react/jsx-first-prop-new-line': ['error', 'multiline'],
146
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-first-prop-new-line.md
147
+ 'react/jsx-first-prop-new-line': ['error', 'multiline'],
167
148
 
168
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
169
- 'react/jsx-handler-names': ['error', { checkLocalVariables: true, eventHandlerPrefix: false }],
149
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md
150
+ 'react/jsx-handler-names': ['error', { checkLocalVariables: true, eventHandlerPrefix: false }],
170
151
 
171
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md
172
- 'react/jsx-indent': ['error'],
152
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-indent.md
153
+ 'react/jsx-indent': ['error'],
173
154
 
174
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
175
- 'react/jsx-no-bind': ['error', { ignoreRefs: true, allowArrowFunctions: true }],
155
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
156
+ 'react/jsx-no-bind': ['error', { ignoreRefs: true, allowArrowFunctions: true }],
176
157
 
177
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
178
- 'react/jsx-pascal-case': ['error'],
158
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md
159
+ 'react/jsx-pascal-case': ['error'],
179
160
 
180
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md
181
- 'react/jsx-tag-spacing': ['error'],
161
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md
162
+ 'react/jsx-tag-spacing': ['error'],
182
163
 
183
- // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md
184
- 'react/jsx-wrap-multilines': ['error'],
185
- },
186
- settings: {
187
- react: {
188
- version: 'detect',
189
- },
190
- },
164
+ // https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md
165
+ 'react/jsx-wrap-multilines': ['error'],
191
166
  },
192
- // eslint-plugin-react-hooks
193
- {
194
- extends: [reactHooks.configs['recommended-latest']],
195
- rules: {
196
- // https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#advanced-configuration
197
- 'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useAsync$' }],
167
+ settings: {
168
+ react: {
169
+ version: 'detect',
198
170
  },
199
171
  },
200
- // typescript-eslint
201
- {
202
- rules: {
203
- // https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
204
- '@typescript-eslint/no-non-null-asserted-nullish-coalescing': ['error'],
205
-
206
- // https://typescript-eslint.io/rules/no-useless-constructor/
207
- '@typescript-eslint/no-useless-constructor': ['error'],
208
-
209
- // https://typescript-eslint.io/rules/no-non-null-assertion
210
- '@typescript-eslint/no-non-null-assertion': ['error'],
211
-
212
- // Rules that require type information
213
- ...(tsconfigRootDir
214
- ? {
215
- // https://typescript-eslint.io/rules/no-unnecessary-condition/
216
- '@typescript-eslint/no-unnecessary-condition': ['error'],
217
-
218
- // https://typescript-eslint.io/rules/require-await/
219
- '@typescript-eslint/require-await': ['error'],
220
- }
221
- : {}),
222
- },
172
+ },
173
+ // eslint-plugin-react-hooks
174
+ {
175
+ name: 'scratch/allScriptRules[eslint-plugin-react-hooks]',
176
+ files: fileGlobs.react,
177
+ extends: [reactHooks.configs['recommended-latest']],
178
+ rules: {
179
+ // https://github.com/facebook/react/blob/main/packages/eslint-plugin-react-hooks/README.md#advanced-configuration
180
+ 'react-hooks/exhaustive-deps': ['error', { additionalHooks: '^useAsync$' }],
223
181
  },
224
- // @eslint-community/eslint-plugin-eslint-comments
225
- {
226
- extends: [
227
- // @ts-expect-error This plugin's recommended rules don't quite match the type `tseslint.config` expects.
228
- eslintComments.recommended,
229
- ],
230
- rules: {
231
- // require a description for eslint control comments other than `eslint-enable`
232
- '@eslint-community/eslint-comments/require-description': ['error', { ignore: ['eslint-enable'] }],
233
- },
182
+ },
183
+ // @eslint-community/eslint-plugin-eslint-comments
184
+ {
185
+ name: 'scratch/allScriptRules[eslint-plugin-eslint-comments]',
186
+ extends: [
187
+ // @ts-expect-error This plugin's recommended rules don't quite match the type `tseslint.config` expects.
188
+ eslintComments.recommended,
189
+ ],
190
+ rules: {
191
+ // require a description for eslint control comments other than `eslint-enable`
192
+ '@eslint-community/eslint-comments/require-description': ['error', { ignore: ['eslint-enable'] }],
234
193
  },
235
- // @eslint/js
236
- {
237
- rules: {
238
- // https://eslint.org/docs/latest/rules/arrow-body-style
239
- 'arrow-body-style': ['error', 'as-needed'],
194
+ },
195
+ // @eslint/js
196
+ {
197
+ name: 'scratch/allScriptRules[@eslint/js]',
198
+ rules: {
199
+ // https://eslint.org/docs/latest/rules/arrow-body-style
200
+ 'arrow-body-style': ['error', 'as-needed'],
201
+
202
+ // https://eslint.org/docs/latest/rules/no-duplicate-imports
203
+ 'no-duplicate-imports': ['error'],
204
+
205
+ // https://eslint.org/docs/latest/rules/no-template-curly-in-string
206
+ 'no-template-curly-in-string': ['error'],
240
207
 
241
- // https://eslint.org/docs/latest/rules/no-duplicate-imports
242
- 'no-duplicate-imports': ['error'],
208
+ // https://eslint.org/docs/latest/rules/no-useless-computed-key
209
+ 'no-useless-computed-key': ['error'],
243
210
 
244
- // https://eslint.org/docs/latest/rules/no-template-curly-in-string
245
- 'no-template-curly-in-string': ['error'],
211
+ // https://eslint.org/docs/latest/rules/no-useless-rename
212
+ 'no-useless-rename': ['error'],
246
213
 
247
- // https://eslint.org/docs/latest/rules/no-useless-computed-key
248
- 'no-useless-computed-key': ['error'],
214
+ // https://eslint.org/docs/latest/rules/prefer-arrow-callback
215
+ 'prefer-arrow-callback': ['error'],
249
216
 
250
- // https://eslint.org/docs/latest/rules/no-useless-rename
251
- 'no-useless-rename': ['error'],
217
+ // https://eslint.org/docs/latest/rules/prefer-const#destructuring
218
+ 'prefer-const': ['error'],
252
219
 
253
- // https://eslint.org/docs/latest/rules/prefer-arrow-callback
254
- 'prefer-arrow-callback': ['error'],
220
+ // https://eslint.org/docs/latest/rules/prefer-spread
221
+ 'prefer-spread': ['error'],
255
222
 
256
- // https://eslint.org/docs/latest/rules/prefer-const#destructuring
257
- 'prefer-const': ['error'],
223
+ // https://eslint.org/docs/latest/rules/require-atomic-updates
224
+ 'require-atomic-updates': ['error'],
258
225
 
259
- // https://eslint.org/docs/latest/rules/prefer-spread
260
- 'prefer-spread': ['error'],
226
+ // https://eslint.org/docs/latest/rules/symbol-description
227
+ 'symbol-description': ['error'],
228
+ },
229
+ },
230
+ ])
231
+
232
+ /**
233
+ * Additional rules recommended when type information is not available or checked.
234
+ */
235
+ const typeFreeRules = tseslint.config([
236
+ {
237
+ name: 'scratch/typeFreeRules[base]',
238
+ extends: [eslint.configs.recommended],
239
+ },
240
+ ...allScriptRules,
241
+ {
242
+ name: 'scratch/typeFreeRules[eslint-plugin-jsdoc]',
243
+ extends: [jsdoc.configs['flat/recommended-error']],
244
+ rules: {
245
+ // If JSDoc comments are present, they must be informative (non-trivial).
246
+ // For example, the description "The foo." on a variable called "foo" is not informative.
247
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
248
+ 'jsdoc/informative-docs': ['error'],
249
+
250
+ // Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
251
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
252
+ 'jsdoc/require-jsdoc': ['off'],
253
+ },
254
+ },
255
+ ])
256
+
257
+ /**
258
+ * Rules recommended when type information is available and checked. This configuration turns off some rules with the
259
+ * assumption that other software, such as TypeScript, will flag those problems. For example, the `no-undef` rule is
260
+ * disabled in this configuration. These rules include `allScriptRules`.
261
+ * These rules require additional configuration.
262
+ * @see https://typescript-eslint.io/getting-started/typed-linting/
263
+ */
264
+ const typeCheckedRules = tseslint.config([
265
+ {
266
+ name: 'scratch/typeCheckedRules[base]',
267
+ extends: [
268
+ eslint.configs.recommended,
269
+ tseslint.configs.recommendedTypeChecked,
270
+ tseslint.configs.stylisticTypeChecked,
271
+ ],
272
+ rules: {
273
+ // https://typescript-eslint.io/rules/no-unnecessary-condition/
274
+ '@typescript-eslint/no-unnecessary-condition': ['error'],
275
+
276
+ // https://typescript-eslint.io/rules/require-await/
277
+ '@typescript-eslint/require-await': ['error'],
278
+ },
279
+ },
280
+ ...allScriptRules,
281
+ {
282
+ name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][1]',
283
+ extends: [jsdoc.configs['flat/recommended-error']],
284
+ },
285
+ {
286
+ name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][2]',
287
+ extends: [jsdoc.configs['flat/recommended-typescript-error']],
288
+ },
289
+ {
290
+ name: 'scratch/typeCheckedRules[eslint-plugin-jsdoc][3]',
291
+ rules: {
292
+ // If JSDoc comments are present, they must be informative (non-trivial).
293
+ // For example, the description "The foo." on a variable called "foo" is not informative.
294
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/informative-docs.md
295
+ 'jsdoc/informative-docs': ['error'],
296
+
297
+ // Don't require JSDoc comments. Library authors should consider turning this on for external interfaces.
298
+ // https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-jsdoc.md
299
+ 'jsdoc/require-jsdoc': ['off'],
300
+ },
301
+ },
302
+ // typescript-eslint
303
+ {
304
+ name: 'scratch/typeCheckedRules[typescript-eslint]',
305
+ rules: {
306
+ // https://typescript-eslint.io/rules/no-non-null-asserted-nullish-coalescing/
307
+ '@typescript-eslint/no-non-null-asserted-nullish-coalescing': ['error'],
308
+
309
+ // https://typescript-eslint.io/rules/no-useless-constructor/
310
+ '@typescript-eslint/no-useless-constructor': ['error'],
311
+
312
+ // https://typescript-eslint.io/rules/no-non-null-assertion
313
+ '@typescript-eslint/no-non-null-assertion': ['error'],
314
+ },
315
+ },
316
+ ])
261
317
 
262
- // https://eslint.org/docs/latest/rules/require-atomic-updates
263
- 'require-atomic-updates': ['error'],
318
+ /**
319
+ * Scratch's recommended configuration when type information is not available.
320
+ */
321
+ const recommendedTypeFree = tseslint.config(typeFreeRules, eslintConfigPrettier)
322
+
323
+ /**
324
+ * Scratch's recommended configuration when type information is available.
325
+ * These rules require additional configuration.
326
+ * WARNING: These rules do not specify the `files` property.
327
+ * @see https://typescript-eslint.io/getting-started/typed-linting/
328
+ */
329
+ const recommendedTypeChecked = tseslint.config(typeCheckedRules, eslintConfigPrettier)
264
330
 
265
- // https://eslint.org/docs/latest/rules/symbol-description
266
- 'symbol-description': ['error'],
331
+ /**
332
+ * Scratch's recommended configuration for general use.
333
+ * Type-checked rules are enabled for files with known TypeScript extensions.
334
+ * If your project includes such files, you must include additional configuration.
335
+ * @see https://typescript-eslint.io/getting-started/typed-linting/
336
+ */
337
+ const recommended = tseslint.config(
338
+ {
339
+ name: 'scratch/recommended',
340
+ },
341
+ {
342
+ files: fileGlobs.allScript,
343
+ extends: [typeFreeRules],
344
+ },
345
+ {
346
+ files: fileGlobs.typeScript,
347
+ extends: [typeCheckedRules],
348
+ languageOptions: {
349
+ parserOptions: {
350
+ projectService: true,
267
351
  },
268
352
  },
269
- // Keep `eslintConfigPrettier` last to turn off rules that conflict with Prettier
270
- eslintConfigPrettier,
271
- )
272
- }
353
+ },
354
+ miscFileRules,
355
+ eslintConfigPrettier,
356
+ )
357
+
358
+ // Helper to get type hints while conveniently merging and extending configurations
359
+ export { config } from 'typescript-eslint'
273
360
 
274
- export { makeEslintConfig }
361
+ // Our exported configurations
362
+ export { recommended, recommendedTypeChecked, recommendedTypeFree, miscFileRules, legacy }
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { makeEslintConfig } from './eslint.mjs'
2
- import { makePrettierConfig } from './prettier.mjs'
1
+ import * as eslintConfigScratch from './eslint.mjs'
2
+ import * as prettierConfigScratch from './prettier.mjs'
3
3
 
4
- export { makeEslintConfig, makePrettierConfig }
4
+ export { eslintConfigScratch, prettierConfigScratch }
@@ -1,3 +1,4 @@
1
+ /** @type {import('eslint').Linter.Config[]} */
1
2
  export default [
2
3
  {
3
4
  languageOptions: {
@@ -14,6 +14,7 @@ const compat = new FlatCompat({
14
14
  allConfig: js.configs.all,
15
15
  })
16
16
 
17
+ /** @type {import('eslint').Linter.Config[]} */
17
18
  export default [
18
19
  ...compat.extends('eslint:recommended'),
19
20
  jsdoc.configs['flat/recommended'],
@@ -1,5 +1,6 @@
1
1
  import globals from 'globals'
2
2
 
3
+ /** @type {import('eslint').Linter.Config[]} */
3
4
  export default [
4
5
  {
5
6
  languageOptions: {
@@ -12,6 +12,7 @@ const compat = new FlatCompat({
12
12
  allConfig: js.configs.all,
13
13
  })
14
14
 
15
+ /** @type {import('eslint').Linter.Config[]} */
15
16
  export default [
16
17
  ...compat.extends('plugin:react/recommended'),
17
18
  {
package/lib/prettier.mjs CHANGED
@@ -4,7 +4,7 @@ import sortImports from '@trivago/prettier-plugin-sort-imports'
4
4
  * @see https://prettier.io/docs/configuration
5
5
  * @type {import("prettier").Config}
6
6
  */
7
- const prettierConfig = {
7
+ const recommended = {
8
8
  // #region Prettier
9
9
  arrowParens: 'avoid',
10
10
  bracketSameLine: false,
@@ -22,10 +22,4 @@ const prettierConfig = {
22
22
  // #endregion @trivago/prettier-plugin-sort-imports
23
23
  }
24
24
 
25
- /**
26
- * Make a Prettier configuration for Scratch style.
27
- * @returns {import("prettier").Config}
28
- */
29
- const makePrettierConfig = () => prettierConfig
30
-
31
- export { makePrettierConfig }
25
+ export { recommended }