@santi020k/eslint-config-basic 3.5.3 → 3.6.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 CHANGED
@@ -1,5 +1,54 @@
1
1
  # @santi020k/eslint-config-basic
2
2
 
3
+ ## 3.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Preserve Node 22.19 support with compatible stable dependencies and deliver the maintained braces recursion guard in affected adapter artifacts. ESLint, GraphQL, and TypeScript remain consumer peers.
8
+
9
+ - Add optional typed formatting preferences with workspace inheritance. Restrict Testing Library import heuristics in Playwright-owned scopes while retaining actual Testing Library checks and unit-test behavior.
10
+
11
+ Doctor now inventories local workspace lint declarations, reports sampled effective-config evidence for potentially redundant Astro workaround fields, and generates missing lint scripts with zero-warning enforcement. Existing scripts, defaults, and overrides remain unchanged.
12
+
13
+ Consumer regression fixtures cover mixed Astro/React workspaces, independently linted Expo apps with typed Astro scopes, Playwright/Testing Library coexistence, and React/Hono runtime scopes. No mandatory migration is required. For custom browser-test locations, configure `testingFiles.playwright`; review Doctor candidates across all affected globs before removing fields.
14
+
15
+ - Refresh the supported dependency ecosystem and security overrides, preserve Zod integration defaults across the plugin upgrade, guarantee TypeScript parsing in Astro frontmatter, and improve the release gates and npm documentation.
16
+
17
+ The Basic README gains light/dark artwork, package selection guidance, and published asset/support links. The documentation site uses npm Lumen Astro 4 with accessible motion, contextual GitHub issue reporting, resilient browser feedback, responsive report output, and full-glyph WOFF2 fonts that substantially reduce transfer size.
18
+
19
+ Release preparation now accepts validated `release/v<stable-semver>` branches alongside Changesets-generated branches; both undergo source/version checks, complete preflight, and a dependency audit before publication.
20
+
21
+ Release verification now retains Node 24 and Windows compatibility checks on
22
+ release pull requests, runs Linux and macOS visual baselines plus Firefox/WebKit
23
+ navigation coverage, and verifies registry artifacts, provenance, release tags,
24
+ published consumer behavior, and live documentation after deployment.
25
+
26
+ ### Patch Changes
27
+
28
+ - Center the documentation mobile menu and close icons, align utility controls, and clarify the open state with a subtle accented button surface.
29
+
30
+ - Animate documentation mobile menu opening and closing with subtle icon and drawer transitions, including a brief blur and immediate reduced-motion states.
31
+
32
+ - Fix narrow mobile header clipping, improve sidebar readability, and remove redundant spacing around the mobile table of contents.
33
+
34
+ - Resolve generated Markdown reference links to published routes across current docs and frozen archives, preserving fragments and deployment base paths.
35
+
36
+ - Clarify the v3.6.0 release candidate, upgrade verification, current troubleshooting, and shared editor and CI lint setup.
37
+ - Updated dependencies [`ddaa3c8`]:
38
+ - @santi020k/eslint-config-core@3.2.0
39
+ - @santi020k/eslint-config-typescript@3.1.6
40
+
41
+ ## 3.5.4
42
+
43
+ ### Patch Changes
44
+
45
+ - [#134](https://github.com/santi020k/eslint-config-basic/pull/134) [`07cb188`](https://github.com/santi020k/eslint-config-basic/commit/07cb188c727606bf00b67928ac20e95c2201ddc6) Thanks [@santi020k](https://github.com/santi020k)! - Replace the minimal npm package READMEs with practical installation,
46
+ configuration, compatibility, and support guidance. The Basic package now
47
+ publishes the comprehensive root README instead of a short placeholder.
48
+ - Updated dependencies [[`07cb188`](https://github.com/santi020k/eslint-config-basic/commit/07cb188c727606bf00b67928ac20e95c2201ddc6)]:
49
+ - @santi020k/eslint-config-core@3.1.3
50
+ - @santi020k/eslint-config-typescript@3.1.5
51
+
3
52
  ## 3.5.3
4
53
 
5
54
  ### Patch Changes
package/README.md CHANGED
@@ -1,11 +1,301 @@
1
- # @santi020k/eslint-config-basic
1
+ <p align="center">
2
+ <a href="https://eslint.santi020k.com/">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/santi020k/eslint-config-basic/main/assets/readme/hero-dark.svg">
5
+ <img src="https://raw.githubusercontent.com/santi020k/eslint-config-basic/main/assets/readme/hero-light.svg" alt="ESLint Config Basic — Less setup. Clearer feedback." width="1200" height="360">
6
+ </picture>
7
+ </a>
8
+ </p>
2
9
 
3
- The primary entry point that composes all feature-specific configurations.
10
+ <h1 align="center">@santi020k/eslint-config-basic</h1>
4
11
 
5
- This package is part of the [`@santi020k/eslint-config-basic`](https://github.com/santi020k/eslint-config-basic) monorepo.
12
+ <p align="center">
13
+ <strong>A lean foundation for consistent JavaScript and TypeScript quality.</strong><br>
14
+ Detect your stack. Add only what you need. Keep feedback useful.
15
+ </p>
6
16
 
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)
17
+ DX-first ESLint 10 flat config for JavaScript and TypeScript, with auto-detection
18
+ and opt-in framework packages.
10
19
 
11
- The canonical documentation lives on the Starlight site, so this README intentionally stays short to avoid duplication.
20
+ <p align="center">
21
+ <a href="https://eslint.santi020k.com/">Documentation</a> ·
22
+ <a href="https://eslint.santi020k.com/guide/config-builder/">Config builder</a> ·
23
+ <a href="https://www.npmjs.com/package/@santi020k/eslint-config-basic">npm</a> ·
24
+ <a href="https://eslint.santi020k.com/guide/migration-v2-to-v3/">Migration to v3</a> ·
25
+ <a href="https://github.com/santi020k/eslint-config-basic/issues/new/choose">Get help</a>
26
+ </p>
27
+
28
+ <p align="center">
29
+ <a href="https://github.com/santi020k/eslint-config-basic/actions/workflows/build.yml"><img src="https://github.com/santi020k/eslint-config-basic/actions/workflows/build.yml/badge.svg" alt="Build"></a>
30
+ <a href="https://github.com/santi020k/eslint-config-basic/actions/workflows/codeql.yml"><img src="https://github.com/santi020k/eslint-config-basic/actions/workflows/codeql.yml/badge.svg" alt="CodeQL"></a>
31
+ <a href="https://www.npmjs.com/package/@santi020k/eslint-config-basic"><img src="https://img.shields.io/npm/v/@santi020k/eslint-config-basic.svg" alt="npm version"></a>
32
+ <a href="https://www.npmjs.com/package/@santi020k/eslint-config-basic"><img src="https://img.shields.io/npm/dm/@santi020k/eslint-config-basic.svg" alt="npm downloads"></a>
33
+ <a href="https://github.com/santi020k/eslint-config-basic/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license"></a>
34
+ </p>
35
+
36
+ **Explore:** [Quick start](#quick-start) · [Why v3 changed dependency ownership](#why-v3-changed-dependency-ownership) · [Custom configuration](#custom-configuration) · [Package choice](#package-choice) · [CLI](#cli) · [Compatibility](#compatibility)
37
+
38
+ ## Choose your path
39
+
40
+ | Your project | Start here | What stays in your control |
41
+ | --- | --- | --- |
42
+ | **JavaScript or TypeScript** | [Basic](https://eslint.santi020k.com/packages/basic/) | A lean foundation with automatic detection |
43
+ | **A framework application** | [Framework guides](https://eslint.santi020k.com/frameworks/react/) | Install the adapter for your actual stack |
44
+ | **Testing and tooling** | [Feature packs](https://eslint.santi020k.com/tooling/overview/) | Opt into the integrations you use |
45
+ | **Everything included** | [Full](https://eslint.santi020k.com/packages/full/) | One package owns the supported integrations |
46
+ | **An existing configuration** | [Interactive builder](https://eslint.santi020k.com/guide/config-builder/) | Preview choices before adopting them |
47
+
48
+ ## Quick start
49
+
50
+ Install the lean package and ESLint:
51
+
52
+ ```sh
53
+ npm install -D eslint @santi020k/eslint-config-basic
54
+ ```
55
+
56
+ Create `eslint.config.mjs`:
57
+
58
+ ```js
59
+ import { defineConfig } from '@santi020k/eslint-config-basic'
60
+
61
+ export default defineConfig()
62
+ ```
63
+
64
+ That is the complete zero-config setup. It detects JavaScript, TypeScript,
65
+ runtime, and installed optional features relative to the config file, even when
66
+ an editor starts ESLint from another working directory.
67
+
68
+ For a framework, add its config package to the same install:
69
+
70
+ ```sh
71
+ npm install -D eslint @santi020k/eslint-config-basic @santi020k/eslint-config-react
72
+ ```
73
+
74
+ For optional behavior, add only the corresponding feature packs:
75
+
76
+ ```sh
77
+ npm install -D @santi020k/eslint-config-testing @santi020k/eslint-config-tools
78
+ npm install -D @santi020k/eslint-config-libraries
79
+ ```
80
+
81
+ If install size is not a concern, the batteries-included package keeps the same
82
+ one-line config:
83
+
84
+ ```sh
85
+ npm install -D eslint @santi020k/eslint-config-full
86
+ ```
87
+
88
+ ```js
89
+ export { default } from '@santi020k/eslint-config-full/recommended'
90
+ ```
91
+
92
+ ## Why v3 changed dependency ownership
93
+
94
+ `@santi020k/eslint-config-basic` no longer installs every supported framework
95
+ and integration. Its production boundary is limited to the composer, core rules,
96
+ TypeScript support, and a small runtime utility. Frameworks and integrations are
97
+ optional peers loaded only when detected or selected.
98
+
99
+ This gives projects:
100
+
101
+ - fewer transitive dependencies and audit paths by default;
102
+ - no Angular, Expo, Storybook, GraphQL, Cypress, or Tailwind packages unless used;
103
+ - actionable errors naming an optional config package when it is missing;
104
+ - a full-package escape hatch for teams that prefer one dependency.
105
+
106
+ The lean security boundary is checked during release and has direct-dependency
107
+ budgets to prevent accidental growth.
108
+
109
+ ## Custom configuration
110
+
111
+ Use the named factory when auto-detection needs an override:
112
+
113
+ ```js
114
+ import { defineConfig } from '@santi020k/eslint-config-basic'
115
+
116
+ export default await defineConfig({
117
+ root: import.meta.dirname,
118
+ frameworks: { react: true },
119
+ strict: 'ci',
120
+ typescript: 'strict'
121
+ })
122
+ ```
123
+
124
+ Optional features require their granular category package:
125
+
126
+ ```js
127
+ import { defineConfig } from '@santi020k/eslint-config-basic'
128
+
129
+ export default await defineConfig({
130
+ features: {
131
+ playwright: true,
132
+ prettier: true,
133
+ tailwind: true
134
+ }
135
+ })
136
+ ```
137
+
138
+ The factory also accepts local flat-config overrides after the options object:
139
+
140
+ ```js
141
+ import { defineConfig } from '@santi020k/eslint-config-basic'
142
+
143
+ export default await defineConfig({}, {
144
+ files: ['scripts/**/*.js'],
145
+ rules: { 'no-console': 'off' }
146
+ })
147
+ ```
148
+
149
+ Pass overrides that reference optional plugin rules directly to `defineConfig()`
150
+ whenever possible. The composer attaches each already-loaded plugin to the rule
151
+ block that needs it. An entry appended after `defineConfig()` has returned is
152
+ outside that attachment pass; wrap a dynamically assembled final array with
153
+ `attachReferencedPlugins()`:
154
+
155
+ ```js
156
+ import {
157
+ attachReferencedPlugins,
158
+ defineConfig
159
+ } from '@santi020k/eslint-config-basic'
160
+
161
+ const generated = await defineConfig()
162
+ const lateOverrides = await loadProjectOverrides()
163
+
164
+ export default attachReferencedPlugins([...generated, ...lateOverrides])
165
+ ```
166
+
167
+ Direct calls from `eslint.config.*` automatically anchor dependency detection,
168
+ TypeScript, Tailwind, workspace packages, and `.gitignore` to that file. Set
169
+ `root` only when the intended project root differs from the config directory.
170
+
171
+ Type-aware projects can keep generated, template, or other out-of-project files
172
+ on syntax-only linting without importing `typescript-eslint`:
173
+
174
+ ```js
175
+ export default await defineConfig({
176
+ root: import.meta.dirname,
177
+ typescript: {
178
+ untypedFiles: ['templates/**/*.ts']
179
+ }
180
+ })
181
+ ```
182
+
183
+ TypeScript config files (`**/*.config.{ts,mts,cts}`) use this syntax fallback
184
+ automatically. Set `untypedFiles: false` to require type information everywhere.
185
+
186
+ ## Formatting preferences
187
+
188
+ Choose common style preferences without repeating individual rule names:
189
+
190
+ ```js
191
+ export default await defineConfig({
192
+ formatting: {
193
+ arrowParens: 'always',
194
+ commaDangle: 'always-multiline',
195
+ quotes: 'double',
196
+ semi: true
197
+ }
198
+ })
199
+ ```
200
+
201
+ Every field is optional. `arrowParens` accepts `always` or `as-needed`,
202
+ `commaDangle` accepts `always-multiline` or `never`, `quotes` accepts `double` or
203
+ `single`, and `semi` is a boolean. Omitted fields retain existing defaults.
204
+ Preferences use warning severity; `strict: 'ci'` or `strict: 'pedantic'` promotes
205
+ them to errors. Correctness rules are unchanged. Prettier compatibility and local
206
+ flat-config overrides take precedence. Root preferences are inherited by
207
+ `projects`; each project can override individual fields. Use
208
+ `optionMergeStrategy: 'replace'` in a project to replace its inherited formatting
209
+ object instead of merging fields.
210
+
211
+ ## Package choice
212
+
213
+ | Package | Use it when | Dependency model |
214
+ | --- | --- | --- |
215
+ | `@santi020k/eslint-config-basic` | Default for applications and libraries | Lean core; frameworks and integrations are optional |
216
+ | `@santi020k/eslint-config-full` | Install simplicity matters more than footprint | Every supported framework and integration |
217
+ | `@santi020k/eslint-config-lite` | Existing v2 lite users | Deprecated v3 compatibility path; removed in v4 |
218
+ | `@santi020k/eslint-config-integrations` | Existing aggregate-package users | Deprecated v3 compatibility path; removed in v4 |
219
+ | Individual framework packages | Custom composition | One framework and its plugin set |
220
+
221
+ ## From setup to useful feedback
222
+
223
+ ```mermaid
224
+ flowchart LR
225
+ A[Install Basic] --> B[Detect your stack]
226
+ B --> C[Compose flat config]
227
+ C --> D[Lint in editor and CI]
228
+ D --> E[Explain and improve]
229
+ ```
230
+
231
+ Detection starts from your configuration directory. Framework adapters and feature packs
232
+ stay explicit dependencies; lint feedback works in your editor and existing CI.
233
+ Use the CLI to understand a rule before changing it, and review a migration before writing it.
234
+
235
+ ## CLI
236
+
237
+ ```sh
238
+ npx @santi020k/eslint-config-basic init
239
+ npx @santi020k/eslint-config-basic explain no-console --file src/index.ts
240
+ npx @santi020k/eslint-config-basic doctor --fix
241
+ npx @santi020k/eslint-config-basic compatibility
242
+ npx @santi020k/eslint-config-basic config-types
243
+ npx @santi020k/eslint-config-basic migrate --to v3
244
+ npx @santi020k/eslint-config-basic baseline --preset pedantic
245
+ npx @santi020k/eslint-config-basic profile --max-warnings 0
246
+ npx @santi020k/eslint-config-basic snapshot
247
+ npx @santi020k/eslint-config-basic diff
248
+ ```
249
+
250
+ `init` creates the zero-argument config. `explain` shows detection or traces an
251
+ effective rule, `doctor --fix` repairs safe setup issues with backups, and
252
+ `compatibility` validates runtime and peer ranges, while `config-types` checks
253
+ that editor-facing ESLint config declarations stay portable. The remaining
254
+ commands automate v3 migration, incremental strict-mode adoption, budgeted
255
+ performance profiling, and effective-rule change review.
256
+
257
+ Doctor's JSON project report includes `lintOwnership.localConfig`, `lintScript`,
258
+ and `guidance` to help review overlapping workspace lint commands. Doctor also
259
+ reports potentially redundant Astro parser and rule fields passed directly to
260
+ `defineConfig()` when the installed composer and current effective config agree
261
+ on sampled files. These are review candidates, never automatic removals.
262
+
263
+ When Playwright and Testing Library are both enabled, Playwright-owned globs
264
+ restrict Testing Library's import heuristics while retaining checks for actual
265
+ Testing Library imports. Use `testingFiles.playwright` for browser tests outside
266
+ the standard Playwright directories; generic `tests/**/*.spec.ts` files are not
267
+ assumed to belong to Playwright.
268
+
269
+ ## Compatibility
270
+
271
+ - Node.js: `>=22.19.0`
272
+ - ESLint: `^10.0.0`
273
+ - TypeScript: `>=5.0.0 <7.0.0` when enabled
274
+ - Package managers: npm, pnpm, Yarn, and Bun
275
+
276
+ Packages in the `@santi020k/eslint-config-*` family use independent minor and
277
+ patch versions. Packages with the same major version are designed to work
278
+ together; use the latest release of each package within that major. Internal
279
+ dependency and peer ranges provide the exact machine-readable compatibility
280
+ requirements.
281
+
282
+ See the [v2 → v3 migration guide](https://eslint.santi020k.com/guide/migration-v2-to-v3/)
283
+ for package moves and copy-paste commands. The
284
+ [planned v4 removals](https://eslint.santi020k.com/guide/v4-removals/) page
285
+ tracks compatibility APIs that are deprecated during v3.
286
+
287
+ ## Contribute and get support
288
+
289
+ - [Report a reproducible bug](https://github.com/santi020k/eslint-config-basic/issues/new/choose), ask a setup question, or propose a framework integration.
290
+ - [Read the contribution guide](https://github.com/santi020k/eslint-config-basic/blob/main/.github/CONTRIBUTING.md) for workspace setup, behavioral tests, and the release checks.
291
+ - [Report security vulnerabilities privately](https://github.com/santi020k/eslint-config-basic/blob/main/.github/SECURITY.md); keep credentials and private project data out of public issues.
292
+ - [Read the changelog](https://eslint.santi020k.com/guide/changelog/) before upgrading.
293
+
294
+ Built by [Santiago Molina](https://santi020k.com), alongside
295
+ [Lumen UI](https://github.com/santi020k/lumen),
296
+ [Quality](https://github.com/santi020k/quality), and
297
+ [Astro Doctor](https://github.com/santi020k/astro-doctor).
298
+
299
+ ## License
300
+
301
+ MIT © [santi020k](https://santi020k.com). See [LICENSE](https://github.com/santi020k/eslint-config-basic/blob/main/LICENSE).
@@ -0,0 +1,24 @@
1
+ import { FlatConfigArray } from '@santi020k/eslint-config-core';
2
+
3
+ interface EffectiveConfig {
4
+ languageOptions?: {
5
+ parserOptions?: Record<string, unknown>;
6
+ };
7
+ rules?: Record<string, unknown>;
8
+ settings?: Record<string, unknown>;
9
+ }
10
+ interface ConfigInspector {
11
+ calculateConfigForFile: (file: string) => Promise<EffectiveConfig | undefined>;
12
+ }
13
+ type InspectorConstructor = new (options: {
14
+ cwd: string;
15
+ overrideConfig: FlatConfigArray;
16
+ overrideConfigFile: true;
17
+ }) => ConfigInspector;
18
+ /** Narrow ESLint's dynamic effective-config result at the API boundary. */
19
+ declare const toEffectiveConfig: (value: unknown) => EffectiveConfig | undefined;
20
+ /** Only report fields proven redundant for a sampled file; never remove a whole override block. */
21
+ declare const compareConsumerOverrides: (cwd: string, config: FlatConfigArray, baseline: FlatConfigArray, Inspector: InspectorConstructor) => Promise<string[]>;
22
+ declare const getConsumerOverrideGuidance: (cwd: string, configPath: null | string) => Promise<string[]>;
23
+
24
+ export { compareConsumerOverrides, getConsumerOverrideGuidance, toEffectiveConfig };
@@ -0,0 +1,146 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { createRequire, findPackageJSON } from "node:module";
3
+ import { dirname, join, relative } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { flattenConfigInputs } from "@santi020k/eslint-config-core";
6
+ import { getDefineConfigMetadata } from "./define-config-metadata.js";
7
+ const isRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
8
+ const isFlatConfig = (value) => Array.isArray(value) && value.every(isRecord);
9
+ const isComposer = (value) => isRecord(value) && typeof value.defineConfig === "function";
10
+ const isEslint = (value) => isRecord(value) && typeof value.ESLint === "function";
11
+ const toEffectiveConfig = (value) => {
12
+ if (!isRecord(value)) return void 0;
13
+ const languageOptions = isRecord(value.languageOptions) ? value.languageOptions : void 0;
14
+ const parserOptions = isRecord(languageOptions?.parserOptions) ? languageOptions.parserOptions : void 0;
15
+ return {
16
+ languageOptions: { parserOptions },
17
+ rules: isRecord(value.rules) ? value.rules : void 0,
18
+ settings: isRecord(value.settings) ? value.settings : void 0
19
+ };
20
+ };
21
+ const ASTRO_WORKAROUND_RULES = /* @__PURE__ */ new Set([
22
+ "@stylistic/indent",
23
+ "@stylistic/jsx-closing-tag-location",
24
+ "@stylistic/jsx-indent",
25
+ "@stylistic/jsx-indent-props",
26
+ "@typescript-eslint/no-unused-vars",
27
+ "@typescript-eslint/no-unused-expressions",
28
+ "no-undef",
29
+ "no-unused-vars",
30
+ "no-unused-expressions"
31
+ ]);
32
+ const collectAstroFiles = (root, files, depth = 0) => {
33
+ if (depth > 8) return;
34
+ const entries = readdirSync(root, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));
35
+ for (const entry of entries) {
36
+ if (files.length >= 32) break;
37
+ const path = join(root, entry.name);
38
+ if (entry.isFile() && entry.name.endsWith(".astro")) files.push(path);
39
+ if (entry.isDirectory() && !entry.name.startsWith(".") && !["node_modules", "dist", "build", "coverage"].includes(entry.name)) collectAstroFiles(path, files, depth + 1);
40
+ }
41
+ };
42
+ const severity = (value) => {
43
+ const first = Array.isArray(value) ? value[0] : value;
44
+ return first === "off" ? 0 : first;
45
+ };
46
+ const createCandidates = (cwd, extras, baseline, Inspector) => extras.filter((entry) => entry.files?.some((pattern) => typeof pattern === "string" && pattern.includes(".astro"))).filter((entry) => entry.languageOptions?.parserOptions?.parser || Object.keys(entry.rules ?? {}).some((rule) => ASTRO_WORKAROUND_RULES.has(rule))).map((entry, index) => ({
47
+ entry,
48
+ inspector: new Inspector({
49
+ cwd,
50
+ overrideConfig: [...baseline, {
51
+ ...entry,
52
+ settings: { ...entry.settings, "eslint-config-basic/doctor-scope": true }
53
+ }],
54
+ overrideConfigFile: true
55
+ }),
56
+ key: `${index}:${entry.name ?? "override"}`
57
+ }));
58
+ const isRedundantRule = (rule, value, current, baseline) => ASTRO_WORKAROUND_RULES.has(rule) && severity(value) === 0 && severity(current.rules?.[rule]) === 0 && severity(baseline.rules?.[rule]) === 0;
59
+ const getParser = (config) => config.languageOptions?.parserOptions?.parser;
60
+ const sameParser = (left, right) => left === right || isRecord(left) && isRecord(right) && typeof left.parseForESLint === "function" && left.parseForESLint === right.parseForESLint && left.parse === right.parse;
61
+ const isRedundantParser = (entry, current, baseline) => {
62
+ const parser = getParser(entry);
63
+ return Boolean(parser) && sameParser(parser, getParser(current)) && sameParser(parser, getParser(baseline));
64
+ };
65
+ const recordCandidate = (candidate, current, baseline, file, guidance) => {
66
+ const { entry, key } = candidate;
67
+ for (const [rule, value] of Object.entries(entry.rules ?? {})) {
68
+ if (!isRedundantRule(rule, value, current, baseline)) continue;
69
+ const id = `${key}:${rule}`;
70
+ guidance.set(id, guidance.get(id) ?? `Review Astro workaround ${rule}: installed generated and current effective configs both disable it for ${file}. Remove only that field after checking all override globs; rerun lint, typecheck, and repeated autofix before removing the override.`);
71
+ }
72
+ if (isRedundantParser(entry, current, baseline)) {
73
+ const id = `${key}:parser`;
74
+ guidance.set(id, guidance.get(id) ?? `Review the explicit Astro TypeScript parser: the installed generated config already supplies the same parser for ${file}. Check every override glob and rerun lint, typecheck, and repeated autofix before removing only the parser field.`);
75
+ }
76
+ };
77
+ const inspectTarget = async (cwd, target, current, generated, candidates, guidance) => {
78
+ const [effective, defaults] = await Promise.all([
79
+ current.calculateConfigForFile(target),
80
+ generated.calculateConfigForFile(target)
81
+ ]);
82
+ if (!effective || !defaults) return;
83
+ for (const candidate of candidates) {
84
+ const scoped = await candidate.inspector.calculateConfigForFile(target);
85
+ if (scoped?.settings?.["eslint-config-basic/doctor-scope"] !== true) continue;
86
+ recordCandidate(candidate, effective, defaults, relative(cwd, target), guidance);
87
+ }
88
+ };
89
+ const compareConsumerOverrides = async (cwd, config, baseline, Inspector) => {
90
+ const metadata = getDefineConfigMetadata(config);
91
+ if (!metadata) return [];
92
+ const candidates = createCandidates(cwd, flattenConfigInputs(metadata.extraConfigs), baseline, Inspector);
93
+ if (candidates.length === 0) return [];
94
+ const current = new Inspector({ cwd, overrideConfig: config, overrideConfigFile: true });
95
+ const generated = new Inspector({ cwd, overrideConfig: baseline, overrideConfigFile: true });
96
+ const guidance = /* @__PURE__ */ new Map();
97
+ const files = [];
98
+ collectAstroFiles(cwd, files);
99
+ for (const file of files) {
100
+ await inspectTarget(cwd, file, current, generated, candidates, guidance);
101
+ await inspectTarget(cwd, `${file}/consumer-script.js`, current, generated, candidates, guidance);
102
+ }
103
+ return [...guidance.values()];
104
+ };
105
+ const resolveComposerEntry = (cwd) => {
106
+ const manifestPath = findPackageJSON(
107
+ "@santi020k/eslint-config-basic",
108
+ pathToFileURL(join(cwd, "package.json"))
109
+ );
110
+ if (!manifestPath) throw new Error("The installed Basic package manifest could not be resolved.");
111
+ const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
112
+ const exports = isRecord(manifest) && isRecord(manifest.exports) ? manifest.exports : void 0;
113
+ const entry = isRecord(exports?.["."]) ? exports["."].import : void 0;
114
+ if (typeof entry !== "string" || !entry.startsWith("./")) {
115
+ throw new Error("The installed Basic package does not declare a supported public root import export.");
116
+ }
117
+ return pathToFileURL(join(dirname(manifestPath), entry)).href;
118
+ };
119
+ const compareInstalledComposer = async (cwd, config) => {
120
+ const metadata = getDefineConfigMetadata(config);
121
+ if (!metadata || metadata.extraConfigs.length === 0) return [];
122
+ const projectRequire = createRequire(join(cwd, "package.json"));
123
+ const composer = await import(resolveComposerEntry(cwd));
124
+ const eslint = projectRequire("eslint");
125
+ if (!isComposer(composer) || !isEslint(eslint)) return [];
126
+ const legacyRoot = Reflect.get(metadata.options ?? {}, "detectRootDir");
127
+ const root = metadata.root ?? metadata.options?.root ?? (typeof legacyRoot === "string" ? legacyRoot : cwd);
128
+ const baseline = await composer.defineConfig({ ...metadata.options, root });
129
+ return await compareConsumerOverrides(cwd, config, baseline, eslint.ESLint);
130
+ };
131
+ const getConsumerOverrideGuidance = async (cwd, configPath) => {
132
+ if (!configPath || !existsSync(configPath)) return [];
133
+ try {
134
+ const loaded = await import(pathToFileURL(configPath).href);
135
+ const config = isRecord(loaded) ? await loaded.default : void 0;
136
+ return isFlatConfig(config) ? await compareInstalledComposer(cwd, config) : [];
137
+ } catch (error) {
138
+ return [`Could not compare consumer overrides with the installed generated config: ${error instanceof Error ? error.message : String(error)}. Keep existing overrides until effective-config comparison succeeds.`];
139
+ }
140
+ };
141
+ export {
142
+ compareConsumerOverrides,
143
+ getConsumerOverrideGuidance,
144
+ toEffectiveConfig
145
+ };
146
+ //# sourceMappingURL=cli-consumer-guidance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli-consumer-guidance.ts"],"sourcesContent":["import { existsSync, readdirSync, readFileSync } from 'node:fs'\nimport { createRequire, findPackageJSON } from 'node:module'\nimport { dirname, join, relative } from 'node:path'\nimport { pathToFileURL } from 'node:url'\n\nimport { type FlatConfigArray, flattenConfigInputs } from '@santi020k/eslint-config-core'\n\nimport { getDefineConfigMetadata } from './define-config-metadata.js'\nimport type { defineConfig } from './index.js'\n\ninterface EffectiveConfig {\n languageOptions?: { parserOptions?: Record<string, unknown> }\n rules?: Record<string, unknown>\n settings?: Record<string, unknown>\n}\n\ninterface ConfigInspector {\n calculateConfigForFile: (file: string) => Promise<EffectiveConfig | undefined>\n}\n\ntype InspectorConstructor = new(options: {\n cwd: string\n overrideConfig: FlatConfigArray\n overrideConfigFile: true\n}) => ConfigInspector\n\nconst isRecord = (value: unknown): value is Record<string, unknown> => (\n value !== null && typeof value === 'object' && !Array.isArray(value)\n)\n\nconst isFlatConfig = (value: unknown): value is FlatConfigArray => (\n Array.isArray(value) && value.every(isRecord)\n)\n\nconst isComposer = (value: unknown): value is { defineConfig: typeof defineConfig } => (\n isRecord(value) && typeof value.defineConfig === 'function'\n)\n\nconst isEslint = (value: unknown): value is { ESLint: InspectorConstructor } => (\n isRecord(value) && typeof value.ESLint === 'function'\n)\n\n/** Narrow ESLint's dynamic effective-config result at the API boundary. */\nexport const toEffectiveConfig = (value: unknown): EffectiveConfig | undefined => {\n if (!isRecord(value)) return undefined\n\n const languageOptions = isRecord(value.languageOptions) ? value.languageOptions : undefined\n const parserOptions = isRecord(languageOptions?.parserOptions) ? languageOptions.parserOptions : undefined\n\n return {\n languageOptions: { parserOptions },\n rules: isRecord(value.rules) ? value.rules : undefined,\n settings: isRecord(value.settings) ? value.settings : undefined\n }\n}\n\nconst ASTRO_WORKAROUND_RULES = new Set([\n '@stylistic/indent',\n '@stylistic/jsx-closing-tag-location',\n '@stylistic/jsx-indent',\n '@stylistic/jsx-indent-props',\n '@typescript-eslint/no-unused-vars',\n '@typescript-eslint/no-unused-expressions',\n 'no-undef',\n 'no-unused-vars',\n 'no-unused-expressions'\n])\n\nconst collectAstroFiles = (root: string, files: string[], depth = 0): void => {\n if (depth > 8) return\n\n const entries = readdirSync(root, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))\n\n for (const entry of entries) {\n if (files.length >= 32) break\n\n const path = join(root, entry.name)\n\n if (entry.isFile() && entry.name.endsWith('.astro')) files.push(path)\n\n if (entry.isDirectory() && !entry.name.startsWith('.') &&\n !['node_modules', 'dist', 'build', 'coverage'].includes(entry.name)) collectAstroFiles(path, files, depth + 1)\n }\n}\n\nconst severity = (value: unknown): unknown => {\n const first: unknown = Array.isArray(value) ? value[0] : value\n\n return first === 'off' ? 0 : first\n}\n\ntype ConfigEntry = FlatConfigArray[number]\n\ninterface Candidate {\n entry: ConfigEntry\n inspector: ConfigInspector\n key: string\n}\n\nconst createCandidates = (\n cwd: string,\n extras: FlatConfigArray,\n baseline: FlatConfigArray,\n Inspector: InspectorConstructor\n): Candidate[] => (\n extras.filter(entry => entry.files?.some(pattern => typeof pattern === 'string' && pattern.includes('.astro')))\n .filter(entry => entry.languageOptions?.parserOptions?.parser ||\n Object.keys(entry.rules ?? {}).some(rule => ASTRO_WORKAROUND_RULES.has(rule)))\n .map((entry, index) => ({\n entry,\n inspector: new Inspector({\n cwd,\n overrideConfig: [...baseline, {\n ...entry,\n settings: { ...entry.settings, 'eslint-config-basic/doctor-scope': true }\n }],\n overrideConfigFile: true\n }),\n key: `${index}:${entry.name ?? 'override'}`\n }))\n)\n\nconst isRedundantRule = (\n rule: string,\n value: unknown,\n current: EffectiveConfig,\n baseline: EffectiveConfig\n): boolean => (\n ASTRO_WORKAROUND_RULES.has(rule) && severity(value) === 0 &&\n severity(current.rules?.[rule]) === 0 && severity(baseline.rules?.[rule]) === 0\n)\n\nconst getParser = (config: EffectiveConfig): unknown => config.languageOptions?.parserOptions?.parser\n\n// ESLint clones nested parser options; entry-point function identity survives that merge.\nconst sameParser = (left: unknown, right: unknown): boolean => (\n left === right || (isRecord(left) && isRecord(right) &&\n typeof left.parseForESLint === 'function' && left.parseForESLint === right.parseForESLint && left.parse === right.parse)\n)\n\nconst isRedundantParser = (entry: ConfigEntry, current: EffectiveConfig, baseline: EffectiveConfig): boolean => {\n const parser = getParser(entry)\n\n return Boolean(parser) && sameParser(parser, getParser(current)) && sameParser(parser, getParser(baseline))\n}\n\nconst recordCandidate = (\n candidate: Candidate,\n current: EffectiveConfig,\n baseline: EffectiveConfig,\n file: string,\n guidance: Map<string, string>\n): void => {\n const { entry, key } = candidate\n\n for (const [rule, value] of Object.entries(entry.rules ?? {})) {\n if (!isRedundantRule(rule, value, current, baseline)) continue\n\n const id = `${key}:${rule}`\n\n guidance.set(id, guidance.get(id) ??\n `Review Astro workaround ${rule}: installed generated and current effective configs both disable it ` +\n `for ${file}. Remove only that field after checking all override globs; ` +\n 'rerun lint, typecheck, and repeated autofix before removing the override.')\n }\n\n if (isRedundantParser(entry, current, baseline)) {\n const id = `${key}:parser`\n\n guidance.set(id, guidance.get(id) ??\n 'Review the explicit Astro TypeScript parser: the installed generated config already supplies the ' +\n `same parser for ${file}. Check every override glob and rerun lint, typecheck, ` +\n 'and repeated autofix before removing only the parser field.')\n }\n}\n\nconst inspectTarget = async (\n cwd: string,\n target: string,\n current: ConfigInspector,\n generated: ConfigInspector,\n candidates: Candidate[],\n guidance: Map<string, string>\n): Promise<void> => {\n const [effective, defaults] = await Promise.all([\n current.calculateConfigForFile(target),\n generated.calculateConfigForFile(target)\n ])\n\n if (!effective || !defaults) return\n\n for (const candidate of candidates) {\n const scoped = await candidate.inspector.calculateConfigForFile(target)\n\n if (scoped?.settings?.['eslint-config-basic/doctor-scope'] !== true) continue\n\n recordCandidate(candidate, effective, defaults, relative(cwd, target), guidance)\n }\n}\n\n/** Only report fields proven redundant for a sampled file; never remove a whole override block. */\nexport const compareConsumerOverrides = async (\n cwd: string,\n config: FlatConfigArray,\n baseline: FlatConfigArray,\n Inspector: InspectorConstructor\n): Promise<string[]> => {\n const metadata = getDefineConfigMetadata(config)\n\n if (!metadata) return []\n\n const candidates = createCandidates(cwd, flattenConfigInputs(metadata.extraConfigs), baseline, Inspector)\n\n if (candidates.length === 0) return []\n\n const current = new Inspector({ cwd, overrideConfig: config, overrideConfigFile: true })\n const generated = new Inspector({ cwd, overrideConfig: baseline, overrideConfigFile: true })\n const guidance = new Map<string, string>()\n const files: string[] = []\n\n collectAstroFiles(cwd, files)\n\n for (const file of files) {\n await inspectTarget(cwd, file, current, generated, candidates, guidance)\n\n await inspectTarget(cwd, `${file}/consumer-script.js`, current, generated, candidates, guidance)\n }\n\n return [...guidance.values()]\n}\n\nconst resolveComposerEntry = (cwd: string): string => {\n const manifestPath = findPackageJSON(\n '@santi020k/eslint-config-basic',\n pathToFileURL(join(cwd, 'package.json'))\n )\n\n if (!manifestPath) throw new Error('The installed Basic package manifest could not be resolved.')\n\n const manifest: unknown = JSON.parse(readFileSync(manifestPath, 'utf8'))\n const exports = isRecord(manifest) && isRecord(manifest.exports) ? manifest.exports : undefined\n const entry = isRecord(exports?.['.']) ? exports['.'].import : undefined\n\n if (typeof entry !== 'string' || !entry.startsWith('./')) {\n throw new Error('The installed Basic package does not declare a supported public root import export.')\n }\n\n return pathToFileURL(join(dirname(manifestPath), entry)).href\n}\n\nconst compareInstalledComposer = async (cwd: string, config: FlatConfigArray): Promise<string[]> => {\n const metadata = getDefineConfigMetadata(config)\n\n if (!metadata || metadata.extraConfigs.length === 0) return []\n\n const projectRequire = createRequire(join(cwd, 'package.json'))\n const composer: unknown = await import(resolveComposerEntry(cwd))\n const eslint: unknown = projectRequire('eslint')\n\n if (!isComposer(composer) || !isEslint(eslint)) return []\n\n const legacyRoot: unknown = Reflect.get(metadata.options ?? {}, 'detectRootDir')\n const root = metadata.root ?? metadata.options?.root ?? (typeof legacyRoot === 'string' ? legacyRoot : cwd)\n const baseline = await composer.defineConfig({ ...metadata.options, root })\n\n return await compareConsumerOverrides(cwd, config, baseline, eslint.ESLint)\n}\n\nexport const getConsumerOverrideGuidance = async (cwd: string, configPath: null | string): Promise<string[]> => {\n if (!configPath || !existsSync(configPath)) return []\n\n try {\n const loaded: unknown = await import(pathToFileURL(configPath).href)\n const config: unknown = isRecord(loaded) ? await loaded.default : undefined\n\n return isFlatConfig(config) ? await compareInstalledComposer(cwd, config) : []\n } catch (error) {\n return [`Could not compare consumer overrides with the installed generated config: ${\n error instanceof Error ? error.message : String(error)\n }. Keep existing overrides until effective-config comparison succeeds.`]\n }\n}\n"],"mappings":"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","names":[]}
@@ -11,7 +11,7 @@ import { getDefineConfigMetadata } from "./define-config-metadata.js";
11
11
  import { defineConfig } from "./index.js";
12
12
  import { isMissingRequestedPackage } from "./optional-package-errors.js";
13
13
  import { resolvePreset } from "./resolvers.js";
14
- const PRESETS = new Set(Object.values(Preset));
14
+ const PRESETS = new Map(Object.values(Preset).map((preset) => [preset, preset]));
15
15
  const CONFIG_FILENAMES = [
16
16
  "eslint.config.js",
17
17
  "eslint.config.mjs",
@@ -484,12 +484,12 @@ const createPresetReport = async (cwd, presetName, file = "eslint.config.js", op
484
484
  throw new Error("Writing preset fixes requires semanticOnly to avoid an unchecked formatting rewrite.");
485
485
  }
486
486
  const normalizedPreset = presetName.toLowerCase();
487
- if (!PRESETS.has(normalizedPreset)) {
487
+ const preset = PRESETS.get(normalizedPreset);
488
+ if (preset === void 0) {
488
489
  throw new Error(
489
- `Unknown preset "${presetName}". Use one of: ${[...PRESETS].sort().join(", ")}.`
490
+ `Unknown preset "${presetName}". Use one of: ${[...PRESETS.keys()].sort().join(", ")}.`
490
491
  );
491
492
  }
492
- const preset = normalizedPreset;
493
493
  const presetOptions = resolvePreset(preset);
494
494
  const current = await loadProjectEslint(cwd).calculateConfigForFile(file);
495
495
  const currentMetadata = await loadDefineConfigMetadata(cwd);