@ncontiero/eslint-config 8.0.0-beta.1 → 8.0.0-beta.11

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/README.md CHANGED
@@ -1,19 +1,18 @@
1
1
  # @ncontiero/eslint-config
2
2
 
3
- Nicolas's ESLint config preset for JavaScript, TypeScript, and Prettier.
3
+ [![Version](https://img.shields.io/npm/v/@ncontiero/eslint-config?color6C47FF)](https://www.npmjs.com/package/@ncontiero/eslint-config)
4
+ [![License](https://img.shields.io/badge/licence-MIT-6C47FF)](https://github.com/ncontiero/eslint-config/blob/master/LICENSE)
4
5
 
5
- [![Version](https://img.shields.io/npm/v/@ncontiero/eslint-config)](https://www.npmjs.com/package/@ncontiero/eslint-config)
6
- [![License](https://img.shields.io/badge/licence-MIT-blue)](https://github.com/ncontiero/eslint-config/blob/master/LICENSE)
6
+ Nicolas's ESLint config preset for JavaScript, TypeScript, and Prettier.
7
7
 
8
8
  ## Features
9
9
 
10
- - Double quotes, with semi.
11
10
  - Format with Prettier.
12
- - Sort imports, `package.json`, `tsconfig.json`...
13
11
  - Reasonable defaults, best practices, only one line of config.
14
- - Designed to work with TypeScript out-of-box.
15
- - Support JSON(5), YAML, TOML, Markdown, HTML with [HTML Eslint](https://html-eslint.org/)...
16
- - [ESLint Flat config](https://eslint.org/docs/latest/use/configure/configuration-files-new), compose easily!
12
+ - Designed to work with TypeScript and JSX. Out-of-box.
13
+ - Support JSON(5), YAML, TOML, Markdown, HTML with [HTML Eslint](https://html-eslint.org/).
14
+ - [ESLint Flat config](https://eslint.org/docs/latest/use/configure/configuration-files), compose easily!
15
+ - Sort imports, `package.json`, `tsconfig.json`...
17
16
  - Ignores common files like `dist`, `node_modules`, `coverage`, and files in `.gitignore`.
18
17
  - Optional [React](https://react.dev/), [NextJs](https://nextjs.org/), [TailwindCSS](https://tailwindcss.com/), [TanStack Query](https://tanstack.com/query/) support.
19
18
  - Requires ESLint v9.20.0+.
@@ -34,20 +33,7 @@ npm i -D eslint @ncontiero/eslint-config
34
33
  // eslint.config.mjs
35
34
  import { ncontiero } from "@ncontiero/eslint-config";
36
35
 
37
- export default ncontiero(
38
- // Features: it'll detect installed dependency and enable necessary features automatically
39
- {
40
- prettier: true,
41
- markdown: true,
42
- react: true, // auto detection
43
- nextjs: false, // auto detection
44
- tailwindcss: false, // auto detection
45
- reactQuery: false, // auto detection
46
- },
47
- {
48
- /* your custom config */
49
- },
50
- );
36
+ export default ncontiero();
51
37
  ```
52
38
 
53
39
  3. Add script for package.json:
@@ -164,6 +150,25 @@ export default ncontiero(
164
150
  );
165
151
  ```
166
152
 
153
+ ### Plugins Renaming
154
+
155
+ Since flat config requires us to explicitly provide the plugin names (instead of the mandatory convention from npm package name), we renamed some plugins to make the overall scope more consistent and easier to write.
156
+
157
+ | New Prefix | Original Prefix | Source Plugin |
158
+ | ---------- | ---------------------- | ------------------------------------------------------------------------------------------ |
159
+ | `ts/*` | `@typescript-eslint/*` | [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint) |
160
+ | `react/*` | `@eslint-react/*` | [@eslint-react/eslint-plugin](https://github.com/Rel1cx/eslint-react) |
161
+
162
+ When you want to override rules, or disable them inline, you need to update to the new prefix:
163
+
164
+ ```diff
165
+ -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
166
+ +// eslint-disable-next-line ts/consistent-type-definitions
167
+ type foo = { bar: 2 }
168
+ ```
169
+
170
+ This preset will automatically rename the plugins also for your custom configs. You can use the original prefix to override the rules directly, but the TypeScript will complain about the unknown rules, so you need to use the new prefix instead.
171
+
167
172
  ### Rules Overrides
168
173
 
169
174
  Certain rules would only be enabled in specific files, for example, `ts/*` rules would only be enabled in `.ts`. We also provided the overrides options in each integration to make it easier:
@@ -213,6 +218,59 @@ npm i -D @tanstack/eslint-plugin-query
213
218
 
214
219
  > Require @tanstack/eslint-plugin-query >= 5.50.0
215
220
 
221
+ ### Optional Rules
222
+
223
+ This config also provides some optional plugins/rules for extended usage.
224
+
225
+ #### `command`
226
+
227
+ Powered by [`eslint-plugin-command`](https://github.com/antfu/eslint-plugin-command). It is not a typical rule for linting, but an on-demand micro-codemod tool that triggers by specific comments.
228
+
229
+ For a few triggers, for example:
230
+
231
+ - `/// to-function` - converts an arrow function to a normal function
232
+ - `/// to-arrow` - converts a normal function to an arrow function
233
+ - `/// to-for-each` - converts a for-in/for-of loop to `.forEach()`
234
+ - `/// to-for-of` - converts a `.forEach()` to a for-of loop
235
+ - `/// keep-sorted` - sorts an object/array/interface
236
+ - ... etc. - refer to the [documentation](https://github.com/antfu/eslint-plugin-command#built-in-commands)
237
+
238
+ You can add the trigger comment one line above the code you want to transform, for example (note the triple slash):
239
+
240
+ <!-- eslint-skip -->
241
+
242
+ ```ts
243
+ /// to-function
244
+ const foo = (msg: string): void => {
245
+ console.log(msg);
246
+ };
247
+ ```
248
+
249
+ Will be transformed to this when you hit save with your editor or run `eslint --fix`:
250
+
251
+ ```ts
252
+ function foo(msg: string): void {
253
+ console.log(msg);
254
+ }
255
+ ```
256
+
257
+ The command comments are usually one-off and will be removed along with the transformation.
258
+
259
+ ### Type Aware Rules
260
+
261
+ You can optionally enable the [type aware rules](https://typescript-eslint.io/linting/typed-linting/) by passing the options object to the `typescript` config:
262
+
263
+ ```js
264
+ // eslint.config.js
265
+ import { ncontiero } from "@ncontiero/eslint-config";
266
+
267
+ export default ncontiero({
268
+ typescript: {
269
+ tsconfigPath: "tsconfig.json",
270
+ },
271
+ });
272
+ ```
273
+
216
274
  ## References and inspirations
217
275
 
218
276
  - [@antfu/eslint-config](https://github.com/antfu/eslint-config) - Anthony's ESLint config preset.
package/dist/index.d.mts CHANGED
@@ -273,6 +273,86 @@ interface RuleOptions {
273
273
  * @see https://eslint.org/docs/latest/rules/dot-notation
274
274
  */
275
275
  'dot-notation'?: Linter.RuleEntry<DotNotation>;
276
+ /**
277
+ * Disallow dependencies in favor of more performant or secure alternatives
278
+ */
279
+ 'e18e/ban-dependencies'?: Linter.RuleEntry<E18EBanDependencies>;
280
+ /**
281
+ * Prefer optimized alternatives to `indexOf()` equality checks
282
+ */
283
+ 'e18e/no-indexof-equality'?: Linter.RuleEntry<[]>;
284
+ /**
285
+ * Prefer Array.prototype.at() over length-based indexing
286
+ */
287
+ 'e18e/prefer-array-at'?: Linter.RuleEntry<[]>;
288
+ /**
289
+ * Prefer Array.prototype.fill() over Array.from or map with constant values
290
+ */
291
+ 'e18e/prefer-array-fill'?: Linter.RuleEntry<[]>;
292
+ /**
293
+ * Prefer Array.from(iterable, mapper) over [...iterable].map(mapper) to avoid intermediate array allocation
294
+ */
295
+ 'e18e/prefer-array-from-map'?: Linter.RuleEntry<[]>;
296
+ /**
297
+ * Prefer Array.some() over Array.find() when checking for element existence
298
+ */
299
+ 'e18e/prefer-array-some'?: Linter.RuleEntry<[]>;
300
+ /**
301
+ * Prefer Array.prototype.toReversed() over copying and reversing arrays
302
+ */
303
+ 'e18e/prefer-array-to-reversed'?: Linter.RuleEntry<[]>;
304
+ /**
305
+ * Prefer Array.prototype.toSorted() over copying and sorting arrays
306
+ */
307
+ 'e18e/prefer-array-to-sorted'?: Linter.RuleEntry<[]>;
308
+ /**
309
+ * Prefer Array.prototype.toSpliced() over copying and splicing arrays
310
+ */
311
+ 'e18e/prefer-array-to-spliced'?: Linter.RuleEntry<[]>;
312
+ /**
313
+ * Prefer Date.now() over new Date().getTime() and +new Date()
314
+ */
315
+ 'e18e/prefer-date-now'?: Linter.RuleEntry<[]>;
316
+ /**
317
+ * Prefer the exponentiation operator ** over Math.pow()
318
+ */
319
+ 'e18e/prefer-exponentiation-operator'?: Linter.RuleEntry<[]>;
320
+ /**
321
+ * Prefer .includes() over indexOf() comparisons for arrays and strings
322
+ */
323
+ 'e18e/prefer-includes'?: Linter.RuleEntry<[]>;
324
+ /**
325
+ * Prefer inline equality checks over temporary object creation for simple comparisons
326
+ */
327
+ 'e18e/prefer-inline-equality'?: Linter.RuleEntry<[]>;
328
+ /**
329
+ * Prefer nullish coalescing operator (?? and ??=) over verbose null checks
330
+ */
331
+ 'e18e/prefer-nullish-coalescing'?: Linter.RuleEntry<[]>;
332
+ /**
333
+ * Prefer Object.hasOwn() over Object.prototype.hasOwnProperty.call() and obj.hasOwnProperty()
334
+ */
335
+ 'e18e/prefer-object-has-own'?: Linter.RuleEntry<[]>;
336
+ /**
337
+ * prefer `RegExp.test()` over `String.match()` and `RegExp.exec()` when only checking for match existence
338
+ */
339
+ 'e18e/prefer-regex-test'?: Linter.RuleEntry<[]>;
340
+ /**
341
+ * Prefer spread syntax over Array.concat(), Array.from(), Object.assign({}, ...), and Function.apply()
342
+ */
343
+ 'e18e/prefer-spread-syntax'?: Linter.RuleEntry<[]>;
344
+ /**
345
+ * Prefer defining regular expressions at module scope to avoid re-compilation on every function call
346
+ */
347
+ 'e18e/prefer-static-regex'?: Linter.RuleEntry<[]>;
348
+ /**
349
+ * Prefer passing function and arguments directly to setTimeout/setInterval instead of wrapping in an arrow function or using bind
350
+ */
351
+ 'e18e/prefer-timer-args'?: Linter.RuleEntry<[]>;
352
+ /**
353
+ * Prefer URL.canParse() over try-catch blocks for URL validation
354
+ */
355
+ 'e18e/prefer-url-canparse'?: Linter.RuleEntry<[]>;
276
356
  /**
277
357
  * Require or disallow newline at the end of files
278
358
  * @see https://eslint.org/docs/latest/rules/eol-last
@@ -492,8 +572,14 @@ interface RuleOptions {
492
572
  /**
493
573
  * Disallow extra spacing around attributes
494
574
  * @see https://html-eslint.org/docs/rules/no-extra-spacing-attrs
575
+ * @deprecated
495
576
  */
496
577
  'html/no-extra-spacing-attrs'?: Linter.RuleEntry<HtmlNoExtraSpacingAttrs>;
578
+ /**
579
+ * Disallow extra spacing inside tags
580
+ * @see https://html-eslint.org/docs/rules/no-extra-spacing-tags
581
+ */
582
+ 'html/no-extra-spacing-tags'?: Linter.RuleEntry<HtmlNoExtraSpacingTags>;
497
583
  /**
498
584
  * Disallow unnecessary consecutive spaces
499
585
  * @see https://html-eslint.org/docs/rules/no-extra-spacing-text
@@ -3472,7 +3558,7 @@ interface RuleOptions {
3472
3558
  * Disallow using promises inside of callbacks.
3473
3559
  * @see https://github.com/eslint-community/eslint-plugin-promise/blob/main/docs/rules/no-promise-in-callback.md
3474
3560
  */
3475
- 'promise/no-promise-in-callback'?: Linter.RuleEntry<[]>;
3561
+ 'promise/no-promise-in-callback'?: Linter.RuleEntry<PromiseNoPromiseInCallback>;
3476
3562
  /**
3477
3563
  * Disallow return statements in `finally()`.
3478
3564
  * @see https://github.com/eslint-community/eslint-plugin-promise/blob/main/docs/rules/no-return-in-finally.md
@@ -3876,6 +3962,11 @@ interface RuleOptions {
3876
3962
  * @see https://eslint-react.xyz/docs/rules/no-unused-props
3877
3963
  */
3878
3964
  'react/no-unused-props'?: Linter.RuleEntry<[]>;
3965
+ /**
3966
+ * Warns about state variables that are defined but never used, or only used in effects.
3967
+ * @see https://eslint-react.xyz/docs/rules/no-unused-state
3968
+ */
3969
+ 'react/no-unused-state'?: Linter.RuleEntry<[]>;
3879
3970
  /**
3880
3971
  * Replaces usage of 'useContext' with 'use'.
3881
3972
  * @see https://eslint-react.xyz/docs/rules/no-use-context
@@ -4166,6 +4257,11 @@ interface RuleOptions {
4166
4257
  * @see https://eslint-react.xyz/docs/rules/no-unused-props
4167
4258
  */
4168
4259
  'react/x-no-unused-props'?: Linter.RuleEntry<[]>;
4260
+ /**
4261
+ * Warns about state variables that are defined but never used, or only used in effects.
4262
+ * @see https://eslint-react.xyz/docs/rules/no-unused-state
4263
+ */
4264
+ 'react/x-no-unused-state'?: Linter.RuleEntry<[]>;
4169
4265
  /**
4170
4266
  * Replaces usage of 'useContext' with 'use'.
4171
4267
  * @see https://eslint-react.xyz/docs/rules/no-use-context
@@ -6707,6 +6803,11 @@ type DotLocation = [] | [("object" | "property")]; // ----- dot-notation -----
6707
6803
  type DotNotation = [] | [{
6708
6804
  allowKeywords?: boolean;
6709
6805
  allowPattern?: string;
6806
+ }]; // ----- e18e/ban-dependencies -----
6807
+ type E18EBanDependencies = [] | [{
6808
+ presets?: string[];
6809
+ modules?: string[];
6810
+ allowed?: string[];
6710
6811
  }]; // ----- eol-last -----
6711
6812
  type EolLast = [] | [("always" | "never" | "unix" | "windows")]; // ----- eqeqeq -----
6712
6813
  type Eqeqeq = ([] | ["always"] | ["always", {
@@ -6807,6 +6908,12 @@ type HtmlNoExtraSpacingAttrs = [] | [{
6807
6908
  disallowMissing?: boolean;
6808
6909
  disallowTabs?: boolean;
6809
6910
  enforceBeforeSelfClose?: boolean;
6911
+ }]; // ----- html/no-extra-spacing-tags -----
6912
+ type HtmlNoExtraSpacingTags = [] | [{
6913
+ disallowInAssignment?: boolean;
6914
+ disallowMissing?: boolean;
6915
+ disallowTabs?: boolean;
6916
+ enforceBeforeSelfClose?: boolean;
6810
6917
  }]; // ----- html/no-extra-spacing-text -----
6811
6918
  type HtmlNoExtraSpacingText = [] | [{
6812
6919
  skip?: string[];
@@ -11936,6 +12043,9 @@ type PromiseCatchOrReturn = [] | [{
11936
12043
  type PromiseNoCallbackInPromise = [] | [{
11937
12044
  exceptions?: string[];
11938
12045
  timeoutsErr?: boolean;
12046
+ }]; // ----- promise/no-promise-in-callback -----
12047
+ type PromiseNoPromiseInCallback = [] | [{
12048
+ exemptDeclarations?: boolean;
11939
12049
  }]; // ----- promise/no-return-wrap -----
11940
12050
  type PromiseNoReturnWrap = [] | [{
11941
12051
  allowReject?: boolean;
@@ -12376,6 +12486,7 @@ type TailwindcssEnforceCanonicalClasses = [] | [{
12376
12486
  rootFontSize?: number;
12377
12487
  cwd?: string;
12378
12488
  collapse?: boolean;
12489
+ ignore?: string[];
12379
12490
  logical?: boolean;
12380
12491
  }]; // ----- tailwindcss/enforce-consistent-class-order -----
12381
12492
  type TailwindcssEnforceConsistentClassOrder = [] | [{
@@ -12972,6 +13083,7 @@ type TailwindcssEnforceConsistentLineWrapping = [] | [{
12972
13083
  preferSingleLine?: boolean;
12973
13084
  printWidth?: number;
12974
13085
  strictness?: ("strict" | "loose");
13086
+ tabWidth?: number;
12975
13087
  }]; // ----- tailwindcss/enforce-consistent-variable-syntax -----
12976
13088
  type TailwindcssEnforceConsistentVariableSyntax = [] | [{
12977
13089
  selectors?: ({
@@ -16375,7 +16487,7 @@ type Yoda = [] | [("always" | "never")] | [("always" | "never"), {
16375
16487
  exceptRange?: boolean;
16376
16488
  onlyEquality?: boolean;
16377
16489
  }]; // Names of all the configs
16378
- type ConfigNames = 'ncontiero/gitignore' | 'ncontiero/global-ignores' | 'ncontiero/javascript/rules' | 'ncontiero/javascript/cli-rules' | 'ncontiero/javascript/test-rules' | 'ncontiero/comments/rules' | 'ncontiero/jsdoc/setup' | 'ncontiero/jsdoc/rules' | 'ncontiero/imports/rules' | 'ncontiero/imports/allow-default-export' | 'ncontiero/node/setup' | 'ncontiero/node/rules' | 'ncontiero/promise/setup' | 'ncontiero/promise/rules' | 'ncontiero/command/rules' | 'ncontiero/perfectionist/rules' | 'ncontiero/de-morgan' | 'ncontiero/unicorn/rules' | 'ncontiero/typescript/setup' | 'ncontiero/typescript/parser' | 'ncontiero/typescript/rules' | 'ncontiero/typescript/dts-rules' | 'ncontiero/typescript/cjs-rules' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/rules' | 'ncontiero/sort/package-json' | 'ncontiero/sort/tsconfig' | 'ncontiero/yml/setup' | 'ncontiero/yml/rules' | 'ncontiero/yml/pnpm-workspace' | 'ncontiero/toml/setup' | 'ncontiero/toml/rules' | 'ncontiero/markdown/setup' | 'ncontiero/markdown/processor' | 'ncontiero/markdown/parser' | 'ncontiero/markdown/rules' | 'ncontiero/regexp/rules' | 'ncontiero/react/setup' | 'ncontiero/tanstack-query' | 'ncontiero/react/rules' | 'ncontiero/nextjs/rules' | 'ncontiero/html/setup' | 'ncontiero/html/rules' | 'ncontiero/tailwindcss/setup' | 'ncontiero/tailwindcss/rules' | 'ncontiero/prettier/setup' | 'ncontiero/prettier/disables' | 'ncontiero/prettier/rules' | 'ncontiero/prettier/markdown' | 'ncontiero/prettier/css' | 'ncontiero/prettier/scss' | 'ncontiero/prettier/less';
16490
+ type ConfigNames = 'ncontiero/gitignore' | 'ncontiero/ignores' | 'ncontiero/javascript/rules' | 'ncontiero/javascript/cli-rules' | 'ncontiero/javascript/test-rules' | 'ncontiero/comments/rules' | 'ncontiero/jsdoc/setup' | 'ncontiero/jsdoc/rules' | 'ncontiero/imports/rules' | 'ncontiero/imports/allow-default-export' | 'ncontiero/node/setup' | 'ncontiero/node/rules' | 'ncontiero/promise/setup' | 'ncontiero/promise/rules' | 'ncontiero/command/rules' | 'ncontiero/perfectionist/rules' | 'ncontiero/de-morgan' | 'ncontiero/e18e/rules' | 'ncontiero/unicorn/rules' | 'ncontiero/jsx/setup' | 'ncontiero/typescript/setup' | 'ncontiero/typescript/parser' | 'ncontiero/typescript/rules' | 'ncontiero/typescript/dts-rules' | 'ncontiero/typescript/cjs-rules' | 'ncontiero/tanstack-query/setup' | 'ncontiero/tanstack-query/rules' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/recommended' | 'ncontiero/jsonc/rules' | 'ncontiero/sort/package-json' | 'ncontiero/sort/tsconfig' | 'ncontiero/yml/setup' | 'ncontiero/yml/rules' | 'ncontiero/yml/pnpm-workspace' | 'ncontiero/toml/setup' | 'ncontiero/toml/rules' | 'ncontiero/markdown/setup' | 'ncontiero/markdown/processor' | 'ncontiero/markdown/parser' | 'ncontiero/markdown/rules' | 'ncontiero/regexp/rules' | 'ncontiero/react/setup' | 'ncontiero/react/rules' | 'ncontiero/react/typescript' | 'ncontiero/nextjs/setup' | 'ncontiero/nextjs/rules' | 'ncontiero/html/setup' | 'ncontiero/html/rules' | 'ncontiero/tailwindcss/setup' | 'ncontiero/tailwindcss/rules' | 'ncontiero/prettier/setup' | 'ncontiero/prettier/disables' | 'ncontiero/prettier/rules' | 'ncontiero/prettier/markdown' | 'ncontiero/prettier/css' | 'ncontiero/prettier/scss' | 'ncontiero/prettier/less';
16379
16491
  //#endregion
16380
16492
  //#region src/types.d.ts
16381
16493
  type Awaitable<T> = T | Promise<T>;
@@ -16435,9 +16547,6 @@ interface OptionsHasNextJs {
16435
16547
  interface OptionsHasRegexp {
16436
16548
  regexp?: boolean;
16437
16549
  }
16438
- interface OptionsHasTanStackReactQuery {
16439
- reactQuery?: boolean;
16440
- }
16441
16550
  interface StyleConfig {
16442
16551
  indent?: number;
16443
16552
  semi?: boolean;
@@ -16484,6 +16593,39 @@ interface OptionsTailwindCSS extends OptionsOverrides {
16484
16593
  */
16485
16594
  cwd?: string;
16486
16595
  }
16596
+ interface OptionsE18e extends OptionsOverrides {
16597
+ /**
16598
+ * Include modernization rules
16599
+ *
16600
+ * @see https://github.com/e18e/eslint-plugin#modernization
16601
+ * @default true
16602
+ */
16603
+ modernization?: boolean;
16604
+ /**
16605
+ * Include module replacements rules
16606
+ *
16607
+ * @see https://github.com/e18e/eslint-plugin#module-replacements
16608
+ * @default false
16609
+ */
16610
+ moduleReplacements?: boolean;
16611
+ /**
16612
+ * Include performance improvements rules
16613
+ *
16614
+ * @see https://github.com/e18e/eslint-plugin#performance-improvements
16615
+ * @default true
16616
+ */
16617
+ performanceImprovements?: boolean;
16618
+ }
16619
+ interface OptionsJSXA11y extends OptionsOverrides {}
16620
+ interface OptionsJSX {
16621
+ /**
16622
+ * Enable JSX accessibility rules.
16623
+ *
16624
+ * Can be a boolean or an object for custom options and overrides.
16625
+ * @default false
16626
+ */
16627
+ a11y?: boolean | OptionsJSXA11y;
16628
+ }
16487
16629
  interface OptionsConfig {
16488
16630
  /**
16489
16631
  * Enable gitignore support.
@@ -16504,12 +16646,26 @@ interface OptionsConfig {
16504
16646
  * @default auto-detect based on the dependencies
16505
16647
  */
16506
16648
  typescript?: boolean | OptionsTypescript;
16649
+ /**
16650
+ * Enable JSX related rules.
16651
+ *
16652
+ * Passing an object to enable JSX accessibility rules.
16653
+ *
16654
+ * @default true
16655
+ */
16656
+ jsx?: boolean | OptionsJSX;
16507
16657
  /**
16508
16658
  * Options for eslint-plugin-unicorn.
16509
16659
  *
16510
16660
  * @default true
16511
16661
  */
16512
16662
  unicorn?: boolean | OptionsUnicorn;
16663
+ /**
16664
+ * Options for [@e18e/eslint-plugin](https://github.com/e18e/eslint-plugin)
16665
+ *
16666
+ * @default true
16667
+ */
16668
+ e18e?: boolean | OptionsE18e;
16513
16669
  /**
16514
16670
  * Enable JSONC support.
16515
16671
  *
@@ -16575,11 +16731,14 @@ interface OptionsConfig {
16575
16731
  */
16576
16732
  tailwindcss?: boolean | OptionsTailwindCSS;
16577
16733
  /**
16578
- * Enable TanStack React Query support.
16734
+ * Enable TanStack Query support.
16579
16735
  *
16580
- * @default auto-detect based on the dependencies
16736
+ * Requires installing:
16737
+ * - `@tanstack/eslint-plugin-query`
16738
+ *
16739
+ * @default false
16581
16740
  */
16582
- reactQuery?: boolean;
16741
+ tanstackQuery?: boolean | OptionsOverrides;
16583
16742
  }
16584
16743
  //#endregion
16585
16744
  //#region src/configs/command.d.ts
@@ -16591,11 +16750,14 @@ declare function comments(): FlatConfigItem[];
16591
16750
  //#region src/configs/de-morgan.d.ts
16592
16751
  declare function deMorgan(): FlatConfigItem[];
16593
16752
  //#endregion
16753
+ //#region src/configs/e18e.d.ts
16754
+ declare function e18e(options?: OptionsE18e): Promise<FlatConfigItem[]>;
16755
+ //#endregion
16594
16756
  //#region src/configs/html.d.ts
16595
16757
  declare function html(options: HTMLOptions): Promise<FlatConfigItem[]>;
16596
16758
  //#endregion
16597
16759
  //#region src/configs/ignores.d.ts
16598
- declare function ignores(userIgnores?: string[]): FlatConfigItem[];
16760
+ declare function ignores(userIgnores?: string[], ignoreTypeScript?: boolean): FlatConfigItem[];
16599
16761
  //#endregion
16600
16762
  //#region src/configs/imports.d.ts
16601
16763
  declare function imports(options?: OptionsHasNextJs): FlatConfigItem[];
@@ -16610,6 +16772,9 @@ declare function jsdoc(): Promise<FlatConfigItem[]>;
16610
16772
  //#region src/configs/jsonc.d.ts
16611
16773
  declare function jsonc(options?: OptionsFiles & OptionsOverrides & StyleOptions): Promise<FlatConfigItem[]>;
16612
16774
  //#endregion
16775
+ //#region src/configs/jsx.d.ts
16776
+ declare function jsx(options?: OptionsJSX): Promise<FlatConfigItem[]>;
16777
+ //#endregion
16613
16778
  //#region src/configs/markdown.d.ts
16614
16779
  declare function markdown(options?: OptionsFiles & OptionsOverrides): Promise<FlatConfigItem[]>;
16615
16780
  //#endregion
@@ -16634,7 +16799,7 @@ declare function prettier(options?: PrettierOptions): Promise<FlatConfigItem[]>;
16634
16799
  declare function promise(): Promise<FlatConfigItem[]>;
16635
16800
  //#endregion
16636
16801
  //#region src/configs/react.d.ts
16637
- declare function react(options?: OptionsOverrides & OptionsFiles & OptionsHasTanStackReactQuery): Promise<FlatConfigItem[]>;
16802
+ declare function react(options?: OptionsTypeScriptParserOptions & OptionsTypeScriptWithTypes & OptionsOverrides & OptionsFiles): Promise<FlatConfigItem[]>;
16638
16803
  //#endregion
16639
16804
  //#region src/configs/regexp.d.ts
16640
16805
  declare function regexp(options?: OptionsOverrides): FlatConfigItem[];
@@ -16647,6 +16812,9 @@ declare const sortPnpmWorkspace: () => FlatConfigItem[];
16647
16812
  //#region src/configs/tailwindcss.d.ts
16648
16813
  declare function tailwindcss(options?: OptionsTailwindCSS): Promise<FlatConfigItem[]>;
16649
16814
  //#endregion
16815
+ //#region src/configs/tanstack-query.d.ts
16816
+ declare function tanstackQuery(options?: OptionsFiles & OptionsOverrides): Promise<FlatConfigItem[]>;
16817
+ //#endregion
16650
16818
  //#region src/configs/toml.d.ts
16651
16819
  declare function toml(options?: OptionsOverrides & OptionsFiles & StyleOptions): Promise<FlatConfigItem[]>;
16652
16820
  //#endregion
@@ -16664,7 +16832,6 @@ declare const hasTypeScript: boolean;
16664
16832
  declare const hasReact: boolean;
16665
16833
  declare const hasNextJs: boolean;
16666
16834
  declare const hasTailwind: boolean;
16667
- declare const hasTanStackReactQuery: boolean;
16668
16835
  //#endregion
16669
16836
  //#region src/factory.d.ts
16670
16837
  declare const defaultPluginRenaming: {
@@ -16690,7 +16857,6 @@ declare const GLOB_JS = "**/*.?([cm])js";
16690
16857
  declare const GLOB_JSX = "**/*.?([cm])jsx";
16691
16858
  declare const GLOB_TS = "**/*.?([cm])ts";
16692
16859
  declare const GLOB_TSX = "**/*.?([cm])tsx";
16693
- declare const GLOB_REACT = "**/*.?([cm])?(j|t)sx";
16694
16860
  declare const GLOB_STYLE = "**/*.{c,le,sc}ss";
16695
16861
  declare const GLOB_CSS = "**/*.css";
16696
16862
  declare const GLOB_POSTCSS = "**/*.{p,post}css";
@@ -16790,4 +16956,4 @@ declare function composer({
16790
16956
  }: ComposerOptions): Promise<FlatConfigItem[]>;
16791
16957
  declare function toArray<T>(value: T | T[]): T[];
16792
16958
  //#endregion
16793
- export { Awaitable, ComposerOptions, type ConfigNames, FlatConfigItem, GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_REACT, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, HTMLOptions, OptionsConfig, OptionsFiles, OptionsHasNextJs, OptionsHasRegexp, OptionsHasTanStackReactQuery, OptionsHasTypeScript, OptionsOverrides, OptionsTailwindCSS, OptionsTypeScriptParserOptions, OptionsTypeScriptWithTypes, OptionsTypescript, OptionsUnicorn, type PrettierOptions, ResolvedOptions, Rules, StyleConfig, StyleOptions, combine, command, comments, composer, deMorgan, defaultPluginRenaming, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTanStackReactQuery, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, renamePluginInConfigs, renameRules, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, toArray, toml, typescript, unicorn, yml };
16959
+ export { Awaitable, ComposerOptions, type ConfigNames, FlatConfigItem, GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, HTMLOptions, OptionsConfig, OptionsE18e, OptionsFiles, OptionsHasNextJs, OptionsHasRegexp, OptionsHasTypeScript, OptionsJSX, OptionsJSXA11y, OptionsOverrides, OptionsTailwindCSS, OptionsTypeScriptParserOptions, OptionsTypeScriptWithTypes, OptionsTypescript, OptionsUnicorn, type PrettierOptions, ResolvedOptions, Rules, StyleConfig, StyleOptions, combine, command, comments, composer, deMorgan, defaultPluginRenaming, e18e, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, jsx, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, renamePluginInConfigs, renameRules, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, tanstackQuery, toArray, toml, typescript, unicorn, yml };
package/dist/index.mjs CHANGED
@@ -43,79 +43,6 @@ function deMorgan() {
43
43
  }];
44
44
  }
45
45
  //#endregion
46
- //#region src/globs.ts
47
- const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
48
- const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
49
- const GLOB_JS = "**/*.?([cm])js";
50
- const GLOB_JSX = "**/*.?([cm])jsx";
51
- const GLOB_TS = "**/*.?([cm])ts";
52
- const GLOB_TSX = "**/*.?([cm])tsx";
53
- const GLOB_REACT = "**/*.?([cm])?(j|t)sx";
54
- const GLOB_STYLE = "**/*.{c,le,sc}ss";
55
- const GLOB_CSS = "**/*.css";
56
- const GLOB_POSTCSS = "**/*.{p,post}css";
57
- const GLOB_LESS = "**/*.less";
58
- const GLOB_SCSS = "**/*.scss";
59
- const GLOB_JSON = "**/*.json";
60
- const GLOB_JSON5 = "**/*.json5";
61
- const GLOB_JSONC = "**/*.jsonc";
62
- const GLOB_MARKDOWN = "**/*.md";
63
- const GLOB_MARKDOWN_IN_MARKDOWN = "**/*.md/*.md";
64
- const GLOB_YAML = "**/*.y?(a)ml";
65
- const GLOB_TOML = "**/*.toml";
66
- const GLOB_HTML = "**/*.htm?(l)";
67
- const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
68
- const GLOB_ALL_SRC = [
69
- GLOB_SRC,
70
- GLOB_STYLE,
71
- GLOB_JSON,
72
- GLOB_JSON5,
73
- GLOB_MARKDOWN,
74
- GLOB_YAML,
75
- GLOB_HTML
76
- ];
77
- const GLOB_NODE_MODULES = "**/node_modules";
78
- const GLOB_DIST = "**/dist";
79
- const GLOB_LOCKFILE = [
80
- "**/package-lock.json",
81
- "**/yarn.lock",
82
- "**/pnpm-lock.yaml",
83
- "**/bun.lockb"
84
- ];
85
- const GLOB_EXCLUDE = [
86
- GLOB_NODE_MODULES,
87
- GLOB_DIST,
88
- ...GLOB_LOCKFILE,
89
- "**/output",
90
- "**/coverage",
91
- "**/temp",
92
- "**/.temp",
93
- "**/tmp",
94
- "**/.tmp",
95
- "**/.history",
96
- "**/fixtures",
97
- "**/.git",
98
- "**/.vitepress/cache",
99
- "**/.nuxt",
100
- "**/.next",
101
- "**/.vercel",
102
- "**/.contentlayer",
103
- "**/.changeset",
104
- "**/.idea",
105
- "**/.cache",
106
- "**/.output",
107
- "**/.vite-inspect",
108
- "**/.yarn",
109
- "**/next-env.d.ts",
110
- "**/.nitro",
111
- "**/CHANGELOG*.md",
112
- "**/*.min.*",
113
- "**/LICENSE*",
114
- "**/__snapshots__",
115
- "**/auto-import?(s).d.ts",
116
- "**/components.d.ts"
117
- ];
118
- //#endregion
119
46
  //#region src/utils.ts
120
47
  const isCwdInScope = isPackageExists("@ncontiero/eslint-config");
121
48
  const parserPlain = {
@@ -212,6 +139,101 @@ function toArray(value) {
212
139
  return Array.isArray(value) ? value : [value];
213
140
  }
214
141
  //#endregion
142
+ //#region src/configs/e18e.ts
143
+ async function e18e(options = {}) {
144
+ const { modernization = true, moduleReplacements = false, overrides = {}, performanceImprovements = true } = options;
145
+ const pluginE18e = await interopDefault(import("@e18e/eslint-plugin"));
146
+ const { configs } = pluginE18e;
147
+ return [{
148
+ name: "ncontiero/e18e/rules",
149
+ plugins: { e18e: pluginE18e },
150
+ rules: {
151
+ ...modernization ? { ...configs.modernization.rules } : {},
152
+ ...moduleReplacements ? { ...configs.moduleReplacements.rules } : {},
153
+ ...performanceImprovements ? { ...configs.performanceImprovements.rules } : {},
154
+ "e18e/prefer-array-at": "off",
155
+ "e18e/prefer-array-from-map": "off",
156
+ "e18e/prefer-array-to-reversed": "off",
157
+ "e18e/prefer-array-to-sorted": "off",
158
+ "e18e/prefer-array-to-spliced": "off",
159
+ "e18e/prefer-spread-syntax": "off",
160
+ ...overrides
161
+ }
162
+ }];
163
+ }
164
+ //#endregion
165
+ //#region src/globs.ts
166
+ const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
167
+ const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
168
+ const GLOB_JS = "**/*.?([cm])js";
169
+ const GLOB_JSX = "**/*.?([cm])jsx";
170
+ const GLOB_TS = "**/*.?([cm])ts";
171
+ const GLOB_TSX = "**/*.?([cm])tsx";
172
+ const GLOB_STYLE = "**/*.{c,le,sc}ss";
173
+ const GLOB_CSS = "**/*.css";
174
+ const GLOB_POSTCSS = "**/*.{p,post}css";
175
+ const GLOB_LESS = "**/*.less";
176
+ const GLOB_SCSS = "**/*.scss";
177
+ const GLOB_JSON = "**/*.json";
178
+ const GLOB_JSON5 = "**/*.json5";
179
+ const GLOB_JSONC = "**/*.jsonc";
180
+ const GLOB_MARKDOWN = "**/*.md";
181
+ const GLOB_MARKDOWN_IN_MARKDOWN = "**/*.md/*.md";
182
+ const GLOB_YAML = "**/*.y?(a)ml";
183
+ const GLOB_TOML = "**/*.toml";
184
+ const GLOB_HTML = "**/*.htm?(l)";
185
+ const GLOB_MARKDOWN_CODE = `${GLOB_MARKDOWN}/${GLOB_SRC}`;
186
+ const GLOB_ALL_SRC = [
187
+ GLOB_SRC,
188
+ GLOB_STYLE,
189
+ GLOB_JSON,
190
+ GLOB_JSON5,
191
+ GLOB_MARKDOWN,
192
+ GLOB_YAML,
193
+ GLOB_HTML
194
+ ];
195
+ const GLOB_NODE_MODULES = "**/node_modules";
196
+ const GLOB_DIST = "**/dist";
197
+ const GLOB_LOCKFILE = [
198
+ "**/package-lock.json",
199
+ "**/yarn.lock",
200
+ "**/pnpm-lock.yaml",
201
+ "**/bun.lockb"
202
+ ];
203
+ const GLOB_EXCLUDE = [
204
+ GLOB_NODE_MODULES,
205
+ GLOB_DIST,
206
+ ...GLOB_LOCKFILE,
207
+ "**/output",
208
+ "**/coverage",
209
+ "**/temp",
210
+ "**/.temp",
211
+ "**/tmp",
212
+ "**/.tmp",
213
+ "**/.history",
214
+ "**/fixtures",
215
+ "**/.git",
216
+ "**/.vitepress/cache",
217
+ "**/.nuxt",
218
+ "**/.next",
219
+ "**/.vercel",
220
+ "**/.contentlayer",
221
+ "**/.changeset",
222
+ "**/.idea",
223
+ "**/.cache",
224
+ "**/.output",
225
+ "**/.vite-inspect",
226
+ "**/.yarn",
227
+ "**/next-env.d.ts",
228
+ "**/.nitro",
229
+ "**/CHANGELOG*.md",
230
+ "**/*.min.*",
231
+ "**/LICENSE*",
232
+ "**/__snapshots__",
233
+ "**/auto-import?(s).d.ts",
234
+ "**/components.d.ts"
235
+ ];
236
+ //#endregion
215
237
  //#region src/configs/html.ts
216
238
  async function html(options) {
217
239
  const { overrides = {} } = options;
@@ -243,7 +265,7 @@ async function html(options) {
243
265
  "html/no-duplicate-id": "error",
244
266
  "html/no-duplicate-in-head": "error",
245
267
  "html/no-empty-headings": "warn",
246
- "html/no-extra-spacing-attrs": ["warn", {
268
+ "html/no-extra-spacing-tags": ["warn", {
247
269
  disallowInAssignment: true,
248
270
  disallowMissing: true,
249
271
  enforceBeforeSelfClose: true
@@ -289,10 +311,12 @@ async function html(options) {
289
311
  }
290
312
  //#endregion
291
313
  //#region src/configs/ignores.ts
292
- function ignores(userIgnores = []) {
314
+ function ignores(userIgnores = [], ignoreTypeScript = false) {
315
+ const ignores = [...GLOB_EXCLUDE, ...userIgnores];
316
+ if (ignoreTypeScript) ignores.push(GLOB_TS, GLOB_TSX);
293
317
  return [{
294
- ignores: [...GLOB_EXCLUDE, ...userIgnores],
295
- name: "ncontiero/global-ignores"
318
+ ignores,
319
+ name: "ncontiero/ignores"
296
320
  }];
297
321
  }
298
322
  //#endregion
@@ -559,6 +583,81 @@ async function jsonc(options = {}) {
559
583
  }];
560
584
  }
561
585
  //#endregion
586
+ //#region src/configs/jsx.ts
587
+ async function jsx(options = {}) {
588
+ const { a11y = false } = options;
589
+ const baseConfig = {
590
+ files: [GLOB_JSX, GLOB_TSX],
591
+ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } },
592
+ name: "ncontiero/jsx/setup",
593
+ plugins: {},
594
+ rules: {}
595
+ };
596
+ if (!a11y) return [baseConfig];
597
+ const jsxA11yPlugin = await interopDefault(import("eslint-plugin-jsx-a11y"));
598
+ const a11yConfig = jsxA11yPlugin.flatConfigs.recommended;
599
+ const overrides = typeof a11y === "object" && a11y.overrides ? a11y.overrides : {};
600
+ return [{
601
+ ...baseConfig,
602
+ ...a11yConfig,
603
+ files: baseConfig.files,
604
+ languageOptions: {
605
+ ...baseConfig.languageOptions,
606
+ ...a11yConfig.languageOptions
607
+ },
608
+ name: baseConfig.name,
609
+ plugins: {
610
+ ...baseConfig.plugins,
611
+ "jsx-a11y": jsxA11yPlugin
612
+ },
613
+ rules: {
614
+ ...baseConfig.rules,
615
+ ...a11yConfig.rules || {},
616
+ "jsx-a11y/anchor-is-valid": ["warn", {
617
+ aspects: [
618
+ "noHref",
619
+ "invalidHref",
620
+ "preferButton"
621
+ ],
622
+ components: ["Link"],
623
+ specialLink: [
624
+ "to",
625
+ "hrefLeft",
626
+ "hrefRight"
627
+ ]
628
+ }],
629
+ "jsx-a11y/control-has-associated-label": ["warn", {
630
+ controlComponents: [],
631
+ depth: 5,
632
+ ignoreElements: [
633
+ "audio",
634
+ "canvas",
635
+ "embed",
636
+ "input",
637
+ "textarea",
638
+ "tr",
639
+ "video"
640
+ ],
641
+ ignoreRoles: [
642
+ "grid",
643
+ "listbox",
644
+ "menu",
645
+ "menubar",
646
+ "radiogroup",
647
+ "row",
648
+ "tablist",
649
+ "toolbar",
650
+ "tree",
651
+ "treegrid"
652
+ ],
653
+ labelAttributes: ["label"]
654
+ }],
655
+ "jsx-a11y/lang": "warn",
656
+ ...overrides
657
+ }
658
+ }];
659
+ }
660
+ //#endregion
562
661
  //#region src/configs/markdown.ts
563
662
  async function markdown(options = {}) {
564
663
  const { files = [GLOB_MARKDOWN], overrides = {} } = options;
@@ -612,11 +711,17 @@ async function markdown(options = {}) {
612
711
  //#endregion
613
712
  //#region src/configs/nextjs.ts
614
713
  async function nextJs(options = {}) {
615
- const { files = [GLOB_REACT], overrides = {} } = options;
714
+ const { files = [GLOB_SRC], overrides = {} } = options;
616
715
  return [{
716
+ name: "ncontiero/nextjs/setup",
717
+ plugins: { nextjs: await interopDefault(import("@next/eslint-plugin-next")) }
718
+ }, {
617
719
  files,
720
+ languageOptions: {
721
+ parserOptions: { ecmaFeatures: { jsx: true } },
722
+ sourceType: "module"
723
+ },
618
724
  name: "ncontiero/nextjs/rules",
619
- plugins: { nextjs: await interopDefault(import("@next/eslint-plugin-next")) },
620
725
  rules: {
621
726
  "nextjs/google-font-display": "warn",
622
727
  "nextjs/google-font-preconnect": "warn",
@@ -640,7 +745,8 @@ async function nextJs(options = {}) {
640
745
  "nextjs/no-typos": "warn",
641
746
  "nextjs/no-unwanted-polyfillio": "warn",
642
747
  ...overrides
643
- }
748
+ },
749
+ settings: { react: { version: "detect" } }
644
750
  }];
645
751
  }
646
752
  //#endregion
@@ -809,13 +915,13 @@ const ReactRouterPackages = [
809
915
  ];
810
916
  const NextJsPackages = ["next"];
811
917
  async function react(options = {}) {
812
- const { files = [GLOB_REACT], overrides = {}, reactQuery } = options;
813
- if (reactQuery) ensurePackages(["@tanstack/eslint-plugin-query"]);
814
- const [pluginA11y, pluginReact, pluginReactRefresh] = await Promise.all([
815
- interopDefault(import("eslint-plugin-jsx-a11y")),
816
- interopDefault(import("@eslint-react/eslint-plugin")),
817
- interopDefault(import("eslint-plugin-react-refresh"))
818
- ]);
918
+ const { files = [GLOB_SRC], filesTypeAware = [GLOB_TS, GLOB_TSX], ignoresTypeAware = [`${GLOB_MARKDOWN}/**`], overrides = {}, tsconfigPath } = options;
919
+ const isTypeAware = !!tsconfigPath;
920
+ const typeAwareRules = {
921
+ "react/no-leaked-conditional-rendering": "error",
922
+ "react/no-unused-props": "warn"
923
+ };
924
+ const [pluginReact, pluginReactRefresh] = await Promise.all([interopDefault(import("@eslint-react/eslint-plugin")), interopDefault(import("eslint-plugin-react-refresh"))]);
819
925
  const isAllowConstantExport = ReactRefreshAllowPackages.some((i) => isPackageExists(i));
820
926
  const isUsingReactRouter = ReactRouterPackages.some((i) => isPackageExists(i));
821
927
  const isUsingNextJs = NextJsPackages.some((i) => isPackageExists(i));
@@ -823,15 +929,10 @@ async function react(options = {}) {
823
929
  {
824
930
  name: "ncontiero/react/setup",
825
931
  plugins: {
826
- "jsx-a11y": pluginA11y,
827
932
  react: pluginReact,
828
933
  "react-refresh": pluginReactRefresh
829
934
  }
830
935
  },
831
- reactQuery ? {
832
- ...(await interopDefault(import("@tanstack/eslint-plugin-query"))).configs["flat/recommended"][0],
833
- name: "ncontiero/tanstack-query"
834
- } : {},
835
936
  {
836
937
  files,
837
938
  languageOptions: {
@@ -841,132 +942,6 @@ async function react(options = {}) {
841
942
  name: "ncontiero/react/rules",
842
943
  rules: {
843
944
  ...pluginReact.configs.recommended.rules,
844
- "jsx-a11y/alt-text": ["warn", {
845
- area: [],
846
- elements: [
847
- "img",
848
- "object",
849
- "area",
850
- "input[type=\"image\"]"
851
- ],
852
- img: [],
853
- "input[type=\"image\"]": [],
854
- object: []
855
- }],
856
- "jsx-a11y/anchor-has-content": ["warn", { components: [] }],
857
- "jsx-a11y/anchor-is-valid": ["warn", {
858
- aspects: [
859
- "noHref",
860
- "invalidHref",
861
- "preferButton"
862
- ],
863
- components: ["Link"],
864
- specialLink: ["to"]
865
- }],
866
- "jsx-a11y/aria-activedescendant-has-tabindex": ["warn"],
867
- "jsx-a11y/aria-props": ["warn"],
868
- "jsx-a11y/aria-proptypes": ["warn"],
869
- "jsx-a11y/aria-role": ["warn", { ignoreNonDOM: false }],
870
- "jsx-a11y/aria-unsupported-elements": ["warn"],
871
- "jsx-a11y/autocomplete-valid": ["off", { inputComponents: [] }],
872
- "jsx-a11y/click-events-have-key-events": ["warn"],
873
- "jsx-a11y/control-has-associated-label": ["warn", {
874
- controlComponents: [],
875
- depth: 5,
876
- ignoreElements: [
877
- "audio",
878
- "canvas",
879
- "embed",
880
- "input",
881
- "textarea",
882
- "tr",
883
- "video"
884
- ],
885
- ignoreRoles: [
886
- "grid",
887
- "listbox",
888
- "menu",
889
- "menubar",
890
- "radiogroup",
891
- "row",
892
- "tablist",
893
- "toolbar",
894
- "tree",
895
- "treegrid"
896
- ],
897
- labelAttributes: ["label"]
898
- }],
899
- "jsx-a11y/heading-has-content": ["warn", { components: [""] }],
900
- "jsx-a11y/html-has-lang": ["warn"],
901
- "jsx-a11y/iframe-has-title": ["warn"],
902
- "jsx-a11y/img-redundant-alt": ["warn"],
903
- "jsx-a11y/interactive-supports-focus": ["warn"],
904
- "jsx-a11y/label-has-associated-control": "warn",
905
- "jsx-a11y/lang": ["warn"],
906
- "jsx-a11y/media-has-caption": ["warn", {
907
- audio: [],
908
- track: [],
909
- video: []
910
- }],
911
- "jsx-a11y/mouse-events-have-key-events": ["warn"],
912
- "jsx-a11y/no-access-key": ["warn"],
913
- "jsx-a11y/no-autofocus": ["warn", { ignoreNonDOM: true }],
914
- "jsx-a11y/no-distracting-elements": ["warn", { elements: ["marquee", "blink"] }],
915
- "jsx-a11y/no-interactive-element-to-noninteractive-role": ["warn", { tr: ["none", "presentation"] }],
916
- "jsx-a11y/no-noninteractive-element-interactions": ["warn", { handlers: [
917
- "onClick",
918
- "onMouseDown",
919
- "onMouseUp",
920
- "onKeyPress",
921
- "onKeyDown",
922
- "onKeyUp"
923
- ] }],
924
- "jsx-a11y/no-noninteractive-element-to-interactive-role": ["warn", {
925
- li: [
926
- "menuitem",
927
- "option",
928
- "row",
929
- "tab",
930
- "treeitem"
931
- ],
932
- ol: [
933
- "listbox",
934
- "menu",
935
- "menubar",
936
- "radiogroup",
937
- "tablist",
938
- "tree",
939
- "treegrid"
940
- ],
941
- table: ["grid"],
942
- td: ["gridcell"],
943
- ul: [
944
- "listbox",
945
- "menu",
946
- "menubar",
947
- "radiogroup",
948
- "tablist",
949
- "tree",
950
- "treegrid"
951
- ]
952
- }],
953
- "jsx-a11y/no-noninteractive-tabindex": ["warn", {
954
- roles: ["tabpanel"],
955
- tags: []
956
- }],
957
- "jsx-a11y/no-redundant-roles": ["warn"],
958
- "jsx-a11y/no-static-element-interactions": ["off", { handlers: [
959
- "onClick",
960
- "onMouseDown",
961
- "onMouseUp",
962
- "onKeyPress",
963
- "onKeyDown",
964
- "onKeyUp"
965
- ] }],
966
- "jsx-a11y/role-has-required-aria-props": ["warn"],
967
- "jsx-a11y/role-supports-aria-props": ["warn"],
968
- "jsx-a11y/scope": ["warn"],
969
- "jsx-a11y/tabindex-no-positive": ["warn"],
970
945
  "react-refresh/only-export-components": ["warn", {
971
946
  allowConstantExport: isAllowConstantExport,
972
947
  allowExportNames: [...isUsingNextJs ? [
@@ -999,21 +974,33 @@ async function react(options = {}) {
999
974
  }],
1000
975
  "react/dom-no-missing-button-type": "warn",
1001
976
  "react/dom-no-missing-iframe-sandbox": "warn",
1002
- "react/dom-no-unknown-property": "warn",
1003
977
  "react/dom-no-unsafe-target-blank": "warn",
1004
978
  "react/globals": "warn",
1005
979
  "react/immutability": "warn",
1006
980
  "react/jsx-no-useless-fragment": "warn",
1007
981
  "react/no-duplicate-key": "warn",
1008
- "react/no-leaked-conditional-rendering": "error",
1009
982
  "react/no-missing-component-display-name": "warn",
1010
983
  "react/no-unstable-context-value": "warn",
1011
984
  "react/no-unstable-default-props": "warn",
1012
- "react/no-unused-props": "warn",
985
+ "react/no-unused-state": "warn",
1013
986
  "react/refs": "warn",
1014
987
  ...overrides
1015
988
  }
1016
- }
989
+ },
990
+ {
991
+ files: filesTypeAware,
992
+ name: "ncontiero/react/typescript",
993
+ rules: {
994
+ "react/dom-no-string-style-prop": "off",
995
+ "react/dom-no-unknown-property": "off"
996
+ }
997
+ },
998
+ ...isTypeAware ? [{
999
+ files: filesTypeAware,
1000
+ ignores: ignoresTypeAware,
1001
+ name: "ncontiero/react/type-aware-rules",
1002
+ rules: { ...typeAwareRules }
1003
+ }] : []
1017
1004
  ];
1018
1005
  }
1019
1006
  //#endregion
@@ -1290,7 +1277,7 @@ async function tailwindcss(options = {}) {
1290
1277
  tailwindConfig: configPath
1291
1278
  } }
1292
1279
  }, {
1293
- files: [GLOB_REACT, GLOB_HTML],
1280
+ files: [GLOB_SRC, GLOB_HTML],
1294
1281
  name: "ncontiero/tailwindcss/rules",
1295
1282
  rules: {
1296
1283
  "tailwindcss/enforce-consistent-class-order": "warn",
@@ -1313,6 +1300,25 @@ async function tailwindcss(options = {}) {
1313
1300
  }];
1314
1301
  }
1315
1302
  //#endregion
1303
+ //#region src/configs/tanstack-query.ts
1304
+ async function tanstackQuery(options = {}) {
1305
+ const { files = [GLOB_SRC], overrides = {} } = options;
1306
+ ensurePackages(["@tanstack/eslint-plugin-query"]);
1307
+ const tanstackQueryPlugin = await interopDefault(import("@tanstack/eslint-plugin-query"));
1308
+ const config = tanstackQueryPlugin.configs.recommended;
1309
+ return [{
1310
+ name: "ncontiero/tanstack-query/setup",
1311
+ plugins: { "@tanstack/query": tanstackQueryPlugin }
1312
+ }, {
1313
+ files,
1314
+ name: "ncontiero/tanstack-query/rules",
1315
+ rules: {
1316
+ ...config.rules,
1317
+ ...overrides
1318
+ }
1319
+ }];
1320
+ }
1321
+ //#endregion
1316
1322
  //#region src/configs/toml.ts
1317
1323
  async function toml(options = {}) {
1318
1324
  const { files = [GLOB_TOML], overrides = {}, style = true } = options;
@@ -1421,7 +1427,7 @@ async function typescript(options = {}) {
1421
1427
  name: "ncontiero/typescript/rules",
1422
1428
  rules: {
1423
1429
  ...renameRules(pluginTs.configs["eslint-recommended"].overrides[0].rules, { "@typescript-eslint": "ts" }),
1424
- ...renameRules(pluginTs.configs.recommended.rules, { "@typescript-eslint": "ts" }),
1430
+ ...renameRules(pluginTs.configs.strict.rules, { "@typescript-eslint": "ts" }),
1425
1431
  "no-dupe-class-members": "off",
1426
1432
  "no-redeclare": "off",
1427
1433
  "no-restricted-syntax": [
@@ -1660,7 +1666,6 @@ const hasTypeScript = isPackageExists("typescript");
1660
1666
  const hasReact = isPackageExists("react");
1661
1667
  const hasNextJs = isPackageExists("next");
1662
1668
  const hasTailwind = isPackageExists("tailwindcss");
1663
- const hasTanStackReactQuery = isPackageExists("@tanstack/react-query");
1664
1669
  //#endregion
1665
1670
  //#region src/factory.ts
1666
1671
  const flatConfigProps = [
@@ -1702,7 +1707,7 @@ function getStyleOptions(options) {
1702
1707
  * @returns Merged ESLint configurations based on provided options.
1703
1708
  */
1704
1709
  function ncontiero(options = {}, ...userConfigs) {
1705
- const { gitignore: enableGitignore = true, nextjs: enableNextJs = hasNextJs, react: enableReact = hasReact, reactQuery: enableTanStackReactQuery = hasTanStackReactQuery, regexp: enableRegexp = true, tailwindcss: enableTailwindCSS = hasTailwind, typescript: enableTypescript = hasTypeScript, unicorn: enableUnicorn = true } = options;
1710
+ const { e18e: enableE18e = true, gitignore: enableGitignore = true, jsx: enableJsx = true, nextjs: enableNextJs = hasNextJs, react: enableReact = hasReact, regexp: enableRegexp = true, tailwindcss: enableTailwindCSS = hasTailwind, tanstackQuery: enableTanStackQuery = false, typescript: enableTypescript = hasTypeScript, unicorn: enableUnicorn = true } = options;
1706
1711
  const prettierOptions = typeof options.prettier === "object" ? options.prettier : {};
1707
1712
  const styleOptions = getStyleOptions(prettierOptions);
1708
1713
  const configs = [];
@@ -1714,16 +1719,18 @@ function ncontiero(options = {}, ...userConfigs) {
1714
1719
  name: "ncontiero/gitignore",
1715
1720
  strict: false
1716
1721
  })]));
1717
- if (!enableTypescript) options.ignores ? options.ignores.push(GLOB_TS, GLOB_TSX) : options.ignores = [GLOB_TS, GLOB_TSX];
1718
1722
  const typescriptOptions = resolveSubOptions(options, "typescript");
1719
1723
  const tsconfigPath = "tsconfigPath" in typescriptOptions ? typescriptOptions.tsconfigPath : void 0;
1720
- configs.push(ignores(options.ignores), javascript({ overrides: getOverrides(options, "javascript") }), comments(), jsdoc(), imports({ nextJs: !!enableNextJs }), node(), promise(), command(), perfectionist(), deMorgan());
1724
+ configs.push(ignores(options.ignores, !enableTypescript), javascript({ overrides: getOverrides(options, "javascript") }), comments(), jsdoc(), imports({ nextJs: !!enableNextJs }), node(), promise(), command(), perfectionist(), deMorgan());
1725
+ if (enableE18e) configs.push(e18e(enableE18e === true ? {} : enableE18e));
1721
1726
  if (enableUnicorn) configs.push(unicorn(enableUnicorn === true ? { regexp: !!enableRegexp } : enableUnicorn));
1727
+ if (enableJsx) configs.push(jsx(enableJsx === true ? {} : enableJsx));
1722
1728
  if (enableTypescript) configs.push(typescript({
1723
1729
  ...typescriptOptions,
1724
1730
  overrides: getOverrides(options, "typescript"),
1725
1731
  tsconfigPath
1726
1732
  }));
1733
+ if (enableTanStackQuery) configs.push(tanstackQuery({ overrides: getOverrides(options, "tanstackQuery") }));
1727
1734
  if (options.jsonc ?? true) configs.push(jsonc({
1728
1735
  overrides: getOverrides(options, "jsonc"),
1729
1736
  style: styleOptions
@@ -1739,8 +1746,9 @@ function ncontiero(options = {}, ...userConfigs) {
1739
1746
  if (options.markdown ?? true) configs.push(markdown({ overrides: getOverrides(options, "markdown") }));
1740
1747
  if (enableRegexp) configs.push(regexp(typeof enableRegexp === "boolean" ? {} : enableRegexp));
1741
1748
  if (enableReact) configs.push(react({
1749
+ ...typescriptOptions,
1742
1750
  overrides: getOverrides(options, "react"),
1743
- reactQuery: !!enableTanStackReactQuery
1751
+ tsconfigPath
1744
1752
  }));
1745
1753
  if (enableNextJs) configs.push(nextJs({ overrides: getOverrides(options, "nextjs") }));
1746
1754
  if (options.html ?? true) configs.push(html({
@@ -1764,4 +1772,4 @@ function ncontiero(options = {}, ...userConfigs) {
1764
1772
  });
1765
1773
  }
1766
1774
  //#endregion
1767
- export { GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_REACT, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, combine, command, comments, composer, deMorgan, defaultPluginRenaming, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTanStackReactQuery, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, renamePluginInConfigs, renameRules, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, toArray, toml, typescript, unicorn, yml };
1775
+ export { GLOB_ALL_SRC, GLOB_CSS, GLOB_DIST, GLOB_EXCLUDE, GLOB_HTML, GLOB_JS, GLOB_JSON, GLOB_JSON5, GLOB_JSONC, GLOB_JSX, GLOB_LESS, GLOB_LOCKFILE, GLOB_MARKDOWN, GLOB_MARKDOWN_CODE, GLOB_MARKDOWN_IN_MARKDOWN, GLOB_NODE_MODULES, GLOB_POSTCSS, GLOB_SCSS, GLOB_SRC, GLOB_SRC_EXT, GLOB_STYLE, GLOB_TOML, GLOB_TS, GLOB_TSX, GLOB_YAML, combine, command, comments, composer, deMorgan, defaultPluginRenaming, e18e, ensurePackages, getOverrides, hasNextJs, hasReact, hasTailwind, hasTypeScript, html, ignores, imports, interopDefault, javascript, jsdoc, jsonc, jsx, markdown, ncontiero, nextJs, node, parserPlain, perfectionist, prettier, promise, react, regexp, renamePluginInConfigs, renameRules, resolveSubOptions, restrictedSyntaxJs, sortPackageJson, sortPnpmWorkspace, sortTsconfig, tailwindcss, tanstackQuery, toArray, toml, typescript, unicorn, yml };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ncontiero/eslint-config",
3
3
  "type": "module",
4
- "version": "8.0.0-beta.1",
4
+ "version": "8.0.0-beta.11",
5
5
  "packageManager": "pnpm@10.33.2",
6
6
  "description": "Nicolas's ESLint config.",
7
7
  "author": {
@@ -51,18 +51,19 @@
51
51
  }
52
52
  },
53
53
  "dependencies": {
54
+ "@e18e/eslint-plugin": "^0.4.1",
54
55
  "@eslint-community/eslint-plugin-eslint-comments": "^4.7.1",
55
- "@eslint-react/eslint-plugin": "5.5.1-beta.1",
56
+ "@eslint-react/eslint-plugin": "^5.7.2",
56
57
  "@eslint/markdown": "^8.0.1",
57
- "@html-eslint/eslint-plugin": "^0.59.0",
58
- "@html-eslint/parser": "^0.59.0",
58
+ "@html-eslint/eslint-plugin": "^0.60.0",
59
+ "@html-eslint/parser": "^0.60.0",
59
60
  "@next/eslint-plugin-next": "^16.2.4",
60
- "@typescript-eslint/eslint-plugin": "^8.59.0",
61
- "@typescript-eslint/parser": "^8.59.0",
61
+ "@typescript-eslint/eslint-plugin": "^8.59.2",
62
+ "@typescript-eslint/parser": "^8.59.2",
62
63
  "eslint-config-flat-gitignore": "^2.3.0",
63
64
  "eslint-merge-processors": "^2.0.0",
64
65
  "eslint-plugin-antfu": "^3.2.2",
65
- "eslint-plugin-better-tailwindcss": "4.4.1",
66
+ "eslint-plugin-better-tailwindcss": "4.5.0",
66
67
  "eslint-plugin-command": "^3.5.2",
67
68
  "eslint-plugin-de-morgan": "^2.1.1",
68
69
  "eslint-plugin-import-x": "^4.16.2",
@@ -72,14 +73,14 @@
72
73
  "eslint-plugin-n": "^17.24.0",
73
74
  "eslint-plugin-perfectionist": "^5.9.0",
74
75
  "eslint-plugin-prettier": "^5.5.5",
75
- "eslint-plugin-promise": "^7.2.1",
76
+ "eslint-plugin-promise": "^7.3.0",
76
77
  "eslint-plugin-react-refresh": "^0.5.2",
77
78
  "eslint-plugin-regexp": "^3.1.0",
78
79
  "eslint-plugin-toml": "^1.3.1",
79
80
  "eslint-plugin-unicorn": "^64.0.0",
80
81
  "eslint-plugin-unused-imports": "^4.4.1",
81
- "eslint-plugin-yml": "^3.3.1",
82
- "globals": "^17.5.0",
82
+ "eslint-plugin-yml": "^3.3.2",
83
+ "globals": "^17.6.0",
83
84
  "local-pkg": "^1.1.2",
84
85
  "prettier": "^3.8.3",
85
86
  "toml-eslint-parser": "^1.0.3",
@@ -87,18 +88,18 @@
87
88
  },
88
89
  "devDependencies": {
89
90
  "@changesets/cli": "^2.31.0",
90
- "@commitlint/cli": "^20.5.2",
91
- "@commitlint/config-conventional": "^20.5.0",
91
+ "@commitlint/cli": "^20.5.3",
92
+ "@commitlint/config-conventional": "^20.5.3",
92
93
  "@ncontiero/changelog-github": "^2.1.3",
93
94
  "@ncontiero/prettier-config": "^1.0.0",
94
- "@tanstack/eslint-plugin-query": "^5.100.5",
95
+ "@tanstack/eslint-plugin-query": "^5.100.9",
95
96
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
96
97
  "@types/node": "^25.6.0",
97
- "eslint": "^10.2.1",
98
+ "eslint": "^10.3.0",
98
99
  "eslint-typegen": "^2.3.1",
99
- "execa": "^9.6.1",
100
100
  "husky": "^9.1.7",
101
- "lint-staged": "^16.4.0",
101
+ "nano-staged": "^1.0.2",
102
+ "tinyexec": "^1.1.2",
102
103
  "tinyglobby": "^0.2.16",
103
104
  "tsdown": "^0.21.10",
104
105
  "tsx": "^4.21.0",
@@ -108,7 +109,7 @@
108
109
  "engines": {
109
110
  "node": ">=20.19.0"
110
111
  },
111
- "lint-staged": {
112
+ "nano-staged": {
112
113
  "*": "pnpm lint:fix"
113
114
  },
114
115
  "prettier": "@ncontiero/prettier-config"