@geoql/vue-doctor 0.1.0-alpha.0 → 0.1.1
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/README.md +86 -17
- package/dist/index.js +259 -17
- package/dist/index.js.map +1 -1
- package/package.json +14 -7
package/README.md
CHANGED
|
@@ -2,33 +2,102 @@
|
|
|
2
2
|
|
|
3
3
|
> Your agent writes bad Vue. This catches it.
|
|
4
4
|
|
|
5
|
-
CLI auditor for Vue 3 apps.
|
|
5
|
+
CLI auditor for Vue 3 apps. It runs a hybrid scan: a template-AST pass over `@vue/compiler-sfc` plus a script pass through `oxlint` (its native `vue` plugin and the doctor JS rules), then merges the findings into a deterministic 0–100 score. Built for catching anti-patterns, AI-slop, and best-practice violations, especially the ones coding agents leave behind.
|
|
6
6
|
|
|
7
|
-
##
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
|
-
```
|
|
10
|
-
|
|
9
|
+
```sh
|
|
10
|
+
# one-off, no install
|
|
11
|
+
npx -y @geoql/vue-doctor
|
|
12
|
+
|
|
13
|
+
# or add it
|
|
14
|
+
npm i @geoql/vue-doctor
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
Published on [npm](https://www.npmjs.com/package/@geoql/vue-doctor) and [JSR](https://jsr.io/@geoql/vue-doctor) at `v0.1.0` with provenance.
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
14
20
|
|
|
21
|
+
```sh
|
|
22
|
+
vue-doctor [path] [options]
|
|
15
23
|
```
|
|
16
|
-
|
|
24
|
+
|
|
25
|
+
`path` defaults to the current directory. The default output format is `agent` (compact, built for LLM consumption), not a verbose human report.
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
# Audit ./src and print the agent report
|
|
29
|
+
vue-doctor ./src
|
|
30
|
+
|
|
31
|
+
# Human-readable output, fail the build on warnings
|
|
32
|
+
vue-doctor --format pretty --fail-on warn
|
|
33
|
+
|
|
34
|
+
# Machine-readable, only files changed vs HEAD, write to a file
|
|
35
|
+
vue-doctor --json --diff --output report.json
|
|
36
|
+
|
|
37
|
+
# Just the score, for piping into a gate
|
|
38
|
+
vue-doctor --score
|
|
17
39
|
```
|
|
18
40
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
| `--
|
|
41
|
+
### Options
|
|
42
|
+
|
|
43
|
+
| Flag | Description |
|
|
44
|
+
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
|
|
45
|
+
| `--format <kind>` | Output format: `agent` (default), `pretty`, `json`, `json-compact`, `sarif`, `html`. |
|
|
46
|
+
| `--json` | Shorthand for `--format json`. |
|
|
47
|
+
| `--json-compact` | Single-line JSON. |
|
|
48
|
+
| `--config <path>` | Path to `doctor.config.ts`. |
|
|
49
|
+
| `--preset <name>` | Base preset: `minimal`, `recommended`, `strict`, `all`. |
|
|
50
|
+
| `--fail-on <level>` | Exit non-zero at this severity or worse: `error` (default), `warn`, `none`. |
|
|
51
|
+
| `--quiet` | Only show the summary. |
|
|
52
|
+
| `--verbose` | Emit per-pass timing and rule diagnostics to stderr. |
|
|
53
|
+
| `--no-color` | Disable colored output. |
|
|
54
|
+
| `--rule <id:level>` | Override one rule, repeatable. Levels: `error`, `warn`, `info`, `off`. e.g. `--rule vue-doctor/no-em-dash-in-string:off`. |
|
|
55
|
+
| `--include <glob>` | Glob of files to include, repeatable. |
|
|
56
|
+
| `--exclude <glob>` | Glob of files to exclude, repeatable. |
|
|
57
|
+
| `--no-dead-code` | Skip the dead-code (knip) analysis pass. |
|
|
58
|
+
| `--no-lint` | Skip the lint passes (template / SFC / oxlint). |
|
|
59
|
+
| `--no-respect-inline-disables` | Surface findings even inside `doctor-disable` comments. |
|
|
60
|
+
| `--threshold <n>` | Minimum passing score, integer `0`–`100`. |
|
|
61
|
+
| `--score` | Print only the numeric score, for piping. |
|
|
62
|
+
| `--annotations` | Emit GitHub Actions `::error::` / `::warning::` lines. |
|
|
63
|
+
| `--ci` | Auto-enable CI behavior (annotations on GitHub Actions). |
|
|
64
|
+
| `--no-ci` | Disable CI auto-detection even when a CI env is set. |
|
|
65
|
+
| `--diff` | Only report findings in files changed vs HEAD. |
|
|
66
|
+
| `--staged` | Only report findings in staged files. |
|
|
67
|
+
| `--full` | Force a complete scan, overriding `--diff` / `--staged`. |
|
|
68
|
+
| `--output <file>` | Write the report to a file instead of stdout. |
|
|
69
|
+
|
|
70
|
+
Severity is `error | warn | info` everywhere. `--diff`/`--staged`, `--verbose`/`--quiet`, and `--score`/`--json` are mutually exclusive.
|
|
71
|
+
|
|
72
|
+
### Subcommands
|
|
73
|
+
|
|
74
|
+
| Command | Description |
|
|
75
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
76
|
+
| `list-rules` | List every registered rule with id, severity, category, source, and preset membership. Filters: `--preset <recommended\|all>`, `--category <name>`, `--source <doctor\|oxlint-builtin\|eslint-plugin-vue>`, `--severity <error\|warn\|info>`, `--json`, `--json-compact`. |
|
|
77
|
+
| `explain <ruleId>` | Print a rule's severity, category, recommendation, and help URL. `--json` for structured output. |
|
|
78
|
+
| `inspect [dir]` | Print the detected project capabilities doctor uses to gate rules. `--json` / `--json-compact`. |
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
vue-doctor list-rules --category ai-slop
|
|
82
|
+
vue-doctor explain vue-doctor/no-em-dash-in-string
|
|
83
|
+
vue-doctor inspect --json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Exit codes
|
|
87
|
+
|
|
88
|
+
| Code | Meaning |
|
|
89
|
+
| ---- | ----------------------------------------------------------------------------------------- |
|
|
90
|
+
| `0` | Clean, or score at/above `--threshold` and no findings at/above `--fail-on`. |
|
|
91
|
+
| `1` | Findings at/above the `--fail-on` level (default: `error`), or score below `--threshold`. |
|
|
92
|
+
| `2` | Configuration error or scan failure. |
|
|
93
|
+
|
|
94
|
+
## Rules
|
|
95
|
+
|
|
96
|
+
`vue-doctor` ships ~24 Vue rules plus oxlint's native `vue` built-ins, grouped by category: `ai-slop`, `reactivity`, `composition`, `performance`, `template`, `template-perf`, `build-quality`, `deps`, `sfc`, and `dead-code`. Run `vue-doctor list-rules` for the live list. Most are on in the `recommended` preset.
|
|
24
97
|
|
|
25
|
-
|
|
98
|
+
## Scope
|
|
26
99
|
|
|
27
|
-
|
|
28
|
-
| ---- | ------------------------------------------------------------ |
|
|
29
|
-
| 0 | No findings at/above `--fail-on` threshold (default: error). |
|
|
30
|
-
| 1 | Findings at/above threshold. |
|
|
31
|
-
| 2 | Configuration error or scan failure. |
|
|
100
|
+
Vue 3 only. For Nuxt 4 projects, use [`@geoql/nuxt-doctor`](../nuxt-doctor), which layers Nuxt rules on top of this set.
|
|
32
101
|
|
|
33
102
|
## Architecture
|
|
34
103
|
|
package/dist/index.js
CHANGED
|
@@ -1,30 +1,234 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
2
3
|
import process from "node:process";
|
|
3
|
-
import {
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { audit, detectProject, encodeAnnotations, format, listChangedFiles, listRules, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, renderVerboseTrace } from "@geoql/doctor-core";
|
|
4
6
|
import { cac } from "cac";
|
|
5
7
|
//#region src/cli.ts
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
+
function readVersion() {
|
|
9
|
+
try {
|
|
10
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
return JSON.parse(readFileSync(resolve(here, "../package.json"), "utf-8")).version ?? "0.0.0";
|
|
12
|
+
} catch {
|
|
13
|
+
return "0.0.0";
|
|
14
|
+
}
|
|
8
15
|
}
|
|
9
|
-
function
|
|
10
|
-
|
|
16
|
+
function isCiEnvironment() {
|
|
17
|
+
const env = process.env;
|
|
18
|
+
return Boolean(env.CI === "true" || env.CI === "1" || env.GITHUB_ACTIONS === "true" || env.GITLAB_CI === "true" || env.CIRCLECI === "true" || env.TRAVIS === "true" || env.BUILDKITE === "true" || env.JENKINS_HOME);
|
|
19
|
+
}
|
|
20
|
+
function projectInfoToJson(p) {
|
|
21
|
+
return {
|
|
22
|
+
framework: p.framework,
|
|
23
|
+
rootDirectory: p.rootDirectory,
|
|
24
|
+
packageJsonPath: p.packageJsonPath,
|
|
25
|
+
vueVersion: p.vueVersion,
|
|
26
|
+
nuxtVersion: p.nuxtVersion,
|
|
27
|
+
nuxtCompatibilityVersion: p.nuxtCompatibilityVersion,
|
|
28
|
+
nitroPreset: p.nitroPreset,
|
|
29
|
+
typescriptVersion: p.typescriptVersion,
|
|
30
|
+
monorepoKind: p.monorepoKind,
|
|
31
|
+
hasAutoImports: p.hasAutoImports,
|
|
32
|
+
hasComponentsAutoImport: p.hasComponentsAutoImport,
|
|
33
|
+
hasPinia: p.hasPinia,
|
|
34
|
+
hasVueRouter: p.hasVueRouter,
|
|
35
|
+
capabilities: [...p.capabilities].sort()
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function renderInspect(p, rootDir) {
|
|
39
|
+
const lines = [];
|
|
40
|
+
lines.push(`@geoql/vue-doctor v${readVersion()} — project capabilities`);
|
|
41
|
+
lines.push("");
|
|
42
|
+
lines.push("framework");
|
|
43
|
+
lines.push(` ${p.framework}`);
|
|
44
|
+
lines.push(` vue: ${p.vueVersion ?? "(none)"}`);
|
|
45
|
+
lines.push(` typescript: ${p.typescriptVersion ?? "(none)"}`);
|
|
46
|
+
lines.push("");
|
|
47
|
+
lines.push("project layout");
|
|
48
|
+
lines.push(` rootDirectory: ${rootDir}`);
|
|
49
|
+
lines.push(` packageJsonPath: ${p.packageJsonPath ?? "(none)"}`);
|
|
50
|
+
lines.push(` monorepoKind: ${p.monorepoKind ?? "(none)"}`);
|
|
51
|
+
lines.push("");
|
|
52
|
+
lines.push("ecosystem");
|
|
53
|
+
lines.push(` hasAutoImports: ${p.hasAutoImports}`);
|
|
54
|
+
lines.push(` hasComponentsAutoImport: ${p.hasComponentsAutoImport}`);
|
|
55
|
+
lines.push(` hasPinia: ${p.hasPinia}`);
|
|
56
|
+
lines.push(` hasVueRouter: ${p.hasVueRouter}`);
|
|
57
|
+
lines.push("");
|
|
58
|
+
lines.push("capability tokens (used by rule gating)");
|
|
59
|
+
lines.push(` ${[...p.capabilities].sort().join("\n ")}`);
|
|
60
|
+
lines.push("");
|
|
61
|
+
lines.push("run 'vue-doctor inspect --json' for machine output");
|
|
62
|
+
lines.push("run 'vue-doctor list-rules' to see which rules these capabilities enable");
|
|
63
|
+
return `${lines.join("\n")}\n`;
|
|
64
|
+
}
|
|
65
|
+
function renderExplain(doc) {
|
|
66
|
+
const lines = [];
|
|
67
|
+
lines.push(`Rule: ${doc.id}`);
|
|
68
|
+
lines.push(`Severity: ${doc.severity}`);
|
|
69
|
+
lines.push(`Category: ${doc.category}`);
|
|
70
|
+
lines.push(`Source: ${doc.source}`);
|
|
71
|
+
lines.push(`Recommended: ${doc.recommended ? "yes" : "no (off in `recommended` preset)"}`);
|
|
72
|
+
lines.push(`Help: ${doc.helpUri}`);
|
|
73
|
+
lines.push("");
|
|
74
|
+
lines.push(doc.description);
|
|
75
|
+
return `${lines.join("\n")}\n`;
|
|
76
|
+
}
|
|
77
|
+
const VALID_LIST_PRESETS = new Set(["recommended", "all"]);
|
|
78
|
+
const VALID_CATEGORIES = new Set([
|
|
79
|
+
"ai-slop",
|
|
80
|
+
"reactivity",
|
|
81
|
+
"composition",
|
|
82
|
+
"performance",
|
|
83
|
+
"template",
|
|
84
|
+
"template-perf",
|
|
85
|
+
"build-quality",
|
|
86
|
+
"deps",
|
|
87
|
+
"dead-code",
|
|
88
|
+
"sfc",
|
|
89
|
+
"vue-builtin"
|
|
90
|
+
]);
|
|
91
|
+
const VALID_SOURCES = new Set([
|
|
92
|
+
"doctor",
|
|
93
|
+
"oxlint-builtin",
|
|
94
|
+
"eslint-plugin-vue"
|
|
95
|
+
]);
|
|
96
|
+
function buildListRulesFilter(flags) {
|
|
97
|
+
const out = {};
|
|
98
|
+
if (flags.preset !== void 0) {
|
|
99
|
+
if (!VALID_LIST_PRESETS.has(flags.preset)) throw new Error(`unknown --preset '${flags.preset}' (expected: recommended | all)`);
|
|
100
|
+
out.preset = flags.preset;
|
|
101
|
+
}
|
|
102
|
+
if (flags.category !== void 0) {
|
|
103
|
+
if (!VALID_CATEGORIES.has(flags.category)) throw new Error(`unknown --category '${flags.category}'`);
|
|
104
|
+
out.category = flags.category;
|
|
105
|
+
}
|
|
106
|
+
if (flags.source !== void 0) {
|
|
107
|
+
if (!VALID_SOURCES.has(flags.source)) throw new Error(`unknown --source '${flags.source}'`);
|
|
108
|
+
out.source = flags.source;
|
|
109
|
+
}
|
|
110
|
+
if (flags.severity !== void 0) {
|
|
111
|
+
if (flags.severity !== "error" && flags.severity !== "warn" && flags.severity !== "info") throw new Error(`unknown --severity '${flags.severity}' (expected: error | warn | info)`);
|
|
112
|
+
out.severity = flags.severity;
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function renderRulesTable(rules) {
|
|
117
|
+
if (rules.length === 0) return "No rules matched.\n";
|
|
118
|
+
const lines = [`@geoql/vue-doctor — ${rules.length} rule${rules.length === 1 ? "" : "s"}`, ""];
|
|
119
|
+
const idWidth = Math.max(...rules.map((r) => r.id.length));
|
|
120
|
+
const sevWidth = Math.max(...rules.map((r) => r.severity.length));
|
|
121
|
+
const catWidth = Math.max(...rules.map((r) => r.category.length));
|
|
122
|
+
for (const r of rules) {
|
|
123
|
+
const tag = r.recommended ? "[recommended]" : " ";
|
|
124
|
+
lines.push(` ${r.id.padEnd(idWidth)} ${r.severity.padEnd(sevWidth)} ${r.category.padEnd(catWidth)} ${r.source} ${tag}`);
|
|
125
|
+
}
|
|
126
|
+
return `${lines.join("\n")}\n`;
|
|
127
|
+
}
|
|
128
|
+
function toArray(value) {
|
|
129
|
+
if (value === void 0) return void 0;
|
|
130
|
+
return Array.isArray(value) ? value : [value];
|
|
131
|
+
}
|
|
132
|
+
function parseRuleOverrides(rules) {
|
|
133
|
+
if (!rules || rules.length === 0) return void 0;
|
|
134
|
+
const out = {};
|
|
135
|
+
for (const entry of rules) {
|
|
136
|
+
const idx = entry.lastIndexOf(":");
|
|
137
|
+
if (idx === -1) throw new Error(`Invalid --rule "${entry}". Expected format <ruleId>:<error|warn|info|off>.`);
|
|
138
|
+
const ruleId = entry.slice(0, idx);
|
|
139
|
+
const level = entry.slice(idx + 1);
|
|
140
|
+
if (level !== "error" && level !== "warn" && level !== "info" && level !== "off") throw new Error(`Invalid severity "${level}" for --rule "${entry}". Expected error|warn|info|off.`);
|
|
141
|
+
if (ruleId.length === 0) throw new Error(`Invalid --rule "${entry}". Rule id must not be empty.`);
|
|
142
|
+
out[ruleId] = level;
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
function resolveFormat(flags) {
|
|
147
|
+
if (flags.jsonCompact) return "json-compact";
|
|
148
|
+
if (flags.json) return "json";
|
|
149
|
+
const kind = flags.format;
|
|
150
|
+
if (kind === "pretty" || kind === "json" || kind === "json-compact" || kind === "sarif" || kind === "html") return kind;
|
|
151
|
+
if (typeof flags.output === "string" && flags.output.toLowerCase().endsWith(".html")) return "html";
|
|
152
|
+
return "agent";
|
|
153
|
+
}
|
|
154
|
+
function isFailOnLevel(v) {
|
|
155
|
+
return v === "error" || v === "warn" || v === "none";
|
|
11
156
|
}
|
|
12
157
|
async function run(argv = process.argv) {
|
|
13
158
|
const cli = cac("vue-doctor");
|
|
14
|
-
cli.command("[path]", "Audit a Vue project").option("--format <kind>", "Output format (
|
|
15
|
-
const reporter =
|
|
16
|
-
|
|
159
|
+
cli.command("[path]", "Audit a Vue project").option("--format <kind>", "Output format (agent|pretty|json|json-compact|sarif|html)", { default: "agent" }).option("--json", "Shorthand for --format json").option("--json-compact", "Emit single-line JSON").option("--config <path>", "Path to doctor.config.ts").option("--preset <name>", "Base preset: minimal|recommended|strict|all").option("--fail-on <level>", "Exit non-zero on this severity or worse (error|warn|none)", { default: "error" }).option("--quiet", "Only show the summary").option("--verbose", "Emit per-pass timing and rule diagnostics to stderr").option("--no-color", "Disable colored output").option("--rule <id:level>", "Override a rule (repeatable), e.g. --rule a/b:off").option("--include <glob>", "Glob of files to include (repeatable)").option("--exclude <glob>", "Glob of files to exclude (repeatable)").option("--no-dead-code", "Skip the dead-code (knip) analysis pass").option("--no-lint", "Skip the lint passes (template/SFC/oxlint)").option("--no-respect-inline-disables", "Surface findings even inside doctor-disable comments").option("--threshold <n>", "Minimum passing score (0-100)").option("--score", "Output only the numeric score (for piping)").option("--annotations", "Emit GitHub Actions ::error::/::warning:: lines").option("--ci", "Auto-enable CI behavior (--annotations on GitHub Actions)").option("--no-ci", "Disable CI auto-detection even when CI env is set").option("--diff", "Only report findings in files changed vs HEAD").option("--staged", "Only report findings in staged files").option("--full", "Force a complete scan (overrides --diff/--staged)").option("--output <file>", "Write the report to a file instead of stdout").action(async (path, flags) => {
|
|
160
|
+
const reporter = resolveFormat(flags);
|
|
161
|
+
if (flags.failOn !== void 0 && !isFailOnLevel(flags.failOn)) {
|
|
162
|
+
process.stderr.write(`vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\n`);
|
|
163
|
+
process.exitCode = 2;
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
17
166
|
const rootDir = resolve(path ?? ".");
|
|
18
167
|
try {
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
168
|
+
if (flags.score && (flags.json || flags.jsonCompact)) throw new Error("--score and --json are mutually exclusive (--score outputs a plaintext integer).");
|
|
169
|
+
const ruleOverrides = parseRuleOverrides(toArray(flags.rule));
|
|
170
|
+
const threshold = flags.threshold === void 0 ? void 0 : Number(flags.threshold);
|
|
171
|
+
if (threshold !== void 0 && (!Number.isInteger(threshold) || threshold < 0 || threshold > 100)) throw new Error(`--threshold must be an integer 0-100, got "${flags.threshold}".`);
|
|
172
|
+
if (flags.diff && flags.staged) throw new Error("--diff and --staged are mutually exclusive.");
|
|
173
|
+
if (flags.verbose && flags.quiet) throw new Error("--verbose and --quiet are mutually exclusive.");
|
|
174
|
+
let scopeFiles;
|
|
175
|
+
if (!flags.full && (flags.diff || flags.staged)) scopeFiles = await listChangedFiles({
|
|
22
176
|
rootDir,
|
|
23
|
-
|
|
177
|
+
mode: flags.staged ? "staged" : "diff"
|
|
178
|
+
});
|
|
179
|
+
const resolved = await loadDoctorConfig(rootDir, {
|
|
180
|
+
...flags.config ? { explicitPath: flags.config } : {},
|
|
181
|
+
...flags.preset ? { presetOverride: flags.preset } : {}
|
|
24
182
|
});
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
183
|
+
const merged = mergeCliOverrides(resolved, {
|
|
184
|
+
failOn: flags.failOn,
|
|
185
|
+
include: toArray(flags.include),
|
|
186
|
+
exclude: toArray(flags.exclude),
|
|
187
|
+
rules: ruleOverrides,
|
|
188
|
+
threshold
|
|
189
|
+
});
|
|
190
|
+
const report = await audit({
|
|
191
|
+
rootDir: merged.rootDir,
|
|
192
|
+
include: merged.include,
|
|
193
|
+
exclude: merged.exclude,
|
|
194
|
+
rules: merged.rules,
|
|
195
|
+
failOn: merged.failOn,
|
|
196
|
+
threshold: merged.threshold,
|
|
197
|
+
deadCode: flags.deadCode,
|
|
198
|
+
lint: flags.lint,
|
|
199
|
+
respectInlineDisables: flags.respectInlineDisables,
|
|
200
|
+
scopeFiles
|
|
201
|
+
});
|
|
202
|
+
const allowedRuleIds = new Set(Object.keys(merged.rules));
|
|
203
|
+
report.diagnostics = report.diagnostics.filter((d) => allowedRuleIds.has(d.ruleId));
|
|
204
|
+
if (flags.verbose) {
|
|
205
|
+
const verboseOutput = renderVerboseTrace(report, { configSource: resolved.source });
|
|
206
|
+
process.stderr.write(`${verboseOutput}\n`);
|
|
207
|
+
}
|
|
208
|
+
if (flags.score) {
|
|
209
|
+
process.stdout.write(`${report.score}\n`);
|
|
210
|
+
process.exitCode = report.exitCode;
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const out = format({
|
|
214
|
+
toolName: "@geoql/vue-doctor",
|
|
215
|
+
toolVersion: readVersion(),
|
|
216
|
+
rootDirectory: report.rootDir,
|
|
217
|
+
analyzedFileCount: report.filesScanned,
|
|
218
|
+
elapsedMs: report.elapsedMs,
|
|
219
|
+
diagnostics: report.diagnostics,
|
|
220
|
+
score: report.scoreResult,
|
|
221
|
+
projectInfo: report.projectInfo
|
|
222
|
+
}, reporter, {
|
|
223
|
+
color: flags.color,
|
|
224
|
+
quiet: flags.quiet
|
|
225
|
+
});
|
|
226
|
+
if (flags.output) writeFileSync(resolve(flags.output), out);
|
|
227
|
+
else process.stdout.write(out);
|
|
228
|
+
const ciExplicitOptOut = flags.ci === false;
|
|
229
|
+
const ciExplicitOptIn = flags.ci === true;
|
|
230
|
+
const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();
|
|
231
|
+
if ((flags.annotations === true || ciExplicitOptIn || ciAutoDetected) && (reporter === "agent" || reporter === "pretty") && !flags.quiet && report.diagnostics.length > 0) process.stdout.write(`${encodeAnnotations(report.diagnostics)}\n`);
|
|
28
232
|
process.exitCode = report.exitCode;
|
|
29
233
|
} catch (err) {
|
|
30
234
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -32,8 +236,46 @@ async function run(argv = process.argv) {
|
|
|
32
236
|
process.exitCode = 2;
|
|
33
237
|
}
|
|
34
238
|
});
|
|
239
|
+
cli.command("list-rules", "List every registered rule with id, severity, category, source, and preset membership").option("--preset <name>", "Filter to: recommended | all").option("--category <name>", "Filter by category").option("--source <name>", "Filter by source: doctor | oxlint-builtin | eslint-plugin-vue").option("--severity <level>", "Filter by: error | warn | info").option("--json", "Emit JSON instead of formatted text").option("--json-compact", "With --json, single-line output").action(async (flags) => {
|
|
240
|
+
try {
|
|
241
|
+
const rules = listRules(buildListRulesFilter(flags));
|
|
242
|
+
if (flags.json || flags.jsonCompact) {
|
|
243
|
+
const payload = {
|
|
244
|
+
count: rules.length,
|
|
245
|
+
rules
|
|
246
|
+
};
|
|
247
|
+
process.stdout.write(flags.jsonCompact ? JSON.stringify(payload) : `${JSON.stringify(payload, null, 2)}\n`);
|
|
248
|
+
} else process.stdout.write(renderRulesTable(rules));
|
|
249
|
+
process.exitCode = 0;
|
|
250
|
+
} catch (err) {
|
|
251
|
+
const msg = err.message;
|
|
252
|
+
process.stderr.write(`vue-doctor list-rules: ${msg}\n`);
|
|
253
|
+
process.exitCode = 2;
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
cli.command("explain <ruleId>", "Print the rule's severity, category, recommendation, and helpUri").option("--json", "Emit structured JSON instead of formatted text").action(async (ruleId, flags) => {
|
|
257
|
+
const doc = loadRuleDoc(ruleId);
|
|
258
|
+
if (!doc) {
|
|
259
|
+
process.stderr.write(`vue-doctor explain: unknown rule '${ruleId}'. Try \`vue-doctor list-rules\` to see registered rules.\n`);
|
|
260
|
+
process.exitCode = 2;
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (flags.json) process.stdout.write(`${JSON.stringify(doc, null, 2)}\n`);
|
|
264
|
+
else process.stdout.write(renderExplain(doc));
|
|
265
|
+
process.exitCode = 0;
|
|
266
|
+
});
|
|
267
|
+
cli.command("inspect [dir]", "Print the detected project capabilities doctor uses to gate rules").option("--json", "Emit structured JSON instead of formatted text").option("--json-compact", "With --json, emit a single-line payload").action(async (dir, flags) => {
|
|
268
|
+
const rootDir = resolve(dir ?? ".");
|
|
269
|
+
const project = await detectProject(rootDir);
|
|
270
|
+
if (flags.json || flags.jsonCompact) {
|
|
271
|
+
const payload = projectInfoToJson(project);
|
|
272
|
+
const out = flags.jsonCompact ? JSON.stringify(payload) : JSON.stringify(payload, null, 2);
|
|
273
|
+
process.stdout.write(`${out}\n`);
|
|
274
|
+
} else process.stdout.write(renderInspect(project, rootDir));
|
|
275
|
+
process.exitCode = 0;
|
|
276
|
+
});
|
|
35
277
|
cli.help();
|
|
36
|
-
cli.version(
|
|
278
|
+
cli.version(readVersion());
|
|
37
279
|
cli.parse(argv, { run: false });
|
|
38
280
|
await cli.runMatchedCommand();
|
|
39
281
|
return process.exitCode ?? 0;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport process from 'node:process';\nimport {\n audit,\n format,\n loadAuditConfig,\n type ReporterFormat,\n} from '@geoql/doctor-core';\nimport { cac } from 'cac';\n\ninterface CliFlags {\n format?: string;\n config?: string;\n failOn?: string;\n}\n\nfunction isReporter(v: string): v is ReporterFormat {\n return v === 'text' || v === 'json';\n}\n\nfunction isSeverity(v: string): v is 'error' | 'warning' {\n return v === 'error' || v === 'warning';\n}\n\nexport async function run(argv: string[] = process.argv): Promise<number> {\n const cli = cac('vue-doctor');\n\n cli\n .command('[path]', 'Audit a Vue project')\n .option('--format <kind>', 'Output format (text|json)', { default: 'text' })\n .option('--config <path>', 'Path to doctor.config.ts')\n .option(\n '--fail-on <level>',\n 'Exit non-zero on this severity or worse (error|warning)',\n {\n default: 'error',\n },\n )\n .action(async (path: string | undefined, flags: CliFlags) => {\n const reporter =\n flags.format && isReporter(flags.format) ? flags.format : 'text';\n const failOn =\n flags.failOn && isSeverity(flags.failOn) ? flags.failOn : 'error';\n const rootDir = resolve(path ?? '.');\n\n try {\n const { config } = await loadAuditConfig(rootDir, flags.config);\n const report = await audit({ ...config, rootDir, failOn });\n const out = format(report, reporter);\n process.stdout.write(out);\n process.stdout.write('\\n');\n process.exitCode = report.exitCode;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`vue-doctor: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli.help();\n cli.version('0.0.0');\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n return process.exitCode ?? 0;\n}\n"],"mappings":";;;;;AAgBA,SAAS,WAAW,GAAgC;CAClD,OAAO,MAAM,UAAU,MAAM;;AAG/B,SAAS,WAAW,GAAqC;CACvD,OAAO,MAAM,WAAW,MAAM;;AAGhC,eAAsB,IAAI,OAAiB,QAAQ,MAAuB;CACxE,MAAM,MAAM,IAAI,aAAa;CAE7B,IACG,QAAQ,UAAU,sBAAsB,CACxC,OAAO,mBAAmB,6BAA6B,EAAE,SAAS,QAAQ,CAAC,CAC3E,OAAO,mBAAmB,2BAA2B,CACrD,OACC,qBACA,2DACA,EACE,SAAS,SACV,CACF,CACA,OAAO,OAAO,MAA0B,UAAoB;EAC3D,MAAM,WACJ,MAAM,UAAU,WAAW,MAAM,OAAO,GAAG,MAAM,SAAS;EAC5D,MAAM,SACJ,MAAM,UAAU,WAAW,MAAM,OAAO,GAAG,MAAM,SAAS;EAC5D,MAAM,UAAU,QAAQ,QAAQ,IAAI;EAEpC,IAAI;GACF,MAAM,EAAE,WAAW,MAAM,gBAAgB,SAAS,MAAM,OAAO;GAC/D,MAAM,SAAS,MAAM,MAAM;IAAE,GAAG;IAAQ;IAAS;IAAQ,CAAC;GAC1D,MAAM,MAAM,OAAO,QAAQ,SAAS;GACpC,QAAQ,OAAO,MAAM,IAAI;GACzB,QAAQ,OAAO,MAAM,KAAK;GAC1B,QAAQ,WAAW,OAAO;WACnB,KAAK;GACZ,MAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;GAC5D,QAAQ,OAAO,MAAM,eAAe,IAAI,IAAI;GAC5C,QAAQ,WAAW;;GAErB;CAEJ,IAAI,MAAM;CACV,IAAI,QAAQ,QAAQ;CACpB,IAAI,MAAM,MAAM,EAAE,KAAK,OAAO,CAAC;CAC/B,MAAM,IAAI,mBAAmB;CAC7B,OAAO,QAAQ,YAAY"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["import { readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport process from 'node:process';\nimport { fileURLToPath } from 'node:url';\nimport {\n audit,\n detectProject,\n encodeAnnotations,\n format,\n listChangedFiles,\n listRules,\n loadDoctorConfig,\n loadRuleDoc,\n mergeCliOverrides,\n renderVerboseTrace,\n type ListRulesFilter,\n type ProjectInfo,\n type RegisteredRule,\n type ReporterFormat,\n type ReporterInput,\n type RuleCategory,\n type RuleSource,\n type Severity,\n} from '@geoql/doctor-core';\nimport { cac } from 'cac';\n\nfunction readVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n const pkg = JSON.parse(\n readFileSync(resolve(here, '../package.json'), 'utf-8'),\n ) as { version?: string };\n return pkg.version ?? '0.0.0';\n } catch {\n return '0.0.0';\n }\n}\n\ninterface ListRulesCliFlags {\n preset?: string;\n category?: string;\n source?: string;\n severity?: string;\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface ExplainCliFlags {\n json?: boolean;\n}\n\ninterface InspectCliFlags {\n json?: boolean;\n jsonCompact?: boolean;\n}\n\ninterface ProjectInfoJsonPayload {\n framework: string;\n rootDirectory: string;\n packageJsonPath: string | null;\n vueVersion: string | null;\n nuxtVersion: string | null;\n nuxtCompatibilityVersion: 3 | 4 | null;\n nitroPreset: string | null;\n typescriptVersion: string | null;\n monorepoKind: string | null;\n hasAutoImports: boolean;\n hasComponentsAutoImport: boolean;\n hasPinia: boolean;\n hasVueRouter: boolean;\n capabilities: string[];\n}\n\nfunction isCiEnvironment(): boolean {\n const env = process.env;\n return Boolean(\n env.CI === 'true' ||\n env.CI === '1' ||\n env.GITHUB_ACTIONS === 'true' ||\n env.GITLAB_CI === 'true' ||\n env.CIRCLECI === 'true' ||\n env.TRAVIS === 'true' ||\n env.BUILDKITE === 'true' ||\n env.JENKINS_HOME,\n );\n}\n\nfunction projectInfoToJson(p: ProjectInfo): ProjectInfoJsonPayload {\n return {\n framework: p.framework,\n rootDirectory: p.rootDirectory,\n packageJsonPath: p.packageJsonPath,\n vueVersion: p.vueVersion,\n nuxtVersion: p.nuxtVersion,\n nuxtCompatibilityVersion: p.nuxtCompatibilityVersion,\n nitroPreset: p.nitroPreset,\n typescriptVersion: p.typescriptVersion,\n monorepoKind: p.monorepoKind,\n hasAutoImports: p.hasAutoImports,\n hasComponentsAutoImport: p.hasComponentsAutoImport,\n hasPinia: p.hasPinia,\n hasVueRouter: p.hasVueRouter,\n capabilities: [...p.capabilities].sort(),\n };\n}\n\nfunction renderInspect(p: ProjectInfo, rootDir: string): string {\n const lines: string[] = [];\n lines.push(`@geoql/vue-doctor v${readVersion()} — project capabilities`);\n lines.push('');\n lines.push('framework');\n lines.push(` ${p.framework}`);\n lines.push(` vue: ${p.vueVersion ?? '(none)'}`);\n lines.push(` typescript: ${p.typescriptVersion ?? '(none)'}`);\n lines.push('');\n lines.push('project layout');\n lines.push(` rootDirectory: ${rootDir}`);\n lines.push(` packageJsonPath: ${p.packageJsonPath ?? '(none)'}`);\n lines.push(` monorepoKind: ${p.monorepoKind ?? '(none)'}`);\n lines.push('');\n lines.push('ecosystem');\n lines.push(` hasAutoImports: ${p.hasAutoImports}`);\n lines.push(` hasComponentsAutoImport: ${p.hasComponentsAutoImport}`);\n lines.push(` hasPinia: ${p.hasPinia}`);\n lines.push(` hasVueRouter: ${p.hasVueRouter}`);\n lines.push('');\n lines.push('capability tokens (used by rule gating)');\n lines.push(` ${[...p.capabilities].sort().join('\\n ')}`);\n lines.push('');\n lines.push(\"run 'vue-doctor inspect --json' for machine output\");\n lines.push(\n \"run 'vue-doctor list-rules' to see which rules these capabilities enable\",\n );\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface ExplainableDoc {\n id: string;\n severity: string;\n category: string;\n source: string;\n recommended: boolean;\n helpUri: string;\n description: string;\n hasOverride: boolean;\n}\n\nfunction renderExplain(doc: ExplainableDoc): string {\n const lines: string[] = [];\n lines.push(`Rule: ${doc.id}`);\n lines.push(`Severity: ${doc.severity}`);\n lines.push(`Category: ${doc.category}`);\n lines.push(`Source: ${doc.source}`);\n lines.push(\n `Recommended: ${doc.recommended ? 'yes' : 'no (off in `recommended` preset)'}`,\n );\n lines.push(`Help: ${doc.helpUri}`);\n lines.push('');\n lines.push(doc.description);\n return `${lines.join('\\n')}\\n`;\n}\n\nconst VALID_LIST_PRESETS = new Set(['recommended', 'all']);\nconst VALID_CATEGORIES = new Set<RuleCategory>([\n 'ai-slop',\n 'reactivity',\n 'composition',\n 'performance',\n 'template',\n 'template-perf',\n 'build-quality',\n 'deps',\n 'dead-code',\n 'sfc',\n 'vue-builtin',\n]);\nconst VALID_SOURCES = new Set<RuleSource>([\n 'doctor',\n 'oxlint-builtin',\n 'eslint-plugin-vue',\n]);\n\nfunction buildListRulesFilter(flags: ListRulesCliFlags): ListRulesFilter {\n const out: {\n preset?: 'recommended' | 'all';\n category?: RuleCategory;\n source?: RuleSource;\n severity?: Severity;\n } = {};\n if (flags.preset !== undefined) {\n if (!VALID_LIST_PRESETS.has(flags.preset)) {\n throw new Error(\n `unknown --preset '${flags.preset}' (expected: recommended | all)`,\n );\n }\n out.preset = flags.preset as 'recommended' | 'all';\n }\n if (flags.category !== undefined) {\n if (!VALID_CATEGORIES.has(flags.category as RuleCategory)) {\n throw new Error(`unknown --category '${flags.category}'`);\n }\n out.category = flags.category as RuleCategory;\n }\n if (flags.source !== undefined) {\n if (!VALID_SOURCES.has(flags.source as RuleSource)) {\n throw new Error(`unknown --source '${flags.source}'`);\n }\n out.source = flags.source as RuleSource;\n }\n if (flags.severity !== undefined) {\n if (\n flags.severity !== 'error' &&\n flags.severity !== 'warn' &&\n flags.severity !== 'info'\n ) {\n throw new Error(\n `unknown --severity '${flags.severity}' (expected: error | warn | info)`,\n );\n }\n out.severity = flags.severity;\n }\n return out;\n}\n\nfunction renderRulesTable(rules: RegisteredRule[]): string {\n if (rules.length === 0) return 'No rules matched.\\n';\n const lines: string[] = [\n `@geoql/vue-doctor — ${rules.length} rule${rules.length === 1 ? '' : 's'}`,\n '',\n ];\n const idWidth = Math.max(...rules.map((r) => r.id.length));\n const sevWidth = Math.max(...rules.map((r) => r.severity.length));\n const catWidth = Math.max(...rules.map((r) => r.category.length));\n for (const r of rules) {\n const tag = r.recommended ? '[recommended]' : ' ';\n lines.push(\n ` ${r.id.padEnd(idWidth)} ${r.severity.padEnd(sevWidth)} ${r.category.padEnd(catWidth)} ${r.source} ${tag}`,\n );\n }\n return `${lines.join('\\n')}\\n`;\n}\n\ninterface CliFlags {\n format?: string;\n config?: string;\n preset?: string;\n failOn?: string;\n json?: boolean;\n jsonCompact?: boolean;\n color?: boolean;\n quiet?: boolean;\n verbose?: boolean;\n output?: string;\n rule?: string | string[];\n include?: string | string[];\n exclude?: string | string[];\n deadCode?: boolean;\n lint?: boolean;\n respectInlineDisables?: boolean;\n threshold?: string;\n score?: boolean;\n annotations?: boolean;\n ci?: boolean;\n diff?: boolean;\n staged?: boolean;\n full?: boolean;\n}\n\nfunction toArray(value: string | string[] | undefined): string[] | undefined {\n if (value === undefined) return undefined;\n return Array.isArray(value) ? value : [value];\n}\n\nfunction parseRuleOverrides(\n rules: string[] | undefined,\n): Record<string, Severity | 'off'> | undefined {\n if (!rules || rules.length === 0) return undefined;\n const out: Record<string, Severity | 'off'> = {};\n for (const entry of rules) {\n const idx = entry.lastIndexOf(':');\n if (idx === -1) {\n throw new Error(\n `Invalid --rule \"${entry}\". Expected format <ruleId>:<error|warn|info|off>.`,\n );\n }\n const ruleId = entry.slice(0, idx);\n const level = entry.slice(idx + 1);\n if (\n level !== 'error' &&\n level !== 'warn' &&\n level !== 'info' &&\n level !== 'off'\n ) {\n throw new Error(\n `Invalid severity \"${level}\" for --rule \"${entry}\". Expected error|warn|info|off.`,\n );\n }\n if (ruleId.length === 0) {\n throw new Error(`Invalid --rule \"${entry}\". Rule id must not be empty.`);\n }\n out[ruleId] = level;\n }\n return out;\n}\n\nfunction resolveFormat(flags: CliFlags): ReporterFormat {\n if (flags.jsonCompact) return 'json-compact';\n if (flags.json) return 'json';\n const kind = flags.format;\n const userPickedFormat =\n kind === 'pretty' ||\n kind === 'json' ||\n kind === 'json-compact' ||\n kind === 'sarif' ||\n kind === 'html';\n if (userPickedFormat) {\n return kind;\n }\n if (\n typeof flags.output === 'string' &&\n flags.output.toLowerCase().endsWith('.html')\n ) {\n return 'html';\n }\n return 'agent';\n}\n\nfunction isFailOnLevel(v: string): v is 'error' | 'warn' | 'none' {\n return v === 'error' || v === 'warn' || v === 'none';\n}\n\nexport async function run(argv: string[] = process.argv): Promise<number> {\n const cli = cac('vue-doctor');\n\n cli\n .command('[path]', 'Audit a Vue project')\n .option(\n '--format <kind>',\n 'Output format (agent|pretty|json|json-compact|sarif|html)',\n {\n default: 'agent',\n },\n )\n .option('--json', 'Shorthand for --format json')\n .option('--json-compact', 'Emit single-line JSON')\n .option('--config <path>', 'Path to doctor.config.ts')\n .option('--preset <name>', 'Base preset: minimal|recommended|strict|all')\n .option(\n '--fail-on <level>',\n 'Exit non-zero on this severity or worse (error|warn|none)',\n {\n default: 'error',\n },\n )\n .option('--quiet', 'Only show the summary')\n .option('--verbose', 'Emit per-pass timing and rule diagnostics to stderr')\n .option('--no-color', 'Disable colored output')\n .option(\n '--rule <id:level>',\n 'Override a rule (repeatable), e.g. --rule a/b:off',\n )\n .option('--include <glob>', 'Glob of files to include (repeatable)')\n .option('--exclude <glob>', 'Glob of files to exclude (repeatable)')\n .option('--no-dead-code', 'Skip the dead-code (knip) analysis pass')\n .option('--no-lint', 'Skip the lint passes (template/SFC/oxlint)')\n .option(\n '--no-respect-inline-disables',\n 'Surface findings even inside doctor-disable comments',\n )\n .option('--threshold <n>', 'Minimum passing score (0-100)')\n .option('--score', 'Output only the numeric score (for piping)')\n .option('--annotations', 'Emit GitHub Actions ::error::/::warning:: lines')\n .option('--ci', 'Auto-enable CI behavior (--annotations on GitHub Actions)')\n .option('--no-ci', 'Disable CI auto-detection even when CI env is set')\n .option('--diff', 'Only report findings in files changed vs HEAD')\n .option('--staged', 'Only report findings in staged files')\n .option('--full', 'Force a complete scan (overrides --diff/--staged)')\n .option('--output <file>', 'Write the report to a file instead of stdout')\n .action(async (path: string | undefined, flags: CliFlags) => {\n const reporter = resolveFormat(flags);\n if (flags.failOn !== undefined && !isFailOnLevel(flags.failOn)) {\n process.stderr.write(\n `vue-doctor: --fail-on must be 'error', 'warn', or 'none', got '${flags.failOn}'\\n`,\n );\n process.exitCode = 2;\n return;\n }\n const rootDir = resolve(path ?? '.');\n\n try {\n if (flags.score && (flags.json || flags.jsonCompact)) {\n throw new Error(\n '--score and --json are mutually exclusive (--score outputs a plaintext integer).',\n );\n }\n const ruleOverrides = parseRuleOverrides(toArray(flags.rule));\n const threshold =\n flags.threshold === undefined ? undefined : Number(flags.threshold);\n if (\n threshold !== undefined &&\n (!Number.isInteger(threshold) || threshold < 0 || threshold > 100)\n ) {\n throw new Error(\n `--threshold must be an integer 0-100, got \"${flags.threshold}\".`,\n );\n }\n if (flags.diff && flags.staged) {\n throw new Error('--diff and --staged are mutually exclusive.');\n }\n if (flags.verbose && flags.quiet) {\n throw new Error('--verbose and --quiet are mutually exclusive.');\n }\n let scopeFiles: string[] | undefined;\n if (!flags.full && (flags.diff || flags.staged)) {\n scopeFiles = await listChangedFiles({\n rootDir,\n mode: flags.staged ? 'staged' : 'diff',\n });\n }\n const resolved = await loadDoctorConfig(rootDir, {\n ...(flags.config ? { explicitPath: flags.config } : {}),\n ...(flags.preset ? { presetOverride: flags.preset } : {}),\n });\n const merged = mergeCliOverrides(resolved, {\n failOn: flags.failOn as 'error' | 'warn' | 'none' | undefined,\n include: toArray(flags.include),\n exclude: toArray(flags.exclude),\n rules: ruleOverrides,\n threshold,\n });\n const report = await audit({\n rootDir: merged.rootDir,\n include: merged.include,\n exclude: merged.exclude,\n rules: merged.rules,\n failOn: merged.failOn,\n threshold: merged.threshold,\n deadCode: flags.deadCode,\n lint: flags.lint,\n respectInlineDisables: flags.respectInlineDisables,\n scopeFiles,\n });\n const allowedRuleIds = new Set(Object.keys(merged.rules));\n report.diagnostics = report.diagnostics.filter((d) =>\n allowedRuleIds.has(d.ruleId),\n );\n if (flags.verbose) {\n const verboseOutput = renderVerboseTrace(report, {\n configSource: resolved.source,\n });\n process.stderr.write(`${verboseOutput}\\n`);\n }\n if (flags.score) {\n process.stdout.write(`${report.score}\\n`);\n process.exitCode = report.exitCode;\n return;\n }\n const input: ReporterInput = {\n toolName: '@geoql/vue-doctor',\n toolVersion: readVersion(),\n rootDirectory: report.rootDir,\n analyzedFileCount: report.filesScanned,\n elapsedMs: report.elapsedMs,\n diagnostics: report.diagnostics,\n score: report.scoreResult,\n projectInfo: report.projectInfo,\n };\n const out = format(input, reporter, {\n color: flags.color,\n quiet: flags.quiet,\n });\n if (flags.output) {\n writeFileSync(resolve(flags.output), out);\n } else {\n process.stdout.write(out);\n }\n const ciExplicitOptOut = flags.ci === false;\n const ciExplicitOptIn = flags.ci === true;\n const ciAutoDetected = !ciExplicitOptOut && isCiEnvironment();\n const wantsAnnotations =\n flags.annotations === true || ciExplicitOptIn || ciAutoDetected;\n const reporterCarriesAnnotations =\n reporter === 'agent' || reporter === 'pretty';\n if (\n wantsAnnotations &&\n reporterCarriesAnnotations &&\n !flags.quiet &&\n report.diagnostics.length > 0\n ) {\n process.stdout.write(`${encodeAnnotations(report.diagnostics)}\\n`);\n }\n process.exitCode = report.exitCode;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`vue-doctor: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'list-rules',\n 'List every registered rule with id, severity, category, source, and preset membership',\n )\n .option('--preset <name>', 'Filter to: recommended | all')\n .option('--category <name>', 'Filter by category')\n .option(\n '--source <name>',\n 'Filter by source: doctor | oxlint-builtin | eslint-plugin-vue',\n )\n .option('--severity <level>', 'Filter by: error | warn | info')\n .option('--json', 'Emit JSON instead of formatted text')\n .option('--json-compact', 'With --json, single-line output')\n .action(async (flags: ListRulesCliFlags) => {\n try {\n const filter = buildListRulesFilter(flags);\n const rules = listRules(filter);\n if (flags.json || flags.jsonCompact) {\n const payload = { count: rules.length, rules };\n process.stdout.write(\n flags.jsonCompact\n ? JSON.stringify(payload)\n : `${JSON.stringify(payload, null, 2)}\\n`,\n );\n } else {\n process.stdout.write(renderRulesTable(rules));\n }\n process.exitCode = 0;\n } catch (err) {\n const msg = (err as Error).message;\n process.stderr.write(`vue-doctor list-rules: ${msg}\\n`);\n process.exitCode = 2;\n }\n });\n\n cli\n .command(\n 'explain <ruleId>',\n \"Print the rule's severity, category, recommendation, and helpUri\",\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .action(async (ruleId: string, flags: ExplainCliFlags) => {\n const doc = loadRuleDoc(ruleId);\n if (!doc) {\n process.stderr.write(\n `vue-doctor explain: unknown rule '${ruleId}'. Try \\`vue-doctor list-rules\\` to see registered rules.\\n`,\n );\n process.exitCode = 2;\n return;\n }\n if (flags.json) {\n process.stdout.write(`${JSON.stringify(doc, null, 2)}\\n`);\n } else {\n process.stdout.write(renderExplain(doc));\n }\n process.exitCode = 0;\n });\n\n cli\n .command(\n 'inspect [dir]',\n 'Print the detected project capabilities doctor uses to gate rules',\n )\n .option('--json', 'Emit structured JSON instead of formatted text')\n .option('--json-compact', 'With --json, emit a single-line payload')\n .action(async (dir: string | undefined, flags: InspectCliFlags) => {\n const rootDir = resolve(dir ?? '.');\n const project = await detectProject(rootDir);\n if (flags.json || flags.jsonCompact) {\n const payload = projectInfoToJson(project);\n const out = flags.jsonCompact\n ? JSON.stringify(payload)\n : JSON.stringify(payload, null, 2);\n process.stdout.write(`${out}\\n`);\n } else {\n process.stdout.write(renderInspect(project, rootDir));\n }\n process.exitCode = 0;\n });\n\n cli.help();\n cli.version(readVersion());\n cli.parse(argv, { run: false });\n await cli.runMatchedCommand();\n return process.exitCode ?? 0;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geoql/vue-doctor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CLI auditor for Vue 3 apps. Detects anti-patterns, AI-slop, and best-practice violations via @vue/compiler-sfc + oxlint. Run with `npx -y @geoql/vue-doctor`.",
|
|
6
6
|
"keywords": [
|
|
@@ -42,27 +42,34 @@
|
|
|
42
42
|
"types": "./dist/index.d.ts"
|
|
43
43
|
}
|
|
44
44
|
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
45
48
|
"dependencies": {
|
|
46
49
|
"cac": "^7.0.0",
|
|
47
50
|
"kolorist": "^1.8.0",
|
|
48
51
|
"oxlint": "^1.67.0",
|
|
49
|
-
"@geoql/doctor-core": "^0.1.
|
|
50
|
-
"@geoql/oxlint-plugin-vue-doctor": "0.1.
|
|
52
|
+
"@geoql/doctor-core": "^0.1.1",
|
|
53
|
+
"@geoql/oxlint-plugin-vue-doctor": "0.1.1"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"@types/node": "^25.9.1",
|
|
57
|
+
"@vue/compiler-core": "^3.5.35",
|
|
58
|
+
"@vue/compiler-sfc": "^3.5.35",
|
|
59
|
+
"c12": "^4.0.0-beta.5",
|
|
60
|
+
"oxc-parser": "0.133.0",
|
|
61
|
+
"picocolors": "^1.1.1",
|
|
62
|
+
"semver": "^7.8.1",
|
|
63
|
+
"tinyglobby": "^0.2.16",
|
|
54
64
|
"typescript": "^6.0.3",
|
|
55
65
|
"vitest": "^4.1.7"
|
|
56
66
|
},
|
|
57
|
-
"publishConfig": {
|
|
58
|
-
"access": "public"
|
|
59
|
-
},
|
|
60
67
|
"scripts": {
|
|
61
68
|
"lint": "vp lint src",
|
|
62
69
|
"lint:fix": "vp lint src --fix",
|
|
63
70
|
"format": "vp fmt",
|
|
64
71
|
"format:check": "vp fmt --check",
|
|
65
72
|
"build": "vp pack",
|
|
66
|
-
"test": "vitest run"
|
|
73
|
+
"test": "vitest run --passWithNoTests"
|
|
67
74
|
}
|
|
68
75
|
}
|