@wkovacs64/eslint-config 6.0.0 → 7.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2019 Justin R. Hall
3
+ Copyright (c) 2024 Justin R. Hall
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a
6
6
  copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -1,30 +1,64 @@
1
1
  # @wkovacs64/eslint-config
2
2
 
3
- This is my base personal ESLint configuration.
3
+ This is my personal [ESLint][eslint] configuration.
4
+
5
+ [![npm Version][npm-image]][npm-url] [![Build Status][ci-image]][ci-url]
6
+ [![changesets][changesets-image]][changesets-url]
4
7
 
5
8
  ### Install
6
9
 
7
10
  ```
8
- npx install-peerdeps --dev --yarn @wkovacs64/eslint-config
11
+ npm install --save-dev @wkovacs64/eslint-config
9
12
  ```
10
13
 
11
- > Drop the `--yarn` when using `npm`.
14
+ > Be sure to install the appropriately versioned `eslint` peer dependency as
15
+ > well.
12
16
 
13
17
  ### Usage
14
18
 
15
- Extend in your `.eslintrc.js`:
19
+ Follow the ESLint documentation on [shared configurations][eslint-sharing]. See
20
+ the documentation on [ignoring files][eslint-ignores] if you need to ignore
21
+ anything the config doesn't already ignore by default.
22
+
23
+ ### Examples
24
+
25
+ #### `eslint.config.js`
16
26
 
17
27
  ```js
18
- module.exports = {
19
- extends: ['@wkovacs64/eslint-config'],
20
- };
28
+ import baseConfig from '@wkovacs64/eslint-config';
29
+
30
+ /** @type {import("eslint").Linter.FlatConfig[]} */
31
+ const config = [
32
+ ...baseConfig,
33
+ // overrides here
34
+ ];
35
+
36
+ export default config;
21
37
  ```
22
38
 
23
- ### Other Configurations
39
+ #### `package.json`
40
+
41
+ ```
42
+ {
43
+ "scripts": {
44
+ ...
45
+ "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .",
46
+ ...
47
+ }
48
+ }
49
+ ```
24
50
 
