@codfish/eslint-config 0.0.0-semantically-released → 12.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/README.md CHANGED
@@ -18,8 +18,6 @@
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)
23
21
  - [Prettier Configuration](#prettier-configuration)
24
22
  - [Use in combination with prettier-plugin-tailwindcss](#use-in-combination-with-prettier-plugin-tailwindcss)
25
23
  - [Example scripts](#example-scripts)
@@ -38,7 +36,7 @@
38
36
  - **Next.js support**: Automatically configures Next.js official plugin linting rules when detected
39
37
  - **Test framework agnostic**: Supports Jest and Vitest with automatic detection
40
38
  - **Testing Library integration**: Automatically includes Testing Library rules for test files
41
- - **Multi-format support**: Built-in linting and formatting for Markdown, HTML, JSON, YAML/YML files
39
+ - **YAML/YML support**: Built-in linting for YAML configuration files
42
40
  - **Prettier integration**: Built-in Prettier configuration with conflict resolution via eslint-config-prettier
43
41
  - **ESM architecture**: Built with ECMAScript modules and full TypeScript typing
44
42
  - **Docker support**: Optional configuration for dockerized applications
@@ -50,34 +48,8 @@ Install the package and required peer dependencies:
50
48
 
51
49
  ```sh
52
50
  npm i -D eslint@9 @codfish/eslint-config
53
-
54
- # Optionally, you can uninstall plugins or presets you don't need to manage anymore,
55
- # @codfish/eslint-config includes them all.
56
- npm uninstall typescript-eslint \
57
- eslint-config-prettier \
58
- eslint-plugin-jest \
59
- eslint-plugin-jsx-a11y \
60
- eslint-plugin-prettier \
61
- eslint-plugin-react \
62
- eslint-plugin-react-hooks \
63
- @tanstack/eslint-plugin-query \
64
- eslint-plugin-simple-import-sort \
65
- eslint-plugin-testing-library \
66
- eslint-plugin-yml \
67
- @next/eslint-plugin-next \
68
- eslint-plugin-next \
69
- commitlint \
70
- @commitlint/cli \
71
- @commitlint/config-conventional \
72
- prettier # optional, see note
73
51
  ```
74
52
 
75
- > [!NOTE]
76
- >
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.
80
-
81
53
  ## Usage
82
54
 
83
55
  Create an `eslint.config.js` file in your project root:
@@ -173,11 +145,6 @@ import codfish from '@codfish/eslint-config';
173
145
  export default defineConfig(
174
146
  codfish,
175
147
 
176
- // Custom ignores
177
- {
178
- ignores: ['some-directory'],
179
- },
180
-
181
148
  {
182
149
  files: ['**/*.spec.{js,ts,jsx,tsx}'],
183
150
  rules: {
@@ -283,49 +250,19 @@ This configuration includes some opinionated settings that you might want to cus
283
250
  See the [configuration examples below](#usage) for instructions on overriding these settings to match your team's
284
251
  preferences.
285
252
 
286
- ## IDE Setup
287
-
288
- ### VS Code / Cursor
253
+ ## Prettier Configuration
289
254
 
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:
255
+ **Prettier is included and runs automatically** through ESLint for JavaScript, TypeScript, JSX, and TSX files using the
256
+ [built-in configuration](./prettier.js). **You don't need to install or configure Prettier separately** for basic usage.
293
257
 
294
- Add these settings to your `.vscode/settings.json` or user settings:
258
+ However, if you want to format other file types (like Markdown, JSON, CSS, or YAML) or run Prettier directly, you can
259
+ install it as a dev dependency:
295
260
 
296
- ```json
297
- {
298
- "editor.codeActionsOnSave": {
299
- "source.fixAll": "explicit"
300
- },
301
- "eslint.validate": [
302
- "javascript",
303
- "javascriptreact",
304
- "typescript",
305
- "typescriptreact",
306
- "markdown",
307
- "json",
308
- "jsonc",
309
- "html",
310
- "yml",
311
- "yaml"
312
- ]
313
- }
261
+ ```sh
262
+ npm i -D prettier
314
263
  ```
315
264
 
316
- This configuration enables:
317
-
318
- - Automatic linting and formatting on save for all supported file types
319
- - ESLint validation for Markdown, JSON, YAML, and HTML files
320
- - A unified development experience across your entire codebase
321
-
322
- ## Prettier Configuration
323
-
324
- **Prettier is included and runs automatically** through ESLint for JavaScript, TypeScript, JSX, TSX, Markdown, HTML,
325
- JSON, and YAML files using the [built-in configuration](./prettier.js). **You don't need to install or configure
326
- Prettier separately** for these file types.
327
-
328
- You can then override the default config by creating your own Prettier config file, or extend the built-in config:
265
+ You can then override the defaults by creating your own Prettier config file, or extend the built-in config:
329
266
 
330
267
  **Option 1: Extend the built-in config (Recommended)**
331
268
 
@@ -385,11 +322,12 @@ npm i -D eslint@9 @codfish/eslint-config prettier-plugin-tailwindcss
385
322
  ```js
386
323
  // prettier.config.js
387
324
 
388
- import codfish from '@codfish/eslint-config/prettier';
325
+ import codfishPrettier from '@codfish/eslint-config/prettier';
326
+ import tailwindcss from 'prettier-plugin-tailwindcss';
389
327
 
390
328
  /** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
391
329
  export default {
392
- ...codfish,
330
+ ...codfishPrettier,
393
331
  plugins: ['prettier-plugin-tailwindcss'],
394
332
  tailwindStylesheet: './src/styles/app.css',
395
333
  tailwindFunctions: ['clsx'], // optional
@@ -400,7 +338,7 @@ export default {
400
338
 
401
339
  Optionally, you can add these scripts to your `package.json` for common linting workflows:
402
340
 
403
- **Recommended scripts:**
341
+ **Basic scripts (no separate Prettier installation needed):**
404
342
 
405
343
  ```json
406
344
  {
@@ -409,32 +347,24 @@ Optionally, you can add these scripts to your `package.json` for common linting
409
347
  "fix": "eslint . --fix"
410
348
  },
411
349
  "lint-staged": {
412
- "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"]
350
+ "*.{js,jsx,ts,tsx}": ["eslint --fix"]
413
351
  }
414
352
  }
415
353
  ```
416
354
 
417
- > [!NOTE]
418
- >
419
- > ESLint now handles linting and formatting for JavaScript, TypeScript, Markdown, HTML, JSON, and YAML files. You don't
420
- > need to run Prettier separately for these file types.
421
-
422
- **With Prettier for other file types (CSS, SCSS, etc.):**
423
-
424
- If you want to format additional file types not covered by ESLint (like CSS, SCSS), you can install Prettier and add
425
- these scripts:
355
+ **With Prettier installed separately (for formatting non-JS files):**
426
356
 
427
357
  ```json
428
358
  {
429
359
  "scripts": {
430
360
  "lint": "eslint .",
431
361
  "fix": "eslint . --fix",
432
- "format": "prettier --write \"**/*.{css,scss}\"",
362
+ "format": "prettier --config ./node_modules/@codfish/eslint-config/prettier.js --write \"**/*.{json,css,md}\"",
433
363
  "check": "npm run lint && npm run format -- --check --no-write"
434
364
  },
435
365
  "lint-staged": {
436
- "*.{js,jsx,ts,tsx,md,json,yml,yaml,html}": ["eslint --fix"],
437
- "*.{css,scss}": ["prettier --write"]
366
+ "*.{js,jsx,ts,tsx}": ["eslint --fix"],
367
+ "*.{json,css,md}": ["prettier --write --config ./node_modules/@codfish/eslint-config/prettier.js"]
438
368
  }
439
369
  }
440
370
  ```
package/dist/dapp.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- declare const _default: import("eslint/config").Config[];
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
2
  export default _default;
3
3
  //# sourceMappingURL=dapp.d.ts.map
package/dist/docker.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- declare const _default: import("eslint/config").Config[];
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
2
  export default _default;
3
3
  //# sourceMappingURL=docker.d.ts.map
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- declare const _default: import("eslint/config").Config[];
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
2
  export default _default;
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,3 @@
1
- declare const _default: import("eslint/config").Config[];
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
2
  export default _default;
3
3
  //# sourceMappingURL=config-files.d.ts.map
@@ -0,0 +1,3 @@
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
+ export default _default;
3
+ //# sourceMappingURL=jest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.d.ts","sourceRoot":"","sources":["../../rules/jest.js"],"names":[],"mappings":""}
@@ -1,3 +1,3 @@
1
- declare const _default: import("eslint/config").Config[];
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
2
  export default _default;
3
3
  //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1,3 @@
1
+ declare const _default: import("eslint").Linter.Config<import("eslint").Linter.RulesRecord>[];
2
+ export default _default;
3
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../rules/vitest.js"],"names":[],"mappings":""}
@@ -1 +1 @@
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"}
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"}
package/index.js CHANGED
@@ -1,10 +1,6 @@
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';
6
2
  import { defineConfig } from 'eslint/config';
7
- import prettierConfig from 'eslint-plugin-prettier/recommended';
3
+ import prettier from 'eslint-plugin-prettier/recommended';
8
4
  import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
9
5
  import ymlPlugin from 'eslint-plugin-yml';
10
6
  import globals from 'globals';
@@ -12,29 +8,26 @@ import tseslint from 'typescript-eslint';
12
8
 
13
9
  import prettierBuiltInConfig from './prettier.js';
14
10
  import configFilesConfig from './rules/config-files.js';
11
+ import jestConfig from './rules/jest.js';
15
12
  import reactConfig from './rules/react.js';
16
- import testConfig from './rules/tests.js';
13
+ import vitestConfig from './rules/vitest.js';
17
14
  import { hasLocalConfig, ifAnyDep } from './utils.js';
18
15
 
19
16
  const useBuiltinPrettierConfig = !hasLocalConfig('prettier');
20
17
 
21
18
  /**
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
19
+ * Modern ESLint configuration with dynamic feature detection
20
+ * Supports TypeScript, React, Jest, Vitest, Prettier, YAML, and Next.js
29
21
  */
30
22
  export default defineConfig([
31
- // Base opinionated rules
32
- {
33
- name: 'codfish/base',
23
+ // Base JavaScript configuration
24
+ js.configs.recommended,
34
25
 
35
- files: ['**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx}'],
26
+ tseslint.configs.recommended,
36
27
 
37
- extends: [js.configs.recommended, tseslint.configs.recommended],
28
+ // Base configuration for all files
29
+ {
30
+ name: 'codfish/base',
38
31
 
39
32
  plugins: {
40
33
  'simple-import-sort': simpleImportSortPlugin,
@@ -51,20 +44,6 @@ export default defineConfig([
51
44
  },
52
45
 
53
46
  rules: {
54
- // Error handling rules to enforce using the Error object
55
- 'no-throw-literal': 'error',
56
- 'prefer-promise-reject-errors': 'error',
57
-
58
- // Asynchronous functions that don’t use await might not need to be asynchronous functions
59
- // Usually result of refactoring, leads to misunderstanding/misusage
60
- 'require-await': 'error',
61
-
62
- // Disallow console statements in regular code (only allowed in test files)
63
- 'no-console': 'error',
64
-
65
- // Consolidate your imports
66
- 'no-duplicate-imports': ['error', { includeExports: false }],
67
-
68
47
  // Custom Grouping: https://github.com/lydell/eslint-plugin-simple-import-sort#custom-grouping
69
48
  // Examples: https://github.com/lydell/eslint-plugin-simple-import-sort/blob/main/examples/.eslintrc.js
70
49
  'simple-import-sort/imports': [
@@ -83,89 +62,28 @@ export default defineConfig([
83
62
  },
84
63
  ],
85
64
  'simple-import-sort/exports': 'error',
86
- 'sort-imports': 'off',
87
-
88
- // Allows destructuring of rest properties even if they are unused
89
- '@typescript-eslint/no-unused-vars': ['error', { ignoreRestSiblings: true }],
90
- '@typescript-eslint/no-empty-function': 'off',
91
- '@typescript-eslint/naming-convention': [
92
- 'error',
93
- {
94
- selector: ['interface', 'typeAlias'],
95
- format: ['PascalCase'],
96
- custom: {
97
- regex: '^I[A-Z]', // prevent prefixing interfaces and type alias declarations with "I"
98
- match: false,
99
- },
100
- },
101
- ],
102
- '@typescript-eslint/ban-ts-comment': [
103
- 'error',
104
- {
105
- // If you need to use a ts comment, make sure you have a description.
106
- 'ts-ignore': 'allow-with-description',
107
- 'ts-expect-error': 'allow-with-description',
108
- 'ts-nocheck': 'allow-with-description',
109
- },
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
65
 
126
- // 1. Encouraging `lodash-es` imports per file
66
+ // 2. Encouraging `lodash-es` imports per file
127
67
  // lodash imports should use `lodash-es` package and should be imported per file.
128
- // E.G: `import get from "lodash-es/get"`
68
+ // E.G: `import get from 'lodash-es/get'`
129
69
  // 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
70
  'no-restricted-imports': [
134
71
  'error',
135
72
  {
136
73
  paths: [
137
74
  {
138
75
  name: 'lodash',
139
- message: 'Please use lodash-es direct import E.G: `import get from "lodash-es/get"`',
76
+ message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
140
77
  },
141
78
  {
142
79
  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.',
80
+ message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
156
81
  },
157
82
  ],
158
83
  patterns: [
159
84
  {
160
85
  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/*"`',
86
+ message: "Please use lodash-es direct import E.G: `import get from 'lodash-es/get'`",
169
87
  },
170
88
  ],
171
89
  },
@@ -184,6 +102,7 @@ export default defineConfig([
184
102
  'coverage',
185
103
  '.vercel',
186
104
  '**/logs/',
105
+ 'bin/*',
187
106
  '**/dist/',
188
107
  '**/dist-ssr/',
189
108
  '**/cache/',
@@ -214,109 +133,37 @@ export default defineConfig([
214
133
  '.expo',
215
134
  '**/tmp/',
216
135
  '**/temp/',
217
- '**/.tmp',
218
- '.nx',
219
- '**/.eslintcache',
136
+ '.tmp',
137
+ '.eslintcache',
220
138
  '*.tsbuildinfo',
221
- 'node_modules',
222
- '**/node_modules/',
223
- '**/pnpm-lock.yaml',
224
- '**/pnpm-lock.*.yaml',
225
- '.history',
226
- '**/.yarn',
227
- '**/yarn.lock',
228
- '**/package-lock.json',
229
- '**/.yarnrc.yml',
230
- 'pacts',
231
- '.claude',
232
- '.__mf__temp',
233
139
  ],
234
140
  },
235
141
 
236
142
  // Configuration files (eslint, prettier, etc.)
237
143
  configFilesConfig,
238
144
 
239
- // React configuration (dynamic)
240
- ifAnyDep('react', reactConfig, []),
241
-
242
- // Jest OR Vitest configuration (dynamic)
243
- ifAnyDep(['jest', 'vitest'], testConfig, []),
244
-
245
145
  // YML files
246
146
  ymlPlugin.configs['flat/standard'],
247
147
  ymlPlugin.configs['flat/prettier'], // handles conflicting rules with the yml plugin
248
148
 
249
- {
250
- name: 'codfish/github-yml-overrides',
251
-
252
- files: ['.github/**/*.yml', '.github/**/*.yaml'],
253
-
254
- rules: {
255
- 'yml/no-empty-mapping-value': 'off', // somewhat common in github workflows
256
- },
257
- },
258
-
259
- // JSON files
260
- {
261
- name: 'codfish/json',
262
- files: ['**/*.json'],
263
- ignores: ['**/tsconfig*.json', '**/package-lock.json'],
264
- plugins: { json },
265
- language: 'json/json',
266
- extends: ['json/recommended'],
267
- },
268
-
269
- // JSONC files (JSON with Comments) - for files that allow comments
270
- {
271
- name: 'codfish/jsonc',
272
- files: ['**/*.jsonc'],
273
- plugins: { json },
274
- language: 'json/jsonc',
275
- extends: ['json/recommended'],
276
- },
277
-
278
- // JSON5 files - for TypeScript configs (supports comments AND trailing commas)
279
- {
280
- name: 'codfish/json5',
281
- files: ['**/tsconfig*.json', '**/*.json5'],
282
- plugins: { json },
283
- language: 'json/json5',
284
- extends: ['json/recommended'],
285
- },
149
+ // React configuration (dynamic)
150
+ ifAnyDep('react', reactConfig, []),
286
151
 
287
- // Markdown files
288
- {
289
- name: 'codfish/markdown',
290
- files: ['**/*.md'],
291
- plugins: { markdown },
292
- extends: ['markdown/recommended'],
293
- rules: {
294
- // Allow GitHub-style alerts and checkbox syntax (task lists)
295
- 'markdown/no-missing-label-refs': [
296
- 'error',
297
- { allowLabels: ['!NOTE', '!WARNING', '!IMPORTANT', '!TIP', '!CAUTION', '', 'x', 'X'] },
298
- ],
299
- },
300
- },
152
+ // Jest OR Vitest configuration (dynamic)
153
+ ifAnyDep('jest', jestConfig, []),
301
154
 
302
- // HTML files
303
- {
304
- name: 'codfish/html',
305
- files: ['**/*.html'],
306
- plugins: { html },
307
- languageOptions: { parser: htmlParser },
308
- },
155
+ // Vitest configuration (dynamic)
156
+ ifAnyDep('vitest', vitestConfig, []),
309
157
 
310
158
  // Prettier plugin is responsible for running prettier as an ESLint
311
159
  // rule and turning off ESLint rules that might conflict.
312
160
  // IMPORTANT: KEEP THIS LAST TO OVERRIDE ESLINT!
313
- {
314
- files: ['**/*.{js,ts,jsx,tsx,md,yml,yaml,html,json,jsonc,json5}'],
315
-
316
- extends: [prettierConfig],
161
+ prettier,
317
162
 
163
+ {
318
164
  rules: {
319
- // Reset prettier rule passing in custom prettier config.
165
+ // Reset prettier rule passing in codfish's prettier config.
166
+ // IMPORTANT: KEEP THIS LAST TO OVERRIDE PRETTIER PLUGIN!
320
167
  'prettier/prettier': useBuiltinPrettierConfig ? ['error', prettierBuiltInConfig] : 'error',
321
168
  },
322
169
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codfish/eslint-config",
3
- "version": "0.0.0-semantically-released",
3
+ "version": "12.0.0",
4
4
  "description": "Modern ESLint configuration with TypeScript, React, and testing framework support.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -45,24 +45,16 @@
45
45
  "scripts": {
46
46
  "build": "tsc --emitDeclarationOnly",
47
47
  "dev": "tsc --watch",
48
- "typecheck": "tsc --noEmit",
49
- "lint": "eslint",
50
- "format": "eslint --fix",
51
- "test": "vitest",
52
- "test:coverage": "vitest --coverage",
48
+ "type-check": "tsc --noEmit",
49
+ "lint": "eslint .",
50
+ "fix": "prettier --write \"**/*.{json,css,scss,md}\" --config ./prettier.js && npm run lint -- --fix",
53
51
  "prepublishOnly": "npm run build",
54
52
  "prepare": "husky"
55
53
  },
56
54
  "dependencies": {
57
- "@commitlint/cli": "^20.4.0",
58
- "@commitlint/config-conventional": "^20.4.0",
55
+ "@commitlint/cli": "^19.8.1",
56
+ "@commitlint/config-conventional": "^19.8.1",
59
57
  "@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",
66
58
  "@vitest/eslint-plugin": "^1.3.10",
67
59
  "cosmiconfig": "^9.0.0",
68
60
  "eslint-config-prettier": "^10.1.8",
@@ -70,25 +62,23 @@
70
62
  "eslint-plugin-jsx-a11y": "^6.10.2",
71
63
  "eslint-plugin-prettier": "^5.5.4",
72
64
  "eslint-plugin-react": "^7.37.5",
73
- "eslint-plugin-react-hooks": "^7.0.1",
74
- "eslint-plugin-react-refresh": "^0.5.0",
65
+ "eslint-plugin-react-hooks": "^5.2.0",
75
66
  "eslint-plugin-simple-import-sort": "^12.1.1",
76
67
  "eslint-plugin-testing-library": "^7.1.0",
77
- "eslint-plugin-yml": "^3.0.0",
78
- "globals": "^17.3.0",
68
+ "eslint-plugin-yml": "^1.16.0",
69
+ "@next/eslint-plugin-next": "^15.1.6",
70
+ "globals": "^16.4.0",
79
71
  "lodash.has": "^4.5.2",
80
- "prettier": ">= 3",
81
- "read-package-up": "^12.0.0",
72
+ "prettier": "^3.6.2",
73
+ "read-package-up": "^11.0.0",
82
74
  "typescript-eslint": "^8.44.0"
83
75
  },
84
76
  "devDependencies": {
85
- "@vitest/coverage-v8": "^4.0.18",
86
77
  "doctoc": "^2.2.1",
87
78
  "eslint": "^9.35.0",
88
79
  "husky": "^9.1.7",
89
80
  "lint-staged": "^16.1.6",
90
- "typescript": "^5.9.2",
91
- "vitest": "^4.0.18"
81
+ "typescript": "^5.9.2"
92
82
  },
93
83
  "engines": {
94
84
  "node": ">=20.0.0"
@@ -104,11 +94,10 @@
104
94
  "commitlint.js"
105
95
  ],
106
96
  "peerDependencies": {
107
- "eslint": ">= 9",
108
- "prettier": ">= 3"
97
+ "eslint": ">= 9"
109
98
  },
110
99
  "peerDependenciesMeta": {
111
- "prettier": {
100
+ "typescript": {
112
101
  "optional": true
113
102
  }
114
103
  },
package/rules/jest.js ADDED
@@ -0,0 +1,34 @@
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/react.js CHANGED
@@ -1,10 +1,8 @@
1
1
  import nextPlugin from '@next/eslint-plugin-next';
2
- import tanstackQuery from '@tanstack/eslint-plugin-query';
3
2
  import { defineConfig } from 'eslint/config';
4
3
  import jsxA11y from 'eslint-plugin-jsx-a11y';
5
4
  import react from 'eslint-plugin-react';
6
5
  import reactHooks from 'eslint-plugin-react-hooks';
7
- import reactRefresh from 'eslint-plugin-react-refresh';
8
6
  import globals from 'globals';
9
7
 
10
8
  import { ifAnyDep } from '../utils.js';
@@ -20,17 +18,10 @@ export default defineConfig([
20
18
  {
21
19
  name: 'codfish/react',
22
20
 
23
- files: ['**/*.jsx', '**/*.tsx', '**/*.mjsx', '**/*.mtsx'],
21
+ files: ['**/*.{js,mjs,cjs,jsx,mjsx,ts,tsx,mtsx}'],
24
22
 
25
- extends: [
26
- react.configs.flat.recommended,
27
- reactHooks.configs.flat['recommended-latest'],
28
- reactRefresh.configs.recommended,
29
- jsxA11y.flatConfigs.recommended,
30
- ifAnyDep('@tanstack/react-query', tanstackQuery.configs['flat/recommended']),
31
- ifAnyDep('next', nextPlugin.configs.recommended),
32
- ifAnyDep('next', nextPlugin.configs['core-web-vitals']),
33
- ].filter(Boolean),
23
+ ...react.configs.flat.recommended,
24
+ ...jsxA11y.flatConfigs.recommended,
34
25
 
35
26
  languageOptions: {
36
27
  globals: {
@@ -38,47 +29,26 @@ export default defineConfig([
38
29
  ...globals.browser,
39
30
  },
40
31
  },
32
+ },
41
33
 
42
- settings: {
43
- react: {
44
- version: 'detect',
45
- },
46
- },
47
-
48
- rules: {
49
- // React 17+ uses automatic JSX runtime, no need to import React
50
- 'react/react-in-jsx-scope': 'off',
51
- 'react/jsx-uses-react': 'off',
52
-
53
- // TypeScript provides type checking, no need for PropTypes
54
- 'react/prop-types': 'off',
55
-
56
- // this rule is too noisey and doesn't account for certain valid use cases
57
- 'react-hooks/exhaustive-deps': 'off',
58
-
59
- // sometimes autoFocus is needed for better UX. @todo - revisit later?
60
- 'jsx-a11y/no-autofocus': 'off',
61
-
62
- 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }],
34
+ // React Hooks configuration
35
+ reactHooks.configs['recommended-latest'],
63
36
 
64
- ...ifAnyDep('@tanstack/react-query', { '@tanstack/query/exhaustive-deps': 'error' }, {}),
37
+ // Next.js configuration (dynamic)
38
+ ifAnyDep(
39
+ 'next',
40
+ {
41
+ ...nextPlugin.flatConfig.recommended,
42
+ ...nextPlugin.flatConfig.coreWebVitals,
65
43
 
66
- ...ifAnyDep(
67
- 'next',
68
- {
69
- // annoying cause this is common in Next.js, i.e. generateMetadata()
70
- 'react-refresh/only-export-components': ['off'],
71
- },
72
- {},
73
- ),
44
+ name: 'codfish/next',
74
45
  },
75
- },
46
+ {},
47
+ ),
76
48
 
77
49
  {
78
- name: 'codfish/react-next-config',
79
- files: ['next.config.js', 'next.config.ts'],
80
50
  rules: {
81
- 'require-await': 'off',
51
+ 'react-hooks/exhaustive-deps': 'off',
82
52
  },
83
53
  },
84
54
  ]);
@@ -0,0 +1,39 @@
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
+ ]);
package/utils.js CHANGED
@@ -8,9 +8,12 @@
8
8
  import fs from 'node:fs';
9
9
  import { cosmiconfigSync } from 'cosmiconfig';
10
10
  import has from 'lodash.has';
11
- import { readPackageUpSync } from 'read-package-up';
11
+ import { readPackageUp } from 'read-package-up';
12
12
 
13
- const { packageJson: pkg, path: pkgPath } = readPackageUpSync({ cwd: fs.realpathSync(process.cwd()) }) || {};
13
+ const { packageJson: pkg, path: pkgPath } =
14
+ (await readPackageUp({
15
+ cwd: fs.realpathSync(process.cwd()),
16
+ })) || {};
14
17
 
15
18
  const hasPkgProp = props => [props].flat().some(prop => has(pkg, prop));
16
19
  const hasPkgSubProp = pkgProp => props => hasPkgProp([props].flat().map(p => `${pkgProp}.${p}`));
@@ -1,3 +0,0 @@
1
- declare const _default: import("eslint/config").Config[];
2
- export default _default;
3
- //# sourceMappingURL=tests.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../rules/tests.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=eslint-execution.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"eslint-execution.spec.d.ts","sourceRoot":"","sources":["../../../tests/integration/eslint-execution.spec.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=rule-application.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rule-application.spec.d.ts","sourceRoot":"","sources":["../../../tests/integration/rule-application.spec.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=edge-cases.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"edge-cases.spec.d.ts","sourceRoot":"","sources":["../../../tests/scenarios/edge-cases.spec.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=config-loading.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config-loading.spec.d.ts","sourceRoot":"","sources":["../../../tests/unit/config-loading.spec.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=dynamic-detection.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dynamic-detection.spec.d.ts","sourceRoot":"","sources":["../../../tests/unit/dynamic-detection.spec.js"],"names":[],"mappings":""}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=utils.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.spec.d.ts","sourceRoot":"","sources":["../../../tests/unit/utils.spec.js"],"names":[],"mappings":""}
@@ -1,3 +0,0 @@
1
- declare const _default: import("vite").UserConfig;
2
- export default _default;
3
- //# sourceMappingURL=vitest.config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vitest.config.d.ts","sourceRoot":"","sources":["../vitest.config.js"],"names":[],"mappings":""}
package/rules/tests.js DELETED
@@ -1,52 +0,0 @@
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('@testing-library/dom', testingLibrary.configs['flat/dom'], false),
34
- ifAnyDep('@testing-library/react', testingLibrary.configs['flat/react'], false),
35
- ifAnyDep('@testing-library/vue', testingLibrary.configs['flat/vue'], false),
36
- ifAnyDep('@testing-library/svelte', testingLibrary.configs['flat/svelte'], false),
37
- ifAnyDep('@testing-library/angular', testingLibrary.configs['flat/angular'], false),
38
- ifAnyDep('@testing-library/marko', testingLibrary.configs['flat/marko'], false),
39
- ].filter(Boolean),
40
-
41
- languageOptions: {
42
- globals: {
43
- ...globals.node,
44
- ...ifAnyDep('jest', globals.jest, {}),
45
- ...ifAnyDep('vitest', globals.vitest, {}),
46
- },
47
- },
48
-
49
- rules: {
50
- 'no-console': 'off',
51
- },
52
- });