@santi020k/eslint-config-basic 3.2.0 → 3.4.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agent-skill-generator.d.ts +7 -18
  3. package/dist/agent-skill-generator.js +54 -18
  4. package/dist/agent-skill-generator.js.map +1 -0
  5. package/dist/cli-advanced.d.ts +7 -0
  6. package/dist/cli-advanced.js +74 -15
  7. package/dist/cli-advanced.js.map +1 -0
  8. package/dist/cli-migration.js +22 -20
  9. package/dist/cli-migration.js.map +1 -0
  10. package/dist/cli-package-manager.d.ts +10 -0
  11. package/dist/cli-package-manager.js +135 -0
  12. package/dist/cli-package-manager.js.map +1 -0
  13. package/dist/cli-preset.d.ts +74 -2
  14. package/dist/cli-preset.js +253 -6
  15. package/dist/cli-preset.js.map +1 -0
  16. package/dist/cli-workflows.js +1 -0
  17. package/dist/cli-workflows.js.map +1 -0
  18. package/dist/cli.d.ts +1 -1
  19. package/dist/cli.js +461 -176
  20. package/dist/cli.js.map +1 -0
  21. package/dist/define-config-metadata.js +2 -4
  22. package/dist/define-config-metadata.js.map +1 -0
  23. package/dist/frameworks.d.ts +1 -0
  24. package/dist/frameworks.js +2 -1
  25. package/dist/frameworks.js.map +1 -0
  26. package/dist/index.d.ts +13 -2
  27. package/dist/index.js +83 -22
  28. package/dist/index.js.map +1 -0
  29. package/dist/integrations.js +1 -0
  30. package/dist/integrations.js.map +1 -0
  31. package/dist/optional-package-errors.js +1 -0
  32. package/dist/optional-package-errors.js.map +1 -0
  33. package/dist/recommended.d.ts +6 -3
  34. package/dist/recommended.js +3 -1
  35. package/dist/recommended.js.map +1 -0
  36. package/dist/resolvers.js +1 -0
  37. package/dist/resolvers.js.map +1 -0
  38. package/dist/tailwind.d.ts +3 -1
  39. package/dist/tailwind.js +115 -13
  40. package/dist/tailwind.js.map +1 -0
  41. package/package.json +25 -25
  42. package/IMPROVEMENTS.md +0 -270
package/dist/cli.js CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawnSync } from "node:child_process";
3
3
  import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
4
- import { createRequire } from "node:module";
4
+ import { createRequire, findPackageJSON } from "node:module";
5
5
  import { basename, dirname, join } from "node:path";
