@mrpalmer/eslint-config 1.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/import.js ADDED
@@ -0,0 +1,72 @@
1
+ module.exports = {
2
+ env: {
3
+ es6: true,
4
+ },
5
+ parserOptions: {
6
+ ecmaVersion: 2018,
7
+ sourceType: 'module',
8
+ },
9
+ plugins: ['import'],
10
+ settings: {
11
+ 'import/ignore': ['node_modules', '.json$', '.(scss|less|css|styl)$'],
12
+ },
13
+ rules: {
14
+ 'import/default': 'error',
15
+ 'import/dynamic-import-chunkname': 'off',
16
+ 'import/export': 'error',
17
+ 'import/exports-last': 'off',
18
+ 'import/extensions': 'off',
19
+ 'import/first': 'error',
20
+ 'import/group-exports': 'off',
21
+ 'import/max-dependencies': 'off',
22
+ 'import/named': 'error',
23
+ 'import/namespace': 'error',
24
+ 'import/newline-after-import': 'off',
25
+ 'import/no-absolute-path': 'error',
26
+ 'import/no-amd': 'error',
27
+ 'import/no-anonymous-default-export': 'error',
28
+ 'import/no-commonjs': 'off',
29
+ 'import/no-cycle': 'off',
30
+ 'import/no-default-export': 'off',
31
+ 'import/no-deprecated': 'warn',
32
+ 'import/no-duplicates': 'error',
33
+ 'import/no-dynamic-require': 'off',
34
+ 'import/no-extraneous-dependencies': 'error',
35
+ 'import/no-import-module-exports': 'error',
36
+ 'import/no-internal-modules': 'off',
37
+ 'import/no-mutable-exports': 'error',
38
+ 'import/no-named-as-default': 'error',
39
+ 'import/no-named-as-default-member': 'error',
40
+ 'import/no-named-default': 'error',
41
+ 'import/no-named-export': 'off',
42
+ 'import/no-namespace': 'off',
43
+ 'import/no-nodejs-modules': 'off',
44
+ 'import/no-relative-packages': 'warn',
45
+ 'import/no-relative-parent-imports': 'off',
46
+ 'import/no-restricted-paths': 'off',
47
+ 'import/no-self-import': 'error',
48
+ 'import/no-unassigned-import': 'off',
49
+ 'import/no-unresolved': 'error',
50
+ 'import/no-unused-modules': 'off',
51
+ 'import/no-useless-path-segments': 'error',
52
+ 'import/no-webpack-loader-syntax': 'error',
53
+ 'import/order': 'off',
54
+ 'import/prefer-default-export': 'off',
55
+ 'import/unambiguous': 'off',
56
+ },
57
+ overrides: [
58
+ {
59
+ files: ['**/*.ts?(x)'],
60
+ extends: 'plugin:import/typescript',
61
+ parser: '@typescript-eslint/parser',
62
+ parserOptions: {
63
+ ecmaVersion: 2018,
64
+ sourceType: 'module',
65
+ },
66
+ plugins: ['@typescript-eslint'],
67
+ rules: {
68
+ 'import/no-unresolved': 'off', // ts(2307)
69
+ },
70
+ },
71
+ ],
72
+ }
package/index.js ADDED
@@ -0,0 +1,427 @@
1
+ const fs = require('fs')
2
+ const path = require('path')
3
+
4
+ const tsConfig = fs.existsSync('tsconfig.json')
5
+ ? path.resolve('tsconfig.json')
6
+ : fs.existsSync('types/tsconfig.json')
7
+ ? path.resolve('types/tsconfig.json')
8
+ : undefined
9
+
10
+ module.exports = {
11
+ env: {
12
+ browser: true,
13
+ es6: true,
14
+ node: true,
15
+ },
16
+ extends: ['prettier', './import.js'],
17
+ rules: {
18
+ 'accessor-pairs': 'error',
19
+ 'array-callback-return': 'error',
20
+ 'arrow-body-style': 'off',
21
+ 'block-scoped-var': 'error',
22
+ camelcase: 'off',
23
+ 'capitalized-comments': 'off',
24
+ 'class-methods-use-this': 'off',
25
+ complexity: ['error', 20],
26
+ 'consistent-return': 'error',
27
+ 'consistent-this': 'off',
28
+ 'constructor-super': 'error',
29
+ curly: ['error', 'multi-line'],
30
+ 'default-case': 'error',
31
+ 'default-case-last': 'error',
32
+ 'default-param-last': 'off',
33
+ 'dot-notation': 'error',
34
+ eqeqeq: ['error', 'smart'],
35
+ 'for-direction': 'error',
36
+ 'func-name-matching': 'error',
37
+ 'func-names': 'error',
38
+ 'func-style': 'off',
39
+ 'getter-return': ['error', { allowImplicit: true }],
40
+ 'grouped-accessor-pairs': 'off',
41
+ 'guard-for-in': 'error',
42
+ 'id-denylist': 'error',
43
+ 'id-length': 'off',
44
+ 'id-match': [
45
+ 'error',
46
+ // camelCase, PascalCase, __filename, CONST_VALUE, stream$, $el
47
+ '^\\$?(__)?(([A-Z]|[a-z]|[0-9]+)|([A-Z_]))*\\$?$',
48
+ ],
49
+ 'init-declarations': 'off',
50
+ 'line-comment-position': 'off',
51
+ 'lines-between-class-members': 'off',
52
+ 'max-classes-per-file': 'off',
53
+ 'max-depth': ['error', 4],
54
+ 'max-lines': [
55
+ 'error',
56
+ { max: 2500, skipBlankLines: false, skipComments: false },
57
+ ],
58
+ 'max-lines-per-function': 'off',
59
+ 'max-nested-callbacks': ['error', 7],
60
+ 'max-params': ['error', 7],
61
+ 'max-statements': 'off',
62
+ 'max-statements-per-line': ['error', { max: 1 }],
63
+ 'multiline-comment-style': 'off',
64
+ 'new-cap': 'error',
65
+ 'no-alert': 'error',
66
+ 'no-array-constructor': 'error',
67
+ 'no-async-promise-executor': 'off',
68
+ 'no-await-in-loop': 'error',
69
+ 'no-bitwise': 'error',
70
+ 'no-caller': 'error',
71
+ 'no-case-declarations': 'error',
72
+ 'no-class-assign': 'error',
73
+ 'no-compare-neg-zero': 'error',
74
+ 'no-cond-assign': 'error',
75
+ 'no-console': 'off',
76
+ 'no-const-assign': 'error',
77
+ 'no-constant-condition': 'error',
78
+ 'no-constructor-return': 'error',
79
+ 'no-continue': 'off',
80
+ 'no-control-regex': 'error',
81
+ 'no-debugger': 'error',
82
+ 'no-delete-var': 'error',
83
+ 'no-div-regex': 'error',
84
+ 'no-dupe-args': 'error',
85
+ 'no-dupe-class-members': 'error',
86
+ 'no-dupe-else-if': 'error',
87
+ 'no-dupe-keys': 'error',
88
+ 'no-duplicate-case': 'error',
89
+ 'no-duplicate-imports': 'error',
90
+ 'no-else-return': 'off',
91
+ 'no-empty': 'error',
92
+ 'no-empty-character-class': 'error',
93
+ 'no-empty-function': 'off',
94
+ 'no-empty-pattern': 'error',
95
+ 'no-eq-null': 'off',
96
+ 'no-eval': 'error',
97
+ 'no-ex-assign': 'error',
98
+ 'no-extend-native': 'error',
99
+ 'no-extra-bind': 'error',
100
+ 'no-extra-boolean-cast': 'off',
101
+ 'no-extra-label': 'error',
102
+ 'no-fallthrough': 'error',
103
+ 'no-func-assign': 'error',
104
+ 'no-global-assign': 'error',
105
+ 'no-implicit-coercion': 'off',
106
+ 'no-implicit-globals': 'error',
107
+ 'no-implied-eval': 'error',
108
+ 'no-import-assign': 'error',
109
+ 'no-inline-comments': 'off',
110
+ 'no-inner-declarations': 'error',
111
+ 'no-invalid-regexp': 'error',
112
+ 'no-invalid-this': 'error',
113
+ 'no-irregular-whitespace': 'error',
114
+ 'no-iterator': 'error',
115
+ 'no-label-var': 'error',
116
+ 'no-labels': 'error',
117
+ 'no-lone-blocks': 'error',
118
+ 'no-lonely-if': 'error',
119
+ 'no-loop-func': 'error',
120
+ 'no-loss-of-precision': 'error',
121
+ 'no-magic-numbers': 'off',
122
+ 'no-misleading-character-class': 'off',
123
+ 'no-multi-assign': 'error',
124
+ 'no-multi-str': 'error',
125
+ 'no-negated-condition': 'error',
126
+ 'no-nested-ternary': 'off',
127
+ 'no-new': 'error',
128
+ 'no-new-func': 'error',
129
+ 'no-new-object': 'error',
130
+ 'no-new-symbol': 'error',
131
+ 'no-new-wrappers': 'error',
132
+ 'no-nonoctal-decimal-escape': 'error',
133
+ 'no-obj-calls': 'error',
134
+ 'no-octal': 'error',
135
+ 'no-octal-escape': 'error',
136
+ 'no-param-reassign': 'off',
137
+ 'no-plusplus': 'off',
138
+ 'no-promise-executor-return': 'off',
139
+ 'no-proto': 'error',
140
+ 'no-prototype-builtins': 'off',
141
+ 'no-redeclare': 'error',
142
+ 'no-regex-spaces': 'error',
143
+ 'no-restricted-exports': 'off',
144
+ 'no-restricted-globals': ['error', 'event', 'fdescribe'],
145
+ 'no-restricted-imports': 'off',
146
+ 'no-restricted-properties': 'off',
147
+ 'no-restricted-syntax': ['error', 'WithStatement'],
148
+ 'no-return-assign': 'error',
149
+ 'no-return-await': 'error',
150
+ 'no-script-url': 'error',
151
+ 'no-self-assign': 'error',
152
+ 'no-self-compare': 'error',
153
+ 'no-sequences': 'error',
154
+ 'no-setter-return': 'error',
155
+ 'no-shadow': 'off',
156
+ 'no-shadow-restricted-names': 'error',
157
+ 'no-sparse-arrays': 'error',
158
+ 'no-template-curly-in-string': 'error',
159
+ 'no-ternary': 'off',
160
+ 'no-this-before-super': 'error',
161
+ 'no-throw-literal': 'error',
162
+ 'no-undef': 'error',
163
+ 'no-undef-init': 'error',
164
+ 'no-undefined': 'off',
165
+ 'no-underscore-dangle': 'off',
166
+ 'no-unmodified-loop-condition': 'error',
167
+ 'no-unneeded-ternary': 'error',
168
+ 'no-unreachable': 'error',
169
+ 'no-unreachable-loop': 'error',
170
+ 'no-unsafe-finally': 'error',
171
+ 'no-unsafe-negation': 'error',
172
+ 'no-unsafe-optional-chaining': 'error',
173
+ 'no-unused-expressions': 'off',
174
+ 'no-unused-labels': 'error',
175
+ 'no-unused-private-class-members': 'error',
176
+ 'no-unused-vars': [
177
+ 'error',
178
+ {
179
+ args: 'after-used',
180
+ argsIgnorePattern: '^_',
181
+ ignoreRestSiblings: true,
182
+ varsIgnorePattern: '^ignored',
183
+ },
184
+ ],
185
+ 'no-use-before-define': ['error', 'nofunc'],
186
+ 'no-useless-backreference': 'error',
187
+ 'no-useless-call': 'error',
188
+ 'no-useless-catch': 'error',
189
+ 'no-useless-computed-key': 'error',
190
+ 'no-useless-concat': 'error',
191
+ 'no-useless-constructor': 'error',
192
+ 'no-useless-escape': 'error',
193
+ 'no-useless-rename': 'error',
194
+ 'no-useless-return': 'error',
195
+ 'no-var': 'error',
196
+ 'no-void': 'off',
197
+ 'no-warning-comments': [
198
+ 'error',
199
+ { location: 'anywhere', terms: ['fixme'] },
200
+ ],
201
+ 'no-with': 'off',
202
+ 'object-shorthand': ['error', 'properties'],
203
+ 'one-var': ['error', { initialized: 'never', uninitialized: 'always' }],
204
+ 'operator-assignment': 'off',
205
+ 'padding-line-between-statements': 'off',
206
+ 'prefer-arrow-callback': [
207
+ 'error',
208
+ { allowNamedFunctions: true, allowUnboundThis: true },
209
+ ],
210
+ 'prefer-const': 'error',
211
+ 'prefer-destructuring': 'off',
212
+ 'prefer-exponentiation-operator': 'warn',
213
+ 'prefer-named-capture-group': 'off',
214
+ 'prefer-numeric-literals': 'error',
215
+ 'prefer-object-has-own': 'error',
216
+ 'prefer-object-spread': 'warn',
217
+ 'prefer-promise-reject-errors': 'off',
218
+ 'prefer-regex-literals': 'off',
219
+ 'prefer-rest-params': 'error',
220
+ 'prefer-spread': 'error',
221
+ 'prefer-template': 'error',
222
+ radix: 'error',
223
+ 'require-atomic-updates': 'off',
224
+ 'require-await': 'off',
225
+ 'require-unicode-regexp': 'off',
226
+ 'require-yield': 'error',
227
+ 'sort-imports': 'off',
228
+ 'sort-keys': 'off',
229
+ 'sort-vars': 'off',
230
+ 'spaced-comment': 'off',
231
+ strict: 'error',
232
+ 'symbol-description': 'error',
233
+ 'use-isnan': 'error',
234
+ 'valid-typeof': 'error',
235
+ 'vars-on-top': 'error',
236
+ yoda: 'error',
237
+ },
238
+ overrides: [
239
+ {
240
+ files: ['**/*.ts?(x)'],
241
+ parser: '@typescript-eslint/parser',
242
+ parserOptions: {
243
+ ecmaVersion: 2018,
244
+ project: tsConfig,
245
+ sourceType: 'module',
246
+ },
247
+ plugins: ['@typescript-eslint'],
248
+ rules: {
249
+ 'constructor-super': 'off', // ts(2335) & ts(2377)
250
+ 'getter-return': 'off', // ts(2378)
251
+ 'no-const-assign': 'off', // ts(2588)
252
+ 'no-dupe-args': 'off', // ts(2300)
253
+ 'no-dupe-keys': 'off', // ts(1117)
254
+ 'no-func-assign': 'off', // ts(2539)
255
+ 'no-import-assign': 'off', // ts(2539) & ts(2540)
256
+ 'no-new-symbol': 'off', // ts(2588)
257
+ 'no-obj-calls': 'off', // ts(2349)
258
+ 'no-setter-return': 'off', // ts(2408)
259
+ 'no-this-before-super': 'off', // ts(2376)
260
+ 'no-undef': 'off', // ts(2304)
261
+ 'no-unreachable': 'off', // ts(7027)
262
+ 'no-unsafe-negation': 'off', // ts(2365) & ts(2360) & ts(2358)
263
+ 'valid-typeof': 'off', // ts(2367)
264
+
265
+ 'consistent-return': 'off', // in TS this is much less an issue
266
+ 'no-var': 'error', // TS transpiles let/const to var, so no need for vars anymore
267
+ 'prefer-const': 'error', // TS provides better types with const
268
+ 'prefer-rest-params': 'error', // TS provides better types with rest args over arguments
269
+ 'prefer-spread': 'error', // TS transpiles spread to apply, so no need for manual apply
270
+
271
+ 'default-param-last': 'off',
272
+ '@typescript-eslint/default-param-last': 'off',
273
+
274
+ 'dot-notation': 'off',
275
+ '@typescript-eslint/dot-notation': 'error',
276
+
277
+ 'init-declarations': 'off',
278
+ '@typescript-eslint/init-declarations': 'off',
279
+
280
+ 'lines-between-class-members': 'off',
281
+ '@typescript-eslint/lines-between-class-members': 'off',
282
+
283
+ 'no-array-constructor': 'off',
284
+ '@typescript-eslint/no-array-constructor': 'error',
285
+
286
+ 'no-dupe-class-members': 'off',
287
+ '@typescript-eslint/no-dupe-class-members': 'off', // ts(2393) & ts(2300)
288
+
289
+ 'no-duplicate-imports': 'off',
290
+ '@typescript-eslint/no-duplicate-imports': 'error',
291
+
292
+ 'no-empty-function': 'off',
293
+ '@typescript-eslint/no-empty-function': 'off',
294
+
295
+ 'no-implied-eval': 'error',
296
+ '@typescript-eslint/no-implied-eval': 'error',
297
+
298
+ 'no-invalid-this': 'off',
299
+ '@typescript-eslint/no-invalid-this': 'error',
300
+
301
+ 'no-loop-func': 'off',
302
+ '@typescript-eslint/no-loop-func': 'error',
303
+
304
+ 'no-loss-of-precision': 'off',
305
+ '@typescript-eslint/no-loss-of-precision': 'error',
306
+
307
+ 'no-magic-numbers': 'off',
308
+ '@typescript-eslint/no-magic-numbers': 'off',
309
+
310
+ 'no-redeclare': 'off',
311
+ '@typescript-eslint/no-redeclare': 'off', // ts(2451)
312
+
313
+ 'no-return-await': 'off',
314
+ '@typescript-eslint/return-await': 'error',
315
+
316
+ 'no-shadow': 'off',
317
+ '@typescript-eslint/no-shadow': 'off',
318
+
319
+ 'no-throw-literal': 'off',
320
+ '@typescript-eslint/no-throw-literal': 'error',
321
+
322
+ 'no-use-before-define': 'off',
323
+ '@typescript-eslint/no-use-before-define': ['error', 'nofunc'],
324
+
325
+ 'no-unused-expressions': 'off',
326
+ '@typescript-eslint/no-unused-expressions': 'off',
327
+
328
+ 'no-unused-vars': 'off',
329
+ '@typescript-eslint/no-unused-vars': [
330
+ 'error',
331
+ {
332
+ args: 'after-used',
333
+ argsIgnorePattern: '^_',
334
+ ignoreRestSiblings: true,
335
+ varsIgnorePattern: '^ignored',
336
+ },
337
+ ],
338
+
339
+ 'no-useless-constructor': 'off',
340
+ '@typescript-eslint/no-useless-constructor': 'error',
341
+
342
+ '@typescript-eslint/adjacent-overload-signatures': 'error',
343
+ '@typescript-eslint/array-type': 'off',
344
+ '@typescript-eslint/await-thenable': 'error',
345
+ '@typescript-eslint/ban-ts-comment': 'error',
346
+ '@typescript-eslint/ban-tslint-comment': 'error',
347
+ '@typescript-eslint/ban-types': 'off',
348
+ '@typescript-eslint/class-literal-property-style': 'off',
349
+ '@typescript-eslint/consistent-indexed-object-style': 'off',
350
+ '@typescript-eslint/consistent-type-assertions': 'off',
351
+ '@typescript-eslint/consistent-type-definitions': 'off',
352
+ '@typescript-eslint/consistent-type-imports': 'off',
353
+ '@typescript-eslint/explicit-function-return-type': 'off',
354
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
355
+ '@typescript-eslint/member-delimiter-style': 'off',
356
+ '@typescript-eslint/member-ordering': 'off',
357
+ '@typescript-eslint/method-signature-style': 'off',
358
+ '@typescript-eslint/naming-convention': 'off',
359
+ '@typescript-eslint/no-base-to-string': 'warn',
360
+ '@typescript-eslint/no-confusing-non-null-assertion': 'off',
361
+ '@typescript-eslint/no-confusing-void-expression': 'off',
362
+ '@typescript-eslint/no-dynamic-delete': 'error',
363
+ '@typescript-eslint/no-empty-interface': 'error',
364
+ '@typescript-eslint/no-explicit-any': 'error',
365
+ '@typescript-eslint/no-extra-non-null-assertion': 'error',
366
+ '@typescript-eslint/no-extraneous-class': 'error',
367
+ '@typescript-eslint/no-floating-promises': 'warn',
368
+ '@typescript-eslint/no-for-in-array': 'error',
369
+ '@typescript-eslint/no-implicit-any-catch': 'warn',
370
+ '@typescript-eslint/no-inferrable-types': 'off',
371
+ '@typescript-eslint/no-invalid-void-type': 'warn',
372
+ '@typescript-eslint/no-misused-new': 'error',
373
+ '@typescript-eslint/no-misused-promises': [
374
+ 'warn',
375
+ { checksVoidReturn: false },
376
+ ],
377
+ '@typescript-eslint/no-namespace': 'error',
378
+ '@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
379
+ '@typescript-eslint/no-non-null-assertion': 'error',
380
+ '@typescript-eslint/no-parameter-properties': 'off',
381
+ '@typescript-eslint/no-require-imports': 'off',
382
+ '@typescript-eslint/no-this-alias': 'error',
383
+ '@typescript-eslint/no-type-alias': 'off',
384
+ '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn',
385
+ '@typescript-eslint/no-unnecessary-condition': 'error',
386
+ '@typescript-eslint/no-unnecessary-qualifier': 'warn',
387
+ '@typescript-eslint/no-unnecessary-type-arguments': 'off',
388
+ '@typescript-eslint/no-unnecessary-type-assertion': 'error',
389
+ '@typescript-eslint/no-unnecessary-type-constraint': 'error',
390
+ '@typescript-eslint/no-unsafe-argument': 'error',
391
+ '@typescript-eslint/no-unsafe-assignment': 'warn',
392
+ '@typescript-eslint/no-unsafe-call': 'warn',
393
+ '@typescript-eslint/no-unsafe-member-access': 'warn',
394
+ '@typescript-eslint/no-unsafe-return': 'off',
395
+ '@typescript-eslint/no-var-requires': 'error',
396
+ '@typescript-eslint/non-nullable-type-assertion-style': 'off',
397
+ '@typescript-eslint/prefer-as-const': 'error',
398
+ '@typescript-eslint/prefer-enum-initializers': 'error',
399
+ '@typescript-eslint/prefer-for-of': 'off',
400
+ '@typescript-eslint/prefer-function-type': 'off',
401
+ '@typescript-eslint/prefer-includes': 'error',
402
+ '@typescript-eslint/prefer-literal-enum-member': 'error',
403
+ '@typescript-eslint/prefer-namespace-keyword': 'error',
404
+ '@typescript-eslint/prefer-nullish-coalescing': 'error',
405
+ '@typescript-eslint/prefer-optional-chain': 'error',
406
+ '@typescript-eslint/prefer-readonly': 'off',
407
+ '@typescript-eslint/prefer-readonly-parameter-types': 'off',
408
+ '@typescript-eslint/prefer-reduce-type-parameter': 'warn',
409
+ '@typescript-eslint/prefer-regexp-exec': 'off',
410
+ '@typescript-eslint/prefer-string-starts-ends-with': 'error',
411
+ '@typescript-eslint/prefer-ts-expect-error': 'error',
412
+ '@typescript-eslint/promise-function-async': 'off',
413
+ '@typescript-eslint/require-array-sort-compare': 'off',
414
+ '@typescript-eslint/require-await': 'off',
415
+ '@typescript-eslint/restrict-plus-operands': 'error',
416
+ '@typescript-eslint/restrict-template-expressions': 'off',
417
+ '@typescript-eslint/sort-type-union-intersection-members': 'off',
418
+ '@typescript-eslint/strict-boolean-expressions': 'off',
419
+ '@typescript-eslint/switch-exhaustiveness-check': 'error',
420
+ '@typescript-eslint/triple-slash-reference': 'error',
421
+ '@typescript-eslint/typedef': 'off',
422
+ '@typescript-eslint/unbound-method': 'error',
423
+ '@typescript-eslint/unified-signatures': 'warn',
424
+ },
425
+ },
426
+ ],
427
+ }
package/jest.js ADDED
@@ -0,0 +1,150 @@
1
+ const readPkgUp = require('read-pkg-up')
2
+
3
+ let hasJestDom = false
4
+ let hasTestingLibrary = false
5
+
6
+ try {
7
+ const { packageJson } = readPkgUp.sync({ normalize: true })
8
+ const allDeps = Object.keys({
9
+ ...packageJson.peerDependencies,
10
+ ...packageJson.devDependencies,
11
+ ...packageJson.dependencies,
12
+ })
13
+
14
+ hasJestDom = allDeps.includes('@testing-library/jest-dom')
15
+ hasTestingLibrary = ['@testing-library/dom', '@testing-library/react'].some(
16
+ (dependency) => allDeps.includes(dependency)
17
+ )
18
+ } catch (error) {
19
+ // ignore error
20
+ }
21
+
22
+ const rules = {
23
+ 'react/display-name': 'off', // we don't need a display name in test files
24
+
25
+ 'jest/consistent-test-it': 'off',
26
+ 'jest/expect-expect': 'off',
27
+ 'jest/max-nested-describe': 'error',
28
+ 'jest/no-alias-methods': 'off',
29
+ 'jest/no-commented-out-tests': 'warn',
30
+ 'jest/no-conditional-expect': 'error',
31
+ 'jest/no-deprecated-functions': 'error',
32
+ 'jest/no-disabled-tests': 'warn',
33
+ 'jest/no-done-callback': 'error',
34
+ 'jest/no-duplicate-hooks': 'off',
35
+ 'jest/no-export': 'error',
36
+ 'jest/no-focused-tests': 'error',
37
+ 'jest/no-hooks': 'off',
38
+ 'jest/no-identical-title': 'error',
39
+ 'jest/no-if': 'error',
40
+ 'jest/no-interpolation-in-snapshots': 'error',
41
+ 'jest/no-jasmine-globals': 'off',
42
+ 'jest/no-jest-import': 'error',
43
+ 'jest/no-large-snapshots': ['warn', { maxSize: 300 }],
44
+ 'jest/no-mocks-import': 'error',
45
+ 'jest/no-restricted-matchers': 'off',
46
+ 'jest/no-standalone-expect': 'off',
47
+ 'jest/no-test-prefixes': 'error',
48
+ 'jest/no-test-return-statement': 'off',
49
+ 'jest/prefer-called-with': 'error',
50
+ 'jest/prefer-expect-assertions': 'off',
51
+ 'jest/prefer-expect-resolves': 'off',
52
+ 'jest/prefer-hooks-on-top': 'error',
53
+ 'jest/prefer-lowercase-title': 'off',
54
+ 'jest/prefer-spy-on': 'off',
55
+ 'jest/prefer-strict-equal': 'off',
56
+ 'jest/prefer-to-be': 'off',
57
+ 'jest/prefer-to-contain': 'warn',
58
+ 'jest/prefer-to-have-length': 'warn',
59
+ 'jest/prefer-todo': 'warn',
60
+ 'jest/require-hook': 'off',
61
+ 'jest/require-to-throw-message': 'off',
62
+ 'jest/require-top-level-describe': 'off',
63
+ 'jest/unbound-method': 'off',
64
+ 'jest/valid-describe-callback': 'error',
65
+ 'jest/valid-expect': 'error',
66
+ 'jest/valid-expect-in-promise': 'error',
67
+ 'jest/valid-title': 'warn',
68
+
69
+ ...(hasJestDom
70
+ ? {
71
+ 'jest-dom/prefer-checked': 'error',
72
+ 'jest-dom/prefer-empty': 'error',
73
+ 'jest-dom/prefer-enabled-disabled': 'error',
74
+ 'jest-dom/prefer-focus': 'error',
75
+ 'jest-dom/prefer-in-document': 'error',
76
+ 'jest-dom/prefer-required': 'error',
77
+ 'jest-dom/prefer-to-have-attribute': 'error',
78
+ 'jest-dom/prefer-to-have-class': 'error',
79
+ 'jest-dom/prefer-to-have-style': 'error',
80
+ 'jest-dom/prefer-to-have-text-content': 'error',
81
+ 'jest-dom/prefer-to-have-value': 'error',
82
+ }
83
+ : null),
84
+
85
+ ...(hasTestingLibrary
86
+ ? {
87
+ 'testing-library/await-async-query': 'error',
88
+ 'testing-library/await-async-utils': 'error',
89
+ 'testing-library/await-fire-event': 'off',
90
+ 'testing-library/consistent-data-testid': 'off',
91
+ 'testing-library/no-await-sync-events': 'error',
92
+ 'testing-library/no-await-sync-query': 'error',
93
+ 'testing-library/no-container': 'error',
94
+ 'testing-library/no-debugging-utils': 'error',
95
+ 'testing-library/no-dom-import': ['error', 'react'],
96
+ 'testing-library/no-manual-cleanup': 'error',
97
+ 'testing-library/no-node-access': 'error',
98
+ 'testing-library/no-promise-in-fire-event': 'error',
99
+ 'testing-library/no-render-in-setup': 'error',
100
+ 'testing-library/no-unnecessary-act': 'error',
101
+ 'testing-library/no-wait-for-empty-callback': 'error',
102
+ 'testing-library/no-wait-for-multiple-assertions': 'error',
103
+ 'testing-library/no-wait-for-side-effects': 'error',
104
+ 'testing-library/no-wait-for-snapshot': 'error',
105
+ 'testing-library/prefer-explicit-assert': 'warn',
106
+ 'testing-library/prefer-find-by': 'error',
107
+ 'testing-library/prefer-presence-queries': 'error',
108
+ 'testing-library/prefer-query-by-disappearance': 'error',
109
+ 'testing-library/prefer-screen-queries': 'error',
110
+ 'testing-library/prefer-user-event': 'error',
111
+ 'testing-library/prefer-wait-for': 'error',
112
+ 'testing-library/render-result-naming-convention': 'error',
113
+ }
114
+ : null),
115
+ }
116
+
117
+ module.exports = {
118
+ env: {
119
+ 'jest/globals': true,
120
+ },
121
+ plugins: [
122
+ 'jest',
123
+ hasJestDom && 'jest-dom',
124
+ hasTestingLibrary && 'testing-library',
125
+ ].filter(Boolean),
126
+ rules: disableAll(rules),
127
+ overrides: [
128
+ {
129
+ files: [
130
+ '**/__tests__/**/*.+(js|ts)?(x)',
131
+ '**/*.{spec,test}.+(js|ts)?(x)',
132
+ ],
133
+ rules,
134
+ },
135
+ {
136
+ files: ['**/__tests__/**/*.ts?(x)', '**/*.{spec,test}.ts?(x)'],
137
+ rules: {
138
+ '@typescript-eslint/unbound-method': 'off',
139
+ 'jest/unbound-method': 'error',
140
+ },
141
+ },
142
+ ],
143
+ }
144
+
145
+ function disableAll(obj) {
146
+ return Object.keys(obj).reduce((rules, rule) => {
147
+ rules[rule] = 'off'
148
+ return rules
149
+ }, {})
150
+ }
package/jsx-a11y.js ADDED
@@ -0,0 +1,48 @@
1
+ module.exports = {
2
+ env: {
3
+ browser: true,
4
+ },
5
+ parserOptions: {
6
+ ecmaFeatures: {
7
+ jsx: true,
8
+ },
9
+ },
10
+ plugins: ['jsx-a11y'],
11
+ rules: {
12
+ 'jsx-a11y/accessible-emoji': 'error',
13
+ 'jsx-a11y/alt-text': 'warn',
14
+ 'jsx-a11y/anchor-has-content': 'error',
15
+ 'jsx-a11y/anchor-is-valid': 'error',
16
+ 'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
17
+ 'jsx-a11y/aria-props': 'error',
18
+ 'jsx-a11y/aria-proptypes': 'error',
19
+ 'jsx-a11y/aria-role': 'error',
20
+ 'jsx-a11y/aria-unsupported-elements': 'error',
21
+ 'jsx-a11y/autocomplete-valid': 'error',
22
+ 'jsx-a11y/click-events-have-key-events': 'error',
23
+ 'jsx-a11y/control-has-associated-label': 'off',
24
+ 'jsx-a11y/heading-has-content': 'error',
25
+ 'jsx-a11y/html-has-lang': 'error',
26
+ 'jsx-a11y/iframe-has-title': 'error',
27
+ 'jsx-a11y/img-redundant-alt': 'error',
28
+ 'jsx-a11y/interactive-supports-focus': 'warn',
29
+ 'jsx-a11y/label-has-associated-control': 'error',
30
+ 'jsx-a11y/lang': 'error',
31
+ 'jsx-a11y/media-has-caption': 'warn',
32
+ 'jsx-a11y/mouse-events-have-key-events': 'error',
33
+ 'jsx-a11y/no-access-key': 'error',
34
+ 'jsx-a11y/no-autofocus': 'off',
35
+ 'jsx-a11y/no-distracting-elements': 'error',
36
+ 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'warn',
37
+ 'jsx-a11y/no-noninteractive-element-interactions': 'warn',
38
+ 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'warn',
39
+ 'jsx-a11y/no-noninteractive-tabindex': 'off',
40
+ 'jsx-a11y/no-onchange': 'off',
41
+ 'jsx-a11y/no-redundant-roles': 'error',
42
+ 'jsx-a11y/no-static-element-interactions': 'off',
43
+ 'jsx-a11y/role-has-required-aria-props': 'error',
44
+ 'jsx-a11y/role-supports-aria-props': 'error',
45
+ 'jsx-a11y/scope': 'error',
46
+ 'jsx-a11y/tabindex-no-positive': 'warn',
47
+ },
48
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@mrpalmer/eslint-config",
3
+ "version": "1.0.0",
4
+ "description": "Mike Palmer's personal ESLint rules",
5
+ "license": "MIT",
6
+ "main": "index.js",
7
+ "scripts": {
8
+ "find-new-rules": "run-p find-new-rules:*",
9
+ "find-new-rules:jest": "eslint-find-rules --unused ./test/jest.js",
10
+ "find-new-rules:jsx-a11y": "eslint-find-rules --unused ./test/jsx-a11y.js",
11
+ "find-new-rules:main": "eslint-find-rules --unused ./index.js",
12
+ "find-new-rules:react": "eslint-find-rules --unused ./test/react.js",
13
+ "lint": "eslint --config index.js .",
14
+ "validate": "run-p lint find-new-rules"
15
+ },
16
+ "dependencies": {
17
+ "@typescript-eslint/eslint-plugin": "^5.9.0",
18
+ "@typescript-eslint/parser": "^5.9.0",
19
+ "eslint-config-prettier": "^8.3.0",
20
+ "eslint-plugin-import": "^2.25.4",
21
+ "eslint-plugin-jest": "^25.3.4",
22
+ "eslint-plugin-jest-dom": "^4.0.1",
23
+ "eslint-plugin-jsx-a11y": "^6.5.1",
24
+ "eslint-plugin-react": "^7.28.0",
25
+ "eslint-plugin-react-hooks": "^4.3.0",
26
+ "eslint-plugin-testing-library": "^5.0.1",
27
+ "read-pkg-up": "^7.0.1",
28
+ "semver": "^7.3.2"
29
+ },
30
+ "peerDependencies": {
31
+ "eslint": "^8.0.0",
32
+ "typescript": "^4.0.0"
33
+ },
34
+ "peerDependenciesMeta": {
35
+ "typescript": {
36
+ "optional": true
37
+ }
38
+ },
39
+ "engines": {
40
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0",
41
+ "npm": ">=6",
42
+ "yarn": ">=1"
43
+ }
44
+ }
package/react.js ADDED
@@ -0,0 +1,139 @@
1
+ const readPkgUp = require('read-pkg-up')
2
+ const semver = require('semver')
3
+
4
+ let oldestSupportedReactVersion = '16.5.2'
5
+
6
+ let hasPropTypes = false
7
+ try {
8
+ const pkg = readPkgUp.sync({ normalize: true })
9
+ // eslint-disable-next-line prefer-object-spread
10
+ const allDeps = Object.assign(
11
+ { react: '16.5.2' },
12
+ pkg.peerDependencies,
13
+ pkg.devDependencies,
14
+ pkg.dependencies
15
+ )
16
+ hasPropTypes = allDeps.hasOwnProp('prop-types')
17
+ oldestSupportedReactVersion = semver
18
+ .validRange(allDeps.react)
19
+ .replace(/[>=<|]/g, ' ')
20
+ .split(' ')
21
+ .filter(Boolean)
22
+ .sort(semver.compare)[0]
23
+ } catch (error) {
24
+ // ignore error
25
+ }
26
+
27
+ module.exports = {
28
+ env: {
29
+ browser: true,
30
+ },
31
+ parserOptions: {
32
+ ecmaFeatures: {
33
+ jsx: true,
34
+ },
35
+ },
36
+ plugins: ['react', 'react-hooks'],
37
+ extends: ['plugin:react-hooks/recommended'],
38
+ settings: {
39
+ react: {
40
+ version: oldestSupportedReactVersion,
41
+ },
42
+ },
43
+ rules: {
44
+ 'react/boolean-prop-naming': 'off',
45
+ 'react/button-has-type': 'off',
46
+ 'react/default-props-match-prop-types': hasPropTypes ? 'error' : 'off',
47
+ 'react/destructuring-assignment': 'off',
48
+ 'react/display-name': ['error', { ignoreTranspilerName: false }],
49
+ 'react/forbid-component-props': 'off',
50
+ 'react/forbid-dom-props': 'off',
51
+ 'react/forbid-elements': 'off',
52
+ 'react/forbid-foreign-prop-types': hasPropTypes ? 'error' : 'off',
53
+ 'react/forbid-prop-types': 'off',
54
+ 'react/function-component-definition': 'off',
55
+ 'react/jsx-boolean-value': 'off',
56
+ 'react/jsx-curly-brace-presence': [
57
+ 'warn',
58
+ { children: 'ignore', props: 'never' },
59
+ ],
60
+ 'react/jsx-filename-extension': ['error', { extensions: ['.js'] }],
61
+ 'react/jsx-fragments': 'off',
62
+ 'react/jsx-handler-names': 'off',
63
+ 'react/jsx-key': 'error',
64
+ 'react/jsx-max-depth': 'off',
65
+ 'react/jsx-no-bind': 'off',
66
+ 'react/jsx-no-comment-textnodes': 'error',
67
+ 'react/jsx-no-constructed-context-values': 'off',
68
+ 'react/jsx-no-duplicate-props': 'error',
69
+ 'react/jsx-no-literals': 'off',
70
+ 'react/jsx-no-script-url': 'error',
71
+ 'react/jsx-no-target-blank': 'error',
72
+ 'react/jsx-no-undef': 'error',
73
+ 'react/jsx-no-useless-fragment': 'warn',
74
+ 'react/jsx-pascal-case': 'error',
75
+ 'react/jsx-props-no-spreading': 'off',
76
+ 'react/jsx-sort-default-props': 'off',
77
+ 'react/jsx-sort-props': 'off',
78
+ 'react/jsx-uses-react': 'error',
79
+ 'react/jsx-uses-vars': 'error',
80
+ 'react/no-access-state-in-setstate': 'error',
81
+ 'react/no-adjacent-inline-elements': 'off',
82
+ 'react/no-array-index-key': 'off', // sometimes you don't care about the issues, or they don't apply
83
+ 'react/no-arrow-function-lifecycle': 'error',
84
+ 'react/no-children-prop': 'off',
85
+ 'react/no-danger': 'off',
86
+ 'react/no-danger-with-children': 'error',
87
+ 'react/no-deprecated': 'error',
88
+ 'react/no-did-mount-set-state': 'error',
89
+ 'react/no-did-update-set-state': 'error',
90
+ 'react/no-direct-mutation-state': 'error',
91
+ 'react/no-find-dom-node': 'error',
92
+ 'react/no-invalid-html-attribute': 'error',
93
+ 'react/no-is-mounted': 'error',
94
+ 'react/no-multi-comp': 'off',
95
+ 'react/no-namespace': 'error',
96
+ 'react/no-redundant-should-component-update': 'error',
97
+ 'react/no-render-return-value': 'error',
98
+ 'react/no-set-state': 'off',
99
+ 'react/no-string-refs': 'error',
100
+ 'react/no-this-in-sfc': 'error',
101
+ 'react/no-typos': 'error',
102
+ 'react/no-unescaped-entities': 'warn',
103
+ 'react/no-unknown-property': 'error',
104
+ 'react/no-unsafe': 'warn', // if you need it there should be a comment explaining why
105
+ 'react/no-unstable-nested-components': ['error', { allowAsProps: true }],
106
+ 'react/no-unused-class-component-methods': 'error',
107
+ 'react/no-unused-prop-types': hasPropTypes ? 'error' : 'off',
108
+ 'react/no-unused-state': 'error',
109
+ 'react/no-will-update-set-state': 'error',
110
+ 'react/prefer-es6-class': 'off',
111
+ 'react/prefer-exact-props': 'off',
112
+ 'react/prefer-read-only-props': 'off',
113
+ 'react/prefer-stateless-function': 'off',
114
+ 'react/prop-types': hasPropTypes ? 'error' : 'off',
115
+ 'react/react-in-jsx-scope': 'error',
116
+ 'react/require-default-props': 'off', // sometimes the default value is undefined so that's fine...
117
+ 'react/require-optimization': 'off',
118
+ 'react/require-render-return': 'error',
119
+ 'react/self-closing-comp': 'error',
120
+ 'react/sort-comp': 'off',
121
+ 'react/sort-prop-types': 'off',
122
+ 'react/state-in-constructor': 'off',
123
+ 'react/static-property-placement': 'off',
124
+ 'react/style-prop-object': 'error',
125
+ 'react/void-dom-elements-no-children': 'error',
126
+ },
127
+ overrides: [
128
+ {
129
+ files: ['**/*.ts?(x)'],
130
+ rules: {
131
+ 'react/jsx-filename-extension': [
132
+ 'error',
133
+ { extensions: ['.ts', '.tsx'] },
134
+ ],
135
+ 'react/prop-types': 'off',
136
+ },
137
+ },
138
+ ],
139
+ }