@croct/eslint-plugin 0.8.1 → 0.8.3

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
@@ -30,50 +30,86 @@ Webpack or Browserify:
30
30
  npm i -D @croct/eslint-plugin
31
31
  ```
32
32
 
33
- Then, add the following to your `.eslintrc.js` file:
33
+ This plugin uses ESLint's [flat config format](https://eslint.org/docs/latest/use/configure/configuration-files) (ESLint v9+).
34
+
35
+ ### JavaScript
36
+
37
+ For JavaScript projects, create an `eslint.config.mjs` file:
34
38
 
35
39
  ```js
36
- // Workaround for https://github.com/eslint/eslint/issues/3458
37
- require("@rushstack/eslint-patch/modern-module-resolution");
40
+ import { defineConfig } from 'eslint/config';
41
+ import croct from '@croct/eslint-plugin';
38
42
 
39
- module.exports = {
40
- "plugins": ["@croct"]
41
- }
43
+ export default defineConfig(
44
+ croct.configs.javascript,
45
+ );
42
46
  ```
43
47
 
44
- Note the `require` call at the top of the file. This is a workaround to avoid adding the transitive dependencies of
45
- the plugin to the project, which is [currently not supported by the ESLint plugin system](https://github.com/eslint/eslint/issues/3458).
46
-
47
48
  ### TypeScript
48
49
 
49
- For TypeScript projects, you need first to install the TypeScript parser:
50
+ For TypeScript projects, create an `eslint.config.mjs` file:
50
51
 
51
- ```sh
52
- npm i -D @typescript-eslint/parser
52
+ ```js
53
+ import { defineConfig } from 'eslint/config';
54
+ import croct from '@croct/eslint-plugin';
55
+
56
+ export default defineConfig(
57
+ croct.configs.typescript,
58
+ );
59
+ ```
60
+
61
+ The TypeScript config includes the JavaScript preset, so there's no need to include both.
62
+
63
+ ### React
64
+
65
+ For React projects, create an `eslint.config.mjs` file:
66
+
67
+ ```js
68
+ import { defineConfig } from 'eslint/config';
69
+ import croct from '@croct/eslint-plugin';
70
+
71
+ export default defineConfig(
72
+ croct.configs.react,
73
+ );
53
74
  ```
54
75
 
55
- Then, add the following to your `.eslintrc.js` file:
76
+ The React config includes the JavaScript preset, so there's no need to include both.
77
+
78
+ ### Cypress
79
+
80
+ For Cypress projects, create an `eslint.config.mjs` file:
81
+
82
+ ```js
83
+ import { defineConfig } from 'eslint/config';
84
+ import croct from '@croct/eslint-plugin';
85
+
86
+ export default defineConfig(
87
+ croct.configs.cypress,
88
+ );
89
+ ```
90
+
91
+ The Cypress config includes the JavaScript preset, so there's no need to include both.
92
+
93
+ ### Custom rules
94
+
95
+ You can add custom rules or override existing ones:
56
96
 
57
97
  ```js
58
- // Workaround for https://github.com/eslint/eslint/issues/3458
59
- require("@rushstack/eslint-patch/modern-module-resolution");
60
-
61
- module.exports = {
62
- "parser": "@typescript-eslint/parser",
63
- "plugins": [
64
- "@croct"
65
- ],
66
- "extends": [
67
- "plugin:@croct/typescript"
68
- ],
69
- "parserOptions": {
70
- "extends": "./tsconfig.json",
71
- "project": ["./tsconfig.json"]
98
+ import { defineConfig } from 'eslint/config';
99
+ import croct from '@croct/eslint-plugin';
100
+
101
+ export default defineConfig(
102
+ croct.configs.typescript,
103
+ {
104
+ files: ['src/**/*.ts'],
105
+ rules: {
106
+ '@typescript-eslint/explicit-function-return-type': 'off',
107
+ },
72
108
  },
73
- }
109
+ );
74
110
  ```
75
111
 
76
- For the list for available presets and rules, see the [reference documentation](docs/README.md).
112
+ For the list of available presets and rules, see the [reference documentation](docs/README.md).
77
113
 
78
114
  ## Basic usage
79
115
 