25
- - React:
26
- [@wkovacs64/eslint-config-react](https://github.com/wKovacs64/eslint-config-react)
27
- - TypeScript:
28
- [@wkovacs64/eslint-config-ts](https://github.com/wKovacs64/eslint-config-ts)
29
- - TypeScript and React:
30
- [@wkovacs64/eslint-config-ts-react](https://github.com/wKovacs64/eslint-config-ts-react)
51
+ [npm-image]:
52
+ https://img.shields.io/npm/v/@wkovacs64/eslint-config.svg?style=flat-square
53
+ [npm-url]: https://www.npmjs.com/package/@wkovacs64/eslint-config
54
+ [ci-image]:
55
+ https://img.shields.io/github/actions/workflow/status/wKovacs64/eslint-config/ci.yml?logo=github&style=flat-square
56
+ [ci-url]: https://github.com/wKovacs64/eslint-config/actions?query=workflow%3Aci
57
+ [changesets-image]:
58
+ https://img.shields.io/badge/maintained%20with-changesets-blue?style=flat-square
59
+ [changesets-url]: https://github.com/changesets/changesets
60
+ [eslint]: https://eslint.org/
61
+ [eslint-sharing]:
62
+ https://eslint.org/docs/latest/use/configure/configuration-files#using-a-shareable-configuration-package
63
+ [eslint-ignores]:
64
+ https://eslint.org/docs/latest/use/configure/migration-guide#ignoring-files
package/index.js CHANGED
@@ -1,58 +1,365 @@
1
- module.exports = {
2
- extends: ['airbnb-base', 'prettier'],
3
- env: {
4
- jest: true,
1
+ // @ts-check
2
+ //
3
+ // Forked from https://github.com/epicweb-dev/config
4
+ //
5
+ import globals from 'globals';
6
+ import eslint from '@eslint/js';
7
+ import tseslint from 'typescript-eslint';
8
+ import playwright from 'eslint-plugin-playwright';
9
+
10
+ const ERROR = 'error';
11
+ const WARN = 'warn';
12
+ const OFF = 'off';
13
+
14
+ const has = (pkg) =>
15
+ import(pkg).then(
16
+ () => true,
17
+ () => false,
18
+ );
19
+
20
+ const hasTypeScript = await has('typescript');
21
+ const hasReact = await has('react');
22
+ const hasTestingLibrary = await has('@testing-library/dom');
23
+ const hasJestDom = await has('@testing-library/jest-dom');
24
+ const hasVitest = await has('vitest');
25
+ const hasPlaywright = await has('playwright');
26
+
27
+ const vitestFiles = ['**/__tests__/**/*', '**/*.test.*'];
28
+ const testFiles = ['**/tests/**', '**/#tests/**', ...vitestFiles];
29
+ const playwrightFiles = ['**/playwright/tests/**'];
30
+
31
+ export const config = [
32
+ {
33
+ ignores: [
34
+ '**/.cache/**',
35
+ '**/node_modules/**',
36
+ '**/build/**',
37
+ '**/public/build/**',
38
+ '**/playwright-report/**',
39
+ '**/playwright-results/**',
40
+ '**/playwright/report/**',
41
+ '**/playwright/results/**',
42
+ '**/server-build/**',
43
+ '**/dist/**',
44
+ '**/coverage/**',
45
+ ],
5
46
  },
6
- parser: 'babel-eslint',
7
- plugins: ['jest'],
8
- rules: {
9
- 'import/prefer-default-export': 'off',
10
- 'jest/consistent-test-it': 'error',
11
- 'jest/expect-expect': 'off',
12
- 'jest/lowercase-name': 'off',
13
- 'jest/no-alias-methods': 'error',
14
- 'jest/no-commented-out-tests': 'warn',
15
- 'jest/no-disabled-tests': 'error',
16
- 'jest/no-duplicate-hooks': 'error',
17
- 'jest/no-empty-title': 'error',
18
- 'jest/no-export': 'error',
19
- 'jest/no-focused-tests': 'error',
20
- 'jest/no-hooks': 'off',
21
- 'jest/no-identical-title': 'error',
22
- 'jest/no-if': 'error',
23
- 'jest/no-jasmine-globals': 'error',
24
- 'jest/no-jest-import': 'error',
25
- 'jest/no-large-snapshots': 'off',
26
- 'jest/no-mocks-import': 'error',
27
- 'jest/no-test-callback': 'error',
28
- 'jest/no-test-prefixes': 'error',
29
- 'jest/no-test-return-statement': 'off',
30
- 'jest/no-truthy-falsy': 'off',
31
- 'jest/prefer-called-with': 'warn',
32
- 'jest/prefer-expect-assertions': 'off',
33
- 'jest/prefer-spy-on': 'warn',
34
- 'jest/prefer-strict-equal': 'off',
35
- 'jest/prefer-to-be-null': 'warn',
36
- 'jest/prefer-to-be-undefined': 'warn',
37
- 'jest/prefer-to-contain': 'warn',
38
- 'jest/prefer-to-have-length': 'warn',
39
- 'jest/prefer-inline-snapshots': 'off',
40
- 'jest/require-tothrow-message': 'off',
41
- 'jest/valid-describe': 'error',
42
- 'jest/valid-expect-in-promise': 'error',
43
- 'jest/valid-expect': 'error',
44
- 'jest/prefer-todo': 'warn',
45
- 'no-nested-ternary': 'off',
46
- 'no-unused-expressions': ['error', { allowTaggedTemplates: true }],
47
- 'valid-jsdoc': [
48
- 'error',
49
- {
50
- prefer: {
51
- arg: 'param',
52
- argument: 'param',
53
- return: 'returns',
54
- },
47
+
48
+ // all files
49
+ eslint.configs.recommended,
50
+ {
51
+ plugins: {
52
+ import: (await import('eslint-plugin-import-x')).default,
53
+ },
54
+ languageOptions: {
55
+ globals: {
56
+ ...globals.browser,
57
+ ...globals.node,
55
58
  },
56
- ],
59
+ },
60
+ rules: {
61
+ 'import/no-duplicates': [WARN, { 'prefer-inline': true }],
62
+ 'import/order': [
63
+ WARN,
64
+ {
65
+ pathGroups: [
66
+ { pattern: '#*/**', group: 'internal' },
67
+ { pattern: '~/**', group: 'internal' },
68
+ ],
69
+ groups: [
70
+ 'builtin',
71
+ 'external',
72
+ 'internal',
73
+ 'parent',
74
+ 'sibling',
75
+ 'index',
76
+ ],
77
+ },
78
+ ],
79
+ },
80
+ },
81
+
82
+ // JSX/TSX files
83
+ hasReact
84
+ ? {
85
+ files: ['**/*.tsx', '**/*.jsx'].filter(Boolean),
86
+ plugins: {
87
+ react: (await import('eslint-plugin-react')).default,
88
+ 'jsx-a11y': (await import('eslint-plugin-jsx-a11y')).default,
89
+ },
90
+ languageOptions: {
91
+ parserOptions: {
92
+ ecmaFeatures: {
93
+ jsx: true,
94
+ },
95
+ },
96
+ },
97
+ rules: {
98
+ 'react/function-component-definition': [
99
+ 'error',
100
+ {
101
+ namedComponents: 'function-declaration',
102
+ unnamedComponents: 'arrow-function',
103
+ },
104
+ ],
105
+ 'react/jsx-key': WARN,
106
+ 'jsx-a11y/label-has-associated-control': [
107
+ ERROR,
108
+ {
109
+ assert: 'either',
110
+ },
111
+ ],
112
+ },
113
+ settings: {
114
+ react: {
115
+ version: 'detect',
116
+ },
117
+ },
118
+ }
119
+ : null,
120
+
121
+ // react-hook rules are applicable in ts/js/tsx/jsx, but only with React as a
122
+ // dep
123
+ hasReact
124
+ ? {
125
+ files: ['**/*.ts?(x)', '**/*.js?(x)'],
126
+ plugins: {
127
+ 'react-hooks': (await import('eslint-plugin-react-hooks')).default,
128
+ },
129
+ rules: {
130
+ 'react-hooks/rules-of-hooks': ERROR,
131
+ 'react-hooks/exhaustive-deps': ERROR,
132
+ },
133
+ }
134
+ : null,
135
+
136
+ // JS and JSX files
137
+ {
138
+ files: ['**/*.js?(x)'],
139
+ rules: {
140
+ // most of these rules are useful for JS but not TS because TS handles these better
141
+ // if it weren't for https://github.com/import-js/eslint-plugin-import/issues/2132
142
+ // we could enable this :(
143
+ // 'import/no-unresolved': ERROR,
144
+ 'no-unused-vars': [
145
+ WARN,
146
+ {
147
+ args: 'after-used',
148
+ argsIgnorePattern: '^_',
149
+ ignoreRestSiblings: true,
150
+ varsIgnorePattern: '^ignored',
151
+ },
152
+ ],
153
+ },
57
154
  },
58
- };
155
+
156
+ // TS and TSX files
157
+ ...(hasTypeScript
158
+ ? [
159
+ // TODO: figure out how to switch to type-checked configs
160
+ ...tseslint.configs.recommended,
161
+ ...tseslint.configs.stylistic,
162
+ ]
163
+ : []),
164
+ hasTypeScript
165
+ ? {
166
+ files: ['**/*.ts?(x)'],
167
+ languageOptions: {
168
+ parserOptions: {
169
+ parser: tseslint.parser,
170
+ projectService: true,
171
+ },
172
+ },
173
+ plugins: {
174
+ '@typescript-eslint': tseslint.plugin,
175
+ },
176
+ rules: {
177
+ '@typescript-eslint/ban-ts-comment': OFF,
178
+ '@typescript-eslint/consistent-type-assertions': [
179
+ ERROR,
180
+ {
181
+ assertionStyle: 'as',
182
+ objectLiteralTypeAssertions: 'allow-as-parameter',
183
+ },
184
+ ],
185
+ '@typescript-eslint/consistent-type-definitions': OFF,
186
+ 'import/consistent-type-specifier-style': [WARN, 'prefer-inline'],
187
+ '@typescript-eslint/consistent-type-imports': [
188
+ ERROR,
189
+ {
190
+ prefer: 'type-imports',
191
+ disallowTypeAnnotations: true,
192
+ fixStyle: 'inline-type-imports',
193
+ },
194
+ ],
195
+ '@typescript-eslint/explicit-module-boundary-types': OFF,
196
+ '@typescript-eslint/naming-convention': [
197
+ ERROR,
198
+ {
199
+ selector: 'typeLike',
200
+ format: ['PascalCase'],
201
+ custom: { regex: '^I[A-Z]', match: false },
202
+ },
203
+ ],
204
+ '@typescript-eslint/no-confusing-void-expression': [
205
+ ERROR,
206
+ {
207
+ ignoreArrowShorthand: true,
208
+ },
209
+ ],
210
+ '@typescript-eslint/no-explicit-any': OFF,
211
+ '@typescript-eslint/no-floating-promises': [
212
+ ERROR,
213
+ {
214
+ ignoreIIFE: true,
215
+ },
216
+ ],
217
+ '@typescript-eslint/no-import-type-side-effects': ERROR,
218
+ 'no-invalid-this': OFF,
219
+ '@typescript-eslint/no-invalid-this': ERROR,
220
+ 'no-redeclare': OFF,
221
+ '@typescript-eslint/no-non-null-assertion': OFF,
222
+ '@typescript-eslint/no-redeclare': ERROR,
223
+ 'no-shadow': OFF,
224
+ '@typescript-eslint/no-shadow': ERROR,
225
+ '@typescript-eslint/no-unnecessary-type-constraint': 'warn',
226
+ '@typescript-eslint/no-unused-vars': [
227
+ ERROR,
228
+ {
229
+ vars: 'all',
230
+ args: 'after-used',
231
+ argsIgnorePattern: '^_',
232
+ ignoreRestSiblings: true,
233
+ },
234
+ ],
235
+ 'no-use-before-define': OFF,
236
+ '@typescript-eslint/no-use-before-define': [
237
+ ERROR,
238
+ {
239
+ functions: false,
240
+ classes: true,
241
+ variables: true,
242
+ },
243
+ ],
244
+ '@typescript-eslint/prefer-nullish-coalescing': OFF,
245
+ '@typescript-eslint/restrict-template-expressions': [
246
+ ERROR,
247
+ {
248
+ allowBoolean: true,
249
+ allowNullish: true,
250
+ },
251
+ ],
252
+ '@typescript-eslint/require-await': OFF,
253
+ '@typescript-eslint/unified-signatures': 'warn',
254
+ },
255
+ }
256
+ : null,
257
+
258
+ // This assumes test files are those which are in the test directory or have
259
+ // *.test.* in the filename. If a file doesn't match this assumption, then it
260
+ // will not be allowed to import test files.
261
+ {
262
+ files: ['**/*.ts?(x)', '**/*.js?(x)'],
263
+ ignores: testFiles,
264
+ rules: {
265
+ 'no-restricted-imports': [
266
+ ERROR,
267
+ {
268
+ patterns: [
269
+ {
270
+ group: testFiles,
271
+ message: 'Do not import test files in source files',
272
+ },
273
+ ],
274
+ },
275
+ ],
276
+ },
277
+ },
278
+
279
+ hasTestingLibrary
280
+ ? {
281
+ files: testFiles,
282
+ ignores: [...playwrightFiles],
283
+ plugins: {
284
+ 'testing-library': (await import('eslint-plugin-testing-library'))
285
+ .default,
286
+ },
287
+ rules: {
288
+ // 'testing-library/await-async-events': ERROR,
289
+ // 'testing-library/await-async-queries': ERROR,
290
+ // 'testing-library/await-async-utils': ERROR,
291
+ // 'testing-library/consistent-data-testid': OFF,
292
+ // 'testing-library/no-await-sync-events': ERROR,
293
+ // 'testing-library/no-await-sync-queries': ERROR,
294
+ // 'testing-library/no-container': ERROR,
295
+ // 'testing-library/no-debugging-utils': OFF,
296
+ // 'testing-library/no-dom-import': [ERROR, 'react'],
297
+ // 'testing-library/no-global-regexp-flag-in-query': ERROR,
298
+ // 'testing-library/no-manual-cleanup': ERROR,
299
+ // 'testing-library/no-node-access': ERROR,
300
+ // 'testing-library/no-promise-in-fire-event': ERROR,
301
+ // 'testing-library/no-render-in-lifecycle': ERROR,
302
+ 'testing-library/no-unnecessary-act': ERROR,
303
+ // 'testing-library/no-wait-for-multiple-assertions': ERROR,
304
+ 'testing-library/no-wait-for-side-effects': ERROR,
305
+ // 'testing-library/no-wait-for-snapshot': ERROR,
306
+ // 'testing-library/prefer-explicit-assert': ERROR,
307
+ 'testing-library/prefer-find-by': ERROR,
308
+ // 'testing-library/prefer-presence-queries': ERROR,
309
+ // 'testing-library/prefer-query-by-disappearance': ERROR,
310
+ // 'testing-library/prefer-query-matchers': OFF,
311
+ // 'testing-library/prefer-screen-queries': ERROR,
312
+ // 'testing-library/prefer-user-event': ERROR,
313
+ // 'testing-library/render-result-naming-convention': ERROR,
314
+ },
315
+ }
316
+ : null,
317
+
318
+ hasJestDom
319
+ ? {
320
+ files: testFiles,
321
+ ignores: [...playwrightFiles],
322
+ plugins: {
323
+ 'jest-dom': (await import('eslint-plugin-jest-dom')).default,
324
+ },
325
+ rules: {
326
+ 'jest-dom/prefer-checked': ERROR,
327
+ 'jest-dom/prefer-empty': ERROR,
328
+ 'jest-dom/prefer-enabled-disabled': ERROR,
329
+ 'jest-dom/prefer-focus': ERROR,
330
+ 'jest-dom/prefer-in-document': ERROR,
331
+ 'jest-dom/prefer-required': ERROR,
332
+ 'jest-dom/prefer-to-have-attribute': ERROR,
333
+ 'jest-dom/prefer-to-have-class': ERROR,
334
+ 'jest-dom/prefer-to-have-style': ERROR,
335
+ 'jest-dom/prefer-to-have-text-content': ERROR,
336
+ 'jest-dom/prefer-to-have-value': ERROR,
337
+ },
338
+ }
339
+ : null,
340
+
341
+ hasVitest
342
+ ? {
343
+ files: testFiles,
344
+ ignores: [...playwrightFiles],
345
+ plugins: {
346
+ vitest: (await import('eslint-plugin-vitest')).default,
347
+ },
348
+ rules: {
349
+ // you don't want the editor to autofix this, but we do want to be
350
+ // made aware of it
351
+ 'vitest/no-focused-tests': [WARN, { fixable: false }],
352
+ },
353
+ }
354
+ : null,
355
+
356
+ hasPlaywright
357
+ ? {
358
+ files: playwrightFiles,
359
+ ...playwright.configs['flat/recommended'],
360
+ }
361
+ : null,
362
+ ].filter(Boolean);
363
+
364
+ // this is for backward compatibility
365
+ export default config;
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@wkovacs64/eslint-config",
3
- "version": "6.0.0",
4
- "description": "@wKovacs64 ESLint config (base)",
3
+ "version": "7.0.1",
4
+ "description": "@wKovacs64 ESLint config",
5
5
  "keywords": [
6
6
  "eslint",
7
7
  "eslintconfig",
8
+ "eslintplugin",
9
+ "eslint-config",
10
+ "eslint-plugin",
8
11
  "wkovacs64"
9
12
  ],
10
13
  "author": {
@@ -12,10 +15,15 @@
12
15
  "email": "justin.r.hall@gmail.com"
13
16
  },
14
17
  "license": "MIT",
18
+ "type": "module",
15
19
  "main": "index.js",
20
+ "exports": {
21
+ ".": "./index.js"
22
+ },
16
23
  "files": [
17
24
  "index.js"
18
25
  ],
26
+ "homepage": "https://github.com/wKovacs64/eslint-config#readme",
19
27
  "repository": {
20
28
  "type": "git",
21
29
  "url": "https://github.com/wKovacs64/eslint-config.git"
@@ -24,41 +32,52 @@
24
32
  "url": "https://github.com/wKovacs64/eslint-config/issues"
25
33
  },
26
34
  "scripts": {
27
- "cm": "git-cz",
28
- "format": "prettier --write \"**/*.{js,ts,tsx,json,md,yml}\"",
35
+ "changeset": "changeset",
36
+ "changeset:version": "changeset version && npm install --package-lock-only",
37
+ "changeset:publish": "changeset publish",
38
+ "format": "prettier --cache --write .",
39
+ "format:check": "prettier --cache --check .",
29
40
  "lint": "eslint .",
30
- "validate": "yarn lint"
31
- },
32
- "config": {
33
- "commitizen": {
34
- "path": "./node_modules/cz-conventional-changelog"
35
- }
41
+ "typecheck": "tsc"
36
42
  },
37
43
  "private": false,
44
+ "prettier": "@wkovacs64/prettier-config",
38
45
  "publishConfig": {
39
46
  "access": "public"
40
47
  },
48
+ "engines": {
49
+ "node": "^18.18.0 || >=20.0.0"
50
+ },
51
+ "peerDependencies": {
52
+ "eslint": "^9.4.0"
53
+ },
41
54
  "dependencies": {
42
- "babel-eslint": "^10.0.1",
43
- "eslint-config-airbnb-base": "^13.1.0",
44
- "eslint-config-prettier": "^6.0.0"
55
+ "@eslint/js": "^9.4.0",
56
+ "eslint-plugin-import-x": "^0.5.1",
57
+ "eslint-plugin-jest-dom": "^5.4.0",
58
+ "eslint-plugin-jsx-a11y": "^6.8.0",
59
+ "eslint-plugin-playwright": "^1.6.2",
60
+ "eslint-plugin-react": "^7.34.2",
61
+ "eslint-plugin-react-hooks": "^4.6.2",
62
+ "eslint-plugin-testing-library": "^6.2.2",
63
+ "eslint-plugin-vitest": "^0.5.4",
64
+ "globals": "^15.3.0",
65
+ "typescript-eslint": "^8.0.0-alpha.20"
45
66
  },
46
67
  "devDependencies": {
47
- "@commitlint/cli": "8.1.0",
48
- "@commitlint/config-conventional": "8.1.0",
49
- "commitizen": "4.0.3",
50
- "cz-conventional-changelog": "3.0.1",
51
- "eslint": "5.16.0",
52
- "eslint-plugin-import": "2.18.2",
53
- "eslint-plugin-jest": "22.12.0",
54
- "husky": "3.0.1",
55
- "lint-staged": "9.2.0",
56
- "prettier": "1.18.2",
57
- "semantic-release": "15.13.18"
68
+ "@changesets/changelog-github": "0.5.0",
69
+ "@changesets/cli": "2.27.5",
70
+ "@types/eslint__js": "8.42.3",
71
+ "@types/node": "20.13.0",
72
+ "@types/react": "18.3.3",
73
+ "@types/react-dom": "18.3.0",
74
+ "@wkovacs64/prettier-config": "4.0.0",
75
+ "eslint": "9.4.0",
76
+ "prettier": "3.3.0",
77
+ "typescript": "5.4.5"
58
78
  },
59
- "peerDependencies": {
60
- "eslint": "^5.13.0",
61
- "eslint-plugin-import": "^2.16.0",
62
- "eslint-plugin-jest": "^22.2.2"
79
+ "// TODO": "remove eslint override once our dependencies include v9 in their peerDependencies",
80
+ "overrides": {
81
+ "eslint": "9.4.0"
63
82
  }
64
83
  }
package/CHANGELOG.md DELETED
@@ -1,5 +0,0 @@
1
- # Change Log
2
-
3
- The changelog is automatically updated using
4
- [semantic-release](https://github.com/semantic-release/semantic-release). You
5
- can see it on the [releases page](../../releases).