@santi020k/eslint-config-basic 3.1.0 → 3.3.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 +54 -0
- package/dist/agent-skill-generator.d.ts +7 -18
- package/dist/agent-skill-generator.js +54 -18
- package/dist/agent-skill-generator.js.map +1 -0
- package/dist/cli-advanced.d.ts +7 -0
- package/dist/cli-advanced.js +74 -15
- package/dist/cli-advanced.js.map +1 -0
- package/dist/cli-migration.js +388 -34
- package/dist/cli-migration.js.map +1 -0
- package/dist/cli-package-manager.d.ts +10 -0
- package/dist/cli-package-manager.js +135 -0
- package/dist/cli-package-manager.js.map +1 -0
- package/dist/cli-preset.d.ts +103 -0
- package/dist/cli-preset.js +562 -0
- package/dist/cli-preset.js.map +1 -0
- package/dist/cli-workflows.js +1 -0
- package/dist/cli-workflows.js.map +1 -0
- package/dist/cli.d.ts +14 -2
- package/dist/cli.js +689 -94
- package/dist/cli.js.map +1 -0
- package/dist/define-config-metadata.d.ts +10 -0
- package/dist/define-config-metadata.js +16 -0
- package/dist/define-config-metadata.js.map +1 -0
- package/dist/frameworks.d.ts +1 -0
- package/dist/frameworks.js +6 -2
- package/dist/frameworks.js.map +1 -0
- package/dist/index.d.ts +10 -3
- package/dist/index.js +145 -27
- package/dist/index.js.map +1 -0
- package/dist/integrations.js +4 -1
- package/dist/integrations.js.map +1 -0
- package/dist/optional-package-errors.d.ts +3 -0
- package/dist/optional-package-errors.js +15 -0
- package/dist/optional-package-errors.js.map +1 -0
- package/dist/recommended.js +1 -0
- package/dist/recommended.js.map +1 -0
- package/dist/resolvers.js +1 -0
- package/dist/resolvers.js.map +1 -0
- package/dist/tailwind.d.ts +3 -1
- package/dist/tailwind.js +115 -13
- package/dist/tailwind.js.map +1 -0
- package/package.json +20 -19
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,16 @@ 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";
|
|
24
|
+
import { handleExplainPreset } from "./cli-preset.js";
|
|
14
25
|
import {
|
|
15
26
|
handleBaseline,
|
|
16
27
|
handleProfile,
|
|
@@ -18,6 +29,7 @@ import {
|
|
|
18
29
|
handleSnapshotDiff
|
|
19
30
|
} from "./cli-workflows.js";
|
|
20
31
|
import { detectProjectOptions } from "./index.js";
|
|
32
|
+
import { findCssComponentClasses } from "./tailwind.js";
|
|
21
33
|
const getDefaultConfigFilename = (cwd) => {
|
|
22
34
|
const packageJsonPath = join(cwd, "package.json");
|
|
23
35
|
if (!existsSync(packageJsonPath)) return "eslint.config.mjs";
|
|
@@ -80,6 +92,13 @@ const CATEGORY_PACKAGES = {
|
|
|
80
92
|
testing: "@santi020k/eslint-config-testing",
|
|
81
93
|
tools: "@santi020k/eslint-config-tools"
|
|
82
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
|
+
};
|
|
83
102
|
const getConfigPathIfPresent = (cwd) => {
|
|
84
103
|
const configPath = ESLINT_CONFIG_FILENAMES.map((filename) => join(cwd, filename)).find((p) => existsSync(p));
|
|
85
104
|
return configPath ?? null;
|
|
@@ -153,41 +172,13 @@ const getInstallPackages = (summary, declaredDependencies, explicitFeaturePackag
|
|
|
153
172
|
for (const packageName of explicitFeaturePackages) {
|
|
154
173
|
packages.add(packageName);
|
|
155
174
|
}
|
|
175
|
+
for (const packageName of PRESET_INSTALL_PACKAGES[summary.preset] ?? []) {
|
|
176
|
+
packages.add(packageName);
|
|
177
|
+
}
|
|
156
178
|
}
|
|
157
179
|
if (summary.typescript) packages.add(TYPESCRIPT_PACKAGE_NAME);
|
|
158
180
|
return [...packages].filter((packageName) => !declaredDependencies.has(packageName));
|
|
159
181
|
};
|
|
160
|
-
const createInstallCommand = (packageManager, packages, workspaceRoot = false) => {
|
|
161
|
-
const packageList = packages.join(" ");
|
|
162
|
-
switch (packageManager) {
|
|
163
|
-
case "bun":
|
|
164
|
-
return `bun add -d ${packageList}`;
|
|
165
|
-
case "npm":
|
|
166
|
-
return `npm install -D ${packageList}`;
|
|
167
|
-
case "yarn":
|
|
168
|
-
return `yarn add -D ${packageList}`;
|
|
169
|
-
default:
|
|
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]];
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
const detectPackageManager = (cwd) => {
|
|
186
|
-
if (existsSync(join(cwd, "pnpm-lock.yaml")) || existsSync(join(cwd, "pnpm-workspace.yaml"))) return "pnpm";
|
|
187
|
-
if (existsSync(join(cwd, "yarn.lock"))) return "yarn";
|
|
188
|
-
if (existsSync(join(cwd, "bun.lockb")) || existsSync(join(cwd, "bun.lock"))) return "bun";
|
|
189
|
-
return "npm";
|
|
190
|
-
};
|
|
191
182
|
const hasLintScript = (cwd) => {
|
|
192
183
|
const packageJson = readPackageJson(cwd);
|
|
193
184
|
return Boolean(packageJson?.scripts?.lint);
|
|
@@ -237,13 +228,13 @@ const detectWorkspaceProjects = (cwd) => {
|
|
|
237
228
|
const createExplicitOptions = (cwd) => {
|
|
238
229
|
const summary = getProjectSummary(cwd);
|
|
239
230
|
const options = [];
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
];
|
|
231
|
+
const features = [.../* @__PURE__ */ new Set([
|
|
232
|
+
...summary.extensions,
|
|
233
|
+
...summary.formats,
|
|
234
|
+
...summary.libraries,
|
|
235
|
+
...summary.testing,
|
|
236
|
+
...summary.tools
|
|
237
|
+
])].sort();
|
|
247
238
|
if (summary.typescript) options.push(" typescript: true,");
|
|
248
239
|
if (summary.frameworks.length > 0) {
|
|
249
240
|
options.push(
|
|
@@ -252,10 +243,12 @@ const createExplicitOptions = (cwd) => {
|
|
|
252
243
|
" },"
|
|
253
244
|
);
|
|
254
245
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
}
|
|
246
|
+
if (features.length > 0) {
|
|
247
|
+
options.push(
|
|
248
|
+
" features: {",
|
|
249
|
+
...features.map((feature) => ` ${toPropertyKey(feature)}: true,`),
|
|
250
|
+
" },"
|
|
251
|
+
);
|
|
259
252
|
}
|
|
260
253
|
return options;
|
|
261
254
|
};
|
|
@@ -280,6 +273,10 @@ const formatList = (values) => {
|
|
|
280
273
|
const getProjectSummary = (cwd) => {
|
|
281
274
|
const options = detectProjectOptions(cwd);
|
|
282
275
|
const workspaceProjects = detectWorkspaceProjects(cwd);
|
|
276
|
+
let typescriptMode = "off";
|
|
277
|
+
if (options.typescript) {
|
|
278
|
+
typescriptMode = options.typescript === "syntax" ? "syntax" : "type-aware";
|
|
279
|
+
}
|
|
283
280
|
return {
|
|
284
281
|
detectedProjects: Object.keys(options.projects ?? {}).sort(),
|
|
285
282
|
extensions: options.extensions ?? [],
|
|
@@ -292,13 +289,56 @@ const getProjectSummary = (cwd) => {
|
|
|
292
289
|
testing: options.testing ?? [],
|
|
293
290
|
tools: options.tools ?? [],
|
|
294
291
|
typescript: Boolean(options.typescript),
|
|
292
|
+
typescriptMode,
|
|
295
293
|
workspaceProjects
|
|
296
294
|
};
|
|
297
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
|
+
});
|
|
298
328
|
const getInstallProjectSummary = (cwd) => {
|
|
299
329
|
const rootSummary = getProjectSummary(cwd);
|
|
300
|
-
const
|
|
330
|
+
const projectPaths = [.../* @__PURE__ */ new Set([
|
|
331
|
+
...rootSummary.detectedProjects,
|
|
332
|
+
...rootSummary.workspaceProjects
|
|
333
|
+
])];
|
|
334
|
+
const projectSummaries = projectPaths.map((projectPath) => getProjectSummary(join(cwd, projectPath)));
|
|
301
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
|
+
}
|
|
302
342
|
return {
|
|
303
343
|
...rootSummary,
|
|
304
344
|
extensions: [...new Set(summaries.flatMap((summary) => summary.extensions))],
|
|
@@ -307,7 +347,8 @@ const getInstallProjectSummary = (cwd) => {
|
|
|
307
347
|
libraries: [...new Set(summaries.flatMap((summary) => summary.libraries))],
|
|
308
348
|
testing: [...new Set(summaries.flatMap((summary) => summary.testing))],
|
|
309
349
|
tools: [...new Set(summaries.flatMap((summary) => summary.tools))],
|
|
310
|
-
typescript: summaries.some((summary) => summary.typescript)
|
|
350
|
+
typescript: summaries.some((summary) => summary.typescript),
|
|
351
|
+
typescriptMode
|
|
311
352
|
};
|
|
312
353
|
};
|
|
313
354
|
const getWorkspaceDeclaredConfigPackages = (cwd, summary) => {
|
|
@@ -368,6 +409,7 @@ const printUsage = () => {
|
|
|
368
409
|
" init Create eslint.config.js/mjs if missing",
|
|
369
410
|
" update Regenerate eslint.config.js/mjs from detection",
|
|
370
411
|
" explain Print detected v3 config inputs",
|
|
412
|
+
" explain-preset Compare the current config with a preset",
|
|
371
413
|
" inspect Print detected inputs and active config features",
|
|
372
414
|
" install Install missing packages for detected v3 features",
|
|
373
415
|
" doctor Check project setup for common v3 adoption issues",
|
|
@@ -381,13 +423,14 @@ const printUsage = () => {
|
|
|
381
423
|
" generate-skill Generate AI agent standards files",
|
|
382
424
|
"",
|
|
383
425
|
"Options:",
|
|
426
|
+
" --analyze-source explain-preset: lint source and preview autofix without writing",
|
|
384
427
|
" --force Overwrite existing generated skill sections/files",
|
|
385
428
|
" --check Verify generated files, migrations, or snapshots without writing",
|
|
386
429
|
" --concurrency profile: off, auto, or a worker count",
|
|
387
430
|
" --create generate-skill: scaffold a root AGENTS.md when missing",
|
|
388
431
|
" --dry-run install: print the detected install command without running it",
|
|
389
432
|
" --explicit init: write detected settings explicitly",
|
|
390
|
-
" --file profile/snapshot/diff:
|
|
433
|
+
" --file explain/explain-preset/profile/snapshot/diff: file or lint target (repeatable)",
|
|
391
434
|
" --fix doctor: safely repair generated config and package metadata",
|
|
392
435
|
" --full migrate --to v3: choose the batteries-included package",
|
|
393
436
|
" --json Print JSON for commands that support it",
|
|
@@ -395,16 +438,107 @@ const printUsage = () => {
|
|
|
395
438
|
" --max-duration profile: maximum duration in milliseconds",
|
|
396
439
|
" --max-rule-time profile: maximum time for the slowest rule in milliseconds",
|
|
397
440
|
" --max-warnings profile: maximum warning count",
|
|
441
|
+
" --compatibility explain-preset: write a temporary compatibility override",
|
|
442
|
+
" --output explain-preset: compatibility override path",
|
|
398
443
|
" --preset baseline: enable ci or pedantic strict mode before suppressing",
|
|
399
444
|
" --prune baseline: remove suppressions for resolved violations",
|
|
400
445
|
" --snapshot-path snapshot/diff: override .eslint-config-snapshot.json",
|
|
401
446
|
" --to migrate: target version (v2 or v3)",
|
|
447
|
+
" --verbose doctor: print per-project activation details",
|
|
402
448
|
" --with-eslint-mcp generate-skill: scaffold the official ESLint MCP server",
|
|
403
449
|
" --write Apply safe migrations for commands that support it",
|
|
404
450
|
" --help, -h Show this help message",
|
|
405
451
|
" --version, -v Show CLI version"
|
|
406
452
|
].join("\n"));
|
|
407
453
|
};
|
|
454
|
+
const COMMAND_OPTIONS = {
|
|
455
|
+
baseline: ["--json", "--preset", "--prune"],
|
|
456
|
+
compatibility: ["--json"],
|
|
457
|
+
diff: ["--file", "--json", "--snapshot-path"],
|
|
458
|
+
docs: [],
|
|
459
|
+
doctor: ["--fix", "--json", "--lite-install", "--verbose"],
|
|
460
|
+
explain: ["--file", "--json"],
|
|
461
|
+
"explain-preset": ["--analyze-source", "--compatibility", "--file", "--json", "--output"],
|
|
462
|
+
"generate-skill": ["--check", "--create", "--force", "--with-eslint-mcp"],
|
|
463
|
+
init: ["--check", "--explicit"],
|
|
464
|
+
inspect: ["--json"],
|
|
465
|
+
install: ["--dry-run"],
|
|
466
|
+
migrate: ["--check", "--full", "--json", "--to", "--write"],
|
|
467
|
+
profile: ["--concurrency", "--file", "--json", "--max-duration", "--max-rule-time", "--max-warnings"],
|
|
468
|
+
snapshot: ["--check", "--file", "--json", "--snapshot-path"],
|
|
469
|
+
update: []
|
|
470
|
+
};
|
|
471
|
+
const VALUE_OPTIONS = /* @__PURE__ */ new Set([
|
|
472
|
+
"--concurrency",
|
|
473
|
+
"--file",
|
|
474
|
+
"--max-duration",
|
|
475
|
+
"--max-rule-time",
|
|
476
|
+
"--max-warnings",
|
|
477
|
+
"--output",
|
|
478
|
+
"--preset",
|
|
479
|
+
"--snapshot-path",
|
|
480
|
+
"--to"
|
|
481
|
+
]);
|
|
482
|
+
const printCommandUsage = (command) => {
|
|
483
|
+
let positional = "";
|
|
484
|
+
if (command === "explain") positional = " [rule]";
|
|
485
|
+
else if (command === "explain-preset") positional = " <preset>";
|
|
486
|
+
const options = COMMAND_OPTIONS[command] ?? [];
|
|
487
|
+
console.log([
|
|
488
|
+
"Usage: basic-eslint <command> [options]",
|
|
489
|
+
"",
|
|
490
|
+
`Usage: basic-eslint ${command}${positional}${options.length > 0 ? " [options]" : ""}`,
|
|
491
|
+
...options.length > 0 ? ["", "Options:", ...options.map((option) => ` ${option}`)] : []
|
|
492
|
+
].join("\n"));
|
|
493
|
+
};
|
|
494
|
+
const validateCommandArguments = (command, args) => {
|
|
495
|
+
const allowed = COMMAND_OPTIONS[command];
|
|
496
|
+
if (!allowed) return { ok: true };
|
|
497
|
+
const allowedOptions = new Set(allowed);
|
|
498
|
+
const positionals = [];
|
|
499
|
+
for (let index = 0; index < args.length; index++) {
|
|
500
|
+
const argument = args[index];
|
|
501
|
+
if (!argument.startsWith("-")) {
|
|
502
|
+
positionals.push(argument);
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
const option = argument.includes("=") ? argument.slice(0, argument.indexOf("=")) : argument;
|
|
506
|
+
if (!allowedOptions.has(option)) {
|
|
507
|
+
console.error(`Unknown option for ${command}: ${option}`);
|
|
508
|
+
process.exitCode = 1;
|
|
509
|
+
return { ok: false };
|
|
510
|
+
}
|
|
511
|
+
if (argument.includes("=") && !VALUE_OPTIONS.has(option)) {
|
|
512
|
+
console.error(`Option ${option} does not accept a value.`);
|
|
513
|
+
process.exitCode = 1;
|
|
514
|
+
return { ok: false };
|
|
515
|
+
}
|
|
516
|
+
if (VALUE_OPTIONS.has(option) && !argument.includes("=")) {
|
|
517
|
+
const value = args[index + 1];
|
|
518
|
+
if (!value || value.startsWith("-")) {
|
|
519
|
+
console.error(
|
|
520
|
+
["--max-duration", "--max-rule-time", "--max-warnings"].includes(option) ? `${option} must be a non-negative number.` : `Option ${option} requires a value.`
|
|
521
|
+
);
|
|
522
|
+
process.exitCode = 1;
|
|
523
|
+
return { ok: false };
|
|
524
|
+
}
|
|
525
|
+
index++;
|
|
526
|
+
} else if (VALUE_OPTIONS.has(option) && argument.endsWith("=")) {
|
|
527
|
+
console.error(
|
|
528
|
+
["--max-duration", "--max-rule-time", "--max-warnings"].includes(option) ? `${option} must be a non-negative number.` : `Option ${option} requires a value.`
|
|
529
|
+
);
|
|
530
|
+
process.exitCode = 1;
|
|
531
|
+
return { ok: false };
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
const maximumPositionals = command === "explain" || command === "explain-preset" ? 1 : 0;
|
|
535
|
+
if (positionals.length > maximumPositionals) {
|
|
536
|
+
console.error(`Unexpected argument for ${command}: ${positionals[maximumPositionals]}`);
|
|
537
|
+
process.exitCode = 1;
|
|
538
|
+
return { ok: false };
|
|
539
|
+
}
|
|
540
|
+
return { ok: true, positional: positionals[0] };
|
|
541
|
+
};
|
|
408
542
|
const handleInit = (cwd = process.cwd(), check = false, explicit = false) => {
|
|
409
543
|
const configPath = resolveConfigPath(cwd);
|
|
410
544
|
if (check) {
|
|
@@ -466,7 +600,8 @@ const handleInspect = async (cwd = process.cwd(), json = false) => {
|
|
|
466
600
|
const payload = {
|
|
467
601
|
activeConfig,
|
|
468
602
|
detected: summary,
|
|
469
|
-
packageManager: detectPackageManager(cwd)
|
|
603
|
+
packageManager: detectPackageManager(cwd),
|
|
604
|
+
tailwindEntryPoints: getTailwindEntryPoints(cwd)
|
|
470
605
|
};
|
|
471
606
|
if (json) {
|
|
472
607
|
console.log(JSON.stringify(payload, null, 2));
|
|
@@ -490,31 +625,135 @@ const handleInspect = async (cwd = process.cwd(), json = false) => {
|
|
|
490
625
|
].join("\n"));
|
|
491
626
|
};
|
|
492
627
|
const semver = createRequire(import.meta.url)("semver");
|
|
493
|
-
const
|
|
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) => {
|
|
494
654
|
let directory = dirname(mainPath);
|
|
495
655
|
for (let depth = 0; depth < 4; depth++) {
|
|
496
656
|
const packagePath = join(directory, "package.json");
|
|
497
657
|
if (existsSync(packagePath)) {
|
|
498
658
|
const metadata = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
499
|
-
if (metadata.name
|
|
659
|
+
if (metadata.name && metadata.version) return metadata;
|
|
500
660
|
}
|
|
501
661
|
directory = dirname(directory);
|
|
502
662
|
}
|
|
503
663
|
return null;
|
|
504
664
|
};
|
|
505
665
|
const resolvePackageMetadata = (cwd, packageName) => {
|
|
506
|
-
|
|
507
|
-
|
|
666
|
+
const candidateRoots = [...new Set([
|
|
667
|
+
cwd,
|
|
668
|
+
findPnpmWorkspaceRoot(cwd)
|
|
669
|
+
].filter((root) => Boolean(root)))];
|
|
670
|
+
for (const candidateRoot of candidateRoots) {
|
|
508
671
|
try {
|
|
509
|
-
|
|
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
|
+
}
|
|
510
680
|
} catch {
|
|
511
|
-
const integrationsMain = projectRequire.resolve(INTEGRATIONS_PACKAGE_NAME);
|
|
512
|
-
const integrationsRequire = createRequire(integrationsMain);
|
|
513
|
-
return readPackageMetadataFromMain(integrationsRequire.resolve(packageName), packageName);
|
|
514
681
|
}
|
|
515
|
-
|
|
516
|
-
|
|
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
|
+
};
|
|
517
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}.`;
|
|
518
757
|
};
|
|
519
758
|
const getAstroDoctorNodeWarning = (metadata) => {
|
|
520
759
|
const nodeRange = metadata.engines?.node;
|
|
@@ -562,7 +801,11 @@ const validateConfigContent = (configContent, hasV1FrameworkImports, declaredDep
|
|
|
562
801
|
if (hasV1FrameworkImports) {
|
|
563
802
|
warnings.push("Config still imports v1 framework packages. Run `basic-eslint migrate --write` or switch to framework booleans.");
|
|
564
803
|
}
|
|
565
|
-
const
|
|
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;
|
|
566
809
|
const hasDetectedProjectScopes = summary.workspaceProjects.every((project) => summary.detectedProjects.includes(project));
|
|
567
810
|
if (summary.workspaceProjects.length > 0 && !configContent.includes("projects:") && !(usesBasicComposer && hasDetectedProjectScopes)) {
|
|
568
811
|
warnings.push("Workspace packages were detected, but the root config does not use `projects` scoping.");
|
|
@@ -585,6 +828,16 @@ const validateConfigContent = (configContent, hasV1FrameworkImports, declaredDep
|
|
|
585
828
|
);
|
|
586
829
|
}
|
|
587
830
|
}
|
|
831
|
+
const missingPackages = getInstallPackages(
|
|
832
|
+
summary,
|
|
833
|
+
declaredDependencies,
|
|
834
|
+
getExplicitConfigFeaturePackages(configContent)
|
|
835
|
+
);
|
|
836
|
+
if (missingPackages.length > 0) {
|
|
837
|
+
warnings.push(
|
|
838
|
+
`The active configuration requires undeclared packages: ${missingPackages.join(", ")}. Run \`basic-eslint install\` to add the modular v3 dependency set.`
|
|
839
|
+
);
|
|
840
|
+
}
|
|
588
841
|
return warnings;
|
|
589
842
|
};
|
|
590
843
|
const validateProjectSetup = (cwd, configPath, activeConfig, hasV1FrameworkImports) => {
|
|
@@ -634,7 +887,13 @@ const checkDuplicateEslint = (cwd, warnings) => {
|
|
|
634
887
|
);
|
|
635
888
|
}
|
|
636
889
|
};
|
|
637
|
-
const
|
|
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) => {
|
|
638
897
|
const { issues, warnings } = validateProjectSetup(cwd, configPath, activeConfig, hasV1FrameworkImports);
|
|
639
898
|
if (configContent) {
|
|
640
899
|
warnings.push(...validateConfigContent(configContent, hasV1FrameworkImports, declaredDependencies, summary));
|
|
@@ -642,9 +901,238 @@ const buildDoctorDiagnosis = (cwd, configPath, configContent, hasV1FrameworkImpo
|
|
|
642
901
|
const astroDoctorEnabled = checkAstroDoctorStatus(activeConfig, configContent, summary, warnings);
|
|
643
902
|
warnings.push(...getAstroDoctorCompatibilityWarnings(cwd, astroDoctorEnabled));
|
|
644
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);
|
|
645
908
|
return { issues, warnings };
|
|
646
909
|
};
|
|
647
|
-
const
|
|
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 = []) => {
|
|
648
1136
|
let status = "passed";
|
|
649
1137
|
if (issues.length > 0) {
|
|
650
1138
|
status = "failed";
|
|
@@ -654,9 +1142,11 @@ const outputDoctorResult = (json, configPath, packageManager, summary, liteInsta
|
|
|
654
1142
|
const payload = {
|
|
655
1143
|
configFile: configPath ? basename(configPath) : null,
|
|
656
1144
|
fixes,
|
|
1145
|
+
...installCommand ? { installCommand } : {},
|
|
657
1146
|
issues,
|
|
658
|
-
liteInstallCommand,
|
|
659
1147
|
packageManager,
|
|
1148
|
+
projects,
|
|
1149
|
+
requiredPackages,
|
|
660
1150
|
status,
|
|
661
1151
|
warnings,
|
|
662
1152
|
workspaceProjects: summary.workspaceProjects
|
|
@@ -671,6 +1161,9 @@ const outputDoctorResult = (json, configPath, packageManager, summary, liteInsta
|
|
|
671
1161
|
`- Package manager: ${payload.packageManager}`,
|
|
672
1162
|
`- Config file: ${payload.configFile ?? "none"}`,
|
|
673
1163
|
`- Workspace projects: ${formatList(summary.workspaceProjects)}`,
|
|
1164
|
+
`- Required packages: ${formatList(requiredPackages)}`,
|
|
1165
|
+
...installCommand ? [`- Install command: ${installCommand}`] : [],
|
|
1166
|
+
...verbose ? formatDoctorProjectTable(projects) : [],
|
|
674
1167
|
...fixes.length > 0 ? ["", "Fixes applied:", ...fixes.map((fix) => `- ${fix}`)] : [],
|
|
675
1168
|
...issues.length > 0 ? ["", "Issues:", ...issues.map((issue) => `- ${issue}`)] : [],
|
|
676
1169
|
...warnings.length > 0 ? ["", "Warnings:", ...warnings.map((warning) => `- ${warning}`)] : []
|
|
@@ -715,6 +1208,15 @@ const applyDoctorFixes = (cwd, packageJson, configPath, configContent, summary)
|
|
|
715
1208
|
fixes.push(`Declared ${packageName}.`);
|
|
716
1209
|
packageChanged = true;
|
|
717
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
|
+
}
|
|
718
1220
|
if (packageChanged) {
|
|
719
1221
|
const packagePath = join(cwd, "package.json");
|
|
720
1222
|
const backupPath = createDoctorBackupPath(packagePath);
|
|
@@ -741,40 +1243,65 @@ const applyDoctorFixes = (cwd, packageJson, configPath, configContent, summary)
|
|
|
741
1243
|
return fixes;
|
|
742
1244
|
};
|
|
743
1245
|
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) => {
|
|
745
|
-
const
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
1246
|
+
const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = false, fix = false, verbose = false) => {
|
|
1247
|
+
const pnpmWorkspaceRoot = findPnpmWorkspaceRoot(cwd);
|
|
1248
|
+
const projectRoot = pnpmWorkspaceRoot ?? cwd;
|
|
1249
|
+
const packageManager = detectPackageManager(projectRoot);
|
|
1250
|
+
const workspaceRoot = packageManager === "pnpm" && pnpmWorkspaceRoot !== void 0;
|
|
1251
|
+
const catalog = workspaceRoot ? getCatalogPreference(readPackageJson(projectRoot)) : false;
|
|
1252
|
+
let configPath = getConfigPathIfPresent(projectRoot);
|
|
1253
|
+
let packageJson = readPackageJson(projectRoot);
|
|
749
1254
|
let declaredDependencies = getDeclaredDependencyNames(packageJson);
|
|
750
|
-
let summary = getInstallProjectSummary(
|
|
751
|
-
let activeConfig = await analyzeEslintConfig(
|
|
1255
|
+
let summary = getInstallProjectSummary(projectRoot);
|
|
1256
|
+
let activeConfig = await analyzeEslintConfig(projectRoot);
|
|
752
1257
|
let configContent = configPath ? readFileSync(configPath, "utf8") : null;
|
|
753
|
-
let liteInstallPackages = getLiteInstallPackages(summary, declaredDependencies);
|
|
754
|
-
let liteInstallCommand = createInstallCommand(packageManager, liteInstallPackages, workspaceRoot);
|
|
755
1258
|
let hasV1FrameworkImports = hasV1FrameworkPackageImports(configContent);
|
|
756
1259
|
if (liteInstall) {
|
|
1260
|
+
const liteInstallPackages = getLiteInstallPackages(summary, declaredDependencies);
|
|
1261
|
+
const liteInstallCommand = createInstallCommand(
|
|
1262
|
+
packageManager,
|
|
1263
|
+
addCompatibleConfigVersions(
|
|
1264
|
+
liteInstallPackages,
|
|
1265
|
+
getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
|
|
1266
|
+
),
|
|
1267
|
+
workspaceRoot,
|
|
1268
|
+
catalog
|
|
1269
|
+
);
|
|
757
1270
|
if (json) {
|
|
758
|
-
console.log(JSON.stringify({
|
|
1271
|
+
console.log(JSON.stringify({
|
|
1272
|
+
command: liteInstallCommand,
|
|
1273
|
+
packageManager,
|
|
1274
|
+
packages: liteInstallPackages
|
|
1275
|
+
}, null, 2));
|
|
759
1276
|
} else {
|
|
760
1277
|
console.log(liteInstallCommand);
|
|
761
1278
|
}
|
|
762
1279
|
return;
|
|
763
1280
|
}
|
|
764
|
-
const fixes = fix ? applyDoctorFixes(
|
|
1281
|
+
const fixes = fix ? applyDoctorFixes(projectRoot, packageJson, configPath, configContent, summary) : [];
|
|
765
1282
|
if (fixes.length > 0) {
|
|
766
|
-
configPath = getConfigPathIfPresent(
|
|
767
|
-
packageJson = readPackageJson(
|
|
1283
|
+
configPath = getConfigPathIfPresent(projectRoot);
|
|
1284
|
+
packageJson = readPackageJson(projectRoot);
|
|
768
1285
|
declaredDependencies = getDeclaredDependencyNames(packageJson);
|
|
769
|
-
summary = getInstallProjectSummary(
|
|
770
|
-
activeConfig = await analyzeEslintConfig(
|
|
1286
|
+
summary = getInstallProjectSummary(projectRoot);
|
|
1287
|
+
activeConfig = await analyzeEslintConfig(projectRoot);
|
|
771
1288
|
configContent = configPath ? readFileSync(configPath, "utf8") : null;
|
|
772
|
-
liteInstallPackages = getLiteInstallPackages(summary, declaredDependencies);
|
|
773
|
-
liteInstallCommand = createInstallCommand(packageManager, liteInstallPackages, workspaceRoot);
|
|
774
1289
|
hasV1FrameworkImports = hasV1FrameworkPackageImports(configContent);
|
|
775
1290
|
}
|
|
1291
|
+
const explicitFeaturePackages = configContent ? getExplicitConfigFeaturePackages(configContent) : [];
|
|
1292
|
+
const requiredPackages = getInstallPackages(summary, declaredDependencies, explicitFeaturePackages);
|
|
1293
|
+
const installCommand = requiredPackages.length > 0 ? createInstallCommand(
|
|
1294
|
+
packageManager,
|
|
1295
|
+
addCompatibleConfigVersions(
|
|
1296
|
+
requiredPackages,
|
|
1297
|
+
getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
|
|
1298
|
+
),
|
|
1299
|
+
workspaceRoot,
|
|
1300
|
+
catalog
|
|
1301
|
+
) : void 0;
|
|
776
1302
|
const { issues, warnings } = buildDoctorDiagnosis(
|
|
777
|
-
|
|
1303
|
+
projectRoot,
|
|
1304
|
+
packageJson,
|
|
778
1305
|
configPath,
|
|
779
1306
|
configContent,
|
|
780
1307
|
hasV1FrameworkImports,
|
|
@@ -782,41 +1309,77 @@ const handleDoctor = async (cwd = process.cwd(), json = false, liteInstall = fal
|
|
|
782
1309
|
declaredDependencies,
|
|
783
1310
|
summary
|
|
784
1311
|
);
|
|
785
|
-
|
|
1312
|
+
const projects = await getDoctorProjectActivations(projectRoot);
|
|
1313
|
+
outputDoctorResult(
|
|
1314
|
+
json,
|
|
1315
|
+
verbose,
|
|
1316
|
+
configPath,
|
|
1317
|
+
packageManager,
|
|
1318
|
+
summary,
|
|
1319
|
+
installCommand,
|
|
1320
|
+
requiredPackages,
|
|
1321
|
+
issues,
|
|
1322
|
+
warnings,
|
|
1323
|
+
projects,
|
|
1324
|
+
fixes
|
|
1325
|
+
);
|
|
786
1326
|
};
|
|
787
|
-
const
|
|
788
|
-
|
|
1327
|
+
const isMinimumReleaseAgeFailure = (output) => /minimum[\s_-]*release[\s_-]*age/i.test(output) || /release (?:is )?(?:too young|not old enough)/i.test(output) || /blocked by (?:the )?minimum age/i.test(output);
|
|
1328
|
+
const handleInstall = (cwd = process.cwd(), dryRun = false, runner = spawnSync) => {
|
|
1329
|
+
const pnpmWorkspaceRoot = findPnpmWorkspaceRoot(cwd);
|
|
1330
|
+
const projectRoot = pnpmWorkspaceRoot ?? cwd;
|
|
1331
|
+
const packageJson = readPackageJson(projectRoot);
|
|
789
1332
|
if (!packageJson) {
|
|
790
1333
|
console.error("\u274C package.json is missing or invalid.");
|
|
791
1334
|
process.exitCode = 1;
|
|
792
1335
|
return;
|
|
793
1336
|
}
|
|
794
|
-
const packageManager = detectPackageManager(
|
|
795
|
-
const configPath = getConfigPathIfPresent(
|
|
1337
|
+
const packageManager = detectPackageManager(projectRoot);
|
|
1338
|
+
const configPath = getConfigPathIfPresent(projectRoot);
|
|
796
1339
|
const explicitFeaturePackages = configPath ? getExplicitConfigFeaturePackages(readFileSync(configPath, "utf8")) : [];
|
|
797
1340
|
const packages = getInstallPackages(
|
|
798
|
-
getInstallProjectSummary(
|
|
1341
|
+
getInstallProjectSummary(projectRoot),
|
|
799
1342
|
getDeclaredDependencyNames(packageJson),
|
|
800
1343
|
explicitFeaturePackages
|
|
801
1344
|
);
|
|
802
|
-
const workspaceRoot = packageManager === "pnpm" &&
|
|
1345
|
+
const workspaceRoot = packageManager === "pnpm" && pnpmWorkspaceRoot !== void 0;
|
|
1346
|
+
const catalog = workspaceRoot ? getCatalogPreference(packageJson) : false;
|
|
1347
|
+
const installPackages = addCompatibleConfigVersions(
|
|
1348
|
+
packages,
|
|
1349
|
+
getCompatibleConfigVersion(packageJson, getCliVersion(), pnpmWorkspaceRoot)
|
|
1350
|
+
);
|
|
803
1351
|
if (packages.length === 0) {
|
|
804
1352
|
console.log("\u2705 All packages required by the detected ESLint configuration are already declared.");
|
|
805
1353
|
return;
|
|
806
1354
|
}
|
|
807
|
-
const installCommand = createInstallCommand(packageManager,
|
|
1355
|
+
const installCommand = createInstallCommand(packageManager, installPackages, workspaceRoot, catalog);
|
|
808
1356
|
if (dryRun) {
|
|
809
1357
|
console.log(installCommand);
|
|
810
1358
|
return;
|
|
811
1359
|
}
|
|
812
1360
|
console.log(`Installing detected ESLint dependencies:
|
|
813
1361
|
${installCommand}`);
|
|
814
|
-
const [command, args] = createInstallInvocation(packageManager,
|
|
815
|
-
const result =
|
|
1362
|
+
const [command, args] = createInstallInvocation(packageManager, installPackages, workspaceRoot, catalog);
|
|
1363
|
+
const result = runner(command, args, {
|
|
1364
|
+
cwd: projectRoot,
|
|
1365
|
+
encoding: "utf8",
|
|
1366
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
1367
|
+
stdio: "pipe"
|
|
1368
|
+
});
|
|
1369
|
+
const stdout = String(result.stdout ?? "");
|
|
1370
|
+
const stderr = String(result.stderr ?? "");
|
|
1371
|
+
if (stdout) process.stdout.write(stdout);
|
|
1372
|
+
if (stderr) process.stderr.write(stderr);
|
|
816
1373
|
if (result.error) {
|
|
817
1374
|
console.error(`\u274C Failed to run ${packageManager}: ${result.error.message}`);
|
|
818
1375
|
process.exitCode = 1;
|
|
819
1376
|
} else if (result.status !== 0) {
|
|
1377
|
+
if (packageManager === "pnpm" && isMinimumReleaseAgeFailure(`${stdout}
|
|
1378
|
+
${stderr}`)) {
|
|
1379
|
+
console.error(
|
|
1380
|
+
`\u274C pnpm minimumReleaseAge temporarily blocks the compatible ESLint config release (${installPackages.join(", ")}). Keep the generated compatible range and either wait for the policy window or add the exact release to minimumReleaseAgeExclude in pnpm-workspace.yaml.`
|
|
1381
|
+
);
|
|
1382
|
+
}
|
|
820
1383
|
process.exitCode = result.status ?? 1;
|
|
821
1384
|
}
|
|
822
1385
|
};
|
|
@@ -943,7 +1506,7 @@ const dispatchCommand = (command, cwd, flags) => {
|
|
|
943
1506
|
break;
|
|
944
1507
|
}
|
|
945
1508
|
case "doctor": {
|
|
946
|
-
handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall, flags.hasFix).catch((error) => {
|
|
1509
|
+
handleDoctor(cwd, flags.hasJson, flags.hasLiteInstall, flags.hasFix, flags.hasVerbose).catch((error) => {
|
|
947
1510
|
console.error(`\u274C Failed to run doctor: ${String(error)}`);
|
|
948
1511
|
process.exitCode = 1;
|
|
949
1512
|
});
|
|
@@ -964,6 +1527,25 @@ const dispatchCommand = (command, cwd, flags) => {
|
|
|
964
1527
|
}
|
|
965
1528
|
break;
|
|
966
1529
|
}
|
|
1530
|
+
case "explain-preset": {
|
|
1531
|
+
if (!flags.positional) {
|
|
1532
|
+
console.error("Missing preset. Usage: basic-eslint explain-preset <preset> [options]");
|
|
1533
|
+
process.exitCode = 1;
|
|
1534
|
+
break;
|
|
1535
|
+
}
|
|
1536
|
+
handleExplainPreset(cwd, flags.positional, {
|
|
1537
|
+
analyzeSource: flags.hasAnalyzeSource,
|
|
1538
|
+
compatibility: flags.hasCompatibility,
|
|
1539
|
+
file: flags.files[0],
|
|
1540
|
+
files: flags.files,
|
|
1541
|
+
json: flags.hasJson,
|
|
1542
|
+
output: flags.output
|
|
1543
|
+
}).catch((error) => {
|
|
1544
|
+
console.error(`\u274C Failed to explain preset: ${String(error)}`);
|
|
1545
|
+
process.exitCode = 1;
|
|
1546
|
+
});
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
967
1549
|
case "generate-skill": {
|
|
968
1550
|
handleGenerateSkill(cwd, flags.hasForce, {
|
|
969
1551
|
check: flags.hasCheck,
|
|
@@ -1065,9 +1647,9 @@ const getNumericFlagValue = (argv, flag) => {
|
|
|
1065
1647
|
};
|
|
1066
1648
|
const runCli = (argv = process.argv, cwd = process.cwd()) => {
|
|
1067
1649
|
const command = argv[2];
|
|
1068
|
-
const isHelp =
|
|
1650
|
+
const isHelp = argv.slice(3).some((argument) => argument === "--help" || argument === "-h");
|
|
1069
1651
|
const isVersion = command === "--version" || command === "-v";
|
|
1070
|
-
if (!command ||
|
|
1652
|
+
if (!command || command === "--help" || command === "-h") {
|
|
1071
1653
|
printUsage();
|
|
1072
1654
|
return;
|
|
1073
1655
|
}
|
|
@@ -1075,10 +1657,19 @@ const runCli = (argv = process.argv, cwd = process.cwd()) => {
|
|
|
1075
1657
|
console.log(getCliVersion());
|
|
1076
1658
|
return;
|
|
1077
1659
|
}
|
|
1660
|
+
if (isHelp) {
|
|
1661
|
+
if (COMMAND_OPTIONS[command]) printCommandUsage(command);
|
|
1662
|
+
else printUsage();
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
const validation = validateCommandArguments(command, argv.slice(3));
|
|
1666
|
+
if (!validation.ok) return;
|
|
1078
1667
|
dispatchCommand(command, cwd, {
|
|
1079
1668
|
concurrency: getFlagValue(argv, "--concurrency"),
|
|
1080
1669
|
files: getFlagValues(argv, "--file"),
|
|
1670
|
+
hasAnalyzeSource: argv.includes("--analyze-source"),
|
|
1081
1671
|
hasCheck: argv.includes("--check"),
|
|
1672
|
+
hasCompatibility: argv.includes("--compatibility"),
|
|
1082
1673
|
hasCreate: argv.includes("--create"),
|
|
1083
1674
|
hasDryRun: argv.includes("--dry-run"),
|
|
1084
1675
|
hasExplicit: argv.includes("--explicit"),
|
|
@@ -1088,13 +1679,16 @@ const runCli = (argv = process.argv, cwd = process.cwd()) => {
|
|
|
1088
1679
|
hasJson: argv.includes("--json"),
|
|
1089
1680
|
hasLiteInstall: argv.includes("--lite-install"),
|
|
1090
1681
|
hasPrune: argv.includes("--prune"),
|
|
1682
|
+
hasVerbose: argv.includes("--verbose"),
|
|
1091
1683
|
hasWithEslintMcp: argv.includes("--with-eslint-mcp"),
|
|
1092
1684
|
hasWrite: argv.includes("--write"),
|
|
1093
1685
|
maxDurationMs: getNumericFlagValue(argv, "--max-duration"),
|
|
1094
1686
|
maxRuleTimeMs: getNumericFlagValue(argv, "--max-rule-time"),
|
|
1095
1687
|
maxWarnings: getNumericFlagValue(argv, "--max-warnings"),
|
|
1688
|
+
output: getFlagValue(argv, "--output"),
|
|
1689
|
+
positional: validation.positional,
|
|
1096
1690
|
preset: getFlagValue(argv, "--preset"),
|
|
1097
|
-
rule:
|
|
1691
|
+
rule: command === "explain" ? validation.positional : void 0,
|
|
1098
1692
|
snapshotPath: getFlagValue(argv, "--snapshot-path"),
|
|
1099
1693
|
target: getFlagValue(argv, "--to")
|
|
1100
1694
|
});
|
|
@@ -1114,3 +1708,4 @@ export {
|
|
|
1114
1708
|
handleUpdate,
|
|
1115
1709
|
runCli
|
|
1116
1710
|
};
|
|
1711
|
+
//# sourceMappingURL=cli.js.map
|