@@ -71,7 +71,6 @@ const baseRules = {
71
71
  'no-fallthrough': 'error',
72
72
  '@stylistic/no-floating-decimal': 'error',
73
73
  'no-global-assign': ['error', { exceptions: [] }],
74
- 'no-native-reassign': 'off',
75
74
  'no-implicit-coercion': ['off', {
76
75
  boolean: false,
77
76
  number: true,
@@ -159,7 +158,6 @@ const baseRules = {
159
158
  message: 'Use the exponentiation operator (**) instead.',
160
159
  }],
161
160
  'no-return-assign': ['error', 'always'],
162
- 'no-return-await': 'error',
163
161
  'no-script-url': 'error',
164
162
  'no-self-assign': ['error', {
165
163
  props: true,
@@ -240,10 +238,8 @@ const baseRules = {
240
238
  'no-unsafe-optional-chaining': ['error', { disallowArithmeticOperators: true }],
241
239
  'no-unused-private-class-members': 'off',
242
240
  'no-useless-backreference': 'error',
243
- 'no-negated-in-lhs': 'off',
244
241
  'require-atomic-updates': 'off',
245
242
  'use-isnan': 'error',
246
- 'valid-jsdoc': 'off',
247
243
  'valid-typeof': ['error', { requireStringLiterals: true }],
248
244
  // Style (from airbnb-base, with customizations)
249
245
  '@stylistic/array-bracket-newline': ['error', 'consistent'],
@@ -264,13 +260,7 @@ const baseRules = {
264
260
  ignoreConsecutiveComments: true,
265
261
  },
266
262
  }],
267
- '@stylistic/comma-dangle': ['error', {
268
- arrays: 'always-multiline',
269
- objects: 'always-multiline',
270
- imports: 'always-multiline',
271
- exports: 'always-multiline',
272
- functions: 'always-multiline',
273
- }],
263
+ '@stylistic/comma-dangle': ['error', 'always-multiline'],
274
264
  '@stylistic/comma-spacing': ['error', { before: false, after: true }],
275
265
  '@stylistic/comma-style': ['error', 'last', {
276
266
  exceptions: {
@@ -291,7 +281,7 @@ const baseRules = {
291
281
  'consistent-this': 'off',
292
282
  '@stylistic/eol-last': ['error', 'always'],
293
283
  '@stylistic/function-call-argument-newline': ['error', 'consistent'],
294
- 'func-call-spacing': ['error', 'never'],
284
+ '@stylistic/function-call-spacing': ['error', 'never'],
295
285
  'func-name-matching': ['off', 'always', {
296
286
  includeCommonJSModuleExports: false,
297
287
  considerPropertyDescriptor: true,
@@ -346,7 +336,7 @@ const baseRules = {
346
336
  ignoreComments: false,
347
337
  },
348
338
  ],
349
- 'jsx-quotes': ['error', 'prefer-double'],
339
+ '@stylistic/jsx-quotes': ['error', 'prefer-double'],
350
340
  '@stylistic/key-spacing': ['error', { beforeColon: false, afterColon: true }],
351
341
  '@stylistic/keyword-spacing': ['error', {
352
342
  before: true,
@@ -365,12 +355,8 @@ const baseRules = {
365
355
  '@stylistic/linebreak-style': ['error', 'unix'],
366
356
  '@stylistic/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }],
367
357
  '@stylistic/lines-around-comment': 'off',
368
- 'lines-around-directive': ['error', {
369
- before: 'always',
370
- after: 'always',
371
- }],
372
358
  'max-depth': ['off', 4],
373
- 'max-len': [
359
+ '@stylistic/max-len': [
374
360
  'error',
375
361
  {
376
362
  code: 120,
@@ -395,7 +381,7 @@ const baseRules = {
395
381
  'max-nested-callbacks': 'off',
396
382
  'max-params': ['off', 3],
397
383
  'max-statements': ['off', 10],
398
- 'max-statements-per-line': ['off', { max: 1 }],
384
+ '@stylistic/max-statements-per-line': ['off', { max: 1 }],
399
385
  '@stylistic/multiline-comment-style': ['off', 'starred-block'],
400
386
  '@stylistic/multiline-ternary': ['error', 'always-multiline'],
401
387
  'new-cap': ['error', {
@@ -405,15 +391,13 @@ const baseRules = {
405
391
  capIsNewExceptions: ['Immutable.Map', 'Immutable.Set', 'Immutable.List'],
406
392
  }],
407
393
  '@stylistic/new-parens': 'error',
408
- 'newline-after-var': 'off',
409
- 'newline-before-return': 'off',
410
394
  '@stylistic/newline-per-chained-call': 'off',
411
395
  'no-array-constructor': 'error',
412
396
  'no-bitwise': 'off',
413
397
  'no-continue': 'off',
414
398
  'no-inline-comments': 'off',
415
399
  'no-lonely-if': 'error',
416
- 'no-mixed-operators': ['error', {
400
+ '@stylistic/no-mixed-operators': ['error', {
417
401
  groups: [
418
402
  ['%', '**'],
419
403
  ['%', '+'],
@@ -427,7 +411,7 @@ const baseRules = {
427
411
  ],
428
412
  allowSamePrecedence: false,
429
413
  }],
430
- 'no-mixed-spaces-and-tabs': 'error',
414
+ '@stylistic/no-mixed-spaces-and-tabs': 'error',
431
415
  'no-multi-assign': ['error'],
432
416
  '@stylistic/no-multiple-empty-lines': [
433
417
  'error',
@@ -439,7 +423,6 @@ const baseRules = {
439
423
  ],
440
424
  'no-negated-condition': 'off',
441
425
  'no-nested-ternary': 'error',
442
- 'no-new-object': 'error',
443
426
  'no-plusplus': 'off',
444
427
  'no-restricted-syntax': [
445
428
  'error',
@@ -447,8 +430,7 @@ const baseRules = {
447
430
  'LabeledStatement',
448
431
  'WithStatement',
449
432
  ],
450
- 'no-spaced-func': 'error',
451
- 'no-tabs': 'error',
433
+ '@stylistic/no-tabs': 'error',
452
434
  'no-ternary': 'off',
453
435
  '@stylistic/no-trailing-spaces': ['error', {
454
436
  skipBlankLines: false,
@@ -472,7 +454,7 @@ const baseRules = {
472
454
  'one-var': ['error', 'never'],
473
455
  '@stylistic/one-var-declaration-per-line': ['error', 'always'],
474
456
  'operator-assignment': ['error', 'always'],
475
- '@stylistic/operator-linebreak': ['error', 'before', { overrides: { '=': 'none' } }],
457
+ '@stylistic/operator-linebreak': ['error', 'before', { overrides: { '=': 'ignore' } }],
476
458
  '@stylistic/padded-blocks': ['error', {
477
459
  blocks: 'never',
478
460
  classes: 'never',
@@ -547,9 +529,8 @@ const baseRules = {
547
529
  ],
548
530
  'prefer-exponentiation-operator': 'error',
549
531
  'prefer-object-spread': 'error',
550
- '@stylistic/quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }],
532
+ '@stylistic/quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: true }],
551
533
  '@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
552
- 'require-jsdoc': 'off',
553
534
  '@stylistic/semi': ['error', 'always'],
554
535
  '@stylistic/semi-spacing': ['error', { before: false, after: true }],
555
536
  '@stylistic/semi-style': ['error', 'last'],
@@ -640,7 +621,6 @@ const baseRules = {
640
621
  enforceForRenamedProperties: false,
641
622
  }],
642
623
  'prefer-numeric-literals': 'error',
643
- 'prefer-reflect': 'off',
644
624
  'prefer-rest-params': 'error',
645
625
  'prefer-spread': 'error',
646
626
  'prefer-template': 'error',
@@ -657,7 +637,6 @@ const baseRules = {
657
637
  '@stylistic/yield-star-spacing': ['error', 'after'],
658
638
  // Variables (from airbnb-base)
659
639
  'init-declarations': 'off',
660
- 'no-catch-shadow': 'off',
661
640
  'no-delete-var': 'error',
662
641
  'no-label-var': 'error',
663
642
  'no-restricted-globals': [
@@ -749,21 +728,8 @@ const baseRules = {
749
728
  ],
750
729
  // Strict (from airbnb-base)
751
730
  strict: ['error', 'never'],
752
- // Node (from airbnb-base)
753
- 'callback-return': 'off',
754
- 'global-require': 'error',
755
- 'handle-callback-err': 'off',
756
- 'no-buffer-constructor': 'error',
757
- 'no-mixed-requires': ['off', false],
758
- 'no-new-require': 'error',
759
- 'no-path-concat': 'error',
760
- 'no-process-env': 'off',
761
- 'no-process-exit': 'off',
762
- 'no-restricted-modules': 'off',
763
- 'no-sync': 'off',
764
731
  // Import rules (from airbnb-base)
765
732
  'import-x/no-unresolved': 'off',
766
- 'import-x/named': 'error',
767
733
  'import-x/default': 'off',
768
734
  'import-x/namespace': 'off',
769
735
  'import-x/export': 'error',
@@ -776,7 +742,6 @@ const baseRules = {
776
742
  'import-x/no-amd': 'error',
777
743
  'import-x/no-nodejs-modules': 'off',
778
744
  'import-x/first': 'error',
779
- 'import-x/imports-first': 'off',
780
745
  'import-x/no-duplicates': 'error',
781
746
  'import-x/no-namespace': 'off',
782
747
  'import-x/extensions': [
@@ -898,15 +863,5 @@ function createJavaScriptConfig(plugin) {
898
863
  ...baseRules,
899
864
  },
900
865
  },
901
- {
902
- name: '@croct/javascript/tests',
903
- files: [
904
- 'src/**/*.test.js',
905
- 'test/**/*.js',
906
- ],
907
- rules: {
908
- 'no-new-object': 'off',
909
- },
910
- },
911
866
  ];
912
867
  }
@@ -29,12 +29,11 @@ const baseRules = {
29
29
  ignoreTypeValueShadow: true,
30
30
  ignoreFunctionTypeParameterNameValueShadow: true,
31
31
  }],
32
- '@typescript-eslint/no-empty-interface': 'off',
33
32
  '@typescript-eslint/explicit-member-accessibility': [
34
33
  'error',
35
34
  ],
36
35
  '@typescript-eslint/explicit-module-boundary-types': 'off',
37
- '@typescript-eslint/explicit-function-return-type': ['error'],
36
+ '@typescript-eslint/explicit-function-return-type': ['error', { allowExpressions: true }],
38
37
  '@typescript-eslint/no-explicit-any': 'off',
39
38
  'no-use-before-define': 'off',
40
39
  '@typescript-eslint/no-use-before-define': 'off',
@@ -75,6 +74,23 @@ const baseRules = {
75
74
  }],
76
75
  'no-undef': 'off',
77
76
  '@typescript-eslint/no-namespace': 'off',
77
+ '@typescript-eslint/restrict-template-expressions': 'off',
78
+ '@typescript-eslint/consistent-type-imports': 'error',
79
+ // Disable rules that turn `any` into `unknown`, places where `unknown` is the preferred type
80
+ // have that type already.
81
+ '@typescript-eslint/no-unsafe-argument': 'off',
82
+ '@typescript-eslint/no-unsafe-assignment': 'off',
83
+ '@typescript-eslint/no-unsafe-call': 'off',
84
+ '@typescript-eslint/no-unsafe-enum-comparison': 'off',
85
+ '@typescript-eslint/no-unsafe-member-access': 'off',
86
+ '@typescript-eslint/no-unsafe-return': 'off',
87
+ '@typescript-eslint/no-unsafe-unary-minus': 'off',
88
+ // Breaks with overloaded functions that implement both callback and Promise signatures
89
+ '@typescript-eslint/no-misused-promises': 'off',
90
+ // Doesn't detect classes that implement `toString` method
91
+ '@typescript-eslint/no-base-to-string': 'off',
92
+ // Conflict with TS promise ignore explicitly (void Promise)
93
+ 'no-void': 'off',
78
94
  };
79
95
  // Factory function to create TypeScript config with the plugin reference
80
96
  function createTypescriptConfig(plugin, javascriptConfig) {
@@ -99,13 +115,11 @@ function createTypescriptConfig(plugin, javascriptConfig) {
99
115
  rules: baseRules,
100
116
  },
101
117
  {
102
- name: '@croct/typescript/tests',
103
- files: [
104
- 'src/**/*.test.ts',
105
- 'test/**/*.ts',
106
- ],
118
+ name: '@croct/typescript-test-files',
119
+ files: ['**/*.test.ts', '**/*.test.tsx'],
107
120
  rules: {
108
- 'no-new-object': 'off',
121
+ // Prevent false warnings when checking mocked interfaces
122
+ '@typescript-eslint/unbound-method': 'off',
109
123
  },
110
124
  },
111
125
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@croct/eslint-plugin",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "ESLint rules and presets applied to all Croct JavaScript projects.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -53,7 +53,7 @@
53
53
  "typescript-eslint": "^8.53.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/jest": "^29.5.14",
56
+ "@types/jest": "^30.0.0",
57
57
  "@types/semver": "^7.5.8",
58
58
  "@typescript-eslint/parser": "^8.53.0",
59
59
  "@typescript-eslint/rule-tester": "^8.53.0",
@@ -62,7 +62,7 @@
62
62
  "eslint": "^9.28.0",
63
63
  "eslint-plugin-eslint-plugin": "^6.4.0",
64
64
  "eslint-plugin-self": "^1.2.1",
65
- "jest": "^29.7.0",
65
+ "jest": "^30.0.0",
66
66
  "ts-jest": "^29.3.4",
67
67
  "typescript": "~5.9.0"
68
68
  },