@geoql/doctor-core 1.3.3 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +45 -1
- package/dist/index.js +253 -53
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,7 @@ type Capability = string;
|
|
|
70
70
|
type MonorepoKind = 'pnpm' | 'yarn' | 'npm' | 'turbo' | null;
|
|
71
71
|
interface ProjectInfo {
|
|
72
72
|
readonly framework: Framework;
|
|
73
|
+
readonly frameworkDetected: boolean;
|
|
73
74
|
readonly rootDirectory: string;
|
|
74
75
|
readonly packageJsonPath: string | null;
|
|
75
76
|
readonly vueVersion: string | null;
|
|
@@ -94,6 +95,7 @@ interface ProjectInfo {
|
|
|
94
95
|
//#region src/reporters/types.d.ts
|
|
95
96
|
interface ProjectInfoLite {
|
|
96
97
|
readonly framework: Framework;
|
|
98
|
+
readonly frameworkDetected: boolean;
|
|
97
99
|
readonly vueVersion: string | null;
|
|
98
100
|
readonly nuxtVersion: string | null;
|
|
99
101
|
readonly capabilities: readonly string[];
|
|
@@ -108,6 +110,8 @@ interface ReporterInput {
|
|
|
108
110
|
readonly diagnostics: readonly Diagnostic[];
|
|
109
111
|
readonly score: ScoreResult;
|
|
110
112
|
readonly projectInfo: ProjectInfoLite;
|
|
113
|
+
readonly incomplete?: boolean;
|
|
114
|
+
readonly skippedCheckReasons?: readonly SkippedCheckReason[];
|
|
111
115
|
}
|
|
112
116
|
interface ReporterOptions {
|
|
113
117
|
readonly color?: boolean;
|
|
@@ -143,6 +147,13 @@ interface AuditConfig {
|
|
|
143
147
|
respectInlineDisables?: boolean;
|
|
144
148
|
fix?: boolean;
|
|
145
149
|
fixExcludes?: string[];
|
|
150
|
+
maxDurationMs?: number;
|
|
151
|
+
}
|
|
152
|
+
interface SkippedCheckReason {
|
|
153
|
+
kind: 'time-budget-exhausted';
|
|
154
|
+
deadlineMs: number;
|
|
155
|
+
elapsedMs: number;
|
|
156
|
+
skippedPasses: readonly string[];
|
|
146
157
|
}
|
|
147
158
|
interface AuditTimings {
|
|
148
159
|
template: number;
|
|
@@ -165,6 +176,8 @@ interface AuditReport {
|
|
|
165
176
|
elapsedMs: number;
|
|
166
177
|
timings: AuditTimings;
|
|
167
178
|
ruleCounts: Record<string, number>;
|
|
179
|
+
incomplete: boolean;
|
|
180
|
+
skippedCheckReasons?: readonly SkippedCheckReason[];
|
|
168
181
|
}
|
|
169
182
|
//#endregion
|
|
170
183
|
//#region src/annotations.d.ts
|
|
@@ -392,6 +405,34 @@ declare function renderDetectSummary(project: ProjectInfo, sfcCount: number): st
|
|
|
392
405
|
declare function detectSummary(dir: string): Promise<string>;
|
|
393
406
|
declare function planInit(options: InitOptions): Promise<InitPlan>;
|
|
394
407
|
//#endregion
|
|
408
|
+
//#region src/ci/provider-detect.d.ts
|
|
409
|
+
type CiProvider = 'github' | 'gitlab' | 'unknown';
|
|
410
|
+
/**
|
|
411
|
+
* Detects the CI provider from an explicit env snapshot. Takes the env as an
|
|
412
|
+
* argument (instead of reading process.env) so provider detection stays a
|
|
413
|
+
* deterministic, fully-testable pure function.
|
|
414
|
+
*/
|
|
415
|
+
declare function detectCiProvider(env: Readonly<Record<string, string | undefined>>): CiProvider;
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/ci/scaffold-workflow.d.ts
|
|
418
|
+
interface CiScaffoldOptions {
|
|
419
|
+
bin: 'vue-doctor' | 'nuxt-doctor';
|
|
420
|
+
framework: 'vue' | 'nuxt';
|
|
421
|
+
dir: string;
|
|
422
|
+
provider?: 'github' | 'gitlab' | 'auto';
|
|
423
|
+
pr?: boolean;
|
|
424
|
+
noComments?: boolean;
|
|
425
|
+
force?: boolean;
|
|
426
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
427
|
+
}
|
|
428
|
+
interface CiScaffoldPlan {
|
|
429
|
+
writes: InitFileWrite[];
|
|
430
|
+
conflict: boolean;
|
|
431
|
+
conflictPath: string | null;
|
|
432
|
+
provider: 'github' | 'gitlab';
|
|
433
|
+
}
|
|
434
|
+
declare function scaffoldCiWorkflow(opts: CiScaffoldOptions): Promise<CiScaffoldPlan>;
|
|
435
|
+
//#endregion
|
|
395
436
|
//#region src/project-info/find-monorepo-root.d.ts
|
|
396
437
|
interface MonorepoResult {
|
|
397
438
|
root: string;
|
|
@@ -519,6 +560,7 @@ interface DoctorReport {
|
|
|
519
560
|
};
|
|
520
561
|
projectInfo: {
|
|
521
562
|
framework: string;
|
|
563
|
+
frameworkDetected: boolean;
|
|
522
564
|
vueVersion: string | null;
|
|
523
565
|
nuxtVersion: string | null;
|
|
524
566
|
capabilities: string[];
|
|
@@ -547,6 +589,8 @@ interface DoctorReport {
|
|
|
547
589
|
elapsedMs: number;
|
|
548
590
|
analyzedFileCount: number;
|
|
549
591
|
};
|
|
592
|
+
incomplete?: boolean;
|
|
593
|
+
skippedCheckReasons?: SkippedCheckReason[];
|
|
550
594
|
}
|
|
551
595
|
declare function buildDoctorReport(input: ReporterInput): DoctorReport;
|
|
552
596
|
declare function jsonReport(input: ReporterInput): string;
|
|
@@ -705,5 +749,5 @@ declare function buildPushPayload(input: PushPayloadInput): PushPayload;
|
|
|
705
749
|
*/
|
|
706
750
|
declare function pushFindings(opts: PushOptions): Promise<PushResult>;
|
|
707
751
|
//#endregion
|
|
708
|
-
export { type ApplyInlineDisablesOptions, type AuditConfig, type AuditReport, type AuditTimings, BUILT_IN_RECOMMENDED, type Capability, type CliOverrides, ConfigCycleError, ConfigFileNotFoundError, type ConfigSource, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, type Diagnostic, type DiagnosticSource, type DimensionScore, type DirectiveLine, type DirectiveRange, type DirectiveSet, type DoctorReport, type DoctorUserConfig, type DoctorUserConfigInput, DoctorUserConfigSchema, type Framework, type GitScopeMode, type InitConfigFormat, type InitFileWrite, type InitOptions, type InitPlan, type InitPreset, InvalidConfigError, LevelSchema, type ListRulesFilter, type LoadRuleDocOptions, type MonorepoKind, type MonorepoResult, OxlintOutputTooLarge, OxlintSpawnFailed, type ProjectInfo, type ProjectInfoLite, type PushOptions, type PushPayload, type PushPayloadInput, type PushResult, type PushedFinding, RULE_REGISTRY, type RawInitAnswers, type RegisteredRule, type ReporterFormat, type ReporterInput, type ReporterOptions, type ResolvedDoctorConfig, type ResolvedInitAnswers, type RuleCategory, type RuleDoc, RuleEntrySchema, type RuleSource, SCORE_DIMENSIONS, type ScoreBreakdownEntry, type ScoreConfig, type ScoreDimension, type ScoreResult, type Severity, type VerboseTraceOptions, type WorkspacePackage, agentReport, applyInlineDisables, audit, buildDoctorReport, buildJsonSchema, buildPushPayload, categoriesForDimension, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, dimensionForCategory, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, isScoreDimension, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderAgentPlaybook, renderDetectSummary, renderRulePrompt, renderVerboseTrace, resolveCategoryScope, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
|
|
752
|
+
export { type ApplyInlineDisablesOptions, type AuditConfig, type AuditReport, type AuditTimings, BUILT_IN_RECOMMENDED, type Capability, type CiProvider, type CiScaffoldOptions, type CiScaffoldPlan, type CliOverrides, ConfigCycleError, ConfigFileNotFoundError, type ConfigSource, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, type Diagnostic, type DiagnosticSource, type DimensionScore, type DirectiveLine, type DirectiveRange, type DirectiveSet, type DoctorReport, type DoctorUserConfig, type DoctorUserConfigInput, DoctorUserConfigSchema, type Framework, type GitScopeMode, type InitConfigFormat, type InitFileWrite, type InitOptions, type InitPlan, type InitPreset, InvalidConfigError, LevelSchema, type ListRulesFilter, type LoadRuleDocOptions, type MonorepoKind, type MonorepoResult, OxlintOutputTooLarge, OxlintSpawnFailed, type ProjectInfo, type ProjectInfoLite, type PushOptions, type PushPayload, type PushPayloadInput, type PushResult, type PushedFinding, RULE_REGISTRY, type RawInitAnswers, type RegisteredRule, type ReporterFormat, type ReporterInput, type ReporterOptions, type ResolvedDoctorConfig, type ResolvedInitAnswers, type RuleCategory, type RuleDoc, RuleEntrySchema, type RuleSource, SCORE_DIMENSIONS, type ScoreBreakdownEntry, type ScoreConfig, type ScoreDimension, type ScoreResult, type Severity, type SkippedCheckReason, type VerboseTraceOptions, type WorkspacePackage, agentReport, applyInlineDisables, audit, buildDoctorReport, buildJsonSchema, buildPushPayload, categoriesForDimension, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectCiProvider, detectProject, detectSummary, dimensionForCategory, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, isScoreDimension, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderAgentPlaybook, renderDetectSummary, renderRulePrompt, renderVerboseTrace, resolveCategoryScope, runCrossFilePass, sarifReport, scaffoldCiWorkflow, scoreDiagnostics, stripFindings, validateConfig };
|
|
709
753
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -12,8 +12,8 @@ import { fileURLToPath } from "node:url";
|
|
|
12
12
|
import { babelParse, parse } from "@vue/compiler-sfc";
|
|
13
13
|
import { loadConfig } from "c12";
|
|
14
14
|
import { z } from "zod";
|
|
15
|
-
import { promisify } from "node:util";
|
|
16
15
|
import process$1 from "node:process";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
17
|
import pc from "picocolors";
|
|
18
18
|
//#region \0rolldown/runtime.js
|
|
19
19
|
var __defProp = Object.defineProperty;
|
|
@@ -1462,6 +1462,7 @@ async function detectProject(rootDirectory) {
|
|
|
1462
1462
|
});
|
|
1463
1463
|
return {
|
|
1464
1464
|
framework,
|
|
1465
|
+
frameworkDetected: framework === "vue" || framework === "nuxt",
|
|
1465
1466
|
rootDirectory,
|
|
1466
1467
|
packageJsonPath,
|
|
1467
1468
|
vueVersion,
|
|
@@ -1819,12 +1820,24 @@ async function runOxlint(opts) {
|
|
|
1819
1820
|
let timer;
|
|
1820
1821
|
const timeoutMs = opts.timeoutMs ?? 6e4;
|
|
1821
1822
|
const maxOutputBytes = opts.maxOutputBytes ?? 33554432;
|
|
1823
|
+
const onAbort = () => {
|
|
1824
|
+
child.kill("SIGKILL");
|
|
1825
|
+
finish(() => reject(/* @__PURE__ */ new Error("oxlint subprocess aborted")));
|
|
1826
|
+
};
|
|
1822
1827
|
const finish = (fn) => {
|
|
1823
1828
|
if (settled) return;
|
|
1824
1829
|
settled = true;
|
|
1825
1830
|
if (timer) clearTimeout(timer);
|
|
1831
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
1826
1832
|
fn();
|
|
1827
1833
|
};
|
|
1834
|
+
if (opts.signal) {
|
|
1835
|
+
if (opts.signal.aborted) {
|
|
1836
|
+
onAbort();
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
1840
|
+
}
|
|
1828
1841
|
if (timeoutMs > 0) timer = setTimeout(() => {
|
|
1829
1842
|
child.kill("SIGKILL");
|
|
1830
1843
|
finish(() => reject(/* @__PURE__ */ new Error(`oxlint subprocess timed out after ${timeoutMs}ms`)));
|
|
@@ -1906,7 +1919,8 @@ async function runScriptPass(opts) {
|
|
|
1906
1919
|
configPath,
|
|
1907
1920
|
oxlintBin,
|
|
1908
1921
|
timeoutMs: opts.timeoutMs,
|
|
1909
|
-
fix: opts.fix
|
|
1922
|
+
fix: opts.fix,
|
|
1923
|
+
signal: opts.signal
|
|
1910
1924
|
});
|
|
1911
1925
|
return {
|
|
1912
1926
|
diagnostics: toCanonicalDiagnostics(raw.diagnostics, opts.rootDir),
|
|
@@ -4200,24 +4214,36 @@ async function audit(config = {}) {
|
|
|
4200
4214
|
exclude
|
|
4201
4215
|
});
|
|
4202
4216
|
const overallStart = performance.now();
|
|
4217
|
+
const maxDurationMs = config.maxDurationMs;
|
|
4218
|
+
const deadlineMs = maxDurationMs ?? 0;
|
|
4219
|
+
const budgetExhausted = () => maxDurationMs !== void 0 && performance.now() - overallStart >= maxDurationMs;
|
|
4220
|
+
const skippedPasses = [];
|
|
4221
|
+
const deadlineController = new AbortController();
|
|
4222
|
+
let deadlineTimer;
|
|
4223
|
+
if (maxDurationMs !== void 0) deadlineTimer = setTimeout(() => deadlineController.abort(), maxDurationMs);
|
|
4203
4224
|
const project = await detectProject(rootDir);
|
|
4204
4225
|
const templateStart = performance.now();
|
|
4205
|
-
|
|
4226
|
+
let templateDiagnostics = [];
|
|
4227
|
+
if (lintEnabled) if (budgetExhausted()) skippedPasses.push("template");
|
|
4228
|
+
else templateDiagnostics = await runTemplatePass({
|
|
4206
4229
|
files,
|
|
4207
4230
|
ruleOverrides: config.rules
|
|
4208
|
-
})
|
|
4231
|
+
});
|
|
4209
4232
|
const templateElapsed = performance.now() - templateStart;
|
|
4210
4233
|
const sfcStart = performance.now();
|
|
4211
|
-
|
|
4234
|
+
let sfcDiagnostics = [];
|
|
4235
|
+
if (lintEnabled) if (budgetExhausted()) skippedPasses.push("sfc");
|
|
4236
|
+
else sfcDiagnostics = await runSfcPass({
|
|
4212
4237
|
files,
|
|
4213
4238
|
ruleOverrides: config.rules,
|
|
4214
4239
|
projectInfo: project
|
|
4215
|
-
})
|
|
4240
|
+
});
|
|
4216
4241
|
const sfcElapsed = performance.now() - sfcStart;
|
|
4217
4242
|
let scriptDiagnostics = [];
|
|
4218
4243
|
let oxlintStderr = "";
|
|
4219
4244
|
const scriptStart = performance.now();
|
|
4220
|
-
if (lintEnabled)
|
|
4245
|
+
if (lintEnabled) if (budgetExhausted()) skippedPasses.push("lint");
|
|
4246
|
+
else try {
|
|
4221
4247
|
const result = await runScriptPass({
|
|
4222
4248
|
rootDir,
|
|
4223
4249
|
targetPath: rootDir,
|
|
@@ -4225,18 +4251,26 @@ async function audit(config = {}) {
|
|
|
4225
4251
|
framework: project.framework === "nuxt" ? "nuxt" : "vue",
|
|
4226
4252
|
fix: config.fix === true && config.scopeFiles === void 0,
|
|
4227
4253
|
fixExcludes: config.fixExcludes,
|
|
4228
|
-
exclude
|
|
4254
|
+
exclude,
|
|
4255
|
+
signal: deadlineController.signal
|
|
4229
4256
|
});
|
|
4230
4257
|
scriptDiagnostics = result.diagnostics;
|
|
4231
4258
|
oxlintStderr = result.stderr;
|
|
4232
4259
|
} catch (err) {
|
|
4233
|
-
|
|
4234
|
-
|
|
4260
|
+
if (deadlineController.signal.aborted) {
|
|
4261
|
+
skippedPasses.push("lint");
|
|
4262
|
+
oxlintStderr = "aborted: time-budget exhausted";
|
|
4263
|
+
} else {
|
|
4264
|
+
oxlintStderr = err instanceof Error ? err.message : String(err);
|
|
4265
|
+
if (process.env.DOCTOR_DEBUG) process.stderr.write(`[doctor-core] script pass failed: ${oxlintStderr}\n`);
|
|
4266
|
+
}
|
|
4235
4267
|
}
|
|
4236
4268
|
const scriptElapsed = performance.now() - scriptStart;
|
|
4237
4269
|
let deadCodeDiagnostics = [];
|
|
4238
4270
|
let deadCodeElapsed = 0;
|
|
4239
|
-
|
|
4271
|
+
const deadCodeEnabled = config.deadCode !== false && !budgetExhausted();
|
|
4272
|
+
if (config.deadCode !== false && !deadCodeEnabled) skippedPasses.push("dead-code");
|
|
4273
|
+
if (deadCodeEnabled) {
|
|
4240
4274
|
const deadCodeStart = performance.now();
|
|
4241
4275
|
try {
|
|
4242
4276
|
const { loadDoctorConfig } = await Promise.resolve().then(() => load_exports);
|
|
@@ -4255,48 +4289,54 @@ async function audit(config = {}) {
|
|
|
4255
4289
|
deadCodeElapsed = performance.now() - deadCodeStart;
|
|
4256
4290
|
}
|
|
4257
4291
|
let buildQualityDiagnostics = [];
|
|
4258
|
-
try {
|
|
4259
|
-
buildQualityDiagnostics = (await checkBuildQuality(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4260
|
-
const override = config.rules?.[d.ruleId];
|
|
4261
|
-
return override ? {
|
|
4262
|
-
...d,
|
|
4263
|
-
severity: override
|
|
4264
|
-
} : d;
|
|
4265
|
-
});
|
|
4266
|
-
} catch {}
|
|
4267
4292
|
let depsDiagnostics = [];
|
|
4268
|
-
try {
|
|
4269
|
-
depsDiagnostics = (await checkDeps(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4270
|
-
const override = config.rules?.[d.ruleId];
|
|
4271
|
-
return override ? {
|
|
4272
|
-
...d,
|
|
4273
|
-
severity: override
|
|
4274
|
-
} : d;
|
|
4275
|
-
});
|
|
4276
|
-
} catch {}
|
|
4277
4293
|
let nuxtProjectDiagnostics = [];
|
|
4278
|
-
if (project.framework === "nuxt") try {
|
|
4279
|
-
nuxtProjectDiagnostics = (await checkNuxtProject(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4280
|
-
const override = config.rules?.[d.ruleId];
|
|
4281
|
-
return override ? {
|
|
4282
|
-
...d,
|
|
4283
|
-
severity: override
|
|
4284
|
-
} : d;
|
|
4285
|
-
});
|
|
4286
|
-
} catch {}
|
|
4287
4294
|
let crossFileDiagnostics = [];
|
|
4288
|
-
if (
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
}
|
|
4298
|
-
}
|
|
4299
|
-
|
|
4295
|
+
if (budgetExhausted()) skippedPasses.push("project");
|
|
4296
|
+
else {
|
|
4297
|
+
try {
|
|
4298
|
+
buildQualityDiagnostics = (await checkBuildQuality(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4299
|
+
const override = config.rules?.[d.ruleId];
|
|
4300
|
+
return override ? {
|
|
4301
|
+
...d,
|
|
4302
|
+
severity: override
|
|
4303
|
+
} : d;
|
|
4304
|
+
});
|
|
4305
|
+
} catch {}
|
|
4306
|
+
try {
|
|
4307
|
+
depsDiagnostics = (await checkDeps(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4308
|
+
const override = config.rules?.[d.ruleId];
|
|
4309
|
+
return override ? {
|
|
4310
|
+
...d,
|
|
4311
|
+
severity: override
|
|
4312
|
+
} : d;
|
|
4313
|
+
});
|
|
4314
|
+
} catch {}
|
|
4315
|
+
if (project.framework === "nuxt") {
|
|
4316
|
+
try {
|
|
4317
|
+
nuxtProjectDiagnostics = (await checkNuxtProject(project)).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4318
|
+
const override = config.rules?.[d.ruleId];
|
|
4319
|
+
return override ? {
|
|
4320
|
+
...d,
|
|
4321
|
+
severity: override
|
|
4322
|
+
} : d;
|
|
4323
|
+
});
|
|
4324
|
+
} catch {}
|
|
4325
|
+
try {
|
|
4326
|
+
crossFileDiagnostics = (await runCrossFilePass({
|
|
4327
|
+
files,
|
|
4328
|
+
projectInfo: project
|
|
4329
|
+
})).filter((d) => config.rules?.[d.ruleId] !== "off").map((d) => {
|
|
4330
|
+
const override = config.rules?.[d.ruleId];
|
|
4331
|
+
return override ? {
|
|
4332
|
+
...d,
|
|
4333
|
+
severity: override
|
|
4334
|
+
} : d;
|
|
4335
|
+
});
|
|
4336
|
+
} catch {}
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
if (deadlineTimer) clearTimeout(deadlineTimer);
|
|
4300
4340
|
const elapsedMs = performance.now() - overallStart;
|
|
4301
4341
|
const timings = {
|
|
4302
4342
|
template: templateElapsed,
|
|
@@ -4315,6 +4355,7 @@ async function audit(config = {}) {
|
|
|
4315
4355
|
const ruleCounts = countRuleCounts(diagnostics);
|
|
4316
4356
|
const projectInfo = {
|
|
4317
4357
|
framework: project.framework,
|
|
4358
|
+
frameworkDetected: project.frameworkDetected,
|
|
4318
4359
|
vueVersion: project.vueVersion,
|
|
4319
4360
|
nuxtVersion: project.nuxtVersion,
|
|
4320
4361
|
capabilities: [...project.capabilities].sort(),
|
|
@@ -4325,6 +4366,13 @@ async function audit(config = {}) {
|
|
|
4325
4366
|
else if (failOn !== "none") {
|
|
4326
4367
|
if ((failOn === "warn" ? scored.errorCount + scored.warnCount : scored.errorCount) > 0) exitCode = 1;
|
|
4327
4368
|
}
|
|
4369
|
+
const incomplete = skippedPasses.length > 0;
|
|
4370
|
+
const skippedCheckReasons = incomplete ? [{
|
|
4371
|
+
kind: "time-budget-exhausted",
|
|
4372
|
+
deadlineMs,
|
|
4373
|
+
elapsedMs,
|
|
4374
|
+
skippedPasses
|
|
4375
|
+
}] : void 0;
|
|
4328
4376
|
return {
|
|
4329
4377
|
rootDir,
|
|
4330
4378
|
filesScanned: files.length,
|
|
@@ -4338,7 +4386,9 @@ async function audit(config = {}) {
|
|
|
4338
4386
|
projectInfo,
|
|
4339
4387
|
elapsedMs,
|
|
4340
4388
|
timings,
|
|
4341
|
-
ruleCounts
|
|
4389
|
+
ruleCounts,
|
|
4390
|
+
incomplete,
|
|
4391
|
+
...skippedCheckReasons ? { skippedCheckReasons } : {}
|
|
4342
4392
|
};
|
|
4343
4393
|
}
|
|
4344
4394
|
//#endregion
|
|
@@ -4719,6 +4769,146 @@ async function planInit(options) {
|
|
|
4719
4769
|
};
|
|
4720
4770
|
}
|
|
4721
4771
|
//#endregion
|
|
4772
|
+
//#region src/ci/provider-detect.ts
|
|
4773
|
+
/**
|
|
4774
|
+
* Detects the CI provider from an explicit env snapshot. Takes the env as an
|
|
4775
|
+
* argument (instead of reading process.env) so provider detection stays a
|
|
4776
|
+
* deterministic, fully-testable pure function.
|
|
4777
|
+
*/
|
|
4778
|
+
function detectCiProvider(env) {
|
|
4779
|
+
if (env.GITHUB_ACTIONS === "true") return "github";
|
|
4780
|
+
if (env.GITLAB_CI === "true") return "gitlab";
|
|
4781
|
+
return "unknown";
|
|
4782
|
+
}
|
|
4783
|
+
//#endregion
|
|
4784
|
+
//#region src/ci/scaffold-workflow.ts
|
|
4785
|
+
function renderGithubWorkflow(opts) {
|
|
4786
|
+
return `name: doctor
|
|
4787
|
+
|
|
4788
|
+
on:
|
|
4789
|
+
push:
|
|
4790
|
+
branches: [main]
|
|
4791
|
+
pull_request:
|
|
4792
|
+
|
|
4793
|
+
permissions:
|
|
4794
|
+
contents: read
|
|
4795
|
+
pull-requests: write
|
|
4796
|
+
|
|
4797
|
+
jobs:
|
|
4798
|
+
audit:
|
|
4799
|
+
runs-on: ubuntu-latest
|
|
4800
|
+
steps:
|
|
4801
|
+
- uses: actions/checkout@v6
|
|
4802
|
+
|
|
4803
|
+
- name: Run doctor
|
|
4804
|
+
uses: geoql/doctor-action@v2
|
|
4805
|
+
with:
|
|
4806
|
+
framework: ${opts.framework}
|
|
4807
|
+
preset: recommended
|
|
4808
|
+
threshold: '0'
|
|
4809
|
+
fail-on: error
|
|
4810
|
+
pr-comment: '${opts.prComment}'
|
|
4811
|
+
api-key: \${{ secrets.DOCTOR_API_KEY }}
|
|
4812
|
+
project: \${{ github.repository }}
|
|
4813
|
+
`;
|
|
4814
|
+
}
|
|
4815
|
+
function renderGithubPrWorkflow(opts) {
|
|
4816
|
+
return `name: doctor-pr
|
|
4817
|
+
|
|
4818
|
+
on:
|
|
4819
|
+
pull_request:
|
|
4820
|
+
|
|
4821
|
+
permissions:
|
|
4822
|
+
contents: read
|
|
4823
|
+
pull-requests: write
|
|
4824
|
+
|
|
4825
|
+
jobs:
|
|
4826
|
+
review:
|
|
4827
|
+
runs-on: ubuntu-latest
|
|
4828
|
+
steps:
|
|
4829
|
+
- uses: actions/checkout@v6
|
|
4830
|
+
with:
|
|
4831
|
+
fetch-depth: 0
|
|
4832
|
+
|
|
4833
|
+
- name: Doctor PR review
|
|
4834
|
+
uses: geoql/doctor-action@v2
|
|
4835
|
+
with:
|
|
4836
|
+
framework: ${opts.framework}
|
|
4837
|
+
preset: recommended
|
|
4838
|
+
fail-on: error
|
|
4839
|
+
diff: 'true'
|
|
4840
|
+
pr-comment: 'true'
|
|
4841
|
+
comment-mode: both
|
|
4842
|
+
api-key: \${{ secrets.DOCTOR_API_KEY }}
|
|
4843
|
+
project: \${{ github.repository }}
|
|
4844
|
+
`;
|
|
4845
|
+
}
|
|
4846
|
+
function renderGitlabPipeline(opts) {
|
|
4847
|
+
return `# Doctor quality gate — https://docs.the-doctor.report
|
|
4848
|
+
doctor:
|
|
4849
|
+
image: node:24
|
|
4850
|
+
stage: test
|
|
4851
|
+
script:
|
|
4852
|
+
- npx -y @geoql/${opts.bin}@latest --fail-on error
|
|
4853
|
+
rules:
|
|
4854
|
+
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
|
|
4855
|
+
- if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
|
|
4856
|
+
`;
|
|
4857
|
+
}
|
|
4858
|
+
async function scaffoldCiWorkflow(opts) {
|
|
4859
|
+
const requested = opts.provider ?? "auto";
|
|
4860
|
+
let provider;
|
|
4861
|
+
if (requested === "auto") provider = detectCiProvider(opts.env ?? process$1.env) === "gitlab" ? "gitlab" : "github";
|
|
4862
|
+
else provider = requested;
|
|
4863
|
+
const writes = [];
|
|
4864
|
+
let conflict = false;
|
|
4865
|
+
let conflictPath = null;
|
|
4866
|
+
if (provider === "gitlab") {
|
|
4867
|
+
const pipelinePath = join(opts.dir, ".gitlab-ci.yml");
|
|
4868
|
+
if (!opts.force && await pathExists(pipelinePath)) {
|
|
4869
|
+
conflict = true;
|
|
4870
|
+
conflictPath = pipelinePath;
|
|
4871
|
+
} else writes.push({
|
|
4872
|
+
path: pipelinePath,
|
|
4873
|
+
content: renderGitlabPipeline({ bin: opts.bin })
|
|
4874
|
+
});
|
|
4875
|
+
return {
|
|
4876
|
+
writes,
|
|
4877
|
+
conflict,
|
|
4878
|
+
conflictPath,
|
|
4879
|
+
provider
|
|
4880
|
+
};
|
|
4881
|
+
}
|
|
4882
|
+
const workflowPath = join(opts.dir, ".github", "workflows", "doctor.yml");
|
|
4883
|
+
if (!opts.force && await pathExists(workflowPath)) {
|
|
4884
|
+
conflict = true;
|
|
4885
|
+
conflictPath = workflowPath;
|
|
4886
|
+
return {
|
|
4887
|
+
writes,
|
|
4888
|
+
conflict,
|
|
4889
|
+
conflictPath,
|
|
4890
|
+
provider
|
|
4891
|
+
};
|
|
4892
|
+
}
|
|
4893
|
+
writes.push({
|
|
4894
|
+
path: workflowPath,
|
|
4895
|
+
content: renderGithubWorkflow({
|
|
4896
|
+
framework: opts.framework,
|
|
4897
|
+
prComment: opts.noComments !== true
|
|
4898
|
+
})
|
|
4899
|
+
});
|
|
4900
|
+
if (opts.pr === true) writes.push({
|
|
4901
|
+
path: join(opts.dir, ".github", "workflows", "doctor-pr.yml"),
|
|
4902
|
+
content: renderGithubPrWorkflow({ framework: opts.framework })
|
|
4903
|
+
});
|
|
4904
|
+
return {
|
|
4905
|
+
writes,
|
|
4906
|
+
conflict,
|
|
4907
|
+
conflictPath,
|
|
4908
|
+
provider
|
|
4909
|
+
};
|
|
4910
|
+
}
|
|
4911
|
+
//#endregion
|
|
4722
4912
|
//#region src/git-scope.ts
|
|
4723
4913
|
const execFileAsync = promisify(execFile);
|
|
4724
4914
|
const SOURCE_EXTENSIONS = [
|
|
@@ -5119,6 +5309,7 @@ function buildDoctorReport(input) {
|
|
|
5119
5309
|
},
|
|
5120
5310
|
projectInfo: {
|
|
5121
5311
|
framework: input.projectInfo.framework,
|
|
5312
|
+
frameworkDetected: input.projectInfo.frameworkDetected,
|
|
5122
5313
|
vueVersion: input.projectInfo.vueVersion,
|
|
5123
5314
|
nuxtVersion: input.projectInfo.nuxtVersion,
|
|
5124
5315
|
capabilities: [...input.projectInfo.capabilities].sort(),
|
|
@@ -5162,7 +5353,16 @@ function buildDoctorReport(input) {
|
|
|
5162
5353
|
timing: {
|
|
5163
5354
|
elapsedMs: input.elapsedMs,
|
|
5164
5355
|
analyzedFileCount: input.analyzedFileCount
|
|
5165
|
-
}
|
|
5356
|
+
},
|
|
5357
|
+
...input.incomplete !== void 0 && input.incomplete ? {
|
|
5358
|
+
incomplete: input.incomplete,
|
|
5359
|
+
skippedCheckReasons: input.skippedCheckReasons?.map((r) => ({
|
|
5360
|
+
kind: r.kind,
|
|
5361
|
+
deadlineMs: r.deadlineMs,
|
|
5362
|
+
elapsedMs: r.elapsedMs,
|
|
5363
|
+
skippedPasses: [...r.skippedPasses]
|
|
5364
|
+
}))
|
|
5365
|
+
} : {}
|
|
5166
5366
|
};
|
|
5167
5367
|
}
|
|
5168
5368
|
function jsonReport(input) {
|
|
@@ -5643,6 +5843,6 @@ async function pushFindings(opts) {
|
|
|
5643
5843
|
}
|
|
5644
5844
|
}
|
|
5645
5845
|
//#endregion
|
|
5646
|
-
export { BUILT_IN_RECOMMENDED, ConfigCycleError, ConfigFileNotFoundError, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, DoctorUserConfigSchema, InvalidConfigError, LevelSchema, OxlintOutputTooLarge, OxlintSpawnFailed, RULE_REGISTRY, RuleEntrySchema, SCORE_DIMENSIONS, agentReport, applyInlineDisables, audit, buildDoctorReport, buildJsonSchema, buildPushPayload, categoriesForDimension, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectProject, detectSummary, dimensionForCategory, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, isScoreDimension, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderAgentPlaybook, renderDetectSummary, renderRulePrompt, renderVerboseTrace, resolveCategoryScope, runCrossFilePass, sarifReport, scoreDiagnostics, stripFindings, validateConfig };
|
|
5846
|
+
export { BUILT_IN_RECOMMENDED, ConfigCycleError, ConfigFileNotFoundError, DOCTOR_REPORT_SCHEMA_VERSION, DeadCodeImportFailed, DeadCodeTimeoutError, DoctorUserConfigSchema, InvalidConfigError, LevelSchema, OxlintOutputTooLarge, OxlintSpawnFailed, RULE_REGISTRY, RuleEntrySchema, SCORE_DIMENSIONS, agentReport, applyInlineDisables, audit, buildDoctorReport, buildJsonSchema, buildPushPayload, categoriesForDimension, checkBuildQuality, checkDeadCode, checkDeps, checkNuxtProject, dedupeDeadCodeAgainstLint, defineConfig, detectCiProvider, detectProject, detectSummary, dimensionForCategory, docsUrl, encodeAnnotation, encodeAnnotations, filterReportByRules, filterRuleIdsByCategory, findMonorepoRoot, format, isNuxtLayoutFile, isNuxtPageFile, isNuxtServerFile, isScoreDimension, jsonCompactReport, jsonReport, listChangedFiles, listRules, listWorkspacePackages, loadAllRuleDocs, loadDoctorConfig, loadRuleDoc, mergeCliOverrides, normalizeInitAnswers, parseDirectives, parseExcludeList, planInit, prCommentReport, prettyReport, pushFindings, renderAgentPlaybook, renderDetectSummary, renderRulePrompt, renderVerboseTrace, resolveCategoryScope, runCrossFilePass, sarifReport, scaffoldCiWorkflow, scoreDiagnostics, stripFindings, validateConfig };
|
|
5647
5847
|
|
|
5648
5848
|
//# sourceMappingURL=index.js.map
|