6
- import { fileURLToPath } from "node:url";
6
+ import { fileURLToPath, pathToFileURL } from "node:url";
7
+ import { findTailwindEntryPoint } from "@santi020k/eslint-config-core";
7
8
  import {
8
9
  analyzeEslintConfig,
9
10
  ESLINT_CONFIG_FILENAMES,
@@ -11,6 +12,15 @@ import {
11
12
  } from "./agent-skill-generator.js";
12
13
  import { handleCompatibility, handleExplainRule } from "./cli-advanced.js";
13
14
  import { getExplicitConfigFeaturePackages, handleMigrateV3 } from "./cli-migration.js";
15
+ import {
16
+ addCompatibleConfigVersions,
17
+ createInstallCommand,
18
+ createInstallInvocation,
19
+ detectPackageManager,
20
+ findPnpmWorkspaceRoot,
21
+ getCatalogPreference,
22
+ getCompatibleConfigVersion
23
+ } from "./cli-package-manager.js";
14
24
  import { handleExplainPreset } from "./cli-preset.js";
15
25
  import {
16
26
  handleBaseline,
@@ -19,6 +29,7 @@ import {
19
29
  handleSnapshotDiff
20
30
  } from "./cli-workflows.js";
21
31
  import { detectProjectOptions } from "./index.js";
32
+ import { findCssComponentClasses } from "./tailwind.js";
22
33
  const getDefaultConfigFilename = (cwd) => {
23
34
  const packageJsonPath = join(cwd, "package.json");
24
35
  if (!existsSync(packageJsonPath)) return "eslint.config.mjs";
@@ -81,6 +92,13 @@ const CATEGORY_PACKAGES = {
81
92
  testing: "@santi020k/eslint-config-testing",
82
93
  tools: "@santi020k/eslint-config-tools"
83
94
  };
95
+ const PRESET_INSTALL_PACKAGES = {
96
+ all: Object.values(CATEGORY_PACKAGES),
97
+ app: [CATEGORY_PACKAGES.testing, CATEGORY_PACKAGES.tools],
98
+ ci: [CATEGORY_PACKAGES.extensions, CATEGORY_PACKAGES.tools],
99
+ library: [CATEGORY_PACKAGES.extensions, CATEGORY_PACKAGES.tools],
100
+ monorepo: [CATEGORY_PACKAGES.extensions, CATEGORY_PACKAGES.tools]
101
+ };
84
102
  const getConfigPathIfPresent = (cwd) => {
85
103
  const configPath = ESLINT_CONFIG_FILENAMES.map((filename) => join(cwd, filename)).find((p) => existsSync(p));
86
104
  return configPath ?? null;
@@ -154,160 +172,13 @@ const getInstallPackages = (summary, declaredDependencies, explicitFeaturePackag
154
172
  for (const packageName of explicitFeaturePackages) {
155
173
  packages.add(packageName);
156
174
  }
175
+ for (const packageName of PRESET_INSTALL_PACKAGES[summary.preset] ?? []) {
176
+ packages.add(packageName);
177
+ }
157
178
  }
158
179
  if (summary.typescript) packages.add(TYPESCRIPT_PACKAGE_NAME);
159
180
  return [...packages].filter((packageName) => !declaredDependencies.has(packageName));
160
181
  };
161
- const createInstallCommand = (packageManager, packages, workspaceRoot = false, catalog = false) => {
162
- const packageList = packages.join(" ");
163
- switch (packageManager) {
164
- case "bun":
165
- return `bun add -d ${packageList}`;
166
- case "npm":
167
- return `npm install -D ${packageList}`;
168
- case "yarn":
169
- return `yarn add -D ${packageList}`;
170
- default:
171
- return `pnpm add -D${workspaceRoot ? " --workspace-root" : ""}` + (catalog === true ? " --save-catalog" : "") + `${typeof catalog === "string" ? ` --save-catalog-name=${catalog}` : ""} ${packageList}`;
172
- }
173
- };
174
- const createInstallInvocation = (packageManager, packages, workspaceRoot = false, catalog = false) => {
175
- switch (packageManager) {
176
- case "bun":
177
- return ["bun", ["add", "-d", ...packages]];
178
- case "npm":
179
- return ["npm", ["install", "-D", ...packages]];
180
- case "yarn":
181
- return ["yarn", ["add", "-D", ...packages]];
182
- default:
183
- return ["pnpm", [
184
- "add",
185
- "-D",
186
- ...workspaceRoot ? ["--workspace-root"] : [],
187
- ...catalog === true ? ["--save-catalog"] : [],
188
- ...typeof catalog === "string" ? [`--save-catalog-name=${catalog}`] : [],
189
- ...packages
190
- ]];
191
- }
192
- };
193
- const findPnpmWorkspaceRoot = (cwd) => {
194
- let current = cwd;
195
- for (; ; ) {
196
- if (existsSync(join(current, "pnpm-workspace.yaml"))) return current;
197
- const parent = dirname(current);
198
- if (parent === current) return void 0;
199
- current = parent;
200
- }
201
- };
202
- const detectPackageManager = (cwd) => {
203
- if (findPnpmWorkspaceRoot(cwd) || existsSync(join(cwd, "pnpm-lock.yaml"))) return "pnpm";
204
- if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
205
- if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
206
- return "npm";
207
- };
208
- const getCatalogPreference = (packageJson) => {
209
- const dependencyFields = ["devDependencies", "dependencies"];
210
- const dependencyRecords = [];
211
- const parseCatalog = (value) => {
212
- if (typeof value !== "string" || !value.startsWith("catalog:")) return false;
213
- const name = value.slice("catalog:".length);
214
- return name && name !== "default" ? name : true;
215
- };
216
- for (const field of dependencyFields) {
217
- const dependencies = packageJson?.[field];
218
- if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) continue;
219
- dependencyRecords.push(dependencies);
220
- }
221
- for (const dependencies of dependencyRecords) {
222
- const basicCatalog = parseCatalog(Reflect.get(dependencies, BASIC_PACKAGE_NAME));
223
- if (basicCatalog) return basicCatalog;
224
- }
225
- for (const dependencies of dependencyRecords) {
226
- for (const value of Object.values(dependencies)) {
227
- const catalog = parseCatalog(value);
228
- if (catalog) return catalog;
229
- }
230
- }
231
- return false;
232
- };
233
- const parseYamlMappingLine = (line) => {
234
- const match = /^(\s*)(?:"([^"]+)"|'([^']+)'|([^'"].*?))\s*:\s*(.*?)\s*$/.exec(line);
235
- if (!match) return void 0;
236
- const value = match[5].replace(/\s+#.*$/, "").trim();
237
- const key = match[2] || match[3] || match[4];
238
- return {
239
- indent: match[1].length,
240
- key: key.trim(),
241
- value: /^(['"]).*\1$/.test(value) ? value.slice(1, -1) : value
242
- };
243
- };
244
- const getCatalogVersion = (workspaceRoot, packageName, catalog) => {
245
- const workspacePath = join(workspaceRoot, "pnpm-workspace.yaml");
246
- if (!existsSync(workspacePath)) return void 0;
247
- const lines = readFileSync(workspacePath, "utf8").split(/\r?\n/);
248
- let sectionIndex = -1;
249
- let sectionIndent = -1;
250
- if (catalog === true) {
251
- sectionIndex = lines.findIndex((line) => {
252
- const mapping = parseYamlMappingLine(line);
253
- return mapping?.indent === 0 && mapping.key === "catalog" && mapping.value === "";
254
- });
255
- sectionIndent = 0;
256
- } else {
257
- const catalogsIndex = lines.findIndex((line) => {
258
- const mapping = parseYamlMappingLine(line);
259
- return mapping?.indent === 0 && mapping.key === "catalogs" && mapping.value === "";
260
- });
261
- if (catalogsIndex >= 0) {
262
- for (let index = catalogsIndex + 1; index < lines.length; index++) {
263
- const mapping = parseYamlMappingLine(lines.at(index) ?? "");
264
- if (!mapping) continue;
265
- if (mapping.indent === 0) break;
266
- if (mapping.key === catalog && mapping.value === "") {
267
- sectionIndex = index;
268
- sectionIndent = mapping.indent;
269
- break;
270
- }
271
- }
272
- }
273
- }
274
- if (sectionIndex < 0) return void 0;
275
- for (let index = sectionIndex + 1; index < lines.length; index++) {
276
- const mapping = parseYamlMappingLine(lines.at(index) ?? "");
277
- if (!mapping) continue;
278
- if (mapping.indent <= sectionIndent) break;
279
- if (mapping.key === packageName) return mapping.value || void 0;
280
- }
281
- return void 0;
282
- };
283
- const getCompatibleConfigVersion = (packageJson, workspaceRoot) => {
284
- const dependencyFields = ["devDependencies", "dependencies", "peerDependencies"];
285
- let basicSpec;
286
- for (const field of dependencyFields) {
287
- const dependencies = packageJson?.[field];
288
- if (!dependencies || typeof dependencies !== "object" || Array.isArray(dependencies)) continue;
289
- const value = Object.entries(dependencies).find(([packageName]) => packageName === BASIC_PACKAGE_NAME)?.[1];
290
- if (typeof value === "string") {
291
- basicSpec = value;
292
- break;
293
- }
294
- }
295
- const catalogName = basicSpec?.startsWith("catalog:") ? basicSpec.slice("catalog:".length) : void 0;
296
- let selectedCatalog;
297
- if (catalogName !== void 0) {
298
- selectedCatalog = catalogName && catalogName !== "default" ? catalogName : true;
299
- }
300
- const resolvedSpec = selectedCatalog && workspaceRoot ? getCatalogVersion(workspaceRoot, BASIC_PACKAGE_NAME, selectedCatalog) : basicSpec;
301
- const match = /(\d+)\.(\d+)\.(\d+)/.exec(resolvedSpec ?? getCliVersion());
302
- return match ? `^${match[1]}.${match[2]}.${match[3]}` : "^3.1.0";
303
- };
304
- const addCompatibleConfigVersions = (packages, version) => {
305
- const major = /\d+/.exec(version)?.[0];
306
- const companionVersion = major ? `^${major}.0.0` : version;
307
- return packages.map(
308
- (packageName) => packageName.startsWith("@santi020k/eslint-config-") && packageName !== LITE_PACKAGE_NAME && packageName !== INTEGRATIONS_PACKAGE_NAME ? `${packageName}@${packageName === BASIC_PACKAGE_NAME ? version : companionVersion}` : packageName
309
- );
310
- };
311
182
  const hasLintScript = (cwd) => {
312
183
  const packageJson = readPackageJson(cwd);
313
184
  return Boolean(packageJson?.scripts?.lint);
@@ -402,6 +273,10 @@ const formatList = (values) => {
402
273
  const getProjectSummary = (cwd) => {
403
274
  const options = detectProjectOptions(cwd);
404
275
  const workspaceProjects = detectWorkspaceProjects(cwd);
276
+ let typescriptMode = "off";
277
+ if (options.typescript) {
278
+ typescriptMode = options.typescript === "syntax" ? "syntax" : "type-aware";
279
+ }
405
280
  return {
406
281
  detectedProjects: Object.keys(options.projects ?? {}).sort(),
407
282
  extensions: options.extensions ?? [],
@@ -414,9 +289,42 @@ const getProjectSummary = (cwd) => {
414
289
  testing: options.testing ?? [],
415
290
  tools: options.tools ?? [],
416
291
  typescript: Boolean(options.typescript),
292
+ typescriptMode,
417
293
  workspaceProjects
418
294
  };
419
295
  };
296
+ const getDetectedProjectPaths = (cwd) => {
297
+ const summary = getProjectSummary(cwd);
298
+ return summary.workspaceProjects.length > 0 ? [.../* @__PURE__ */ new Set([...summary.detectedProjects, ...summary.workspaceProjects])] : ["."];
299
+ };
300
+ const getTailwindUnknownClassPolicy = (detected, entryPoint, componentClasses = 0) => {
301
+ if (!detected) return "off";
302
+ if (!entryPoint) return "fallback-off";
303
+ return componentClasses > 0 ? "strict-with-css-components" : "strict";
304
+ };
305
+ const getTailwindEntryPoints = (cwd) => getDetectedProjectPaths(cwd).flatMap((projectPath) => {
306
+ const projectRoot = projectPath === "." ? cwd : join(cwd, projectPath);
307
+ const summary = getProjectSummary(projectRoot);
308
+ if (!summary.libraries.includes("tailwind")) return [];
309
+ const entryPoint = findTailwindEntryPoint(projectRoot) ?? null;
310
+ const componentClasses = entryPoint ? findCssComponentClasses(projectRoot, entryPoint).length : 0;
311
+ return [{
312
+ componentClasses,
313
+ entryPoint,
314
+ path: projectPath,
315
+ unknownClassPolicy: getTailwindUnknownClassPolicy(true, entryPoint, componentClasses)
316
+ }];
317
+ });
318
+ const getDeclarationProjectWarnings = (cwd) => getDetectedProjectPaths(cwd).flatMap((projectPath) => {
319
+ const projectRoot = projectPath === "." ? cwd : join(cwd, projectPath);
320
+ const summary = getProjectSummary(projectRoot);
321
+ if (summary.typescriptMode !== "syntax") return [];
322
+ const packageName = readPackageJson(projectRoot)?.name;
323
+ const packageLabel = typeof packageName === "string" ? `${projectPath} (${packageName})` : projectPath;
324
+ return [
325
+ `TypeScript declarations were detected in ${packageLabel}, but no supported package-local tsconfig was found. The package uses syntax-only linting to avoid a project-service parse failure. Add a tsconfig.json that includes the declarations for type-aware linting, or add their patterns to \`typescript.untypedFiles\` when a parent type-aware project intentionally owns them.`
326
+ ];
327
+ });
420
328
  const getInstallProjectSummary = (cwd) => {
421
329
  const rootSummary = getProjectSummary(cwd);
422
330
  const projectPaths = [.../* @__PURE__ */ new Set([
@@ -425,6 +333,12 @@ const getInstallProjectSummary = (cwd) => {
425
333
  ])];
426
334
  const projectSummaries = projectPaths.map((projectPath) => getProjectSummary(join(cwd, projectPath)));
427
335
  const summaries = [rootSummary, ...projectSummaries];
336
+ let typescriptMode = "off";
337
+ if (summaries.some((summary) => summary.typescriptMode === "type-aware")) {
338
+ typescriptMode = "type-aware";
339
+ } else if (summaries.some((summary) => summary.typescriptMode === "syntax")) {
340
+ typescriptMode = "syntax";
341
+ }
428
342
  return {
429
343
  ...rootSummary,
430
344
  extensions: [...new Set(summaries.flatMap((summary) => summary.extensions))],
@@ -433,7 +347,8 @@ const getInstallProjectSummary = (cwd) => {
433
347
  libraries: [...new Set(summaries.flatMap((summary) => summary.libraries))],
434
348
  testing: [...new Set(summaries.flatMap((summary) => summary.testing))],
435
349
  tools: [...new Set(summaries.flatMap((summary) => summary.tools))],
436
- typescript: summaries.some((summary) => summary.typescript)
350
+ typescript: summaries.some((summary) => summary.typescript),
351
+ typescriptMode
437
352
  };
438
353
  };
439
354
  const getWorkspaceDeclaredConfigPackages = (cwd, summary) => {
@@ -508,13 +423,14 @@ const printUsage = () => {
508
423
  " generate-skill Generate AI agent standards files",
509
424
  "",
510
425
  "Options:",
426
+ " --analyze-source explain-preset: lint source and preview autofix without writing",
511
427
  " --force Overwrite existing generated skill sections/files",
512
428
  " --check Verify generated files, migrations, or snapshots without writing",
513
429
  " --concurrency profile: off, auto, or a worker count",
514
430
  " --create generate-skill: scaffold a root AGENTS.md when missing",
515
431
  " --dry-run install: print the detected install command without running it",
516
432
  " --explicit init: write detected settings explicitly",
517
- " --file explain/profile/snapshot/diff: representative file or lint target (repeatable)",
433
+ " --file explain/explain-preset/profile/snapshot/diff: file or lint target (repeatable)",
518
434
  " --fix doctor: safely repair generated config and package metadata",
519
435
  " --full migrate --to v3: choose the batteries-included package",
520
436
  " --json Print JSON for commands that support it",
@@ -528,6 +444,7 @@ const printUsage = () => {
528
444
  " --prune baseline: remove suppressions for resolved violations",
529
445
  " --snapshot-path snapshot/diff: override .eslint-config-snapshot.json",
530
446
  " --to migrate: target version (v2 or v3)",
447
+ " --verbose doctor: print per-project activation details",
531
448
  " --with-eslint-mcp generate-skill: scaffold the official ESLint MCP server",
532
449
  " --write Apply safe migrations for commands that support it",
533
450
  " --help, -h Show this help message",
@@ -539,9 +456,9 @@ const COMMAND_OPTIONS = {
539
456
  compatibility: ["--json"],
540
457
  diff: ["--file", "--json", "--snapshot-path"],
541
458
  docs: [],
542
- doctor: ["--fix", "--json", "--lite-install"],
459
+ doctor: ["--fix", "--json", "--lite-install", "--verbose"],
543
460
  explain: ["--file", "--json"],
544
- "explain-preset": ["--compatibility", "--file", "--json", "--output"],
461
+ "explain-preset": ["--analyze-source", "--compatibility", "--file", "--json", "--output"],
545
462
  "generate-skill": ["--check", "--create", "--force", "--with-eslint-mcp"],
546
463
  init: ["--check", "--explicit"],
547
464
  inspect: ["--json"],
@@ -683,7 +600,8 @@ const handleInspect = async (cwd = process.cwd(), json = false) => {
683
600
  const payload = {
684
601
  activeConfig,
685
602
  detected: summary,
686
- packageManager: detectPackageManager(cwd)
603
+ packageManager: detectPackageManager(cwd),
604
+ tailwindEntryPoints: getTailwindEntryPoints(cwd)
687
605
  };
688
606
  if (json) {
689
607
  console.log(JSON.stringify(payload, null, 2));
@@ -707,31 +625,135 @@ const handleInspect = async (cwd = process.cwd(), json = false) => {
707
625
  ].join("\n"));
708
626
  };
709
627
  const semver = createRequire(import.meta.url)("semver");
710
- const readPackageMetadataFromMain = (mainPath, packageName) => {
628
+ const intersectTwoSemverRanges = (left, right) => {
629
+ if (semver.subset(left, right)) return left;
630
+ if (semver.subset(right, left)) return right;
631
+ const intersections = /* @__PURE__ */ new Set();
632
+ const leftRange = new semver.Range(left);
633
+ const rightRange = new semver.Range(right);
634
+ for (const leftComparators of leftRange.set) {
635
+ for (const rightComparators of rightRange.set) {
636
+ const candidate = [...leftComparators, ...rightComparators].map((comparator) => comparator.value).filter(Boolean).join(" ");
637
+ const normalized = new semver.Range(candidate || "*").range || "*";
638
+ if (semver.minVersion(normalized)) intersections.add(normalized);
639
+ }
640
+ }
641
+ return intersections.size > 0 ? [...intersections].join(" || ") : null;
642
+ };
643
+ const intersectSemverRanges = (ranges) => {
644
+ const validRanges = ranges.filter((range) => semver.validRange(range) !== null);
645
+ if (validRanges.length !== ranges.length || validRanges.length === 0) return null;
646
+ let intersection = validRanges[0];
647
+ for (const range of validRanges.slice(1)) {
648
+ intersection = intersectTwoSemverRanges(intersection, range) ?? "";
649
+ if (!intersection) return null;
650
+ }
651
+ return intersection;
652
+ };
653
+ const readPackageMetadataFromMain = (mainPath) => {
711
654
  let directory = dirname(mainPath);
712
655
  for (let depth = 0; depth < 4; depth++) {
713
656
  const packagePath = join(directory, "package.json");
714
657
  if (existsSync(packagePath)) {
715
658
  const metadata = JSON.parse(readFileSync(packagePath, "utf8"));
716
- if (metadata.name === packageName) return metadata;
659
+ if (metadata.name && metadata.version) return metadata;
717
660
  }
718
661
  directory = dirname(directory);
719
662
  }
720
663
  return null;
721
664
  };
722
665
  const resolvePackageMetadata = (cwd, packageName) => {
723
- try {
724
- const projectRequire = createRequire(join(cwd, "package.json"));
666
+ const candidateRoots = [...new Set([
667
+ cwd,
668
+ findPnpmWorkspaceRoot(cwd)
669
+ ].filter((root) => Boolean(root)))];
670
+ for (const candidateRoot of candidateRoots) {
725
671
  try {
726
- return readPackageMetadataFromMain(projectRequire.resolve(packageName), packageName);
672
+ const manifestPath = findPackageJSON(
673
+ packageName,
674
+ pathToFileURL(join(candidateRoot, "package.json"))
675
+ );
676
+ if (manifestPath) {
677
+ const metadata = JSON.parse(readFileSync(manifestPath, "utf8"));
678
+ return metadata;
679
+ }
727
680
  } catch {
728
- const integrationsMain = projectRequire.resolve(INTEGRATIONS_PACKAGE_NAME);
729
- const integrationsRequire = createRequire(integrationsMain);
730
- return readPackageMetadataFromMain(integrationsRequire.resolve(packageName), packageName);
731
681
  }
732
- } catch {
733
- return null;
682
+ try {
683
+ const projectRequire = createRequire(join(candidateRoot, "package.json"));
684
+ try {
685
+ const metadata = readPackageMetadataFromMain(projectRequire.resolve(packageName));
686
+ if (metadata) return metadata;
687
+ } catch {
688
+ const integrationsMain = projectRequire.resolve(INTEGRATIONS_PACKAGE_NAME);
689
+ const integrationsRequire = createRequire(integrationsMain);
690
+ const metadata = readPackageMetadataFromMain(integrationsRequire.resolve(packageName));
691
+ if (metadata) return metadata;
692
+ }
693
+ } catch {
694
+ }
695
+ for (const projectPath of detectWorkspaceProjects(candidateRoot)) {
696
+ const metadata = readPackageJson(join(candidateRoot, projectPath));
697
+ if (metadata?.name === packageName) return metadata;
698
+ }
699
+ }
700
+ return null;
701
+ };
702
+ const assessConsumerNodeEngine = (cwd, packageJson) => {
703
+ const declaredConfigPackages = [...getDeclaredDependencyNames(packageJson)].filter((packageName) => packageName.startsWith("@santi020k/eslint-config-"));
704
+ const requiredRanges = declaredConfigPackages.flatMap((packageName) => {
705
+ const range = resolvePackageMetadata(cwd, packageName)?.engines?.node;
706
+ return range ? [range] : [];
707
+ });
708
+ const requiredRange = intersectSemverRanges(requiredRanges);
709
+ const engines = packageJson?.engines;
710
+ const consumerRange = engines && typeof engines === "object" && !Array.isArray(engines) ? engines.node : null;
711
+ const normalizedConsumerRange = typeof consumerRange === "string" ? consumerRange : null;
712
+ if (requiredRanges.length === 0) {
713
+ return {
714
+ compatible: true,
715
+ consumerRange: normalizedConsumerRange,
716
+ requiredRange: null,
717
+ targetRange: null
718
+ };
719
+ }
720
+ if (!requiredRange) {
721
+ return {
722
+ compatible: false,
723
+ consumerRange: normalizedConsumerRange,
724
+ requiredRange: null,
725
+ targetRange: null
726
+ };
734
727
  }
728
+ if (!normalizedConsumerRange || !semver.validRange(normalizedConsumerRange)) {
729
+ return {
730
+ compatible: false,
731
+ consumerRange: normalizedConsumerRange,
732
+ requiredRange,
733
+ targetRange: requiredRange
734
+ };
735
+ }
736
+ const compatible = requiredRanges.every((range) => semver.subset(normalizedConsumerRange, range));
737
+ return {
738
+ compatible,
739
+ consumerRange: normalizedConsumerRange,
740
+ requiredRange,
741
+ targetRange: compatible ? null : intersectSemverRanges([normalizedConsumerRange, ...requiredRanges])
742
+ };
743
+ };
744
+ const getConsumerNodeEngineWarning = (cwd, packageJson) => {
745
+ const assessment = assessConsumerNodeEngine(cwd, packageJson);
746
+ if (assessment.compatible) return null;
747
+ if (!assessment.requiredRange) {
748
+ return "Installed config packages do not share a compatible Node engine range.";
749
+ }
750
+ if (!assessment.consumerRange) {
751
+ return `package.json does not declare engines.node; installed config packages require ${assessment.requiredRange}.`;
752
+ }
753
+ if (!semver.validRange(assessment.consumerRange)) {
754
+ return `package.json engines.node ${assessment.consumerRange} is invalid; installed config packages require ${assessment.requiredRange}.`;
755
+ }
756
+ return `package.json engines.node ${assessment.consumerRange} permits unsupported runtimes; installed config packages require ${assessment.requiredRange}.`;
735
757
  };
736
758
  const getAstroDoctorNodeWarning = (metadata) => {
737
759
  const nodeRange = metadata.engines?.node;
@@ -779,7 +801,11 @@ const validateConfigContent = (configContent, hasV1FrameworkImports, declaredDep
779
801
  if (hasV1FrameworkImports) {
780
802
  warnings.push("Config still imports v1 framework packages. Run `basic-eslint migrate --write` or switch to framework booleans.");
781
803
  }
782
- const usesBasicComposer = /from\s*['"]@santi020k\/eslint-config-(?:basic|full|lite)['"]/.test(configContent) && /\b(?:defineConfig|eslintConfig)\s*\(/.test(configContent);
804
+ const usesComposerCall = /from\s*['"]@santi020k\/eslint-config-(?:basic|full|lite)['"]/.test(configContent) && /\b(?:defineConfig|eslintConfig)\s*\(/.test(configContent);
805
+ const usesRecommendedComposer = /from\s*['"]@santi020k\/eslint-config-(?:basic|full)\/recommended['"]/.test(
806
+ configContent
807
+ );
808
+ const usesBasicComposer = usesComposerCall || usesRecommendedComposer;
783
809
  const hasDetectedProjectScopes = summary.workspaceProjects.every((project) => summary.detectedProjects.includes(project));
784
810
  if (summary.workspaceProjects.length > 0 && !configContent.includes("projects:") && !(usesBasicComposer && hasDetectedProjectScopes)) {
785
811
  warnings.push("Workspace packages were detected, but the root config does not use `projects` scoping.");
@@ -861,7 +887,13 @@ const checkDuplicateEslint = (cwd, warnings) => {
861
887
  );
862
888
  }
863
889
  };
864
- const buildDoctorDiagnosis = (cwd, configPath, configContent, hasV1FrameworkImports, activeConfig, declaredDependencies, summary) => {
890
+ const getTailwindEntryPointWarnings = (cwd) => getTailwindEntryPoints(cwd).flatMap(({ entryPoint, path: projectPath }) => {
891
+ if (entryPoint) return [];
892
+ return [
893
+ `Tailwind CSS was detected in ${projectPath}, but no conventional CSS entry point was found. \`better-tailwindcss/no-unknown-classes\` falls back to off; set \`tailwind.entryPoint\` to enable strict validation or \`tailwind.noUnknownClasses: false\` to document a mixed CSS system.`
894
+ ];
895
+ });
896
+ const buildDoctorDiagnosis = (cwd, packageJson, configPath, configContent, hasV1FrameworkImports, activeConfig, declaredDependencies, summary) => {
865
897
  const { issues, warnings } = validateProjectSetup(cwd, configPath, activeConfig, hasV1FrameworkImports);
866
898
  if (configContent) {
867
899
  warnings.push(...validateConfigContent(configContent, hasV1FrameworkImports, declaredDependencies, summary));
@@ -869,9 +901,238 @@ const buildDoctorDiagnosis = (cwd, configPath, configContent, hasV1FrameworkImpo
869
901
  const astroDoctorEnabled = checkAstroDoctorStatus(activeConfig, configContent, summary, warnings);
870
902
  warnings.push(...getAstroDoctorCompatibilityWarnings(cwd, astroDoctorEnabled));
871
903
  checkDuplicateEslint(cwd, warnings);
904
+ warnings.push(...getDeclarationProjectWarnings(cwd));
905
+ warnings.push(...getTailwindEntryPointWarnings(cwd));
906
+ const nodeEngineWarning = getConsumerNodeEngineWarning(cwd, packageJson);
907
+ if (nodeEngineWarning) warnings.push(nodeEngineWarning);
872
908
  return { issues, warnings };
873
909
  };
874
- const outputDoctorResult = (json, configPath, packageManager, summary, installCommand, requiredPackages, issues, warnings, fixes = []) => {
910
+ const DOCTOR_FEATURE_ALIASES = {
911
+ "astro-doctor": "Astro Doctor",
912
+ "best-practices": "Best Practices",
913
+ compat: "Browser Compat",
914
+ nest: "NestJS",
915
+ next: "Next.js",
916
+ node: "Node.js",
917
+ solid: "SolidJS",
918
+ "tanstack-start": "TanStack Start",
919
+ tailwind: "Tailwind CSS"
920
+ };
921
+ const normalizeDoctorFeature = (value) => {
922
+ const comparableValue = DOCTOR_FEATURE_ALIASES[value] ?? value;
923
+ return comparableValue.toLowerCase().replaceAll(/[^a-z0-9]/g, "");
924
+ };
925
+ const isDoctorFeatureEnabled = (name, enabled) => {
926
+ const normalizedName = normalizeDoctorFeature(name);
927
+ return enabled.some((value) => normalizeDoctorFeature(value) === normalizedName);
928
+ };
929
+ const getTypeScriptConfig = (cwd) => [
930
+ "tsconfig.json",
931
+ "tsconfig.base.json",
932
+ "tsconfig.app.json",
933
+ "tsconfig.node.json",
934
+ "tsconfig.eslint.json"
935
+ ].find((fileName) => existsSync(join(cwd, fileName))) ?? null;
936
+ const getCategoryPackageInstalled = (cwd, packageName) => [
937
+ packageName,
938
+ INTEGRATIONS_PACKAGE_NAME,
939
+ FULL_PACKAGE_NAME
940
+ ].some((candidate) => resolvePackageMetadata(cwd, candidate) !== null);
941
+ const createDoctorFeatureActivations = (cwd, category, detected, enabled, includeEnabled = false) => {
942
+ const names = [...detected];
943
+ if (includeEnabled) {
944
+ for (const enabledName of enabled) {
945
+ if (!isDoctorFeatureEnabled(enabledName, names)) names.push(enabledName);
946
+ }
947
+ }
948
+ return names.map((name) => {
949
+ const packageName = category === "frameworks" ? Object.entries(FRAMEWORK_KEY_TO_PACKAGE).find(([framework]) => isDoctorFeatureEnabled(name, [framework]))?.[1] : CATEGORY_PACKAGES[category];
950
+ if (!packageName) throw new Error(`Unknown doctor ${category} feature: ${name}`);
951
+ const installed = getCategoryPackageInstalled(cwd, packageName);
952
+ const isDetected = isDoctorFeatureEnabled(name, detected);
953
+ const isEnabled = installed && isDoctorFeatureEnabled(name, enabled);
954
+ let reason;
955
+ if (!installed) {
956
+ reason = isDetected ? `Detected, but ${packageName} is not installed.` : `Enabled in the active config, but ${packageName} is not installed.`;
957
+ } else if (!isEnabled) {
958
+ reason = "Detected and installed, but the active config did not load this pack.";
959
+ }
960
+ return {
961
+ detected: isDetected,
962
+ enabled: isEnabled,
963
+ installed,
964
+ name,
965
+ package: packageName,
966
+ ...reason ? { reason } : {}
967
+ };
968
+ });
969
+ };
970
+ const getInactiveDoctorPackages = (cwd, detectedFrameworks, summary, activeConfig) => {
971
+ const inactivePackages = Object.entries(FRAMEWORK_KEY_TO_PACKAGE).filter(([framework, packageName]) => !detectedFrameworks.includes(framework) && !isDoctorFeatureEnabled(framework, activeConfig?.frameworks ?? []) && resolvePackageMetadata(cwd, packageName) !== null).map(([, packageName]) => ({
972
+ package: packageName,
973
+ reason: "Installed, but no matching framework signal was detected in this project."
974
+ }));
975
+ for (const [category, packageName] of Object.entries(CATEGORY_PACKAGES)) {
976
+ if (summary[category].length === 0 && (activeConfig?.[category]?.length ?? 0) === 0 && resolvePackageMetadata(cwd, packageName) !== null) {
977
+ inactivePackages.push({
978
+ package: packageName,
979
+ reason: `Installed, but no ${category} signals were detected in this project.`
980
+ });
981
+ }
982
+ }
983
+ return inactivePackages;
984
+ };
985
+ const getDoctorProjectActivations = async (cwd) => {
986
+ const rootSummary = getProjectSummary(cwd);
987
+ const projectPaths = [.../* @__PURE__ */ new Set([
988
+ ".",
989
+ ...rootSummary.detectedProjects,
990
+ ...rootSummary.workspaceProjects
991
+ ])].sort((left, right) => {
992
+ if (left === ".") return -1;
993
+ if (right === ".") return 1;
994
+ return left.localeCompare(right);
995
+ });
996
+ return Promise.all(projectPaths.map(async (projectPath) => {
997
+ const projectRoot = projectPath === "." ? cwd : join(cwd, projectPath);
998
+ const summary = getProjectSummary(projectRoot);
999
+ const projectActiveConfig = await analyzeEslintConfig(cwd, { projectPath, projectPaths }) ?? void 0;
1000
+ const tsconfig = getTypeScriptConfig(projectRoot);
1001
+ const typescriptInstalled = resolvePackageMetadata(projectRoot, TYPESCRIPT_PACKAGE_NAME) !== null;
1002
+ const typescriptEnabled = summary.typescript && Boolean(projectActiveConfig?.typescript);
1003
+ const tailwindDetected = summary.libraries.includes("tailwind");
1004
+ const tailwindEntryPoint = tailwindDetected ? findTailwindEntryPoint(projectRoot) ?? null : null;
1005
+ const tailwindComponentClasses = tailwindEntryPoint ? findCssComponentClasses(projectRoot, tailwindEntryPoint).length : 0;
1006
+ let typescriptMode = "off";
1007
+ let typescriptReason;
1008
+ if (typescriptEnabled) {
1009
+ typescriptMode = summary.typescriptMode;
1010
+ }
1011
+ if (!typescriptInstalled && summary.typescript) {
1012
+ typescriptReason = summary.typescriptMode === "syntax" ? "TypeScript declarations were detected, but TypeScript is not installed." : "A tsconfig was detected, but TypeScript is not installed.";
1013
+ } else if (typescriptInstalled && !summary.typescript) {
1014
+ typescriptReason = "Installed, but no supported tsconfig was detected in this project.";
1015
+ } else if (typescriptInstalled && summary.typescript && !typescriptEnabled) {
1016
+ typescriptReason = "Detected and installed, but the active config did not enable TypeScript.";
1017
+ }
1018
+ return {
1019
+ extensions: createDoctorFeatureActivations(
1020
+ projectRoot,
1021
+ "extensions",
1022
+ summary.extensions,
1023
+ projectActiveConfig?.extensions ?? [],
1024
+ true
1025
+ ),
1026
+ formats: createDoctorFeatureActivations(
1027
+ projectRoot,
1028
+ "formats",
1029
+ summary.formats,
1030
+ projectActiveConfig?.formats ?? [],
1031
+ true
1032
+ ),
1033
+ frameworks: createDoctorFeatureActivations(
1034
+ projectRoot,
1035
+ "frameworks",
1036
+ summary.frameworks,
1037
+ projectActiveConfig?.frameworks ?? [],
1038
+ true
1039
+ ),
1040
+ ignores: projectActiveConfig?.ignores ?? [],
1041
+ inactivePackages: getInactiveDoctorPackages(projectRoot, summary.frameworks, summary, projectActiveConfig),
1042
+ libraries: createDoctorFeatureActivations(
1043
+ projectRoot,
1044
+ "libraries",
1045
+ summary.libraries,
1046
+ projectActiveConfig?.libraries ?? [],
1047
+ true
1048
+ ),
1049
+ path: projectPath,
1050
+ runtime: summary.runtime,
1051
+ tailwind: {
1052
+ componentClasses: tailwindComponentClasses,
1053
+ detected: tailwindDetected,
1054
+ entryPoint: tailwindEntryPoint,
1055
+ unknownClassPolicy: getTailwindUnknownClassPolicy(
1056
+ tailwindDetected,
1057
+ tailwindEntryPoint,
1058
+ tailwindComponentClasses
1059
+ )
1060
+ },
1061
+ testing: createDoctorFeatureActivations(
1062
+ projectRoot,
1063
+ "testing",
1064
+ summary.testing,
1065
+ projectActiveConfig?.testing ?? [],
1066
+ true
1067
+ ),
1068
+ tools: createDoctorFeatureActivations(
1069
+ projectRoot,
1070
+ "tools",
1071
+ summary.tools,
1072
+ projectActiveConfig?.tools ?? [],
1073
+ true
1074
+ ),
1075
+ typescript: {
1076
+ detected: summary.typescript,
1077
+ enabled: typescriptEnabled,
1078
+ installed: typescriptInstalled,
1079
+ mode: typescriptMode,
1080
+ package: TYPESCRIPT_PACKAGE_NAME,
1081
+ ...typescriptReason ? { reason: typescriptReason } : {},
1082
+ tsconfig
1083
+ }
1084
+ };
1085
+ }));
1086
+ };
1087
+ const formatDoctorActivation = (features) => features.length === 0 ? "none" : features.map((feature) => {
1088
+ const states = [
1089
+ feature.installed ? "I" : "-",
1090
+ feature.detected ? "D" : "-",
1091
+ feature.enabled ? "E" : "-"
1092
+ ].join("");
1093
+ return `${feature.name}[${states}]`;
1094
+ }).join(",");
1095
+ const formatDoctorProjectTable = (projects) => [
1096
+ "",
1097
+ "Per-project activation (I=installed, D=detected, E=enabled):",
1098
+ "Project | Runtime | TypeScript | Tailwind entry | Frameworks | Formats | Libraries | Extensions | Testing | Tools | Ignores",
1099
+ "--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---",
1100
+ ...projects.flatMap((project) => {
1101
+ const typescriptStates = [
1102
+ project.typescript.installed ? "I" : "-",
1103
+ project.typescript.detected ? "D" : "-",
1104
+ project.typescript.enabled ? "E" : "-"
1105
+ ].join("");
1106
+ const row = [
1107
+ project.path,
1108
+ project.runtime,
1109
+ `${project.typescript.mode}:${project.typescript.tsconfig ?? "none"}[${typescriptStates}]`,
1110
+ project.tailwind.componentClasses > 0 ? `${project.tailwind.entryPoint ?? "none"} (${project.tailwind.componentClasses} CSS classes)` : project.tailwind.entryPoint ?? "none",
1111
+ formatDoctorActivation(project.frameworks),
1112
+ formatDoctorActivation(project.formats),
1113
+ formatDoctorActivation(project.libraries),
1114
+ formatDoctorActivation(project.extensions),
1115
+ formatDoctorActivation(project.testing),
1116
+ formatDoctorActivation(project.tools),
1117
+ formatList(project.ignores)
1118
+ ].join(" | ");
1119
+ const explanations = [
1120
+ project.typescript.reason,
1121
+ ...project.frameworks.map((feature) => feature.reason),
1122
+ ...project.formats.map((feature) => feature.reason),
1123
+ ...project.libraries.map((feature) => feature.reason),
1124
+ ...project.extensions.map((feature) => feature.reason),
1125
+ ...project.testing.map((feature) => feature.reason),
1126
+ ...project.tools.map((feature) => feature.reason),
1127
+ ...project.inactivePackages.map((item) => `${item.package}: ${item.reason}`)
1128
+ ].filter((reason) => Boolean(reason));
1129
+ return [
1130
+ row,
1131
+ ...explanations.map((reason) => ` ${project.path}: ${reason}`)
1132
+ ];
1133
+ })
1134
+ ];
1135
+ const outputDoctorResult = (json, verbose, configPath, packageManager, summary, installCommand, requiredPackages, issues, warnings, projects, fixes = []) => {
875
1136
  let status = "passed";
876
1137
  if (issues.length > 0) {
877
1138
  status = "failed";
@@ -884,6 +1145,7 @@ const outputDoctorResult = (json, configPath, packageManager, summary, installCo
884
1145
  ...installCommand ? { installCommand } : {},
885
1146
  issues,
886
1147
  packageManager,
1148
+ projects,
887
1149
  requiredPackages,
888
1150
  status,
889
1151
  warnings,
@@ -901,6 +1163,7 @@ const outputDoctorResult = (json, configPath, packageManager, summary, installCo
901
1163
  `- Workspace projects: ${formatList(summary.workspaceProjects)}`,
902
1164
  `- Required packages: ${formatList(requiredPackages)}`,
903
1165
  ...installCommand ? [`- Install command: ${installCommand}`] : [],
1166
+ ...verbose ? formatDoctorProjectTable(projects) : [],
904
1167
  ...fixes.length > 0 ? ["", "Fixes applied:", ...fixes.map((fix) => `- ${fix}`)] : [],
905
1168
  ...issues.length > 0 ? ["", "Issues:", ...issues.map((issue) => `- ${issue}`)] : [],
906
1169
  ...warnings.length > 0 ? ["", "Warnings:", ...warnings.map((warning) => `- ${warning}`)] : []
@@ -945,6 +1208,15 @@ const applyDoctorFixes = (cwd, packageJson, configPath, configContent, summary)
945
1208
  fixes.push(`Declared ${packageName}.`);
946
1209
  packageChanged = true;
947
1210
  }
1211
+ const nodeEngine = assessConsumerNodeEngine(cwd, packageJson);
1212
+ if (!nodeEngine.compatible && nodeEngine.targetRange) {
1213
+ updated.engines ??= {};
1214
+ updated.engines.node = nodeEngine.targetRange;
1215
+ packageChanged = true;
1216
+ fixes.push(
1217
+ nodeEngine.consumerRange ? `Changed engines.node from ${nodeEngine.consumerRange} to ${nodeEngine.targetRange}.` : `Declared engines.node as ${nodeEngine.targetRange}.`
1218
+ );
1219
+ }
948
1220
  if (packageChanged) {
949
1221
  const packagePath = join(cwd, "package.json");
950
1222
  const backupPath = createDoctorBackupPath(packagePath);
@@ -971,7 +1243,7 @@ const applyDoctorFixes = (cwd, packageJson, configPath, configContent, summary)
971
1243
  return fixes;
972
1244
  };
973
1245
  const hasV1FrameworkPackageImports = (configContent) => configContent !== null && Object.keys(FRAMEWORK_PACKAGE_TO_KEY).some((packageName) => configContent.includes(packageName));
974
- const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = false, fix = false) => {
1246
+ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = false, fix = false, verbose = false) => {
975
1247
  const pnpmWorkspaceRoot = findPnpmWorkspaceRoot(cwd);
976
1248
  const projectRoot = pnpmWorkspaceRoot ?? cwd;
977
1249
  const packageManager = detectPackageManager(projectRoot);
@@ -990,13 +1262,17 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
990
1262
  packageManager,
991
1263
  addCompatibleConfigVersions(
992
1264
  liteInstallPackages,
993
- getCompatibleConfigVersion(packageJson, pnpmWorkspaceRoot)
1265
+ getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
994
1266
  ),
995
1267
  workspaceRoot,
996
1268
  catalog
997
1269
  );
998
1270
  if (json) {
999
- console.log(JSON.stringify({ command: liteInstallCommand, packageManager, packages: liteInstallPackages }, null, 2));
1271
+ console.log(JSON.stringify({
1272
+ command: liteInstallCommand,
1273
+ packageManager,
1274
+ packages: liteInstallPackages
1275
+ }, null, 2));
1000
1276
  } else {
1001
1277
  console.log(liteInstallCommand);
1002
1278
  }
@@ -1018,13 +1294,14 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
1018
1294
  packageManager,
1019
1295
  addCompatibleConfigVersions(
1020
1296
  requiredPackages,
1021
- getCompatibleConfigVersion(packageJson, pnpmWorkspaceRoot)
1297
+ getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
1022
1298
  ),
1023
1299
  workspaceRoot,
1024
1300
  catalog
1025
1301
  ) : void 0;
1026
1302
  const { issues, warnings } = buildDoctorDiagnosis(
1027
1303
  projectRoot,
1304
+ packageJson,
1028
1305
  configPath,
1029
1306
  configContent,
1030
1307
  hasV1FrameworkImports,
@@ -1032,8 +1309,10 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
1032
1309
  declaredDependencies,
1033
1310
  summary
1034
1311
  );
1312
+ const projects = await getDoctorProjectActivations(projectRoot);
1035
1313
  outputDoctorResult(
1036
1314
  json,
1315
+ verbose,
1037
1316
  configPath,
1038
1317
  packageManager,
1039
1318
  summary,
@@ -1041,6 +1320,7 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
1041
1320
  requiredPackages,
1042
1321
  issues,
1043
1322
  warnings,
1323
+ projects,
1044
1324
  fixes
1045
1325
  );
1046
1326
  };
@@ -1066,7 +1346,7 @@ const handleInstall = (cwd = process.cwd(), dryRun = false, runner = spawnSync)
1066
1346
  const catalog = workspaceRoot ? getCatalogPreference(packageJson) : false;
1067
1347
  const installPackages = addCompatibleConfigVersions(
1068
1348
  packages,
1069
- getCompatibleConfigVersion(packageJson, pnpmWorkspaceRoot)
1349
+ getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
1070
1350
  );
1071
1351
  if (packages.length === 0) {
1072
1352
  console.log("\u2705 All packages required by the detected ESLint configuration are already declared.");
@@ -1226,7 +1506,7 @@ const dispatchCommand = (command, cwd, flags) => {
1226
1506
  break;
1227
1507
  }
1228
1508
  case "doctor": {
1229
- handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall, flags.hasFix).catch((error) => {
1509
+ handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall, flags.hasFix, flags.hasVerbose).catch((error) => {
1230
1510
  console.error(`\u274C Failed to run doctor: ${String(error)}`);
1231
1511
  process.exitCode = 1;
1232
1512
  });
@@ -1254,8 +1534,10 @@ const dispatchCommand = (command, cwd, flags) => {
1254
1534
  break;
1255
1535
  }
1256
1536
  handleExplainPreset(cwd, flags.positional, {
1537
+ analyzeSource: flags.hasAnalyzeSource,
1257
1538
  compatibility: flags.hasCompatibility,
1258
1539
  file: flags.files[0],
1540
+ files: flags.files,
1259
1541
  json: flags.hasJson,
1260
1542
  output: flags.output
1261
1543
  }).catch((error) => {
@@ -1385,6 +1667,7 @@ const runCli = (argv = process.argv, cwd = process.cwd()) => {
1385
1667
  dispatchCommand(command, cwd, {
1386
1668
  concurrency: getFlagValue(argv, "--concurrency"),
1387
1669
  files: getFlagValues(argv, "--file"),
1670
+ hasAnalyzeSource: argv.includes("--analyze-source"),
1388
1671
  hasCheck: argv.includes("--check"),
1389
1672
  hasCompatibility: argv.includes("--compatibility"),
1390
1673
  hasCreate: argv.includes("--create"),
@@ -1396,6 +1679,7 @@ const runCli = (argv = process.argv, cwd = process.cwd()) => {
1396
1679
  hasJson: argv.includes("--json"),
1397
1680
  hasLiteInstall: argv.includes("--lite-install"),
1398
1681
  hasPrune: argv.includes("--prune"),
1682
+ hasVerbose: argv.includes("--verbose"),
1399
1683
  hasWithEslintMcp: argv.includes("--with-eslint-mcp"),
1400
1684
  hasWrite: argv.includes("--write"),
1401
1685
  maxDurationMs: getNumericFlagValue(argv, "--max-duration"),
@@ -1424,3 +1708,4 @@ export {
1424
1708
  handleUpdate,
1425
1709
  runCli
1426
1710
  };
1711
+ //# sourceMappingURL=cli.js.map