@codfish/eslint-config 0.0.0-PR-131--3be23f9 → 0.0.0-PR-132--f93ca2a

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
@@ -18,6 +18,8 @@
18
18
  - [Installation](#installation)
19
19
  - [Usage](#usage)
20
20
  - [Opinionated Highlights](#opinionated-highlights)
21
+ - [IDE Setup](#ide-setup)
22
+ - [VS Code / Cursor](#vs-code--cursor)
21
23
  - [Prettier Configuration](#prettier-configuration)
22
24
  - [Use in combination with prettier-plugin-tailwindcss](#use-in-combination-with-prettier-plugin-tailwindcss)
23
25
  - [Example scripts](#example-scripts)
@@ -36,7 +38,7 @@
36
38
  - **Next.js support**: Automatically configures Next.js official plugin linting rules when detected
37
39
  - **Test framework agnostic**: Supports Jest and Vitest with automatic detection
38
40
  - **Testing Library integration**: Automatically includes Testing Library rules for test files
39
- - **YAML/YML support**: Built-in linting for YAML configuration files
41
+ - **Multi-format support**: Built-in linting and formatting for Markdown, HTML, JSON, YAML/YML files
40
42
  - **Prettier integration**: Built-in Prettier configuration with conflict resolution via eslint-config-prettier
41
43
  - **ESM architecture**: Built with ECMAScript modules and full TypeScript typing
42
44
  - **Docker support**: Optional configuration for dockerized applications
@@ -58,6 +60,7 @@ npm uninstall typescript-eslint \
58
60
  eslint-plugin-prettier \
59
61
  eslint-plugin-react \
60
62
  eslint-plugin-react-hooks \
63
+ @tanstack/eslint-plugin-query \
61
64
  eslint-plugin-simple-import-sort \
62
65
  eslint-plugin-testing-library \
63
66
  eslint-plugin-yml \
@@ -65,14 +68,15 @@ npm uninstall typescript-eslint \
65
68
  eslint-plugin-next \
66
69
  commitlint \
67
70
  @commitlint/cli \
68
- @commitlint/config-conventional
71
+ @commitlint/config-conventional \
72
+ prettier # optional, see note
69
73
  ```
70
74
 
71
75
  > [!NOTE]
72
76
  >
73
- > Optionally, you can uninstall prettier as well. If you don't have prettier installed already but you want to format
74
- > other file types (like Markdown, JSON, CSS, etc.), you can install it as a dev dependency: `npm i -D prettier`. Then
75
- > you can use Prettier to format your non-JS files directly. Eslint will still run Prettier as an ESLint rule.
77
+ > ESLint now handles linting and formatting for JavaScript, TypeScript, Markdown, HTML, JSON, and YAML files
78
+ > automatically. If you want to format additional file types (like CSS, SCSS, etc.), you can leave Prettier installed as
79
+ > a dev dependency in your project.
76
80
 
77
81
  ## Usage
78
82
 
@@ -279,10 +283,51 @@ This configuration includes some opinionated settings that you might want to cus
279
283
  See the [configuration examples below](#usage) for instructions on overriding these settings to match your team's
280
284
  preferences.
281
285
 
286
+ ## IDE Setup
287
+
288
+ ### VS Code / Cursor
289
+
290
+ For the best development experience with VS Code or Cursor (or any VS Code-based IDE), install the
291
+ [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) and configure it to
292
+ auto-fix on save:
293
+
294
+ Add these settings to your `.vscode/settings.json` or user settings:
295
+
296
+ ```json
297
+ {
298
+ "editor.codeActionsOnSave": {
299
+ "source.fixAll": "explicit"
300
+ },
301
+ "eslint.validate": [
302
+ "javascript",
303
+ "javascriptreact",
304
+ "typescript",
305
+ "typescriptreact",
306
+ "vue",
307
+ "markdown",
308
+ "json",
309
+ "jsonc",
310
+ "github-actions-workflow",
311
+ "html",
312
+ "mdx",
313
+ "css",
314
+ "yml",
315
+ "yaml"
316
+ ]
317
+ }
318
+ ```
319
+
320
+ This configuration enables:
321
+
322
+ - Automatic linting and formatting on save for all supported file types
323
+ - ESLint validation for Markdown, JSON, YAML, and HTML files
324
+ - A unified development experience across your entire codebase
325
+
282
326
  ## Prettier Configuration
283
327
 
284
- **Prettier is included and runs automatically** through ESLint for JavaScript, TypeScript, JSX, and TSX files using the
285
- [built-in configuration](./prettier.js). **You don't need to install or configure Prettier separately** for basic usage.
328
+ **Prettier is included and runs automatically** through ESLint for JavaScript, TypeScript, JSX, TSX, Markdown, HTML,
329
+ JSON, and YAML files using the [built-in configuration](./prettier.js). **You don't need to install or configure
330
+ Prettier separately** for these file types.
286
331
 
287
332
  You can then override the default config by creating your own Prettier config file, or extend the built-in config:
288
333
 
@@ -359,7 +404,7 @@ export default {
359
404
 
360
405
  Optionally, you can add these scripts to your `package.json` for common linting workflows:
361
406
 
362
- **Basic scripts (no separate Prettier installation needed):**
407
+ **Recommended scripts:**
363
408
 
364
409
  ```json
365
410
  {
@@ -368,24 +413,32 @@ Optionally, you can add these scripts to your `package.json` for common linting
368
413
  "fix": "eslint . --fix"
369
414
  },
370
415
  "lint-staged": {
371
- "*.{js,jsx,ts,tsx}": ["eslint --fix"]
416
+ "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"]
372
417
  }
373
418
  }
374
419
  ```
375
420
 
376
- **With Prettier installed separately (for formatting non-JS files):**
421
+ > [!NOTE]
422
+ >
423
+ > ESLint now handles linting and formatting for JavaScript, TypeScript, Markdown, HTML, JSON, and YAML files. You don't
424
+ > need to run Prettier separately for these file types.
425
+
426
+ **With Prettier for other file types (CSS, SCSS, etc.):**
427
+
428
+ If you want to format additional file types not covered by ESLint (like CSS, SCSS), you can install Prettier and add
429
+ these scripts:
377
430
 
378
431
  ```json
379
432
  {
380
433
  "scripts": {
381
434
  "lint": "eslint .",
382
435
  "fix": "eslint . --fix",
383
- "format": "prettier --config ./node_modules/@codfish/eslint-config/prettier.js --write \"**/*.{json,css,md}\"",
436
+ "format": "prettier --write \"**/*.{css,scss}\"",
384
437
  "check": "npm run lint && npm run format -- --check --no-write"
385
438
  },
386
439
  "lint-staged": {
387
- "*.{js,jsx,ts,tsx}": ["eslint --fix"],
388
- "*.{json,css,md}": ["prettier --write --config ./node_modules/@codfish/eslint-config/prettier.js"]
440
+ "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"],
441
+ "*.{css,scss}": ["prettier --write"]
389
442
  }
390
443
  }
391
444
  ```
@@ -1,3 +1,3 @@
1
1
  declare const _default: import("eslint/config").Config[];
2
2
  export default _default;
3
- //# sourceMappingURL=jest.d.ts.map
3
+ //# sourceMappingURL=tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../rules/tests.js"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"AA4BA,6EAKC;AAfgC,4CAA+D;AAA/D,+CAA+D;AAA/D,gDAA+D;AAMzF,8CAA8E;AAE9E,yDAAmE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../utils.js"],"names":[],"mappings":"AAyBA,6EAKC;AAfgC,4CAA+D;AAA/D,+CAA+D;AAA/D,gDAA+D;AAMzF,8CAA8E;AAE9E,yDAAmE"}
package/index.js CHANGED
@@ -1,6 +1,10 @@
1
1
  import js from '@eslint/js';
2
+ import json from '@eslint/json';
3
+ import markdown from '@eslint/markdown';
4
+ import html from '@html-eslint/eslint-plugin';
5
+ import htmlParser from '@html-eslint/parser';
2
6
  import { defineConfig } from 'eslint/config';
3
- import prettier from 'eslint-plugin-prettier/recommended';
7
+ import prettierPlugin from 'eslint-plugin-prettier';
4
8
  import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
5
9
  import ymlPlugin from 'eslint-plugin-yml';
6
10
  import globals from 'globals';
@@ -8,26 +12,30 @@ import tseslint from 'typescript-eslint';
8
12
 
9
13
  import prettierBuiltInConfig from './prettier.js';
10
14
  import configFilesConfig from './rules/config-files.js';
11
- import jestConfig from './rules/jest.js';
12
15
  import reactConfig from './rules/react.js';
13
- import vitestConfig from './rules/vitest.js';
16
+ import testConfig from './rules/tests.js';
14
17
  import { hasLocalConfig, ifAnyDep } from './utils.js';
15
18
 
16
19
  const useBuiltinPrettierConfig = !hasLocalConfig('prettier');
17
20
 
18
21
  /**
19
- * Modern ESLint configuration with dynamic feature detection
20
- * Supports TypeScript, React, Jest, Vitest, Prettier, YAML, and Next.js
22
+ * Modern ESLint configuration with dynamic feature detection.
23
+ * Supports TypeScript, React, Vitest, Prettier, YAML, and more.
24
+ *
25
+ * React ESLint configuration. Includes React, React Hooks, and JSX accessibility rules.
26
+ * - https://github.com/jsx-eslint/eslint-plugin-react
27
+ * - https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
28
+ * - https://github.com/jsx-eslint/eslint-plugin-jsx-a11y
21
29
  */
22
30
  export default defineConfig([
23
- js.configs.recommended,
24
-
25
- tseslint.configs.recommended,
26
-
27
31
  // Base opinionated rules
28
32
  {
29
33
  name: 'codfish/base',
30
34
 
35
+ files: ['**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}'],
36
+
37
+ extends: [js.configs.recommended, tseslint.configs.recommended],
38
+
31
39
  plugins: {
32
40
  'simple-import-sort': simpleImportSortPlugin,
33
41
  },
@@ -75,41 +83,10 @@ export default defineConfig([
75
83
  },
76
84
  ],
77
85
  'simple-import-sort/exports': 'error',
86
+ 'sort-imports': 'off',
78
87
 
79
- // 2. Encouraging `lodash-es` imports per file
80
- // lodash imports should use `lodash-es` package and should be imported per file.
81
- // E.G: `import get from 'lodash-es/get'`
82
- // More details in https://stackoverflow.com/questions/35250500/correct-way-to-import-lodash#answer-35251059
83
- 'no-restricted-imports': [
84
- 'error',
85
- {
86
- paths: [
87
- {
88
- name: 'lodash',
89
- message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
90
- },
91
- {
92
- name: 'lodash-es',
93
- message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
94
- },
95
- ],
96
- patterns: [
97
- {
98
- group: ['lodash/*'],
99
- message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
100
- },
101
- ],
102
- },
103
- ],
104
- },
105
- },
106
-
107
- {
108
- name: 'codfish/ts-overrides',
109
-
110
- files: ['**/*.ts', '**/*.tsx'],
111
-
112
- rules: {
88
+ // Allows destructuring of rest properties even if they are unused
89
+ '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
113
90
  '@typescript-eslint/no-empty-function': 'off',
114
91
  '@typescript-eslint/naming-convention': [
115
92
  'error',
@@ -131,6 +108,68 @@ export default defineConfig([
131
108
  'ts-nocheck': 'allow-with-description',
132
109
  },
133
110
  ],
111
+
112
+ '@typescript-eslint/no-restricted-types': [
113
+ 'error',
114
+ {
115
+ types: {
116
+ 'React.FC': {
117
+ message: 'Useless and has some drawbacks, see https://github.com/facebook/create-react-app/pull/8177',
118
+ },
119
+ 'React.FunctionComponent': {
120
+ message: 'Useless and has some drawbacks, see https://github.com/facebook/create-react-app/pull/8177',
121
+ },
122
+ },
123
+ },
124
+ ],
125
+
126
+ // 1. Encouraging `lodash-es` imports per file
127
+ // lodash imports should use `lodash-es` package and should be imported per file.
128
+ // E.G: `import get from "lodash-es/get"`
129
+ // More details in https://stackoverflow.com/questions/35250500/correct-way-to-import-lodash#answer-35251059
130
+ // 2. Prevent relative imports - use path aliases instead
131
+ // Use `@/` for src imports and `@tests/` for test imports
132
+ // 3. Prevent vitest globals imports - these are available globally
133
+ 'no-restricted-imports': [
134
+ 'error',
135
+ {
136
+ paths: [
137
+ {
138
+ name: 'lodash',
139
+ message: 'Please use lodash-es direct import E.G: `import get from "lodash-es/get"`',
140
+ },
141
+ {
142
+ name: 'lodash-es',
143
+ message: 'Please use lodash-es direct import E.G: `import get from "lodash-es/get"`',
144
+ },
145
+ {
146
+ name: 'jest',
147
+ importNames: ['describe', 'expect', 'test', 'it', 'jest'],
148
+ message:
149
+ 'These jest globals (describe, expect, test, it, jest) are available globally and should not be imported. Remove this import statement.',
150
+ },
151
+ {
152
+ name: 'vitest',
153
+ importNames: ['describe', 'expect', 'test', 'it', 'vi'],
154
+ message:
155
+ 'These vitest globals (describe, expect, test, it, vi) are available globally and should not be imported. Remove this import statement.',
156
+ },
157
+ ],
158
+ patterns: [
159
+ {
160
+ group: ['lodash/*'],
161
+ message: 'Please use lodash-es direct import E.G: `import get from "lodash-es/get"`',
162
+ },
163
+ {
164
+ // Prevent relative imports going back 1+ directories. Use @/ or @tests/ aliases instead.
165
+ // Ensures consistent and cleaner import paths, and avoids confusing import hell with deeply nested files.
166
+ group: ['../*', '../../*', '../../../*', '../../../../*'],
167
+ message:
168
+ 'Relative imports going back 1+ directories are not allowed. Use path aliases: @/ for src/*, @tests/ for tests/* imports. For example, instead of `import x from "../components/*"`, use `import x from "@/components/*"`',
169
+ },
170
+ ],
171
+ },
172
+ ],
134
173
  },
135
174
  },
136
175
 
@@ -184,12 +223,24 @@ export default defineConfig([
184
223
  'pnpm-lock.yaml',
185
224
  'pnpm-lock.*.yaml',
186
225
  '.history',
226
+ '.yarn',
227
+ 'yarn.lock',
228
+ '.yarnrc.yml',
229
+ 'pacts',
230
+ '.claude',
231
+ '.__mf__temp',
187
232
  ],
188
233
  },
189
234
 
190
235
  // Configuration files (eslint, prettier, etc.)
191
236
  configFilesConfig,
192
237
 
238
+ // React configuration (dynamic)
239
+ ifAnyDep('react', reactConfig, []),
240
+
241
+ // Jest OR Vitest configuration (dynamic)
242
+ ifAnyDep(['jest', 'vitest'], testConfig, []),
243
+
193
244
  // YML files
194
245
  ymlPlugin.configs['flat/standard'],
195
246
  ymlPlugin.configs['flat/prettier'], // handles conflicting rules with the yml plugin
@@ -204,24 +255,69 @@ export default defineConfig([
204
255
  },
205
256
  },
206
257
 
207
- // React configuration (dynamic)
208
- ifAnyDep('react', reactConfig, []),
258
+ // JSON files
259
+ {
260
+ name: 'codfish/json',
261
+ files: ['**/*.json'],
262
+ ignores: ['**/tsconfig*.json', '**/package-lock.json'],
263
+ plugins: { json },
264
+ language: 'json/json',
265
+ extends: ['json/recommended'],
266
+ },
209
267
 
210
- // Jest OR Vitest configuration (dynamic)
211
- ifAnyDep('jest', jestConfig, []),
268
+ // JSONC files (JSON with Comments) - for files that allow comments
269
+ {
270
+ name: 'codfish/jsonc',
271
+ files: ['**/*.jsonc'],
272
+ plugins: { json },
273
+ language: 'json/jsonc',
274
+ extends: ['json/recommended'],
275
+ },
212
276
 
213
- // Vitest configuration (dynamic)
214
- ifAnyDep('vitest', vitestConfig, []),
277
+ // JSON5 files - for TypeScript configs (supports comments AND trailing commas)
278
+ {
279
+ name: 'codfish/json5',
280
+ files: ['**/tsconfig*.json', '**/*.json5'],
281
+ plugins: { json },
282
+ language: 'json/json5',
283
+ extends: ['json/recommended'],
284
+ },
285
+
286
+ // Markdown files
287
+ {
288
+ name: 'codfish/markdown',
289
+ files: ['**/*.md'],
290
+ plugins: { markdown },
291
+ extends: ['markdown/recommended'],
292
+ rules: {
293
+ // Allow GitHub-style alerts and checkbox syntax (task lists)
294
+ 'markdown/no-missing-label-refs': [
295
+ 'error',
296
+ { allowLabels: ['!NOTE', '!WARNING', '!IMPORTANT', '!TIP', '', 'x', 'X'] },
297
+ ],
298
+ },
299
+ },
300
+
301
+ // HTML files
302
+ {
303
+ name: 'codfish/html',
304
+ files: ['**/*.html'],
305
+ plugins: { html },
306
+ languageOptions: { parser: htmlParser },
307
+ },
215
308
 
216
309
  // Prettier plugin is responsible for running prettier as an ESLint
217
310
  // rule and turning off ESLint rules that might conflict.
218
311
  // IMPORTANT: KEEP THIS LAST TO OVERRIDE ESLINT!
219
- prettier,
220
-
221
312
  {
313
+ files: ['**/*.{js,ts,jsx,tsx,md,yml,yaml,html,json,jsonc}'],
314
+
315
+ plugins: {
316
+ prettier: prettierPlugin,
317
+ },
318
+
222
319
  rules: {
223
- // Reset prettier rule passing in codfish's prettier config.
224
- // IMPORTANT: KEEP THIS LAST TO OVERRIDE PRETTIER PLUGIN!
320
+ // Reset prettier rule passing in custom prettier config.
225
321
  'prettier/prettier': useBuiltinPrettierConfig ? ['error', prettierBuiltInConfig] : 'error',
226
322
  },
227
323
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codfish/eslint-config",
3
- "version": "0.0.0-PR-131--3be23f9",
3
+ "version": "0.0.0-PR-132--f93ca2a",
4
4
  "description": "Modern ESLint configuration with TypeScript, React, and testing framework support.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -46,18 +46,23 @@
46
46
  "build": "tsc --emitDeclarationOnly",
47
47
  "dev": "tsc --watch",
48
48
  "type-check": "tsc --noEmit",
49
- "lint": "eslint .",
50
- "fix": "prettier --write \"**/*.{json,css,scss,md}\" --config ./prettier.js && npm run lint -- --fix",
49
+ "lint": "eslint",
50
+ "format": "eslint --fix",
51
51
  "test": "vitest",
52
- "test:watch": "vitest --watch",
53
52
  "test:coverage": "vitest --coverage",
54
53
  "prepublishOnly": "npm run build",
55
54
  "prepare": "husky"
56
55
  },
57
56
  "dependencies": {
58
- "@commitlint/cli": "^19.8.1",
59
- "@commitlint/config-conventional": "^19.8.1",
57
+ "@commitlint/cli": "^20.4.0",
58
+ "@commitlint/config-conventional": "^20.4.0",
60
59
  "@eslint/js": "^9.35.0",
60
+ "@eslint/json": "^1.0.0",
61
+ "@eslint/markdown": "^7.5.1",
62
+ "@html-eslint/eslint-plugin": "^0.54.2",
63
+ "@html-eslint/parser": "^0.54.0",
64
+ "@next/eslint-plugin-next": "^16.1.6",
65
+ "@tanstack/eslint-plugin-query": "^5.91.4",
61
66
  "@vitest/eslint-plugin": "^1.3.10",
62
67
  "cosmiconfig": "^9.0.0",
63
68
  "eslint-config-prettier": "^10.1.8",
@@ -65,15 +70,15 @@
65
70
  "eslint-plugin-jsx-a11y": "^6.10.2",
66
71
  "eslint-plugin-prettier": "^5.5.4",
67
72
  "eslint-plugin-react": "^7.37.5",
68
- "eslint-plugin-react-hooks": "^5.2.0",
73
+ "eslint-plugin-react-hooks": "^7.0.1",
74
+ "eslint-plugin-react-refresh": "^0.5.0",
69
75
  "eslint-plugin-simple-import-sort": "^12.1.1",
70
76
  "eslint-plugin-testing-library": "^7.1.0",
71
- "eslint-plugin-yml": "^1.16.0",
72
- "@next/eslint-plugin-next": "^15.1.6",
73
- "globals": "^16.4.0",
77
+ "eslint-plugin-yml": "^3.0.0",
78
+ "globals": "^17.3.0",
74
79
  "lodash.has": "^4.5.2",
75
- "prettier": "^3.6.2",
76
- "read-package-up": "^11.0.0",
80
+ "prettier": ">= 3",
81
+ "read-package-up": "^12.0.0",
77
82
  "typescript-eslint": "^8.44.0"
78
83
  },
79
84
  "devDependencies": {
@@ -82,7 +87,7 @@
82
87
  "husky": "^9.1.7",
83
88
  "lint-staged": "^16.1.6",
84
89
  "typescript": "^5.9.2",
85
- "vitest": "^1.0.0"
90
+ "vitest": "^4.0.18"
86
91
  },
87
92
  "engines": {
88
93
  "node": ">=20.0.0"
package/rules/react.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import nextPlugin from '@next/eslint-plugin-next';
2
+ import tanstackQuery from '@tanstack/eslint-plugin-query';
2
3
  import { defineConfig } from 'eslint/config';
3
4
  import jsxA11y from 'eslint-plugin-jsx-a11y';
4
5
  import react from 'eslint-plugin-react';
5
6
  import reactHooks from 'eslint-plugin-react-hooks';
7
+ import reactRefresh from 'eslint-plugin-react-refresh';
6
8
  import globals from 'globals';
7
9
 
8
10
  import { ifAnyDep } from '../utils.js';
@@ -14,41 +16,50 @@ import { ifAnyDep } from '../utils.js';
14
16
  * - https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
15
17
  * - https://github.com/jsx-eslint/eslint-plugin-jsx-a11y
16
18
  */
17
- export default defineConfig([
18
- {
19
- name: 'codfish/react',
19
+ export default defineConfig({
20
+ name: 'codfish/react',
20
21
 
21
- files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
22
+ files: ['**/*.jsx', '**/*.tsx', '**/*.mjsx', '**/*.mtsx'],
22
23
 
23
- ...react.configs.flat.recommended,
24
- ...jsxA11y.flatConfigs.recommended,
24
+ extends: [
25
+ react.configs.flat.recommended,
26
+ reactHooks.configs.flat['recommended-latest'],
27
+ reactRefresh.configs.recommended,
28
+ jsxA11y.flatConfigs.recommended,
29
+ ifAnyDep('@tanstack/react-query', tanstackQuery.configs['flat/recommended']),
30
+ ifAnyDep('next', nextPlugin.configs.recommended),
31
+ ifAnyDep('next', nextPlugin.configs['core-web-vitals']),
32
+ ].filter(Boolean),
25
33
 
26
- languageOptions: {
27
- globals: {
28
- ...globals.serviceworker,
29
- ...globals.browser,
30
- },
34
+ languageOptions: {
35
+ globals: {
36
+ ...globals.serviceworker,
37
+ ...globals.browser,
31
38
  },
32
39
  },
33
40
 
34
- // React Hooks configuration
35
- reactHooks.configs['recommended-latest'],
41
+ settings: {
42
+ react: {
43
+ version: 'detect',
44
+ },
45
+ },
36
46
 
37
- // Next.js configuration (dynamic)
38
- ifAnyDep(
39
- 'next',
40
- {
41
- ...(nextPlugin?.flatConfig?.recommended || {}),
42
- ...(nextPlugin?.flatConfig?.coreWebVitals || {}),
47
+ rules: {
48
+ // React 17+ uses automatic JSX runtime, no need to import React
49
+ 'react/react-in-jsx-scope': 'off',
50
+ 'react/jsx-uses-react': 'off',
43
51
 
44
- name: 'codfish/next',
45
- },
46
- {},
47
- ),
52
+ // TypeScript provides type checking, no need for PropTypes
53
+ 'react/prop-types': 'off',
48
54
 
49
- {
50
- rules: {
51
- 'react-hooks/exhaustive-deps': 'off',
52
- },
55
+ // this rule is too noisey and doesn"t account for certain valid use cases
56
+ 'react-hooks/exhaustive-deps': 'off',
57
+
58
+ // sometimes autoFocus is needed for better UX. @todo - revisit later?
59
+ 'jsx-a11y/no-autofocus': 'off',
60
+
61
+ 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
62
+
63
+ ...ifAnyDep('@tanstack/react-query', { '@tanstack/query/exhaustive-deps': 'error' }, {}),
53
64
  },
54
- ]);
65
+ });
package/rules/tests.js ADDED
@@ -0,0 +1,48 @@
1
+ import vitest from '@vitest/eslint-plugin';
2
+ import { defineConfig } from 'eslint/config';
3
+ import jest from 'eslint-plugin-jest';
4
+ import testingLibrary from 'eslint-plugin-testing-library';
5
+ import globals from 'globals';
6
+
7
+ import { ifAnyDep } from '../utils.js';
8
+
9
+ /**
10
+ * Jest ESLint configuration for flat config format
11
+ * Includes Jest-specific rules, globals, and Testing Library rules for test files
12
+ */
13
+ export default defineConfig({
14
+ name: 'codfish/tests',
15
+
16
+ files: [
17
+ '**/*.{spec,test}.{js,ts,jsx,tsx}',
18
+ '**/tests/**/*.{js,ts,jsx,tsx}',
19
+ '**/__mocks__/**/*.{js,ts,jsx,tsx}',
20
+ '**/__tests__/**/*.{js,ts,jsx,tsx}',
21
+ '**/jest.config.{js,ts}',
22
+ '**/jest.setup.{js,ts}',
23
+ '**/vitest.config.{js,ts}',
24
+ '**/vitest.setup.{js,ts}',
25
+ '**/setupTests.{js,ts}',
26
+ '**/testUtils.{js,ts}',
27
+ 'tests/**/*.{js,ts,jsx,tsx}',
28
+ ],
29
+
30
+ extends: [
31
+ ifAnyDep('jest', jest.configs['flat/recommended'], false),
32
+ ifAnyDep('vitest', vitest.configs.recommended, false),
33
+ ifAnyDep('react-testing-library', testingLibrary.configs['flat/react'], false),
34
+ ifAnyDep('vue-testing-library', testingLibrary.configs['flat/vue'], false),
35
+ ].filter(Boolean),
36
+
37
+ languageOptions: {
38
+ globals: {
39
+ ...globals.node,
40
+ ...ifAnyDep('jest', globals.jest, {}),
41
+ ...ifAnyDep('vitest', globals.vitest, {}),
42
+ },
43
+ },
44
+
45
+ rules: {
46
+ 'no-console': 'off',
47
+ },
48
+ });
package/utils.js CHANGED
@@ -8,12 +8,9 @@
8
8
  import fs from 'node:fs';
9
9
  import { cosmiconfigSync } from 'cosmiconfig';
10
10
  import has from 'lodash.has';
11
- import { readPackageUp } from 'read-package-up';
11
+ import { readPackageUpSync } from 'read-package-up';
12
12
 
13
- const { packageJson: pkg, path: pkgPath } =
14
- (await readPackageUp({
15
- cwd: fs.realpathSync(process.cwd()),
16
- })) || {};
13
+ const { packageJson: pkg, path: pkgPath } = readPackageUpSync({ cwd: fs.realpathSync(process.cwd()) }) || {};
17
14
 
18
15
  const hasPkgProp = props => [props].flat().some(prop => has(pkg, prop));
19
16
  const hasPkgSubProp = pkgProp => props => hasPkgProp([props].flat().map(p => `${pkgProp}.${p}`));
@@ -1 +0,0 @@
1
- {"version":3,"file":"jest.d.ts","sourceRoot":"","sources":["../../rules/jest.js"],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- declare const _default: import("eslint/config").Config[];
2
- export default _default;
3
- //# sourceMappingURL=vitest.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../rules/vitest.js"],"names":[],"mappings":""}
package/rules/jest.js DELETED
@@ -1,34 +0,0 @@
1
- import { defineConfig } from 'eslint/config';
2
- import jest from 'eslint-plugin-jest';
3
- import testingLibrary from 'eslint-plugin-testing-library';
4
-
5
- import { ifAnyDep } from '../utils.js';
6
-
7
- /**
8
- * Jest ESLint configuration for flat config format
9
- * Includes Jest-specific rules, globals, and Testing Library rules for test files
10
- */
11
- export default defineConfig([
12
- {
13
- name: 'codfish/jest',
14
-
15
- files: [
16
- '**/__tests__/**/*.{js,ts,jsx,tsx}',
17
- '**/*.{test,spec}.{js,ts,jsx,tsx}',
18
- '**/jest.config.{js,ts}',
19
- '**/jest.setup.{js,ts}',
20
- '**/setupTests.{js,ts}',
21
- '**/testUtils.{js,ts}',
22
- 'tests/**/*.{js,ts,jsx,tsx}',
23
- ],
24
-
25
- ...jest.configs['flat/recommended'],
26
-
27
- ...ifAnyDep('react-testing-library', testingLibrary.configs['flat/react'], {}),
28
- ...ifAnyDep('vue-testing-library', testingLibrary.configs['flat/vue'], {}),
29
-
30
- rules: {
31
- 'no-console': 'off',
32
- },
33
- },
34
- ]);
package/rules/vitest.js DELETED
@@ -1,39 +0,0 @@
1
- import vitest from '@vitest/eslint-plugin';
2
- import { defineConfig } from 'eslint/config';
3
- import testingLibrary from 'eslint-plugin-testing-library';
4
- import globals from 'globals';
5
-
6
- import { ifAnyDep } from '../utils.js';
7
-
8
- /**
9
- * Vitest ESLint configuration for flat config format
10
- * Includes Vitest-specific rules, globals, and Testing Library rules for test files
11
- */
12
- export default defineConfig([
13
- {
14
- files: [
15
- '**/*.{spec,test}.{js,ts,jsx,tsx}',
16
- '**/tests/**/*.{js,ts,jsx,tsx}',
17
- '**/__mocks__/**/*.{js,ts,jsx,tsx}',
18
- '**/__tests__/**/*.{js,ts,jsx,tsx}',
19
- ],
20
-
21
- ...vitest.configs.recommended,
22
-
23
- ...ifAnyDep('react-testing-library', testingLibrary.configs['flat/react'], {}),
24
- ...ifAnyDep('vue-testing-library', testingLibrary.configs['flat/vue'], {}),
25
-
26
- name: 'codfish/vitest',
27
-
28
- languageOptions: {
29
- globals: {
30
- ...vitest.environments.env.globals,
31
- ...globals.node,
32
- },
33
- },
34
-
35
- rules: {
36
- 'no-console': 'off',
37
- },
38
- },
39
- ]);