@santi020k/eslint-config-basic 3.0.0 → 3.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/CHANGELOG.md +93 -0
- package/IMPROVEMENTS.md +270 -0
- package/README.md +6 -14
- package/dist/agent-skill-generator.d.ts +11 -2
- package/dist/agent-skill-generator.js +56 -8
- package/dist/cli-advanced.d.ts +23 -0
- package/dist/cli-advanced.js +217 -0
- package/dist/cli-migration.d.ts +26 -0
- package/dist/cli-migration.js +1134 -0
- package/dist/cli-preset.d.ts +31 -0
- package/dist/cli-preset.js +315 -0
- package/dist/cli-workflows.d.ts +63 -0
- package/dist/cli-workflows.js +444 -0
- package/dist/cli.d.ts +17 -4
- package/dist/cli.js +762 -55
- package/dist/define-config-metadata.d.ts +10 -0
- package/dist/define-config-metadata.js +18 -0
- package/dist/frameworks.d.ts +1 -25
- package/dist/frameworks.js +5 -23
- package/dist/index.d.ts +11 -10
- package/dist/index.js +146 -74
- package/dist/integrations.js +31 -23
- package/dist/optional-package-errors.d.ts +3 -0
- package/dist/optional-package-errors.js +14 -0
- package/package.json +72 -52
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,98 @@
|
|
|
1
1
|
# @santi020k/eslint-config-basic
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#104](https://github.com/santi020k/eslint-config-basic/pull/104) [`522aed9`](https://github.com/santi020k/eslint-config-basic/commit/522aed93495faebff095f6e48dc731b7730e8f7a) Thanks [@santi020k](https://github.com/santi020k)! - Add `explain-preset` adoption reports and optional temporary compatibility
|
|
8
|
+
overrides grouped by formatting, correctness, security, framework, and domain
|
|
9
|
+
rules.
|
|
10
|
+
|
|
11
|
+
Make generated configs safer for real ESLint 10 monorepos by attaching referenced plugins locally, inheriting shared project detection and Tailwind options, composing untyped TypeScript overrides after framework parsers, and making doctor/install planning aware of workspace projects and modern feature selections.
|
|
12
|
+
|
|
13
|
+
Improve the CLI for pnpm workspaces by detecting the workspace root from nested
|
|
14
|
+
projects, preserving default and named catalogs during installation, and
|
|
15
|
+
requesting companion config packages with a version range compatible with the
|
|
16
|
+
installed Basic release. Command parsing is now strict, subcommand help is
|
|
17
|
+
side-effect free, minimum-release-age blocks are diagnosed, and safe v3 config
|
|
18
|
+
syntax is modernized during migration. Optional framework and feature-pack load
|
|
19
|
+
errors distinguish missing packages from evaluation failures while retaining the
|
|
20
|
+
original cause. Packed modular monorepo and peer-health release checks now verify
|
|
21
|
+
doctor, ESLint, TypeScript, frozen pnpm installs, and owned exceptions against
|
|
22
|
+
published-package boundaries.
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [[`a93a940`](https://github.com/santi020k/eslint-config-basic/commit/a93a9406d4e69d53f0e177c65b9aceeeec6ae1ba)]:
|
|
27
|
+
- @santi020k/eslint-config-typescript@3.1.1
|
|
28
|
+
|
|
29
|
+
## 3.1.0
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Add an opt-in, lazily loaded Astro Doctor ESLint integration through `Extension.AstroDoctor` or `features: { "astro-doctor": true }`, including the recommended proprietary and official Astro rules, configuration validation, and `basic-eslint doctor` compatibility guidance.
|
|
34
|
+
|
|
35
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Add a `basic-eslint install` command that detects missing framework, feature
|
|
36
|
+
category, ESLint, and TypeScript dependencies and installs them with the
|
|
37
|
+
project's package manager. Document the command in the v3 setup flow.
|
|
38
|
+
|
|
39
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Complete the v3 lean dependency boundary and one-line recommended entry point. Framework and feature-pack packages are now optional, dynamically loaded peers with actionable installation errors. Feature factories move from the basic root export to granular extensions, formats, libraries, testing, and tools packages; `@santi020k/eslint-config-integrations` remains as a compatibility aggregate, while the new `@santi020k/eslint-config-full` package provides the batteries-included installation path. Core no longer owns JSX accessibility rules, the CLI writes the one-line configuration, and release checks enforce dependency budgets, packed-consumer compatibility, and lean production-audit boundaries.
|
|
40
|
+
|
|
41
|
+
Remove the v1 compatibility exports (`*Config`, `jsConfig`, `tsConfig`,
|
|
42
|
+
`astroConfig`, `rules`, `loadModule`, and `eslintConfig`) from the aggregate
|
|
43
|
+
v3 API. Remove the deprecated Remix package and `frameworks.remix` option;
|
|
44
|
+
Remix dependencies now resolve through the React Router v7 configuration.
|
|
45
|
+
The Lite package is now a thin compatibility re-export of Basic.
|
|
46
|
+
|
|
47
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Complete the v3 modular dependency boundary with five category feature packs.
|
|
48
|
+
Each pack owns its plugin dependencies and exposes a self-describing feature
|
|
49
|
+
registry through the shared `ConfigFeature` contract. The lean Basic composer
|
|
50
|
+
loads only registries for selected categories, Full installs every pack, and
|
|
51
|
+
Integrations remains as a compatibility aggregate.
|
|
52
|
+
|
|
53
|
+
Move agent-skill generation from the Basic root export to
|
|
54
|
+
`@santi020k/eslint-config-basic/agent`, keeping Node-oriented tooling out of the
|
|
55
|
+
configuration runtime entry point.
|
|
56
|
+
|
|
57
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Polish real-world consumer configuration: add one `root` option for detection,
|
|
58
|
+
TypeScript, Tailwind, projects, and `.gitignore`; auto-detect and scope workspace
|
|
59
|
+
packages without leaking root framework dependencies; accept native project
|
|
60
|
+
service options; and syntax-lint TypeScript config files plus explicit
|
|
61
|
+
`untypedFiles` without consumer-side `typescript-eslint` imports.
|
|
62
|
+
|
|
63
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Drop Node.js 20 support and require Node.js 22.19.0 or newer across the package
|
|
64
|
+
suite. This aligns the published engine contract with the current ESLint,
|
|
65
|
+
Lighthouse, GraphQL, and Unicorn toolchain.
|
|
66
|
+
|
|
67
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Add v3 lifecycle tooling to the `basic-eslint` CLI: an automated v2-to-v3
|
|
68
|
+
migration planner and writer, incremental strict-mode baselines backed by ESLint
|
|
69
|
+
bulk suppressions, rule-performance profiling with concurrency comparison,
|
|
70
|
+
effective-rule snapshots and diffs, and optional official ESLint MCP
|
|
71
|
+
scaffolding for generated agent standards.
|
|
72
|
+
|
|
73
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Add v3 project-assistance workflows: safe doctor repairs, explicit init output,
|
|
74
|
+
effective rule/source explanations, package compatibility reporting, lint
|
|
75
|
+
warning and performance budgets, real v2 migration fixtures, and a reusable
|
|
76
|
+
GitHub Action for consumer checks.
|
|
77
|
+
|
|
78
|
+
### Patch Changes
|
|
79
|
+
|
|
80
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Resolve package-scoped Tailwind and Astro configuration roots, keep lazy framework imports anchored to their declaring package, add shared `projectDefaults` inheritance for monorepos, and move integration host libraries to optional peers.
|
|
81
|
+
|
|
82
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Deprecate the Lite and Integrations compatibility packages during v3 and
|
|
83
|
+
schedule their removal for v4. Add TypeScript deprecation annotations,
|
|
84
|
+
migration guidance, and post-publish npm registry warnings. Also deprecate the
|
|
85
|
+
`integrations` option alias and `doctor --lite-install` compatibility workflow.
|
|
86
|
+
|
|
87
|
+
- [#99](https://github.com/santi020k/eslint-config-basic/pull/99) [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967) Thanks [@santi020k](https://github.com/santi020k)! - Include explicit feature, preset, and strict config selections in CLI install
|
|
88
|
+
and doctor repair planning, preserve runtime dependency fields and peer metadata
|
|
89
|
+
during v3 migrations, analyze TypeScript ESLint config files during doctor
|
|
90
|
+
checks, and honor dot-prefixed workspace exclusion patterns during project
|
|
91
|
+
detection.
|
|
92
|
+
- Updated dependencies [[`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967), [`0f89b89`](https://github.com/santi020k/eslint-config-basic/commit/0f89b89b466732ea582650b4e0baee0c47b0c967)]:
|
|
93
|
+
- @santi020k/eslint-config-core@3.1.0
|
|
94
|
+
- @santi020k/eslint-config-typescript@3.1.0
|
|
95
|
+
|
|
3
96
|
## 3.0.0
|
|
4
97
|
|
|
5
98
|
### Major Changes
|
package/IMPROVEMENTS.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# Consumer-driven improvements
|
|
2
|
+
|
|
3
|
+
These opportunities came from migrating a real ESLint 10, pnpm, TypeScript,
|
|
4
|
+
Astro, Tailwind, and Vitest monorepo to the modular v3 packages. They are
|
|
5
|
+
ordered by how directly they affect whether a consumer can install and lint
|
|
6
|
+
successfully.
|
|
7
|
+
|
|
8
|
+
## Implemented for the next release
|
|
9
|
+
|
|
10
|
+
- Every generated rule block now receives the plugin objects it references,
|
|
11
|
+
with a contract test covering the complete optional-feature registry.
|
|
12
|
+
- Doctor and install planning now inspect every pnpm workspace project and
|
|
13
|
+
explicit `features` selection.
|
|
14
|
+
- Normal doctor output reports modular v3 packages and no longer recommends
|
|
15
|
+
deprecated Lite packages unless `--lite-install` is requested.
|
|
16
|
+
- Scoped projects inherit root detection and Tailwind defaults, while
|
|
17
|
+
`projectDefaults` and project options remain the more specific layers.
|
|
18
|
+
- TypeScript `untypedFiles` overrides are composed after framework parser
|
|
19
|
+
configs, including Astro projects.
|
|
20
|
+
- `init --explicit` now generates the v3 `features` map.
|
|
21
|
+
- Preset adoption reports compare effective rules by category and can write a
|
|
22
|
+
temporary compatibility override.
|
|
23
|
+
- CLI subcommands now have strict, side-effect-free help and flag validation.
|
|
24
|
+
- Minimum-release-age failures receive a dedicated compatible-range diagnostic.
|
|
25
|
+
- Release checks enforce peer health with owned, conditional exceptions.
|
|
26
|
+
- Safe migration rewrites prefer `features` and `root`, while dynamic cases
|
|
27
|
+
receive explicit manual-action guidance.
|
|
28
|
+
- The release check now installs a packed pnpm monorepo consumer with Astro,
|
|
29
|
+
Tailwind, TypeScript, Vitest, format, tool, and extension packs; it runs
|
|
30
|
+
doctor, ESLint 10, config typechecking, and a frozen-lockfile reinstall.
|
|
31
|
+
|
|
32
|
+
## Completed — Make every emitted config independently valid in ESLint 10
|
|
33
|
+
|
|
34
|
+
The Tailwind settings config can reference
|
|
35
|
+
`better-tailwindcss/no-unknown-classes` while the
|
|
36
|
+
`better-tailwindcss` plugin is registered on another config object. ESLint 10
|
|
37
|
+
then reports that it cannot find the plugin, especially when a consumer adds a
|
|
38
|
+
later file-scoped override for the rule.
|
|
39
|
+
|
|
40
|
+
Improve the composer so every emitted config object that references a plugin
|
|
41
|
+
rule also carries that plugin registration. This should be guaranteed for all
|
|
42
|
+
framework and feature-pack configs, not only Tailwind.
|
|
43
|
+
|
|
44
|
+
Acceptance criteria:
|
|
45
|
+
|
|
46
|
+
- `defineConfig()` output can be passed directly to ESLint 10 without a
|
|
47
|
+
consumer-side plugin-copying workaround.
|
|
48
|
+
- A later `{ files, rules }` override can disable a feature-pack rule without
|
|
49
|
+
manually importing or locating the plugin.
|
|
50
|
+
- A contract test checks every emitted `plugin/rule` entry against the plugins
|
|
51
|
+
available to that config object.
|
|
52
|
+
|
|
53
|
+
## Completed — Make the install planner project-aware
|
|
54
|
+
|
|
55
|
+
A monorepo can disable root-level library detection and still enable a
|
|
56
|
+
library-backed feature inside a scoped project. For example, an Astro docs
|
|
57
|
+
project can detect Tailwind and require
|
|
58
|
+
`@santi020k/eslint-config-libraries`, even when the root has
|
|
59
|
+
`detection: { libraries: false }`.
|
|
60
|
+
|
|
61
|
+
The doctor and install commands should resolve the same project scopes,
|
|
62
|
+
detection controls, and feature graph as `defineConfig()`. They should report
|
|
63
|
+
all required category packages before ESLint is started.
|
|
64
|
+
|
|
65
|
+
Acceptance criteria:
|
|
66
|
+
|
|
67
|
+
- `basic-eslint doctor` reports a missing category package required by any
|
|
68
|
+
configured project.
|
|
69
|
+
- `basic-eslint install --dry-run` returns the exact modular packages required
|
|
70
|
+
by root and project-scoped features.
|
|
71
|
+
- Detection disabled at one scope does not suppress requirements discovered in
|
|
72
|
+
another scope.
|
|
73
|
+
|
|
74
|
+
## Completed — Replace deprecated Lite advice in normal doctor output
|
|
75
|
+
|
|
76
|
+
The JSON doctor result currently includes a `liteInstallCommand` that recommends
|
|
77
|
+
`@santi020k/eslint-config-lite` and
|
|
78
|
+
`@santi020k/eslint-config-integrations`, even when the consumer uses the v3
|
|
79
|
+
basic package and did not request `--lite-install`.
|
|
80
|
+
|
|
81
|
+
Normal doctor output should recommend the lean v3 package set. Deprecated Lite
|
|
82
|
+
advice should only be produced when `--lite-install` is explicitly requested
|
|
83
|
+
and should be labeled as a compatibility workflow.
|
|
84
|
+
|
|
85
|
+
Acceptance criteria:
|
|
86
|
+
|
|
87
|
+
- Default JSON output exposes a modular `installCommand` or structured
|
|
88
|
+
`requiredPackages` result.
|
|
89
|
+
- `liteInstallCommand` is omitted unless `--lite-install` is present.
|
|
90
|
+
- The suggested command uses the detected framework and category packages.
|
|
91
|
+
|
|
92
|
+
## Completed — Clarify root versus project option inheritance
|
|
93
|
+
|
|
94
|
+
Settings such as `tailwind.noUnknownClasses` may need to be repeated inside a
|
|
95
|
+
project scope to affect that project's generated config. Consumers need a clear
|
|
96
|
+
way to express shared defaults without learning this through lint failures.
|
|
97
|
+
|
|
98
|
+
Prefer one of these approaches:
|
|
99
|
+
|
|
100
|
+
1. inherit safe root options into projects consistently; or
|
|
101
|
+
2. require `projectDefaults`, document it prominently, and have doctor suggest
|
|
102
|
+
moving misplaced root options there.
|
|
103
|
+
|
|
104
|
+
Acceptance criteria:
|
|
105
|
+
|
|
106
|
+
- The monorepo guide shows how root options, `projectDefaults`, and project
|
|
107
|
+
overrides compose.
|
|
108
|
+
- Doctor warns when a root option has no effect on scoped projects.
|
|
109
|
+
- Tests cover Tailwind, TypeScript, detection controls, and runtime defaults.
|
|
110
|
+
|
|
111
|
+
## Completed — Add a realistic modular-consumer release fixture
|
|
112
|
+
|
|
113
|
+
Add a packed-package fixture matching a production monorepo:
|
|
114
|
+
|
|
115
|
+
- pnpm workspace with multiple package projects;
|
|
116
|
+
- Astro and Tailwind in one app only;
|
|
117
|
+
- TypeScript project service;
|
|
118
|
+
- Vitest, JSONC, Markdown, YAML, pnpm, CSpell, boundaries, and Unicorn;
|
|
119
|
+
- explicit `features` configuration;
|
|
120
|
+
- ESLint 10 with `--max-warnings=0`.
|
|
121
|
+
|
|
122
|
+
The fixture should install from packed tarballs, run doctor, lint, typecheck,
|
|
123
|
+
and verify that the lockfile is frozen-installable. This catches package
|
|
124
|
+
boundary and resolver errors that workspace symlinks can hide.
|
|
125
|
+
|
|
126
|
+
## Completed — Preserve pnpm workspace catalogs during installation
|
|
127
|
+
|
|
128
|
+
`basic-eslint install` currently writes a direct semver range into the nearest
|
|
129
|
+
`package.json`. In catalog-managed workspaces this immediately violates
|
|
130
|
+
`pnpm/json-enforce-catalog` and forces the consumer to move every dependency by
|
|
131
|
+
hand.
|
|
132
|
+
|
|
133
|
+
The installer should discover `pnpm-workspace.yaml`, update the appropriate
|
|
134
|
+
catalog entry, and write `catalog:` in the package manifest. It should also
|
|
135
|
+
detect the workspace package manager from the root instead of falling back to
|
|
136
|
+
npm when invoked from a nested package.
|
|
137
|
+
|
|
138
|
+
Acceptance criteria:
|
|
139
|
+
|
|
140
|
+
- Existing `catalog:`, `catalog:default`, and named-catalog conventions are
|
|
141
|
+
preserved.
|
|
142
|
+
- `--dry-run` shows both manifest and workspace-catalog changes.
|
|
143
|
+
- Running from a nested workspace package still uses the root package manager.
|
|
144
|
+
- A second installation is idempotent.
|
|
145
|
+
|
|
146
|
+
The installer now resolves the pnpm workspace root even when invoked from a
|
|
147
|
+
nested package and asks pnpm to save new dependencies into the existing
|
|
148
|
+
default or named catalog. Dry runs print the exact catalog-aware command.
|
|
149
|
+
Idempotency is delegated to pnpm's catalog-aware `add` operation.
|
|
150
|
+
|
|
151
|
+
## Completed — Keep `untypedFiles` last enough to be effective
|
|
152
|
+
|
|
153
|
+
In Astro consumers, the generated `eslint-config-typescript/untyped-files`
|
|
154
|
+
entry can appear before a later framework entry re-enables typed parser
|
|
155
|
+
options. Consumers then receive a parser error saying that `project` and
|
|
156
|
+
`projectService` are enabled together, and must find and append the generated
|
|
157
|
+
entry manually.
|
|
158
|
+
|
|
159
|
+
Compose untyped-file overrides after framework and project configs, or merge
|
|
160
|
+
parser options with a deterministic precedence model.
|
|
161
|
+
|
|
162
|
+
Acceptance criteria:
|
|
163
|
+
|
|
164
|
+
- `typescript.untypedFiles: ['**/*.astro']` works with the Astro framework.
|
|
165
|
+
- The same behavior works inside a scoped monorepo project.
|
|
166
|
+
- Consumers never need to inspect config names and reorder generated entries.
|
|
167
|
+
|
|
168
|
+
## Completed — Preserve nested import failures
|
|
169
|
+
|
|
170
|
+
When an optional framework package exists but fails while importing one of its
|
|
171
|
+
own dependencies, the current error can say only that the optional framework
|
|
172
|
+
could not be loaded. During migration, a React integration failure caused by a
|
|
173
|
+
Zod compatibility mismatch looked like a missing React package.
|
|
174
|
+
|
|
175
|
+
Error handling should distinguish:
|
|
176
|
+
|
|
177
|
+
- package not installed;
|
|
178
|
+
- installed package failed to evaluate;
|
|
179
|
+
- incompatible peer or transitive dependency.
|
|
180
|
+
|
|
181
|
+
Always retain the original error as `cause`, show the failing package path, and
|
|
182
|
+
include the one-command remediation only when the package is genuinely absent.
|
|
183
|
+
|
|
184
|
+
## Completed — Make preset expansion visible and adoption-friendly
|
|
185
|
+
|
|
186
|
+
`Preset.App`, `Preset.Worker`, and `Preset.Library` can activate substantially
|
|
187
|
+
more rules than an existing default config. That is useful for new projects,
|
|
188
|
+
but a migration can unexpectedly introduce hundreds of formatting or
|
|
189
|
+
domain-specific findings.
|
|
190
|
+
|
|
191
|
+
Add `basic-eslint explain-preset <preset>` and a migration mode that compares
|
|
192
|
+
the current effective config with the selected preset. Group the output into
|
|
193
|
+
formatting, correctness, security, framework, and domain rules, and generate a
|
|
194
|
+
temporary compatibility override when requested.
|
|
195
|
+
|
|
196
|
+
## Completed — Make CLI help side-effect free
|
|
197
|
+
|
|
198
|
+
`basic-eslint install --help` should print subcommand help and exit without
|
|
199
|
+
installing packages. Add command-level parsing tests for `--help`, `--dry-run`,
|
|
200
|
+
and invalid flags so documentation discovery can never mutate a workspace.
|
|
201
|
+
|
|
202
|
+
Help flags are intercepted before command dispatch, dedicated subcommand help
|
|
203
|
+
lists only supported flags, and strict parsing rejects unknown or incomplete
|
|
204
|
+
options before any handler can mutate the workspace.
|
|
205
|
+
|
|
206
|
+
## Completed — Respect release compatibility under minimum-age policies
|
|
207
|
+
|
|
208
|
+
With pnpm's `minimumReleaseAge`, requesting an unversioned companion package can
|
|
209
|
+
resolve an older major while Basic itself is already pinned to 3.1. The install
|
|
210
|
+
planner should derive companion ranges from the installed Basic major/minor
|
|
211
|
+
instead of asking the package manager for an unconstrained latest version.
|
|
212
|
+
|
|
213
|
+
For Basic 3.1, generated install requests should use a compatible 3.1 range and
|
|
214
|
+
produce a clear message when workspace supply-chain policy temporarily blocks
|
|
215
|
+
that release.
|
|
216
|
+
|
|
217
|
+
Install and doctor commands derive a compatible caret range from the installed
|
|
218
|
+
Basic dependency, including catalog-backed versions, and use that range for
|
|
219
|
+
modular companion packages. Install also recognizes pnpm minimum-release-age
|
|
220
|
+
failures and explains the wait-or-exclude policy without suggesting an
|
|
221
|
+
incompatible release.
|
|
222
|
+
|
|
223
|
+
## Completed — Treat peer health as a release signal
|
|
224
|
+
|
|
225
|
+
The modular consumer installed and linted successfully but `pnpm peers check`
|
|
226
|
+
still reported stale ESLint and TypeScript peer ranges from transitive plugins.
|
|
227
|
+
These warnings make genuine missing-peer failures harder to notice.
|
|
228
|
+
|
|
229
|
+
Add a release report that:
|
|
230
|
+
|
|
231
|
+
- runs the package manager's peer checker in packed consumer fixtures;
|
|
232
|
+
- identifies which direct package introduces each warning;
|
|
233
|
+
- fails for newly introduced actionable warnings;
|
|
234
|
+
- records explicitly accepted upstream range gaps with an owner and removal
|
|
235
|
+
condition.
|
|
236
|
+
|
|
237
|
+
## Completed — Generate modern configuration during migration
|
|
238
|
+
|
|
239
|
+
Migration output should prefer the v3 `features` map and `root` option over
|
|
240
|
+
legacy category arrays and root-directory aliases. When custom classes are
|
|
241
|
+
detected, it should offer the supported project-scoped Tailwind option instead
|
|
242
|
+
of generating a raw rule override.
|
|
243
|
+
|
|
244
|
+
`init --explicit` emits `features`. The v3 migration rewrites literal category
|
|
245
|
+
arrays, the deprecated `integrations` alias, and unambiguous root aliases.
|
|
246
|
+
Expressions, spreads, comments inside dynamic selections, conflicting root
|
|
247
|
+
aliases, and raw Tailwind rule overrides are preserved and reported as manual
|
|
248
|
+
actions so migration never changes behavior it cannot prove safe.
|
|
249
|
+
|
|
250
|
+
An ideal generated result is concise and self-explanatory:
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
export default await defineConfig({
|
|
254
|
+
features: {
|
|
255
|
+
boundaries: true,
|
|
256
|
+
jsonc: true,
|
|
257
|
+
markdown: true,
|
|
258
|
+
pnpm: true,
|
|
259
|
+
unicorn: true,
|
|
260
|
+
vitest: true,
|
|
261
|
+
yaml: true
|
|
262
|
+
},
|
|
263
|
+
projects: {
|
|
264
|
+
'apps/docs': {
|
|
265
|
+
tailwind: { noUnknownClasses: false }
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
root: import.meta.dirname
|
|
269
|
+
})
|
|
270
|
+
```
|
package/README.md
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
1
|
# @santi020k/eslint-config-basic
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The primary entry point that composes all feature-specific configurations.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
npm install -D eslint @santi020k/eslint-config-basic
|
|
7
|
-
```
|
|
5
|
+
This package is part of the [`@santi020k/eslint-config-basic`](https://github.com/santi020k/eslint-config-basic) monorepo.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
- Docs: [Main package](https://eslint.santi020k.com/packages/basic)
|
|
8
|
+
- Repository: [santi020k/eslint-config-basic](https://github.com/santi020k/eslint-config-basic)
|
|
9
|
+
- Author: [santi020k](https://santi020k.com)
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
that the project uses. Choose `@santi020k/eslint-config-full` for the
|
|
15
|
-
batteries-included dependency model.
|
|
16
|
-
|
|
17
|
-
- [Documentation](https://eslint.santi020k.com/)
|
|
18
|
-
- [Migration from v2](https://eslint.santi020k.com/guide/migration-v2-to-v3/)
|
|
19
|
-
- [Repository](https://github.com/santi020k/eslint-config-basic)
|
|
11
|
+
The canonical documentation lives on the Starlight site, so this README intentionally stays short to avoid duplication.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
declare const ESLINT_CONFIG_FILENAMES: readonly ["eslint.config.js", "eslint.config.mjs", "eslint.config.cjs", "eslint.config.ts", "eslint.config.mts", "eslint.config.cts"];
|
|
1
2
|
interface AgentTarget {
|
|
2
3
|
/** Format variant used when generating content */
|
|
3
4
|
format: 'cursor' | 'frontmatter' | 'kiro' | 'plain';
|
|
@@ -50,6 +51,12 @@ interface GenerateSkillOptions {
|
|
|
50
51
|
* @default false
|
|
51
52
|
*/
|
|
52
53
|
force?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Add the official `eslint --mcp` server to the project root `.mcp.json`.
|
|
56
|
+
* Existing unrelated MCP server entries are preserved.
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
withEslintMcp?: boolean;
|
|
53
60
|
}
|
|
54
61
|
interface GenerateSkillResult {
|
|
55
62
|
skipped: string[];
|
|
@@ -90,7 +97,7 @@ declare const generateSkillContent: (features: EslintConfigFeatures, format: Age
|
|
|
90
97
|
*
|
|
91
98
|
* @example
|
|
92
99
|
* ```ts
|
|
93
|
-
* import { generateAgentSkills } from '@santi020k/eslint-config-basic'
|
|
100
|
+
* import { generateAgentSkills } from '@santi020k/eslint-config-basic/agent'
|
|
94
101
|
*
|
|
95
102
|
* const result = await generateAgentSkills({ cwd: process.cwd() })
|
|
96
103
|
* process.stdout.write(`Written to: ${result.written}\n`)
|
|
@@ -102,7 +109,9 @@ interface HandleGenerateSkillFlags {
|
|
|
102
109
|
check?: boolean;
|
|
103
110
|
/** Create a root `AGENTS.md` when it does not exist */
|
|
104
111
|
createAgentsMd?: boolean;
|
|
112
|
+
/** Add the official ESLint MCP server to `.mcp.json` */
|
|
113
|
+
withEslintMcp?: boolean;
|
|
105
114
|
}
|
|
106
115
|
declare const handleGenerateSkill: (cwd?: string, force?: boolean, flags?: HandleGenerateSkillFlags) => Promise<void>;
|
|
107
116
|
|
|
108
|
-
export { AGENT_TARGETS, type AgentTarget, type EslintConfigFeatures, type GenerateSkillOptions, type GenerateSkillResult, type HandleGenerateSkillFlags, analyzeEslintConfig, generateAgentSkills, generateSkillContent, handleGenerateSkill };
|
|
117
|
+
export { AGENT_TARGETS, type AgentTarget, ESLINT_CONFIG_FILENAMES, type EslintConfigFeatures, type GenerateSkillOptions, type GenerateSkillResult, type HandleGenerateSkillFlags, analyzeEslintConfig, generateAgentSkills, generateSkillContent, handleGenerateSkill };
|
|
@@ -2,6 +2,14 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { detectProjectOptions } from "./index.js";
|
|
5
|
+
const ESLINT_CONFIG_FILENAMES = [
|
|
6
|
+
"eslint.config.js",
|
|
7
|
+
"eslint.config.mjs",
|
|
8
|
+
"eslint.config.cjs",
|
|
9
|
+
"eslint.config.ts",
|
|
10
|
+
"eslint.config.mts",
|
|
11
|
+
"eslint.config.cts"
|
|
12
|
+
];
|
|
5
13
|
const AGENT_TARGETS = [
|
|
6
14
|
{
|
|
7
15
|
format: "frontmatter",
|
|
@@ -97,7 +105,6 @@ const FEATURE_MAP = [
|
|
|
97
105
|
["eslint-config-expo/", "frameworks", "Expo"],
|
|
98
106
|
["eslint-config-qwik/", "frameworks", "Qwik"],
|
|
99
107
|
["eslint-config-react-router/", "frameworks", "React Router"],
|
|
100
|
-
["eslint-config-remix/", "frameworks", "Remix"],
|
|
101
108
|
["eslint-config-solid/", "frameworks", "SolidJS"],
|
|
102
109
|
["eslint-config-nuxt/", "frameworks", "Nuxt"],
|
|
103
110
|
["eslint-config-lit/", "frameworks", "Lit"],
|
|
@@ -187,7 +194,6 @@ const DETECTED_FRAMEWORK_LABELS = /* @__PURE__ */ new Map([
|
|
|
187
194
|
["qwik", "Qwik"],
|
|
188
195
|
["react", "React"],
|
|
189
196
|
["react-router", "React Router"],
|
|
190
|
-
["remix", "Remix"],
|
|
191
197
|
["slidev", "Slidev"],
|
|
192
198
|
["solid", "SolidJS"],
|
|
193
199
|
["svelte", "Svelte"],
|
|
@@ -302,7 +308,7 @@ const extractFeatures = (configs, lintCommand, configFile) => {
|
|
|
302
308
|
return features;
|
|
303
309
|
};
|
|
304
310
|
const findEslintConfig = (cwd) => {
|
|
305
|
-
for (const name of
|
|
311
|
+
for (const name of ESLINT_CONFIG_FILENAMES) {
|
|
306
312
|
const p = join(cwd, name);
|
|
307
313
|
if (existsSync(p)) return p;
|
|
308
314
|
}
|
|
@@ -594,10 +600,46 @@ const resolveSkillOptions = (opts) => ({
|
|
|
594
600
|
check: opts.check ?? false,
|
|
595
601
|
createAgentsMd: opts.createAgentsMd ?? false,
|
|
596
602
|
cwd: opts.cwd ?? process.cwd(),
|
|
597
|
-
force: opts.force ?? false
|
|
603
|
+
force: opts.force ?? false,
|
|
604
|
+
withEslintMcp: opts.withEslintMcp ?? false
|
|
598
605
|
});
|
|
606
|
+
const createEslintMcpServer = (cwd) => {
|
|
607
|
+
if (existsSync(join(cwd, "pnpm-lock.yaml")) || existsSync(join(cwd, "pnpm-workspace.yaml"))) {
|
|
608
|
+
return { args: ["exec", "eslint", "--mcp"], command: "pnpm" };
|
|
609
|
+
}
|
|
610
|
+
if (existsSync(join(cwd, "yarn.lock"))) {
|
|
611
|
+
return { args: ["exec", "eslint", "--mcp"], command: "yarn" };
|
|
612
|
+
}
|
|
613
|
+
if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) {
|
|
614
|
+
return { args: ["eslint", "--mcp"], command: "bunx" };
|
|
615
|
+
}
|
|
616
|
+
return { args: ["eslint", "--mcp"], command: "npx" };
|
|
617
|
+
};
|
|
618
|
+
const handleEslintMcpConfig = (cwd, force, check) => {
|
|
619
|
+
const configPath = join(cwd, ".mcp.json");
|
|
620
|
+
const server = createEslintMcpServer(cwd);
|
|
621
|
+
let config = {};
|
|
622
|
+
if (existsSync(configPath)) {
|
|
623
|
+
try {
|
|
624
|
+
config = JSON.parse(readFileSync(configPath, "utf8"));
|
|
625
|
+
} catch {
|
|
626
|
+
throw new Error(".mcp.json is invalid JSON; fix it before adding the ESLint MCP server.");
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
const current = config.mcpServers?.eslint;
|
|
630
|
+
const isCurrent = JSON.stringify(current) === JSON.stringify(server);
|
|
631
|
+
if (check) return isCurrent ? "skipped" : "stale";
|
|
632
|
+
if (current && !force) return "skipped";
|
|
633
|
+
config.mcpServers = {
|
|
634
|
+
...config.mcpServers,
|
|
635
|
+
eslint: server
|
|
636
|
+
};
|
|
637
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}
|
|
638
|
+
`);
|
|
639
|
+
return "written";
|
|
640
|
+
};
|
|
599
641
|
const generateAgentSkills = async (opts = {}) => {
|
|
600
|
-
const { check, createAgentsMd, cwd, force } = resolveSkillOptions(opts);
|
|
642
|
+
const { check, createAgentsMd, cwd, force, withEslintMcp } = resolveSkillOptions(opts);
|
|
601
643
|
const written = [];
|
|
602
644
|
const skipped = [];
|
|
603
645
|
const stale = [];
|
|
@@ -620,6 +662,10 @@ const generateAgentSkills = async (opts = {}) => {
|
|
|
620
662
|
recordGuardedResult(copilotPath, handleGuardedSectionFile(copilotPath, plainBody, force, check));
|
|
621
663
|
const agentsMdPath = join(cwd, AGENTS_MD_PATH);
|
|
622
664
|
recordGuardedResult(agentsMdPath, handleGuardedSectionFile(agentsMdPath, plainBody, force, check, createAgentsMd));
|
|
665
|
+
if (withEslintMcp) {
|
|
666
|
+
const eslintMcpPath = join(cwd, ".mcp.json");
|
|
667
|
+
recordGuardedResult(eslintMcpPath, handleEslintMcpConfig(cwd, force, check));
|
|
668
|
+
}
|
|
623
669
|
for (const target of AGENT_TARGETS) {
|
|
624
670
|
const agentFolder = join(cwd, target.markerFolder);
|
|
625
671
|
if (!existsSync(agentFolder)) continue;
|
|
@@ -640,7 +686,8 @@ const generateAgentSkills = async (opts = {}) => {
|
|
|
640
686
|
};
|
|
641
687
|
const resolveGenerateSkillFlags = (flags) => ({
|
|
642
688
|
check: flags.check ?? false,
|
|
643
|
-
createAgentsMd: flags.createAgentsMd ?? false
|
|
689
|
+
createAgentsMd: flags.createAgentsMd ?? false,
|
|
690
|
+
withEslintMcp: flags.withEslintMcp ?? false
|
|
644
691
|
});
|
|
645
692
|
const outputGenerateSkillResults = (check, result, cwd) => {
|
|
646
693
|
if (check) {
|
|
@@ -675,7 +722,7 @@ const outputGenerateSkillResults = (check, result, cwd) => {
|
|
|
675
722
|
}
|
|
676
723
|
};
|
|
677
724
|
const handleGenerateSkill = async (cwd = process.cwd(), force = false, flags = {}) => {
|
|
678
|
-
const { check, createAgentsMd } = resolveGenerateSkillFlags(flags);
|
|
725
|
+
const { check, createAgentsMd, withEslintMcp } = resolveGenerateSkillFlags(flags);
|
|
679
726
|
console.log(check ? "\u{1F50D} Checking AI agent skill files..." : "\u{1F50D} Scanning for AI agent folders...");
|
|
680
727
|
const configFile = findEslintConfig(cwd);
|
|
681
728
|
if (configFile) {
|
|
@@ -683,7 +730,7 @@ const handleGenerateSkill = async (cwd = process.cwd(), force = false, flags = {
|
|
|
683
730
|
} else {
|
|
684
731
|
console.log("\u26A0\uFE0F No eslint.config.js found \u2014 falling back to package.json detection.");
|
|
685
732
|
}
|
|
686
|
-
const result = await generateAgentSkills({ check, createAgentsMd, cwd, force });
|
|
733
|
+
const result = await generateAgentSkills({ check, createAgentsMd, cwd, force, withEslintMcp });
|
|
687
734
|
if (result.written.length === 0 && result.skipped.length === 0 && result.stale.length === 0) {
|
|
688
735
|
console.log("\n\u26A0\uFE0F No agent folders found (.agent, .agents, .claude, .cursor, .windsurf, .copilot, .aider, .gemini, .clinerules, .roo, .kiro) and no AGENTS.md.");
|
|
689
736
|
console.log(" Create one of those folders first (or re-run with --create to scaffold AGENTS.md).");
|
|
@@ -693,6 +740,7 @@ const handleGenerateSkill = async (cwd = process.cwd(), force = false, flags = {
|
|
|
693
740
|
};
|
|
694
741
|
export {
|
|
695
742
|
AGENT_TARGETS,
|
|
743
|
+
ESLINT_CONFIG_FILENAMES,
|
|
696
744
|
analyzeEslintConfig,
|
|
697
745
|
generateAgentSkills,
|
|
698
746
|
generateSkillContent,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ExplainRuleOptions {
|
|
2
|
+
file?: string;
|
|
3
|
+
json?: boolean;
|
|
4
|
+
rule: string;
|
|
5
|
+
}
|
|
6
|
+
declare const handleExplainRule: (cwd: string | undefined, options: ExplainRuleOptions) => Promise<void>;
|
|
7
|
+
declare const createCompatibilityReport: (cwd?: string) => {
|
|
8
|
+
compatible: boolean;
|
|
9
|
+
packages: {
|
|
10
|
+
declared: string;
|
|
11
|
+
issues: string[];
|
|
12
|
+
name: string;
|
|
13
|
+
resolved: string | null;
|
|
14
|
+
}[];
|
|
15
|
+
runtime: {
|
|
16
|
+
eslint: string | null;
|
|
17
|
+
node: string;
|
|
18
|
+
typescript: string | null;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
declare const handleCompatibility: (cwd?: string, json?: boolean) => void;
|
|
22
|
+
|
|
23
|
+
export { type ExplainRuleOptions, createCompatibilityReport, handleCompatibility, handleExplainRule };
|