@whoj/eslint-config 7.5.2 → 9.2.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@whoj/eslint-config",
3
3
  "type": "module",
4
- "version": "7.5.2",
4
+ "version": "9.2.0",
5
5
  "description": "ESLint config",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/who-jonson/eslint-config",
@@ -20,35 +20,32 @@
20
20
  "./cli": "./dist/cli.mjs",
21
21
  "./package.json": "./package.json"
22
22
  },
23
- "main": "./dist/index.mjs",
24
- "module": "./dist/index.mjs",
25
23
  "types": "./dist/index.d.mts",
26
- "bin": "./bin/index.mjs",
27
24
  "files": [
28
25
  "bin",
29
- "dist"
26
+ "dist",
27
+ "skills"
30
28
  ],
31
29
  "peerDependencies": {
32
- "@angular-eslint/eslint-plugin": "^21.2.0",
33
- "@angular-eslint/eslint-plugin-template": "^21.2.0",
34
- "@angular-eslint/template-parser": "^21.2.0",
35
- "@eslint-react/eslint-plugin": "^2.12.4",
36
- "@next/eslint-plugin-next": "^16.1.6",
30
+ "@angular-eslint/eslint-plugin": "^22.1.0",
31
+ "@angular-eslint/eslint-plugin-template": "^22.1.0",
32
+ "@angular-eslint/template-parser": "^22.1.0",
33
+ "@eslint-react/eslint-plugin": "^5.14.7",
34
+ "@next/eslint-plugin-next": "^16.2.10",
37
35
  "@prettier/plugin-xml": "^3.4.2",
38
- "@unocss/eslint-plugin": "^66.6.0",
39
- "astro-eslint-parser": "^1.2.2",
40
- "eslint": "^9.39.2||^10.0.0",
41
- "eslint-plugin-astro": "^1.5.0",
42
- "eslint-plugin-format": "^1.4.0",
36
+ "@unocss/eslint-plugin": "^66.7.5",
37
+ "astro-eslint-parser": "^2.1 || ^3.0",
38
+ "eslint": "^9.0.0 || ^10.7.0",
39
+ "eslint-plugin-astro": "^2.1 || ^3.0",
40
+ "eslint-plugin-format": "^2.0.1",
43
41
  "eslint-plugin-jsx-a11y": "^6.10.2",
44
- "eslint-plugin-react-hooks": "^7.0.1",
45
- "eslint-plugin-react-refresh": "^0.5.0",
42
+ "eslint-plugin-react-refresh": "^0.5.3",
46
43
  "eslint-plugin-solid": "^0.14.5",
47
- "eslint-plugin-svelte": "^3.15.0",
48
- "eslint-plugin-vuejs-accessibility": "^2.4.1",
44
+ "eslint-plugin-svelte": "^3.20.0",
45
+ "eslint-plugin-vuejs-accessibility": "^2.5.0",
49
46
  "prettier-plugin-astro": "^0.14.1",
50
47
  "prettier-plugin-slidev": "^1.0.5",
51
- "svelte-eslint-parser": "^1.4.1"
48
+ "svelte-eslint-parser": "^1.8.0"
52
49
  },
