@santi020k/eslint-config-basic 2.1.1 → 3.1.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 +83 -0
- package/dist/agent-skill-generator.d.ts +11 -2
- package/dist/agent-skill-generator.js +59 -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 +782 -0
- package/dist/cli-workflows.d.ts +63 -0
- package/dist/cli-workflows.js +444 -0
- package/dist/cli.d.ts +5 -4
- package/dist/cli.js +539 -88
- package/dist/frameworks.d.ts +1 -25
- package/dist/frameworks.js +2 -23
- package/dist/index.d.ts +4 -10
- package/dist/index.js +160 -262
- package/dist/integrations.d.ts +6 -1
- package/dist/integrations.js +44 -1
- package/dist/recommended.d.ts +5 -0
- package/dist/recommended.js +5 -0
- package/dist/resolvers.js +1 -6
- package/dist/tailwind.d.ts +6 -0
- package/dist/tailwind.js +25 -0
- package/package.json +137 -31
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
2
3
|
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import { basename, dirname, join } from "node:path";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
analyzeEslintConfig,
|
|
9
|
+
ESLINT_CONFIG_FILENAMES,
|
|
10
|
+
handleGenerateSkill
|
|
11
|
+
} from "./agent-skill-generator.js";
|
|
12
|
+
import { handleCompatibility, handleExplainRule } from "./cli-advanced.js";
|
|
13
|
+
import { getExplicitConfigFeaturePackages, handleMigrateV3 } from "./cli-migration.js";
|
|
14
|
+
import {
|
|
15
|
+
handleBaseline,
|
|
16
|
+
handleProfile,
|
|
17
|
+
handleSnapshot,
|
|
18
|
+
handleSnapshotDiff
|
|
19
|
+
} from "./cli-workflows.js";
|
|
7
20
|
import { detectProjectOptions } from "./index.js";
|
|
8
21
|
const getDefaultConfigFilename = (cwd) => {
|
|
9
22
|
const packageJsonPath = join(cwd, "package.json");
|
|
@@ -16,15 +29,12 @@ const getDefaultConfigFilename = (cwd) => {
|
|
|
16
29
|
}
|
|
17
30
|
};
|
|
18
31
|
const resolveConfigPath = (cwd) => {
|
|
19
|
-
const existingConfigPath =
|
|
20
|
-
"eslint.config.js",
|
|
21
|
-
"eslint.config.mjs"
|
|
22
|
-
].map((filename) => join(cwd, filename)).find((p) => existsSync(p));
|
|
32
|
+
const existingConfigPath = ESLINT_CONFIG_FILENAMES.map((filename) => join(cwd, filename)).find((p) => existsSync(p));
|
|
23
33
|
return existingConfigPath ?? join(cwd, getDefaultConfigFilename(cwd));
|
|
24
34
|
};
|
|
25
35
|
const getFrameworkKeys = (detectedFrameworks) => {
|
|
26
36
|
const frameworkKeys = new Set(detectedFrameworks ?? []);
|
|
27
|
-
if (frameworkKeys.has("next") || frameworkKeys.has("expo") || frameworkKeys.has("
|
|
37
|
+
if (frameworkKeys.has("next") || frameworkKeys.has("expo") || frameworkKeys.has("react-router") || frameworkKeys.has("tanstack-start") && !frameworkKeys.has("solid")) {
|
|
28
38
|
frameworkKeys.add("react");
|
|
29
39
|
}
|
|
30
40
|
return [...frameworkKeys].sort();
|
|
@@ -43,7 +53,7 @@ const FRAMEWORK_PACKAGE_TO_KEY = {
|
|
|
43
53
|
"@santi020k/eslint-config-qwik": "qwik",
|
|
44
54
|
"@santi020k/eslint-config-react": "react",
|
|
45
55
|
"@santi020k/eslint-config-react-router": "react-router",
|
|
46
|
-
"@santi020k/eslint-config-remix": "
|
|
56
|
+
"@santi020k/eslint-config-remix": "react-router",
|
|
47
57
|
"@santi020k/eslint-config-slidev": "slidev",
|
|
48
58
|
"@santi020k/eslint-config-solid": "solid",
|
|
49
59
|
"@santi020k/eslint-config-svelte": "svelte",
|
|
@@ -51,18 +61,27 @@ const FRAMEWORK_PACKAGE_TO_KEY = {
|
|
|
51
61
|
"@santi020k/eslint-config-vite": "vite",
|
|
52
62
|
"@santi020k/eslint-config-vue": "vue"
|
|
53
63
|
};
|
|
54
|
-
const FRAMEWORK_KEY_TO_PACKAGE =
|
|
55
|
-
Object.
|
|
56
|
-
)
|
|
64
|
+
const FRAMEWORK_KEY_TO_PACKAGE = {
|
|
65
|
+
...Object.fromEntries(
|
|
66
|
+
Object.entries(FRAMEWORK_PACKAGE_TO_KEY).filter(([packageName]) => packageName !== "@santi020k/eslint-config-remix").map(([packageName, framework]) => [framework, packageName])
|
|
67
|
+
),
|
|
68
|
+
"react-router": "@santi020k/eslint-config-react-router"
|
|
69
|
+
};
|
|
57
70
|
const LITE_PACKAGE_NAME = "@santi020k/eslint-config-lite";
|
|
58
71
|
const INTEGRATIONS_PACKAGE_NAME = "@santi020k/eslint-config-integrations";
|
|
72
|
+
const ASTRO_DOCTOR_PACKAGE_NAME = "@santi020k/eslint-plugin-astro-doctor";
|
|
73
|
+
const BASIC_PACKAGE_NAME = "@santi020k/eslint-config-basic";
|
|
74
|
+
const FULL_PACKAGE_NAME = "@santi020k/eslint-config-full";
|
|
59
75
|
const TYPESCRIPT_PACKAGE_NAME = "typescript";
|
|
76
|
+
const CATEGORY_PACKAGES = {
|
|
77
|
+
extensions: "@santi020k/eslint-config-extensions",
|
|
78
|
+
formats: "@santi020k/eslint-config-formats",
|
|
79
|
+
libraries: "@santi020k/eslint-config-libraries",
|
|
80
|
+
testing: "@santi020k/eslint-config-testing",
|
|
81
|
+
tools: "@santi020k/eslint-config-tools"
|
|
82
|
+
};
|
|
60
83
|
const getConfigPathIfPresent = (cwd) => {
|
|
61
|
-
const configPath =
|
|
62
|
-
"eslint.config.js",
|
|
63
|
-
"eslint.config.mjs",
|
|
64
|
-
"eslint.config.cjs"
|
|
65
|
-
].map((filename) => join(cwd, filename)).find((p) => existsSync(p));
|
|
84
|
+
const configPath = ESLINT_CONFIG_FILENAMES.map((filename) => join(cwd, filename)).find((p) => existsSync(p));
|
|
66
85
|
return configPath ?? null;
|
|
67
86
|
};
|
|
68
87
|
const readPackageJson = (cwd) => {
|
|
@@ -112,7 +131,33 @@ const getLiteInstallPackages = (summary, declaredDependencies) => {
|
|
|
112
131
|
}
|
|
113
132
|
return [...packages];
|
|
114
133
|
};
|
|
115
|
-
const
|
|
134
|
+
const getInstallPackages = (summary, declaredDependencies, explicitFeaturePackages = []) => {
|
|
135
|
+
const usesFullPackage = declaredDependencies.has(FULL_PACKAGE_NAME);
|
|
136
|
+
const packages = /* @__PURE__ */ new Set(["eslint"]);
|
|
137
|
+
if (!usesFullPackage) packages.add(BASIC_PACKAGE_NAME);
|
|
138
|
+
if (!usesFullPackage) {
|
|
139
|
+
for (const framework of summary.frameworks) {
|
|
140
|
+
const packageName = FRAMEWORK_KEY_TO_PACKAGE[framework];
|
|
141
|
+
if (packageName) packages.add(packageName);
|
|
142
|
+
}
|
|
143
|
+
const categorySelections = [
|
|
144
|
+
[summary.extensions, CATEGORY_PACKAGES.extensions],
|
|
145
|
+
[summary.formats, CATEGORY_PACKAGES.formats],
|
|
146
|
+
[summary.libraries, CATEGORY_PACKAGES.libraries],
|
|
147
|
+
[summary.testing, CATEGORY_PACKAGES.testing],
|
|
148
|
+
[summary.tools, CATEGORY_PACKAGES.tools]
|
|
149
|
+
];
|
|
150
|
+
for (const [features, packageName] of categorySelections) {
|
|
151
|
+
if (features.length > 0) packages.add(packageName);
|
|
152
|
+
}
|
|
153
|
+
for (const packageName of explicitFeaturePackages) {
|
|
154
|
+
packages.add(packageName);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (summary.typescript) packages.add(TYPESCRIPT_PACKAGE_NAME);
|
|
158
|
+
return [...packages].filter((packageName) => !declaredDependencies.has(packageName));
|
|
159
|
+
};
|
|
160
|
+
const createInstallCommand = (packageManager, packages, workspaceRoot = false) => {
|
|
116
161
|
const packageList = packages.join(" ");
|
|
117
162
|
switch (packageManager) {
|
|
118
163
|
case "bun":
|
|
@@ -122,7 +167,19 @@ const createInstallCommand = (packageManager, packages) => {
|
|
|
122
167
|
case "yarn":
|
|
123
168
|
return `yarn add -D ${packageList}`;
|
|
124
169
|
default:
|
|
125
|
-
return `pnpm add -D ${packageList}`;
|
|
170
|
+
return `pnpm add -D${workspaceRoot ? " --workspace-root" : ""} ${packageList}`;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
const createInstallInvocation = (packageManager, packages, workspaceRoot = false) => {
|
|
174
|
+
switch (packageManager) {
|
|
175
|
+
case "bun":
|
|
176
|
+
return ["bun", ["add", "-d", ...packages]];
|
|
177
|
+
case "npm":
|
|
178
|
+
return ["npm", ["install", "-D", ...packages]];
|
|
179
|
+
case "yarn":
|
|
180
|
+
return ["yarn", ["add", "-D", ...packages]];
|
|
181
|
+
default:
|
|
182
|
+
return ["pnpm", ["add", "-D", ...workspaceRoot ? ["--workspace-root"] : [], ...packages]];
|
|
126
183
|
}
|
|
127
184
|
};
|
|
128
185
|
const detectPackageManager = (cwd) => {
|
|
@@ -177,39 +234,40 @@ const detectWorkspaceProjects = (cwd) => {
|
|
|
177
234
|
];
|
|
178
235
|
return [...new Set(patterns.flatMap((pattern) => expandWorkspacePattern(cwd, pattern)))].sort();
|
|
179
236
|
};
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
${
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
237
|
+
const createExplicitOptions = (cwd) => {
|
|
238
|
+
const summary = getProjectSummary(cwd);
|
|
239
|
+
const options = [];
|
|
240
|
+
const optionalCategories = [
|
|
241
|
+
["libraries", summary.libraries],
|
|
242
|
+
["testing", summary.testing],
|
|
243
|
+
["formats", summary.formats],
|
|
244
|
+
["tools", summary.tools],
|
|
245
|
+
["extensions", summary.extensions]
|
|
246
|
+
];
|
|
247
|
+
if (summary.typescript) options.push(" typescript: true,");
|
|
248
|
+
if (summary.frameworks.length > 0) {
|
|
249
|
+
options.push(
|
|
250
|
+
" frameworks: {",
|
|
251
|
+
...summary.frameworks.map((framework) => ` ${toPropertyKey(framework)}: true,`),
|
|
252
|
+
" },"
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
for (const [category, values] of optionalCategories) {
|
|
256
|
+
if (values.length > 0) {
|
|
257
|
+
options.push(` ${category}: ${JSON.stringify(values)},`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return options;
|
|
261
|
+
};
|
|
262
|
+
const createConfigContent = (cwd, explicit = false) => {
|
|
263
|
+
const explicitOptions = explicit ? createExplicitOptions(cwd) : [];
|
|
264
|
+
const invocation = explicitOptions.length > 0 ? ["export default defineConfig({", ...explicitOptions, "})"] : ["export default defineConfig()"];
|
|
265
|
+
const configContent = [
|
|
266
|
+
"import { defineConfig } from '@santi020k/eslint-config-basic'",
|
|
267
|
+
"",
|
|
268
|
+
...invocation,
|
|
269
|
+
""
|
|
270
|
+
].join("\n");
|
|
213
271
|
return {
|
|
214
272
|
configContent,
|
|
215
273
|
configPath: resolveConfigPath(cwd)
|
|
@@ -223,6 +281,7 @@ const getProjectSummary = (cwd) => {
|
|
|
223
281
|
const options = detectProjectOptions(cwd);
|
|
224
282
|
const workspaceProjects = detectWorkspaceProjects(cwd);
|
|
225
283
|
return {
|
|
284
|
+
detectedProjects: Object.keys(options.projects ?? {}).sort(),
|
|
226
285
|
extensions: options.extensions ?? [],
|
|
227
286
|
formats: options.formats ?? [],
|
|
228
287
|
frameworks: getFrameworkKeys(options.detectedFrameworks),
|
|
@@ -236,6 +295,30 @@ const getProjectSummary = (cwd) => {
|
|
|
236
295
|
workspaceProjects
|
|
237
296
|
};
|
|
238
297
|
};
|
|
298
|
+
const getInstallProjectSummary = (cwd) => {
|
|
299
|
+
const rootSummary = getProjectSummary(cwd);
|
|
300
|
+
const projectSummaries = rootSummary.detectedProjects.map((projectPath) => getProjectSummary(join(cwd, projectPath)));
|
|
301
|
+
const summaries = [rootSummary, ...projectSummaries];
|
|
302
|
+
return {
|
|
303
|
+
...rootSummary,
|
|
304
|
+
extensions: [...new Set(summaries.flatMap((summary) => summary.extensions))],
|
|
305
|
+
formats: [...new Set(summaries.flatMap((summary) => summary.formats))],
|
|
306
|
+
frameworks: [...new Set(summaries.flatMap((summary) => summary.frameworks))].sort(),
|
|
307
|
+
libraries: [...new Set(summaries.flatMap((summary) => summary.libraries))],
|
|
308
|
+
testing: [...new Set(summaries.flatMap((summary) => summary.testing))],
|
|
309
|
+
tools: [...new Set(summaries.flatMap((summary) => summary.tools))],
|
|
310
|
+
typescript: summaries.some((summary) => summary.typescript)
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
const getWorkspaceDeclaredConfigPackages = (cwd, summary) => {
|
|
314
|
+
const packages = /* @__PURE__ */ new Set();
|
|
315
|
+
for (const projectPath of summary.detectedProjects) {
|
|
316
|
+
for (const packageName of getDeclaredDependencyNames(readPackageJson(join(cwd, projectPath)))) {
|
|
317
|
+
if (packageName.startsWith("@santi020k/eslint-config-")) packages.add(packageName);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return [...packages];
|
|
321
|
+
};
|
|
239
322
|
const createStandardsContent = (cwd) => {
|
|
240
323
|
const summary = getProjectSummary(cwd);
|
|
241
324
|
return [
|
|
@@ -259,9 +342,9 @@ const createStandardsContent = (cwd) => {
|
|
|
259
342
|
"## Recommended Config",
|
|
260
343
|
"",
|
|
261
344
|
"```js",
|
|
262
|
-
"import {
|
|
345
|
+
"import { defineConfig } from '@santi020k/eslint-config-basic'",
|
|
263
346
|
"",
|
|
264
|
-
"export default await
|
|
347
|
+
"export default await defineConfig()",
|
|
265
348
|
"```",
|
|
266
349
|
"",
|
|
267
350
|
"Use `basic-eslint explain` when you want to inspect what the zero-config setup detects."
|
|
@@ -284,25 +367,45 @@ const printUsage = () => {
|
|
|
284
367
|
"Commands:",
|
|
285
368
|
" init Create eslint.config.js/mjs if missing",
|
|
286
369
|
" update Regenerate eslint.config.js/mjs from detection",
|
|
287
|
-
" explain Print detected
|
|
370
|
+
" explain Print detected v3 config inputs",
|
|
288
371
|
" inspect Print detected inputs and active config features",
|
|
289
|
-
"
|
|
372
|
+
" install Install missing packages for detected v3 features",
|
|
373
|
+
" doctor Check project setup for common v3 adoption issues",
|
|
374
|
+
" compatibility Check runtime and config package compatibility",
|
|
290
375
|
" docs Generate ESLINT_STANDARDS.md from detection",
|
|
291
|
-
" migrate
|
|
376
|
+
" migrate Plan or apply v1-to-v2 and v2-to-v3 migrations",
|
|
377
|
+
" baseline Suppress existing violations for incremental adoption",
|
|
378
|
+
" profile Compare ESLint performance and report slow rules",
|
|
379
|
+
" snapshot Save the effective rules for representative files",
|
|
380
|
+
" diff Compare effective rules with the saved snapshot",
|
|
292
381
|
" generate-skill Generate AI agent standards files",
|
|
293
382
|
"",
|
|
294
383
|
"Options:",
|
|
295
384
|
" --force Overwrite existing generated skill sections/files",
|
|
296
|
-
" --check
|
|
385
|
+
" --check Verify generated files, migrations, or snapshots without writing",
|
|
386
|
+
" --concurrency profile: off, auto, or a worker count",
|
|
297
387
|
" --create generate-skill: scaffold a root AGENTS.md when missing",
|
|
388
|
+
" --dry-run install: print the detected install command without running it",
|
|
389
|
+
" --explicit init: write detected settings explicitly",
|
|
390
|
+
" --file profile/snapshot/diff: representative file or lint target (repeatable)",
|
|
391
|
+
" --fix doctor: safely repair generated config and package metadata",
|
|
392
|
+
" --full migrate --to v3: choose the batteries-included package",
|
|
298
393
|
" --json Print JSON for commands that support it",
|
|
299
|
-
" --lite-install doctor: print the
|
|
394
|
+
" --lite-install doctor: print the deprecated v2 Lite install command (removed in v4)",
|
|
395
|
+
" --max-duration profile: maximum duration in milliseconds",
|
|
396
|
+
" --max-rule-time profile: maximum time for the slowest rule in milliseconds",
|
|
397
|
+
" --max-warnings profile: maximum warning count",
|
|
398
|
+
" --preset baseline: enable ci or pedantic strict mode before suppressing",
|
|
399
|
+
" --prune baseline: remove suppressions for resolved violations",
|
|
400
|
+
" --snapshot-path snapshot/diff: override .eslint-config-snapshot.json",
|
|
401
|
+
" --to migrate: target version (v2 or v3)",
|
|
402
|
+
" --with-eslint-mcp generate-skill: scaffold the official ESLint MCP server",
|
|
300
403
|
" --write Apply safe migrations for commands that support it",
|
|
301
404
|
" --help, -h Show this help message",
|
|
302
405
|
" --version, -v Show CLI version"
|
|
303
406
|
].join("\n"));
|
|
304
407
|
};
|
|
305
|
-
const handleInit = (cwd = process.cwd(), check = false) => {
|
|
408
|
+
const handleInit = (cwd = process.cwd(), check = false, explicit = false) => {
|
|
306
409
|
const configPath = resolveConfigPath(cwd);
|
|
307
410
|
if (check) {
|
|
308
411
|
const exists = existsSync(configPath);
|
|
@@ -319,7 +422,7 @@ const handleInit = (cwd = process.cwd(), check = false) => {
|
|
|
319
422
|
return;
|
|
320
423
|
}
|
|
321
424
|
console.log("\u{1F50D} Detecting project settings...");
|
|
322
|
-
const { configContent } = createConfigContent(cwd);
|
|
425
|
+
const { configContent } = createConfigContent(cwd, explicit);
|
|
323
426
|
writeFileSync(configPath, configContent);
|
|
324
427
|
console.log(`\u2705 Created ${basename(configPath)} with auto-detected settings!`);
|
|
325
428
|
console.log("\u{1F680} Ready to lint!");
|
|
@@ -386,6 +489,58 @@ const handleInspect = async (cwd = process.cwd(), json = false) => {
|
|
|
386
489
|
`- Workspace projects: ${formatList(summary.workspaceProjects)}`
|
|
387
490
|
].join("\n"));
|
|
388
491
|
};
|
|
492
|
+
const semver = createRequire(import.meta.url)("semver");
|
|
493
|
+
const readPackageMetadataFromMain = (mainPath, packageName) => {
|
|
494
|
+
let directory = dirname(mainPath);
|
|
495
|
+
for (let depth = 0; depth < 4; depth++) {
|
|
496
|
+
const packagePath = join(directory, "package.json");
|
|
497
|
+
if (existsSync(packagePath)) {
|
|
498
|
+
const metadata = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
499
|
+
if (metadata.name === packageName) return metadata;
|
|
500
|
+
}
|
|
501
|
+
directory = dirname(directory);
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
};
|
|
505
|
+
const resolvePackageMetadata = (cwd, packageName) => {
|
|
506
|
+
try {
|
|
507
|
+
const projectRequire = createRequire(join(cwd, "package.json"));
|
|
508
|
+
try {
|
|
509
|
+
return readPackageMetadataFromMain(projectRequire.resolve(packageName), packageName);
|
|
510
|
+
} catch {
|
|
511
|
+
const integrationsMain = projectRequire.resolve(INTEGRATIONS_PACKAGE_NAME);
|
|
512
|
+
const integrationsRequire = createRequire(integrationsMain);
|
|
513
|
+
return readPackageMetadataFromMain(integrationsRequire.resolve(packageName), packageName);
|
|
514
|
+
}
|
|
515
|
+
} catch {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const getAstroDoctorNodeWarning = (metadata) => {
|
|
520
|
+
const nodeRange = metadata.engines?.node;
|
|
521
|
+
if (!nodeRange || semver.satisfies(process.versions.node, nodeRange)) return null;
|
|
522
|
+
return `Astro Doctor ${metadata.version ?? ""} requires Node ${nodeRange}, but the current runtime is ${process.versions.node}.`;
|
|
523
|
+
};
|
|
524
|
+
const getAstroDoctorEslintWarning = (metadata, eslintMetadata) => {
|
|
525
|
+
const eslintRange = metadata.peerDependencies?.eslint;
|
|
526
|
+
const eslintVersion = eslintMetadata?.version;
|
|
527
|
+
if (!eslintRange || !eslintVersion || semver.satisfies(eslintVersion, eslintRange)) return null;
|
|
528
|
+
return `Astro Doctor ${metadata.version ?? ""} requires ESLint ${eslintRange}, but this project resolves ${eslintVersion}.`;
|
|
529
|
+
};
|
|
530
|
+
const getAstroDoctorCompatibilityWarnings = (cwd, enabled) => {
|
|
531
|
+
if (!enabled) return [];
|
|
532
|
+
const pluginMetadata = resolvePackageMetadata(cwd, ASTRO_DOCTOR_PACKAGE_NAME);
|
|
533
|
+
if (!pluginMetadata) {
|
|
534
|
+
return [
|
|
535
|
+
`Astro Doctor is enabled, but ${ASTRO_DOCTOR_PACKAGE_NAME} could not be resolved. Reinstall dependencies or disable the \`astro-doctor\` feature.`
|
|
536
|
+
];
|
|
537
|
+
}
|
|
538
|
+
const eslintMetadata = resolvePackageMetadata(cwd, "eslint");
|
|
539
|
+
return [
|
|
540
|
+
getAstroDoctorNodeWarning(pluginMetadata),
|
|
541
|
+
getAstroDoctorEslintWarning(pluginMetadata, eslintMetadata)
|
|
542
|
+
].filter((warning) => warning !== null);
|
|
543
|
+
};
|
|
389
544
|
const findDuplicateEslint = (cwd = process.cwd()) => {
|
|
390
545
|
try {
|
|
391
546
|
const projectRequire = createRequire(join(cwd, "package.json"));
|
|
@@ -407,7 +562,9 @@ const validateConfigContent = (configContent, hasV1FrameworkImports, declaredDep
|
|
|
407
562
|
if (hasV1FrameworkImports) {
|
|
408
563
|
warnings.push("Config still imports v1 framework packages. Run `basic-eslint migrate --write` or switch to framework booleans.");
|
|
409
564
|
}
|
|
410
|
-
|
|
565
|
+
const usesBasicComposer = /from\s*['"]@santi020k\/eslint-config-(?:basic|full|lite)['"]/.test(configContent) && /\b(?:defineConfig|eslintConfig)\s*\(/.test(configContent);
|
|
566
|
+
const hasDetectedProjectScopes = summary.workspaceProjects.every((project) => summary.detectedProjects.includes(project));
|
|
567
|
+
if (summary.workspaceProjects.length > 0 && !configContent.includes("projects:") && !(usesBasicComposer && hasDetectedProjectScopes)) {
|
|
411
568
|
warnings.push("Workspace packages were detected, but the root config does not use `projects` scoping.");
|
|
412
569
|
}
|
|
413
570
|
if (configContent.includes(LITE_PACKAGE_NAME)) {
|
|
@@ -430,7 +587,7 @@ const validateConfigContent = (configContent, hasV1FrameworkImports, declaredDep
|
|
|
430
587
|
}
|
|
431
588
|
return warnings;
|
|
432
589
|
};
|
|
433
|
-
const
|
|
590
|
+
const validateProjectSetup = (cwd, configPath, activeConfig, hasV1FrameworkImports) => {
|
|
434
591
|
const issues = [];
|
|
435
592
|
const warnings = [];
|
|
436
593
|
if (!readPackageJson(cwd)) {
|
|
@@ -444,18 +601,50 @@ const buildDoctorDiagnosis = (cwd, configPath, configContent, hasV1FrameworkImpo
|
|
|
444
601
|
if (!hasLintScript(cwd)) {
|
|
445
602
|
warnings.push("No `lint` script found in package.json.");
|
|
446
603
|
}
|
|
447
|
-
|
|
448
|
-
|
|
604
|
+
return { issues, warnings };
|
|
605
|
+
};
|
|
606
|
+
const isAstroDoctorConfigured = (activeConfig, configContent) => [
|
|
607
|
+
activeConfig?.extensions.includes("Astro Doctor") ?? false,
|
|
608
|
+
configContent?.includes("astro-doctor") ?? false,
|
|
609
|
+
configContent?.includes("AstroDoctor") ?? false
|
|
610
|
+
].includes(true);
|
|
611
|
+
const isAstroConfigured = (activeConfig, summary) => [
|
|
612
|
+
activeConfig?.frameworks.includes("Astro") ?? false,
|
|
613
|
+
summary.frameworks.includes("astro")
|
|
614
|
+
].some(Boolean);
|
|
615
|
+
const checkAstroDoctorStatus = (activeConfig, configContent, summary, warnings) => {
|
|
616
|
+
const astroDoctorEnabled = isAstroDoctorConfigured(activeConfig, configContent);
|
|
617
|
+
const astroEnabled = isAstroConfigured(activeConfig, summary);
|
|
618
|
+
if (astroDoctorEnabled && !astroEnabled) {
|
|
619
|
+
warnings.push(
|
|
620
|
+
"Astro Doctor is enabled without Astro. Enable `frameworks: { astro: true }` or remove the `astro-doctor` feature."
|
|
621
|
+
);
|
|
622
|
+
} else if (astroEnabled && !astroDoctorEnabled) {
|
|
623
|
+
warnings.push(
|
|
624
|
+
'Astro was detected without Astro Doctor. Enable `features: { "astro-doctor": true }` for additional Astro diagnostics.'
|
|
625
|
+
);
|
|
449
626
|
}
|
|
627
|
+
return astroDoctorEnabled;
|
|
628
|
+
};
|
|
629
|
+
const checkDuplicateEslint = (cwd, warnings) => {
|
|
450
630
|
const duplicateEslint = findDuplicateEslint(cwd);
|
|
451
631
|
if (duplicateEslint) {
|
|
452
632
|
warnings.push(
|
|
453
633
|
`Two ESLint copies are installed: the project resolves ${duplicateEslint.projectVersion} while the config packages resolve ${duplicateEslint.configVersion}. Only ESLint 10 is supported, and parallel copies can apply different rule behavior between your editor and CLI. Align the project's eslint version with the config's (or dedupe via your package manager).`
|
|
454
634
|
);
|
|
455
635
|
}
|
|
636
|
+
};
|
|
637
|
+
const buildDoctorDiagnosis = (cwd, configPath, configContent, hasV1FrameworkImports, activeConfig, declaredDependencies, summary) => {
|
|
638
|
+
const { issues, warnings } = validateProjectSetup(cwd, configPath, activeConfig, hasV1FrameworkImports);
|
|
639
|
+
if (configContent) {
|
|
640
|
+
warnings.push(...validateConfigContent(configContent, hasV1FrameworkImports, declaredDependencies, summary));
|
|
641
|
+
}
|
|
642
|
+
const astroDoctorEnabled = checkAstroDoctorStatus(activeConfig, configContent, summary, warnings);
|
|
643
|
+
warnings.push(...getAstroDoctorCompatibilityWarnings(cwd, astroDoctorEnabled));
|
|
644
|
+
checkDuplicateEslint(cwd, warnings);
|
|
456
645
|
return { issues, warnings };
|
|
457
646
|
};
|
|
458
|
-
const outputDoctorResult = (json, configPath, packageManager, summary, liteInstallCommand, issues, warnings) => {
|
|
647
|
+
const outputDoctorResult = (json, configPath, packageManager, summary, liteInstallCommand, issues, warnings, fixes = []) => {
|
|
459
648
|
let status = "passed";
|
|
460
649
|
if (issues.length > 0) {
|
|
461
650
|
status = "failed";
|
|
@@ -464,6 +653,7 @@ const outputDoctorResult = (json, configPath, packageManager, summary, liteInsta
|
|
|
464
653
|
}
|
|
465
654
|
const payload = {
|
|
466
655
|
configFile: configPath ? basename(configPath) : null,
|
|
656
|
+
fixes,
|
|
467
657
|
issues,
|
|
468
658
|
liteInstallCommand,
|
|
469
659
|
packageManager,
|
|
@@ -481,22 +671,88 @@ const outputDoctorResult = (json, configPath, packageManager, summary, liteInsta
|
|
|
481
671
|
`- Package manager: ${payload.packageManager}`,
|
|
482
672
|
`- Config file: ${payload.configFile ?? "none"}`,
|
|
483
673
|
`- Workspace projects: ${formatList(summary.workspaceProjects)}`,
|
|
674
|
+
...fixes.length > 0 ? ["", "Fixes applied:", ...fixes.map((fix) => `- ${fix}`)] : [],
|
|
484
675
|
...issues.length > 0 ? ["", "Issues:", ...issues.map((issue) => `- ${issue}`)] : [],
|
|
485
676
|
...warnings.length > 0 ? ["", "Warnings:", ...warnings.map((warning) => `- ${warning}`)] : []
|
|
486
677
|
].join("\n"));
|
|
487
678
|
if (issues.length > 0) process.exitCode = 1;
|
|
488
679
|
};
|
|
489
|
-
const
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
680
|
+
const createDoctorBackupPath = (filePath) => {
|
|
681
|
+
const preferred = `${filePath}.doctor.bak`;
|
|
682
|
+
if (!existsSync(preferred)) return preferred;
|
|
683
|
+
let index = 2;
|
|
684
|
+
while (existsSync(`${preferred}.${index}`)) index++;
|
|
685
|
+
return `${preferred}.${index}`;
|
|
686
|
+
};
|
|
687
|
+
const getDoctorDependencyVersion = (packageName) => {
|
|
688
|
+
if (packageName === "eslint") return "^10.0.0";
|
|
689
|
+
if (packageName === TYPESCRIPT_PACKAGE_NAME) return "^5.0.0";
|
|
690
|
+
return "^3.0.0";
|
|
691
|
+
};
|
|
692
|
+
const applyDoctorFixes = (cwd, packageJson, configPath, configContent, summary) => {
|
|
693
|
+
const fixes = [];
|
|
694
|
+
if (packageJson) {
|
|
695
|
+
const updated = structuredClone(packageJson);
|
|
696
|
+
let packageChanged = false;
|
|
697
|
+
updated.scripts ??= {};
|
|
698
|
+
if (!updated.scripts.lint) {
|
|
699
|
+
updated.scripts.lint = "eslint .";
|
|
700
|
+
packageChanged = true;
|
|
701
|
+
fixes.push("Added the `lint` script.");
|
|
702
|
+
}
|
|
703
|
+
updated.devDependencies ??= {};
|
|
704
|
+
const declared = getDeclaredDependencyNames(packageJson);
|
|
705
|
+
const explicitFeaturePackages = configContent ? getExplicitConfigFeaturePackages(configContent) : [];
|
|
706
|
+
const missingPackages = getInstallPackages(summary, declared, explicitFeaturePackages);
|
|
707
|
+
const missingDependencies = Object.fromEntries(
|
|
708
|
+
missingPackages.map((packageName) => [packageName, getDoctorDependencyVersion(packageName)])
|
|
709
|
+
);
|
|
710
|
+
updated.devDependencies = {
|
|
711
|
+
...updated.devDependencies,
|
|
712
|
+
...missingDependencies
|
|
713
|
+
};
|
|
714
|
+
for (const packageName of missingPackages) {
|
|
715
|
+
fixes.push(`Declared ${packageName}.`);
|
|
716
|
+
packageChanged = true;
|
|
717
|
+
}
|
|
718
|
+
if (packageChanged) {
|
|
719
|
+
const packagePath = join(cwd, "package.json");
|
|
720
|
+
const backupPath = createDoctorBackupPath(packagePath);
|
|
721
|
+
writeFileSync(backupPath, `${JSON.stringify(packageJson, null, 2)}
|
|
722
|
+
`);
|
|
723
|
+
writeFileSync(packagePath, `${JSON.stringify(updated, null, 2)}
|
|
724
|
+
`);
|
|
725
|
+
fixes.push(`Backed up package.json to ${basename(backupPath)}.`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
if (!configPath) {
|
|
729
|
+
const generated = createConfigContent(cwd);
|
|
730
|
+
writeFileSync(generated.configPath, generated.configContent);
|
|
731
|
+
fixes.push(`Created ${basename(generated.configPath)}.`);
|
|
732
|
+
} else if (configContent && Object.keys(FRAMEWORK_PACKAGE_TO_KEY).some((packageName) => configContent.includes(packageName))) {
|
|
733
|
+
const result = migrateConfigContent(configContent);
|
|
734
|
+
if (result.changed) {
|
|
735
|
+
const backupPath = createDoctorBackupPath(configPath);
|
|
736
|
+
writeFileSync(backupPath, configContent);
|
|
737
|
+
writeFileSync(configPath, result.content);
|
|
738
|
+
fixes.push(`Migrated v1 framework imports and backed up the config to ${basename(backupPath)}.`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return fixes;
|
|
742
|
+
};
|
|
743
|
+
const hasV1FrameworkPackageImports = (configContent) => configContent !== null && Object.keys(FRAMEWORK_PACKAGE_TO_KEY).some((packageName) => configContent.includes(packageName));
|
|
744
|
+
const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = false, fix = false) => {
|
|
496
745
|
const packageManager = detectPackageManager(cwd);
|
|
497
|
-
const
|
|
498
|
-
|
|
499
|
-
|
|
746
|
+
const workspaceRoot = packageManager === "pnpm" && existsSync(join(cwd, "pnpm-workspace.yaml"));
|
|
747
|
+
let configPath = getConfigPathIfPresent(cwd);
|
|
748
|
+
let packageJson = readPackageJson(cwd);
|
|
749
|
+
let declaredDependencies = getDeclaredDependencyNames(packageJson);
|
|
750
|
+
let summary = getInstallProjectSummary(cwd);
|
|
751
|
+
let activeConfig = await analyzeEslintConfig(cwd);
|
|
752
|
+
let configContent = configPath ? readFileSync(configPath, "utf8") : null;
|
|
753
|
+
let liteInstallPackages = getLiteInstallPackages(summary, declaredDependencies);
|
|
754
|
+
let liteInstallCommand = createInstallCommand(packageManager, liteInstallPackages, workspaceRoot);
|
|
755
|
+
let hasV1FrameworkImports = hasV1FrameworkPackageImports(configContent);
|
|
500
756
|
if (liteInstall) {
|
|
501
757
|
if (json) {
|
|
502
758
|
console.log(JSON.stringify({ command: liteInstallCommand, packageManager, packages: liteInstallPackages }, null, 2));
|
|
@@ -505,8 +761,64 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
|
|
|
505
761
|
}
|
|
506
762
|
return;
|
|
507
763
|
}
|
|
508
|
-
const
|
|
509
|
-
|
|
764
|
+
const fixes = fix ? applyDoctorFixes(cwd, packageJson, configPath, configContent, summary) : [];
|
|
765
|
+
if (fixes.length > 0) {
|
|
766
|
+
configPath = getConfigPathIfPresent(cwd);
|
|
767
|
+
packageJson = readPackageJson(cwd);
|
|
768
|
+
declaredDependencies = getDeclaredDependencyNames(packageJson);
|
|
769
|
+
summary = getInstallProjectSummary(cwd);
|
|
770
|
+
activeConfig = await analyzeEslintConfig(cwd);
|
|
771
|
+
configContent = configPath ? readFileSync(configPath, "utf8") : null;
|
|
772
|
+
liteInstallPackages = getLiteInstallPackages(summary, declaredDependencies);
|
|
773
|
+
liteInstallCommand = createInstallCommand(packageManager, liteInstallPackages, workspaceRoot);
|
|
774
|
+
hasV1FrameworkImports = hasV1FrameworkPackageImports(configContent);
|
|
775
|
+
}
|
|
776
|
+
const { issues, warnings } = buildDoctorDiagnosis(
|
|
777
|
+
cwd,
|
|
778
|
+
configPath,
|
|
779
|
+
configContent,
|
|
780
|
+
hasV1FrameworkImports,
|
|
781
|
+
activeConfig,
|
|
782
|
+
declaredDependencies,
|
|
783
|
+
summary
|
|
784
|
+
);
|
|
785
|
+
outputDoctorResult(json, configPath, packageManager, summary, liteInstallCommand, issues, warnings, fixes);
|
|
786
|
+
};
|
|
787
|
+
const handleInstall = (cwd = process.cwd(), dryRun = false) => {
|
|
788
|
+
const packageJson = readPackageJson(cwd);
|
|
789
|
+
if (!packageJson) {
|
|
790
|
+
console.error("\u274C package.json is missing or invalid.");
|
|
791
|
+
process.exitCode = 1;
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const packageManager = detectPackageManager(cwd);
|
|
795
|
+
const configPath = getConfigPathIfPresent(cwd);
|
|
796
|
+
const explicitFeaturePackages = configPath ? getExplicitConfigFeaturePackages(readFileSync(configPath, "utf8")) : [];
|
|
797
|
+
const packages = getInstallPackages(
|
|
798
|
+
getInstallProjectSummary(cwd),
|
|
799
|
+
getDeclaredDependencyNames(packageJson),
|
|
800
|
+
explicitFeaturePackages
|
|
801
|
+
);
|
|
802
|
+
const workspaceRoot = packageManager === "pnpm" && existsSync(join(cwd, "pnpm-workspace.yaml"));
|
|
803
|
+
if (packages.length === 0) {
|
|
804
|
+
console.log("\u2705 All packages required by the detected ESLint configuration are already declared.");
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
const installCommand = createInstallCommand(packageManager, packages, workspaceRoot);
|
|
808
|
+
if (dryRun) {
|
|
809
|
+
console.log(installCommand);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
console.log(`Installing detected ESLint dependencies:
|
|
813
|
+
${installCommand}`);
|
|
814
|
+
const [command, args] = createInstallInvocation(packageManager, packages, workspaceRoot);
|
|
815
|
+
const result = spawnSync(command, args, { cwd, stdio: "inherit" });
|
|
816
|
+
if (result.error) {
|
|
817
|
+
console.error(`\u274C Failed to run ${packageManager}: ${result.error.message}`);
|
|
818
|
+
process.exitCode = 1;
|
|
819
|
+
} else if (result.status !== 0) {
|
|
820
|
+
process.exitCode = result.status ?? 1;
|
|
821
|
+
}
|
|
510
822
|
};
|
|
511
823
|
const handleDocs = (cwd = process.cwd()) => {
|
|
512
824
|
const outputPath = join(cwd, "ESLINT_STANDARDS.md");
|
|
@@ -520,9 +832,9 @@ const migrateConfigContent = (content) => {
|
|
|
520
832
|
}
|
|
521
833
|
const frameworks = getFrameworkKeys(importedFrameworks);
|
|
522
834
|
const migrated = [
|
|
523
|
-
"import {
|
|
835
|
+
"import { defineConfig } from '@santi020k/eslint-config-basic'",
|
|
524
836
|
"",
|
|
525
|
-
"export default await
|
|
837
|
+
"export default await defineConfig({",
|
|
526
838
|
" frameworks: {",
|
|
527
839
|
` ${frameworks.map((key) => `${toPropertyKey(key)}: true`).join(",\n ")}`,
|
|
528
840
|
" }",
|
|
@@ -547,7 +859,7 @@ const processConfigMigration = (configPath, write, suggestions) => {
|
|
|
547
859
|
const backupPath = `${configPath}.bak`;
|
|
548
860
|
writeFileSync(backupPath, configContent);
|
|
549
861
|
writeFileSync(configPath, result.content);
|
|
550
|
-
const message = `Rewrote ${basename(configPath)} with
|
|
862
|
+
const message = `Rewrote ${basename(configPath)} with framework booleans: ${result.frameworks.join(", ")}. Original backed up to ${basename(backupPath)}.`;
|
|
551
863
|
applied.push(message);
|
|
552
864
|
suggestions.push(`- ${message}`);
|
|
553
865
|
} else {
|
|
@@ -555,15 +867,33 @@ const processConfigMigration = (configPath, write, suggestions) => {
|
|
|
555
867
|
}
|
|
556
868
|
return applied;
|
|
557
869
|
};
|
|
558
|
-
const handleMigrate = (cwd = process.cwd(), write = false, json = false) => {
|
|
870
|
+
const handleMigrate = (cwd = process.cwd(), write = false, json = false, target = "v2", check = false, full = false) => {
|
|
871
|
+
if (target === "v3" || target === "3") {
|
|
872
|
+
const summary = getInstallProjectSummary(cwd);
|
|
873
|
+
handleMigrateV3(cwd, {
|
|
874
|
+
declaredConfigPackages: getWorkspaceDeclaredConfigPackages(cwd, summary),
|
|
875
|
+
extensions: summary.extensions,
|
|
876
|
+
formats: summary.formats,
|
|
877
|
+
frameworks: summary.frameworks,
|
|
878
|
+
libraries: summary.libraries,
|
|
879
|
+
packageManager: detectPackageManager(cwd),
|
|
880
|
+
testing: summary.testing,
|
|
881
|
+
tools: summary.tools,
|
|
882
|
+
typescript: summary.typescript
|
|
883
|
+
}, { check, full, json, write });
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
if (target !== "v2" && target !== "2") {
|
|
887
|
+
throw new Error(`Unsupported migration target "${target}". Use --to v2 or --to v3.`);
|
|
888
|
+
}
|
|
559
889
|
const configPath = getConfigPathIfPresent(cwd) ?? join(cwd, getDefaultConfigFilename(cwd));
|
|
560
890
|
const suggestions = [
|
|
561
891
|
"v1 to v2 migration suggestions:",
|
|
562
892
|
"- Install only @santi020k/eslint-config-basic for the public config API.",
|
|
563
893
|
"- Replace framework imports with bundled booleans such as frameworks: { react: true, next: true }.",
|
|
564
894
|
"- Remove app-level @santi020k/eslint-config-react/next/vue/etc. config package imports.",
|
|
565
|
-
"- Try
|
|
566
|
-
"- Use basic-eslint explain to review what
|
|
895
|
+
"- Try defineConfig() first; v3 auto-detects supported frameworks and integrations.",
|
|
896
|
+
"- Use basic-eslint explain to review what v3 detects before committing the migration."
|
|
567
897
|
];
|
|
568
898
|
const applied = processConfigMigration(configPath, write, suggestions);
|
|
569
899
|
if (json) {
|
|
@@ -580,30 +910,73 @@ const handleMigrate = (cwd = process.cwd(), write = false, json = false) => {
|
|
|
580
910
|
};
|
|
581
911
|
const dispatchCommand = (command, cwd, flags) => {
|
|
582
912
|
switch (command) {
|
|
913
|
+
case "baseline": {
|
|
914
|
+
try {
|
|
915
|
+
handleBaseline(cwd, {
|
|
916
|
+
json: flags.hasJson,
|
|
917
|
+
preset: flags.preset,
|
|
918
|
+
prune: flags.hasPrune
|
|
919
|
+
});
|
|
920
|
+
} catch (error) {
|
|
921
|
+
console.error(`\u274C Failed to create ESLint baseline: ${String(error)}`);
|
|
922
|
+
process.exitCode = 1;
|
|
923
|
+
}
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
case "compatibility": {
|
|
927
|
+
handleCompatibility(cwd, flags.hasJson);
|
|
928
|
+
break;
|
|
929
|
+
}
|
|
930
|
+
case "diff": {
|
|
931
|
+
handleSnapshotDiff(cwd, {
|
|
932
|
+
files: flags.files,
|
|
933
|
+
json: flags.hasJson,
|
|
934
|
+
snapshotPath: flags.snapshotPath
|
|
935
|
+
}).catch((error) => {
|
|
936
|
+
console.error(`\u274C Failed to diff ESLint configuration: ${String(error)}`);
|
|
937
|
+
process.exitCode = 1;
|
|
938
|
+
});
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
583
941
|
case "docs": {
|
|
584
942
|
handleDocs(cwd);
|
|
585
943
|
break;
|
|
586
944
|
}
|
|
587
945
|
case "doctor": {
|
|
588
|
-
handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall).catch((error) => {
|
|
946
|
+
handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall, flags.hasFix).catch((error) => {
|
|
589
947
|
console.error(`\u274C Failed to run doctor: ${String(error)}`);
|
|
590
948
|
process.exitCode = 1;
|
|
591
949
|
});
|
|
592
950
|
break;
|
|
593
951
|
}
|
|
594
952
|
case "explain": {
|
|
595
|
-
|
|
953
|
+
if (flags.rule) {
|
|
954
|
+
handleExplainRule(cwd, {
|
|
955
|
+
file: flags.files[0],
|
|
956
|
+
json: flags.hasJson,
|
|
957
|
+
rule: flags.rule
|
|
958
|
+
}).catch((error) => {
|
|
959
|
+
console.error(`\u274C Failed to explain ESLint rule: ${String(error)}`);
|
|
960
|
+
process.exitCode = 1;
|
|
961
|
+
});
|
|
962
|
+
} else {
|
|
963
|
+
handleExplain(cwd, flags.hasJson);
|
|
964
|
+
}
|
|
596
965
|
break;
|
|
597
966
|
}
|
|
598
967
|
case "generate-skill": {
|
|
599
|
-
handleGenerateSkill(cwd, flags.hasForce, {
|
|
968
|
+
handleGenerateSkill(cwd, flags.hasForce, {
|
|
969
|
+
check: flags.hasCheck,
|
|
970
|
+
createAgentsMd: flags.hasCreate,
|
|
971
|
+
withEslintMcp: flags.hasWithEslintMcp
|
|
972
|
+
}).catch((error) => {
|
|
600
973
|
console.error(`\u274C Failed to generate skill files: ${String(error)}`);
|
|
601
974
|
process.exitCode = 1;
|
|
602
975
|
});
|
|
603
976
|
break;
|
|
604
977
|
}
|
|
605
978
|
case "init": {
|
|
606
|
-
handleInit(cwd, flags.hasCheck);
|
|
979
|
+
handleInit(cwd, flags.hasCheck, flags.hasExplicit);
|
|
607
980
|
break;
|
|
608
981
|
}
|
|
609
982
|
case "inspect": {
|
|
@@ -613,8 +986,52 @@ const dispatchCommand = (command, cwd, flags) => {
|
|
|
613
986
|
});
|
|
614
987
|
break;
|
|
615
988
|
}
|
|
989
|
+
case "install": {
|
|
990
|
+
handleInstall(cwd, flags.hasDryRun);
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
616
993
|
case "migrate": {
|
|
617
|
-
|
|
994
|
+
try {
|
|
995
|
+
handleMigrate(
|
|
996
|
+
cwd,
|
|
997
|
+
flags.hasWrite,
|
|
998
|
+
flags.hasJson,
|
|
999
|
+
flags.target ?? "v2",
|
|
1000
|
+
flags.hasCheck,
|
|
1001
|
+
flags.hasFull
|
|
1002
|
+
);
|
|
1003
|
+
} catch (error) {
|
|
1004
|
+
console.error(`\u274C Failed to migrate ESLint configuration: ${String(error)}`);
|
|
1005
|
+
process.exitCode = 1;
|
|
1006
|
+
}
|
|
1007
|
+
break;
|
|
1008
|
+
}
|
|
1009
|
+
case "profile": {
|
|
1010
|
+
try {
|
|
1011
|
+
handleProfile(cwd, {
|
|
1012
|
+
concurrency: flags.concurrency,
|
|
1013
|
+
files: flags.files,
|
|
1014
|
+
json: flags.hasJson,
|
|
1015
|
+
maxDurationMs: flags.maxDurationMs,
|
|
1016
|
+
maxRuleTimeMs: flags.maxRuleTimeMs,
|
|
1017
|
+
maxWarnings: flags.maxWarnings
|
|
1018
|
+
});
|
|
1019
|
+
} catch (error) {
|
|
1020
|
+
console.error(`\u274C Failed to profile ESLint: ${String(error)}`);
|
|
1021
|
+
process.exitCode = 1;
|
|
1022
|
+
}
|
|
1023
|
+
break;
|
|
1024
|
+
}
|
|
1025
|
+
case "snapshot": {
|
|
1026
|
+
handleSnapshot(cwd, {
|
|
1027
|
+
check: flags.hasCheck,
|
|
1028
|
+
files: flags.files,
|
|
1029
|
+
json: flags.hasJson,
|
|
1030
|
+
snapshotPath: flags.snapshotPath
|
|
1031
|
+
}).catch((error) => {
|
|
1032
|
+
console.error(`\u274C Failed to snapshot ESLint configuration: ${String(error)}`);
|
|
1033
|
+
process.exitCode = 1;
|
|
1034
|
+
});
|
|
618
1035
|
break;
|
|
619
1036
|
}
|
|
620
1037
|
case "update": {
|
|
@@ -628,6 +1045,24 @@ const dispatchCommand = (command, cwd, flags) => {
|
|
|
628
1045
|
}
|
|
629
1046
|
}
|
|
630
1047
|
};
|
|
1048
|
+
const getFlagValue = (argv, flag) => {
|
|
1049
|
+
const inline = argv.find((argument) => argument.startsWith(`${flag}=`));
|
|
1050
|
+
if (inline) return inline.slice(flag.length + 1);
|
|
1051
|
+
const index = argv.indexOf(flag);
|
|
1052
|
+
return index >= 0 ? argv[index + 1] : void 0;
|
|
1053
|
+
};
|
|
1054
|
+
const getFlagValues = (argv, flag) => argv.flatMap((argument, index) => {
|
|
1055
|
+
if (argument.startsWith(`${flag}=`)) return [argument.slice(flag.length + 1)];
|
|
1056
|
+
if (argument === flag && argv[index + 1]) return [argv[index + 1]];
|
|
1057
|
+
return [];
|
|
1058
|
+
});
|
|
1059
|
+
const getNumericFlagValue = (argv, flag) => {
|
|
1060
|
+
const value = getFlagValue(argv, flag);
|
|
1061
|
+
const present = argv.includes(flag) || argv.some((argument) => argument.startsWith(`${flag}=`));
|
|
1062
|
+
if (!present) return void 0;
|
|
1063
|
+
if (value === void 0 || value.trim() === "" || value.startsWith("--")) return Number.NaN;
|
|
1064
|
+
return Number(value);
|
|
1065
|
+
};
|
|
631
1066
|
const runCli = (argv = process.argv, cwd = process.cwd()) => {
|
|
632
1067
|
const command = argv[2];
|
|
633
1068
|
const isHelp = command === "--help" || command === "-h";
|
|
@@ -641,12 +1076,27 @@ const runCli = (argv = process.argv, cwd = process.cwd()) => {
|
|
|
641
1076
|
return;
|
|
642
1077
|
}
|
|
643
1078
|
dispatchCommand(command, cwd, {
|
|
1079
|
+
concurrency: getFlagValue(argv, "--concurrency"),
|
|
1080
|
+
files: getFlagValues(argv, "--file"),
|
|
644
1081
|
hasCheck: argv.includes("--check"),
|
|
645
1082
|
hasCreate: argv.includes("--create"),
|
|
1083
|
+
hasDryRun: argv.includes("--dry-run"),
|
|
1084
|
+
hasExplicit: argv.includes("--explicit"),
|
|
1085
|
+
hasFix: argv.includes("--fix"),
|
|
646
1086
|
hasForce: argv.includes("--force"),
|
|
1087
|
+
hasFull: argv.includes("--full"),
|
|
647
1088
|
hasJson: argv.includes("--json"),
|
|
648
1089
|
hasLiteInstall: argv.includes("--lite-install"),
|
|
649
|
-
|
|
1090
|
+
hasPrune: argv.includes("--prune"),
|
|
1091
|
+
hasWithEslintMcp: argv.includes("--with-eslint-mcp"),
|
|
1092
|
+
hasWrite: argv.includes("--write"),
|
|
1093
|
+
maxDurationMs: getNumericFlagValue(argv, "--max-duration"),
|
|
1094
|
+
maxRuleTimeMs: getNumericFlagValue(argv, "--max-rule-time"),
|
|
1095
|
+
maxWarnings: getNumericFlagValue(argv, "--max-warnings"),
|
|
1096
|
+
preset: getFlagValue(argv, "--preset"),
|
|
1097
|
+
rule: argv[3] && !argv[3].startsWith("-") ? argv[3] : void 0,
|
|
1098
|
+
snapshotPath: getFlagValue(argv, "--snapshot-path"),
|
|
1099
|
+
target: getFlagValue(argv, "--to")
|
|
650
1100
|
});
|
|
651
1101
|
};
|
|
652
1102
|
if (process.argv[1] && (process.argv[1] === fileURLToPath(import.meta.url) || process.argv[1].endsWith("cli.js") || process.argv[1].endsWith("cli.ts"))) {
|
|
@@ -659,6 +1109,7 @@ export {
|
|
|
659
1109
|
handleExplain,
|
|
660
1110
|
handleInit,
|
|
661
1111
|
handleInspect,
|
|
1112
|
+
handleInstall,
|
|
662
1113
|
handleMigrate,
|
|
663
1114
|
handleUpdate,
|
|
664
1115
|
runCli
|