53
50
  "peerDependenciesMeta": {
54
51
  "@angular-eslint/eslint-plugin": {
@@ -84,9 +81,6 @@
84
81
  "eslint-plugin-jsx-a11y": {
85
82
  "optional": true
86
83
  },
87
- "eslint-plugin-react-hooks": {
88
- "optional": true
89
- },
90
84
  "eslint-plugin-react-refresh": {
91
85
  "optional": true
92
86
  },
@@ -111,88 +105,90 @@
111
105
  },
112
106
  "dependencies": {
113
107
  "@antfu/install-pkg": "^1.1.0",
114
- "@clack/prompts": "^1.0.0",
115
- "@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
116
- "@eslint/markdown": "^7.5.1",
117
- "@stylistic/eslint-plugin": "^5.8.0",
118
- "@typescript-eslint/eslint-plugin": "^8.55.0",
119
- "@typescript-eslint/parser": "^8.55.0",
120
- "@vitest/eslint-plugin": "^1.6.7",
108
+ "@clack/prompts": "^1.7.0",
109
+ "@e18e/eslint-plugin": "^0.5.1",
110
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.7.2",
111
+ "@eslint/markdown": "^8.0.3",
112
+ "@stylistic/eslint-plugin": "^5.10.0",
113
+ "@typescript-eslint/eslint-plugin": "^8.63.0",
114
+ "@typescript-eslint/parser": "^8.63.0",
115
+ "@vitest/eslint-plugin": "^1.6.23",
121
116
  "@whoj/utils-core": "^2.4.0",
122
- "ansis": "^4.2.0",
123
- "cac": "^6.7.14",
124
- "eslint-config-flat-gitignore": "^2.1.0",
125
- "eslint-flat-config-utils": "^3.0.1",
117
+ "ansis": "^4.3.1",
118
+ "cac": "^7.0.0",
119
+ "eslint-config-flat-gitignore": "^2.3.0",
120
+ "eslint-flat-config-utils": "^3.2.0",
126
121
  "eslint-merge-processors": "^2.0.0",
127
- "eslint-plugin-antfu": "^3.2.1",
128
- "eslint-plugin-command": "^3.4.0",
129
- "eslint-plugin-import-lite": "^0.5.0",
130
- "eslint-plugin-jsdoc": "^62.5.4",
131
- "eslint-plugin-jsonc": "^2.21.1",
132
- "eslint-plugin-n": "^17.23.2",
133
- "eslint-plugin-no-only-tests": "^3.3.0",
134
- "eslint-plugin-perfectionist": "^5.5.0",
135
- "eslint-plugin-pnpm": "^1.5.0",
136
- "eslint-plugin-regexp": "^3.0.0",
137
- "eslint-plugin-toml": "^1.0.4",
138
- "eslint-plugin-unicorn": "^63.0.0",
122
+ "eslint-plugin-antfu": "^3.2.3",
123
+ "eslint-plugin-command": "^3.5.2",
124
+ "eslint-plugin-import-lite": "^0.6.0",
125
+ "eslint-plugin-jsdoc": "^63.0.13",
126
+ "eslint-plugin-jsonc": "^3.3.0",
127
+ "eslint-plugin-n": "^18.2.2",
128
+ "eslint-plugin-no-only-tests": "^3.4.0",
129
+ "eslint-plugin-perfectionist": "^5.10.0",
130
+ "eslint-plugin-pnpm": "^1.6.1",
131
+ "eslint-plugin-regexp": "^3.1.1",
132
+ "eslint-plugin-toml": "^1.4.0",
133
+ "eslint-plugin-unicorn": "^71.1.0",
139
134
  "eslint-plugin-unused-imports": "^4.4.1",
140
- "eslint-plugin-vue": "^10.7.0",
141
- "eslint-plugin-yml": "^3.1.2",
135
+ "eslint-plugin-vue": "^10.9.2",
136
+ "eslint-plugin-yml": "^3.6.0",
142
137
  "eslint-processor-vue-blocks": "^2.0.0",
143
- "fast-xml-parser": "^5.3.2",
144
- "globals": "^17.3.0",
145
- "jsonc-eslint-parser": "^2.4.2",
146
- "local-pkg": "^1.1.2",
138
+ "fast-xml-parser": "^5.10.0",
139
+ "globals": "^17.7.0",
140
+ "local-pkg": "^1.2.1",
147
141
  "parse-gitignore": "^2.0.0",
148
142
  "toml-eslint-parser": "^1.0.3",
149
- "vue-eslint-parser": "^10.2.0",
150
- "yaml-eslint-parser": "^2.0.0"
143
+ "vue-eslint-parser": "^10.4.1",
144
+ "yaml-eslint-parser": "^2.1.0"
151
145
  },
152
146
  "devDependencies": {
153
- "@angular-eslint/eslint-plugin": "^21.2.0",
154
- "@angular-eslint/eslint-plugin-template": "^21.2.0",
155
- "@angular-eslint/template-parser": "^21.2.0",
156
- "@angular/core": "^21.1.3",
157
- "@antfu/ni": "^28.2.0",
158
- "@eslint-react/eslint-plugin": "^2.12.4",
159
- "@eslint/config-inspector": "^1.4.2",
160
- "@next/eslint-plugin-next": "^16.1.6",
147
+ "@angular-eslint/eslint-plugin": "^22.1.0",
148
+ "@angular-eslint/eslint-plugin-template": "^22.1.0",
149
+ "@angular-eslint/template-parser": "^22.1.0",
150
+ "@angular/core": "^22.0.6",
151
+ "@antfu/ni": "^30.2.0",
152
+ "@eslint-react/eslint-plugin": "^5.14.7",
153
+ "@eslint/config-inspector": "^3.0.4",
154
+ "@next/eslint-plugin-next": "^16.2.10",
161
155
  "@prettier/plugin-xml": "^3.4.2",
162
156
  "@types/eslint-plugin-jsx-a11y": "^6.10.1",
163
- "@types/node": "^25.2.3",
164
- "@unocss/eslint-plugin": "^66.6.0",
165
- "astro-eslint-parser": "^1.2.2",
166
- "baseline-browser-mapping": "^2.9.19",
167
- "bumpp": "^10.4.1",
168
- "eslint": "^9.39.2||^10.0.0",
169
- "eslint-plugin-astro": "^1.5.0",
170
- "eslint-plugin-erasable-syntax-only": "^0.4.0",
171
- "eslint-plugin-format": "^1.4.0",
157
+ "@types/node": "^26.1.1",
158
+ "@unocss/eslint-plugin": "^66.7.5",
159
+ "astro-eslint-parser": "^2.1 || ^3.0",
160
+ "baseline-browser-mapping": "^2.10.43",
161
+ "bumpp": "^11.1.0",
162
+ "eslint": "^9.0.0 || ^10.7.0",
163
+ "eslint-plugin-astro": "^2.1 || ^3.0",
164
+ "eslint-plugin-erasable-syntax-only": "^0.4.2",
165
+ "eslint-plugin-format": "^2.0.1",
172
166
  "eslint-plugin-jsx-a11y": "^6.10.2",
173
- "eslint-plugin-react-hooks": "^7.0.1",
174
- "eslint-plugin-react-refresh": "^0.5.0",
167
+ "eslint-plugin-react-refresh": "^0.5.3",
175
168
  "eslint-plugin-solid": "^0.14.5",
176
- "eslint-plugin-svelte": "^3.15.0",
177
- "eslint-plugin-vuejs-accessibility": "^2.4.1",
178
- "eslint-typegen": "^2.3.0",
169
+ "eslint-plugin-svelte": "^3.20.0",
170
+ "eslint-plugin-vuejs-accessibility": "^2.5.0",
171
+ "eslint-typegen": "^2.3.1",
179
172
  "execa": "^9.6.1",
180
173
  "find-up-simple": "^1.0.1",
181
- "jiti": "^2.6.1",
182
- "lint-staged": "^16.2.7",
183
- "pnpm-workspace-yaml": "^1.5.0",
174
+ "jiti": "^2.7.0",
175
+ "lint-staged": "^17.0.8",
176
+ "pnpm-workspace-yaml": "^1.6.1",
184
177
  "prettier-plugin-astro": "^0.14.1",
185
178
  "prettier-plugin-slidev": "^1.0.5",
186
179
  "simple-git-hooks": "^2.13.1",
187
- "svelte": "^5.50.1",
188
- "svelte-eslint-parser": "^1.4.1",
189
- "tinyglobby": "^0.2.15",
190
- "tsdown": "^0.18.4",
191
- "tsx": "^4.21.0",
192
- "typescript": "^5.9.3",
193
- "vitest": "^4.0.18",
194
- "vue": "^3.5.28",
195
- "@whoj/eslint-config": "7.5.2"
180
+ "skills-npm": "^1.2.0",
181
+ "svelte": "^5.56.4",
182
+ "svelte-eslint-parser": "^1.8.0",
183
+ "tinyglobby": "^0.2.17",
184
+ "tsdown": "^0.22.7",
185
+ "tsdown-stale-guard": "^0.1.3",
186
+ "tsnapi": "^1.0.0",
187
+ "tsx": "^4.23.1",
188
+ "typescript": "^6.0.3",
189
+ "vitest": "^4.1.10",
190
+ "vue": "^3.5.39",
191
+ "@whoj/eslint-config": "9.2.0"
196
192
  },
197
193
  "resolutions": {
198
194
  "@eslint-community/eslint-utils": "catalog:peer",
@@ -200,6 +196,7 @@
200
196
  "@typescript-eslint/utils": "catalog:peer",
201
197
  "chokidar": "catalog:dev",
202
198
  "eslint": "catalog:peer",
199
+ "js-yaml": "catalog:prod",
203
200
  "semver": "catalog:dev",
204
201
  "synckit": "catalog:dev",
205
202
  "tsx": "catalog:dev"
@@ -210,11 +207,25 @@
210
207
  "lint-staged": {
211
208
  "*": "eslint --fix"
212
209
  },
210
+ "inlinedDependencies": {
211
+ "@eslint-community/eslint-utils": "4.9.1",
212
+ "@typescript-eslint/scope-manager": "8.62.0",
213
+ "@typescript-eslint/types": "8.62.0",
214
+ "@typescript-eslint/utils": "8.62.0",
215
+ "@typescript-eslint/visitor-keys": "8.62.0",
216
+ "cached-factory": "0.3.0",
217
+ "eslint-plugin-erasable-syntax-only": "0.4.2",
218
+ "eslint-visitor-keys": [
219
+ "3.4.3",
220
+ "5.0.1"
221
+ ],
222
+ "find-up-simple": "1.0.1"
223
+ },
213
224
  "scripts": {
214
225
  "build": "nr typegen && tsdown --clean --dts",
215
226
  "stub": "tsdown",
216
227
  "dev": "npx @eslint/config-inspector --config eslint.config.ts",
217
- "build:inspector": "pnpm build && npx @eslint/config-inspector build",
228
+ "build:inspector": "pnpm build && npx @eslint/config-inspector build --config eslint.config.ts --out-dir ./.eslint-config-inspector",
218
229
  "watch": "tsdown --watch",
219
230
  "lint": "eslint .",
220
231
  "lint:fix": "eslint . --fix",
@@ -222,5 +233,8 @@
222
233
  "release": "bumpp && pnpm publish",
223
234
  "test": "vitest",
224
235
  "typecheck": "tsc --noEmit"
236
+ },
237
+ "bin": {
238
+ "eslint-config": "./bin/index.mjs"
225
239
  }
226
240
  }
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: whoj-eslint-config
3
+ description: Configure ESLint flat config, customize rules, or fix lint errors in a repo that depends on @whoj/eslint-config; also covers scaffolding eslint.config with its CLI wizard.
4
+ ---
5
+
6
+ # @whoj/eslint-config
7
+
8
+ This skill applies when working in a repository that has `@whoj/eslint-config` listed as a
9
+ dependency (usually a `devDependency`) in `package.json`. It covers three things: configuring the
10
+ `whoj()` factory, fixing lint errors this config produces, and running the setup/migration CLI.
11
+
12
+ ## 1. Configuring the `whoj()` factory
13
+
14
+ The package default-exports a factory function. The minimal setup is:
15
+
16
+ ```js
17
+ // eslint.config.mjs
18
+ import whoj from '@whoj/eslint-config';
19
+
20
+ export default whoj();
21
+ ```
22
+
23
+ Key shape rules — verified against the package's own `src/factory.ts` and `src/types.ts`:
24
+
25
+ - **Boolean-or-options-object pattern**: most integration keys on the first argument (e.g. `vue`,
26
+ `react`, `typescript`, `stylistic`, `formatters`, and others) accept either
27
+ `true`/`false` or a config object for that integration. Passing `true` uses that integration's
28
+ defaults; passing an object lets you set sub-options (including that integration's own
29
+ `overrides`).
30
+ - Several integrations auto-detect and enable themselves based on installed dependencies or repo
31
+ files (e.g. `typescript` turns on if `typescript` is installed, `vue` if `vue`/`nuxt`/`vitepress`/
32
+ `@slidev/cli` is installed, `pnpm` if a `pnpm-workspace.yaml` is found). You only need to pass the
33
+ key explicitly to override the default/auto-detected value.
34
+ - **`overrides` pattern**: nearly every sub-config accepts an `overrides` field
35
+ (`OptionsOverrides`) — a plain ESLint `rules` object merged in last, so it always wins over the
36
+ config's own defaults. Prefer setting `overrides` on the specific integration rather than a
37
+ top-level blanket rule change. There is also a **deprecated** top-level `overrides` map on
38
+ `OptionsConfig`, covering the same integration keys, for the same purpose — prefer the
39
+ per-integration `overrides` field on new code.
40
+ - The factory returns a `FlatConfigComposer`, which supports chaining, e.g.
41
+ `whoj({ ... }).overrides({ 'whoj/some-name': { rules: { ... } } })` to target a specific named
42
+ config block by its `name` field, or `.append(...)` for extra flat config objects. `whoj()` also
43
+ accepts any number of extra plain flat-config-object arguments after the first, which are
44
+ appended as-is (no merging into `overrides`).
45
+ - The first argument must NOT contain a `files` property (the factory throws if it does) — pass
46
+ file-scoped rules as additional flat config arguments instead.
47
+
48
+ **Do not hardcode an exhaustive list of every option field here — it will go stale across
49
+ versions.** Before configuring anything non-trivial, read the actual shipped types and/or docs for
50
+ the installed version:
51
+
52
+ - `node_modules/@whoj/eslint-config/dist/index.d.mts` — authoritative, version-accurate type
53
+ definitions for `OptionsConfig` and every sub-options interface.
54
+ - The package's `README.md` (`Customization` section) if available, for narrative examples.
55
+
56
+ ## 2. Fixing lint errors produced by this config
57
+
58
+ This config renames plugin prefixes in reported rule IDs via `composer.renamePlugins(...)` (see
59
+ `defaultPluginRenaming` in the package's `src/factory.ts`), so error output will show the renamed
60
+ prefix, not the upstream plugin's own prefix. The verified rename table is:
61
+
62
+ | Reported prefix | Underlying plugin |
63
+ | --------------- | ------------------------------- |
64
+ | `node/*` | `n` (eslint-plugin-n) |
65
+ | `yaml/*` | `yml` (eslint-plugin-yml) |
66
+ | `test/*` | `vitest` (eslint-plugin-vitest) |
67
+ | `next/*` | `@next/next` |
68
+ | `style/*` | `@stylistic` |
69
+ | `import/*` | `import-lite` |
70
+ | `react/*` | `@eslint-react` |
71
+ | `ts/*` | `@typescript-eslint` |
72
+
73
+ This table reflects `defaultPluginRenaming` in the installed version — if a prefix doesn't match,
74
+ check `node_modules/@whoj/eslint-config/dist/index.mjs` (or `dist/index.d.mts`) for the current
75
+ exported `defaultPluginRenaming`.
76
+
77
+ When you see a rule ID like `ts/no-unused-vars`, `style/quotes`, or `node/no-process-exit` in lint
78
+ output, that maps to the upstream plugin on the right — use that to find the rule's documentation
79
+ (the renamed prefix will not resolve on the plugin's own docs site).
80
+
81
+ Rules with no rename listed above (e.g. plain `unicorn/*`, `vue/*`, `jsdoc/*`, `perfectionist/*`,
82
+ `regexp/*`) are reported under their original plugin prefix.
83
+
84
+ **To suppress or adjust a specific rule that doesn't fit the codebase**: prefer adding it to the
85
+ `overrides` object of the relevant sub-config in `eslint.config.mjs`, not a blanket
86
+ `// eslint-disable` comment or file-wide disable. Example — silencing a TypeScript rule and a
87
+ stylistic rule:
88
+
89
+ ```js
90
+ // eslint.config.mjs
91
+ import whoj from '@whoj/eslint-config';
92
+
93
+ export default whoj({
94
+ stylistic: {
95
+ overrides: {
96
+ 'style/quotes': ['error', 'double'],
97
+ },
98
+ },
99
+ typescript: {
100
+ overrides: {
101
+ 'ts/no-unused-vars': 'off',
102
+ },
103
+ },
104
+ });
105
+ ```
106
+
107
+ Only fall back to inline `// eslint-disable-next-line <rule>` for a genuinely one-off exception in
108
+ a single location; use the `overrides` mechanism when the rule should not apply project-wide or
109
+ for a whole integration's file set.
110
+
111
+ ## 3. CLI setup / migration wizard
112
+
113
+ The package ships a CLI (`bin/index.mjs`, wired to `src/cli/index.ts` → `src/cli/run.ts`) invoked
114
+ via:
115
+
116
+ ```bash
117
+ pnpm dlx @whoj/eslint-config@latest
118
+ # or
119
+ npx @whoj/eslint-config@latest
120
+ # or
121
+ bunx @whoj/eslint-config@latest
122
+ ```
123
+
124
+ Verified behavior of the wizard (reading `src/cli/run.ts`, `src/cli/constants.ts`, and
125
+ `src/cli/stages/*.ts`) — this reflects the installed version; run the CLI with `--help` to confirm
126
+ current flags/behavior if something doesn't match:
127
+
128
+ - It refuses to run if `eslint.config.js` already exists in the current directory (exits with an
129
+ error) — it is an initial-setup/migration tool, not an incremental re-configurator.
130
+ - If the repo has uncommitted git changes, it prompts for confirmation before continuing (skip
131
+ this and all prompts with `--yes`/`-y`, or `SKIP_PROMPT=1` env var).
132
+ - It prompts (multiselect) for optional framework templates to enable: `vue`, `react`, `solid`,
133
+ `svelte`, `slidev`, `astro` — pass one or more non-interactively with `--template <name>`
134
+ (repeatable), e.g. `--template vue --template react`.
135
+ - It prompts (multiselect) for optional extras: `unocss` and `formatter` (external
136
+ Prettier/dprint formatting support) — pass non-interactively with `--extra <name>` (repeatable).
137
+ - It generates `eslint.config.js` (if the consumer's `package.json` has `"type": "module"`) or
138
+ `eslint.config.mjs` (otherwise), setting the corresponding `true` flags (e.g. `vue: true`,
139
+ `formatters: true`, `unocss: true`) in the generated `whoj({...})` call based on your selections.
140
+ - If a legacy `.eslintignore` file exists, it parses it and merges its patterns into the generated
141
+ config's top-level `ignores` array (negated `!pattern` entries are preserved for un-ignoring).
142
+ - It offers (confirm prompts, default yes) to also update `.idea/jsLinters/` config for
143
+ JetBrains IDEs (WebStorm/PhpStorm) and `.vscode/settings.json` for VS Code, wiring ESLint as the
144
+ formatter and silencing stylistic rules in-editor while keeping them auto-fixable on save.
145
+ - It does not delete legacy ESLint/Prettier config files automatically — it lists any it finds
146
+ (files matching `/eslint|prettier/` but not the new `eslint.config.*`) and tells you that you can
147
+ remove them manually.
148
+ - After running, it does not install dependencies or run ESLint itself — it prints a reminder to
149
+ run `pnpm install` and `eslint --fix` next.
150
+
151
+ Use `--yes` for non-interactive/CI scaffolding when the defaults (no frameworks, no extras,
152
+ JetBrains config on) are acceptable; otherwise omit flags and let the wizard prompt.