@doccov/cli 0.28.0 → 0.28.2
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/cli.js +228 -810
- package/package.json +2 -3
package/dist/cli.js
CHANGED
|
@@ -87,26 +87,75 @@ import {
|
|
|
87
87
|
parseExamplesFlag,
|
|
88
88
|
resolveTarget
|
|
89
89
|
} from "@doccov/sdk";
|
|
90
|
-
import
|
|
90
|
+
import chalk6 from "chalk";
|
|
91
91
|
|
|
92
|
-
//
|
|
92
|
+
// src/utils/filter-options.ts
|
|
93
|
+
import { mergeFilters, parseListFlag } from "@doccov/sdk";
|
|
93
94
|
import chalk from "chalk";
|
|
95
|
+
var parseVisibilityFlag = (value) => {
|
|
96
|
+
if (!value)
|
|
97
|
+
return;
|
|
98
|
+
const validTags = ["public", "beta", "alpha", "internal"];
|
|
99
|
+
const parsed = parseListFlag(value);
|
|
100
|
+
if (!parsed)
|
|
101
|
+
return;
|
|
102
|
+
const result = [];
|
|
103
|
+
for (const tag of parsed) {
|
|
104
|
+
const lower = tag.toLowerCase();
|
|
105
|
+
if (validTags.includes(lower)) {
|
|
106
|
+
result.push(lower);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return result.length > 0 ? result : undefined;
|
|
110
|
+
};
|
|
111
|
+
var formatList = (label, values) => `${label}: ${values.map((value) => chalk.cyan(value)).join(", ")}`;
|
|
112
|
+
var mergeFilterOptions = (config, cliOptions) => {
|
|
113
|
+
const messages = [];
|
|
114
|
+
if (config?.include) {
|
|
115
|
+
messages.push(formatList("include filters from config", config.include));
|
|
116
|
+
}
|
|
117
|
+
if (config?.exclude) {
|
|
118
|
+
messages.push(formatList("exclude filters from config", config.exclude));
|
|
119
|
+
}
|
|
120
|
+
if (cliOptions.include) {
|
|
121
|
+
messages.push(formatList("apply include filters from CLI", cliOptions.include));
|
|
122
|
+
}
|
|
123
|
+
if (cliOptions.exclude) {
|
|
124
|
+
messages.push(formatList("apply exclude filters from CLI", cliOptions.exclude));
|
|
125
|
+
}
|
|
126
|
+
if (cliOptions.visibility) {
|
|
127
|
+
messages.push(formatList("apply visibility filter from CLI", cliOptions.visibility));
|
|
128
|
+
}
|
|
129
|
+
const resolved = mergeFilters(config, cliOptions);
|
|
130
|
+
if (!resolved.include && !resolved.exclude && !cliOptions.visibility) {
|
|
131
|
+
return { messages };
|
|
132
|
+
}
|
|
133
|
+
const source = resolved.source === "override" ? "cli" : resolved.source;
|
|
134
|
+
return {
|
|
135
|
+
include: resolved.include,
|
|
136
|
+
exclude: resolved.exclude,
|
|
137
|
+
visibility: cliOptions.visibility,
|
|
138
|
+
source,
|
|
139
|
+
messages
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
// src/utils/progress/colors.ts
|
|
94
144
|
import chalk2 from "chalk";
|
|
95
|
-
import { Worker } from "node:worker_threads";
|
|
96
145
|
var colors = {
|
|
97
|
-
success:
|
|
98
|
-
error:
|
|
99
|
-
warning:
|
|
100
|
-
info:
|
|
101
|
-
muted:
|
|
102
|
-
bold:
|
|
103
|
-
dim:
|
|
104
|
-
underline:
|
|
105
|
-
primary:
|
|
106
|
-
secondary:
|
|
107
|
-
path:
|
|
108
|
-
number:
|
|
109
|
-
code:
|
|
146
|
+
success: chalk2.green,
|
|
147
|
+
error: chalk2.red,
|
|
148
|
+
warning: chalk2.yellow,
|
|
149
|
+
info: chalk2.cyan,
|
|
150
|
+
muted: chalk2.gray,
|
|
151
|
+
bold: chalk2.bold,
|
|
152
|
+
dim: chalk2.dim,
|
|
153
|
+
underline: chalk2.underline,
|
|
154
|
+
primary: chalk2.cyan,
|
|
155
|
+
secondary: chalk2.magenta,
|
|
156
|
+
path: chalk2.cyan,
|
|
157
|
+
number: chalk2.yellow,
|
|
158
|
+
code: chalk2.gray
|
|
110
159
|
};
|
|
111
160
|
var symbols = {
|
|
112
161
|
success: "✓",
|
|
@@ -145,6 +194,10 @@ var prefix = {
|
|
|
145
194
|
warning: colors.warning(symbols.warning),
|
|
146
195
|
info: colors.info(symbols.info)
|
|
147
196
|
};
|
|
197
|
+
// src/utils/progress/spinner.ts
|
|
198
|
+
import chalk3 from "chalk";
|
|
199
|
+
|
|
200
|
+
// src/utils/progress/utils.ts
|
|
148
201
|
function isTTY() {
|
|
149
202
|
return Boolean(process.stdout.isTTY);
|
|
150
203
|
}
|
|
@@ -166,18 +219,6 @@ function getTerminalWidth() {
|
|
|
166
219
|
const width = process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
|
|
167
220
|
return Math.max(width, MIN_TERMINAL_WIDTH);
|
|
168
221
|
}
|
|
169
|
-
function formatDuration(ms) {
|
|
170
|
-
if (ms < 1000) {
|
|
171
|
-
return `${ms}ms`;
|
|
172
|
-
}
|
|
173
|
-
const seconds = ms / 1000;
|
|
174
|
-
if (seconds < 60) {
|
|
175
|
-
return `${seconds.toFixed(1)}s`;
|
|
176
|
-
}
|
|
177
|
-
const minutes = Math.floor(seconds / 60);
|
|
178
|
-
const remainingSeconds = Math.floor(seconds % 60);
|
|
179
|
-
return `${minutes}m ${remainingSeconds}s`;
|
|
180
|
-
}
|
|
181
222
|
var cursor = {
|
|
182
223
|
hide: "\x1B[?25l",
|
|
183
224
|
show: "\x1B[?25h",
|
|
@@ -196,11 +237,6 @@ function clearLine() {
|
|
|
196
237
|
process.stdout.write(cursor.clearLine + cursor.left);
|
|
197
238
|
}
|
|
198
239
|
}
|
|
199
|
-
function moveCursorUp(lines = 1) {
|
|
200
|
-
if (isTTY()) {
|
|
201
|
-
process.stdout.write(cursor.up(lines));
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
240
|
function hideCursor() {
|
|
205
241
|
if (isTTY()) {
|
|
206
242
|
process.stdout.write(cursor.hide);
|
|
@@ -221,391 +257,15 @@ function stripAnsi(text) {
|
|
|
221
257
|
return text.replace(ANSI_REGEX, "");
|
|
222
258
|
}
|
|
223
259
|
|
|
224
|
-
|
|
225
|
-
bars = new Map;
|
|
226
|
-
barOrder = [];
|
|
227
|
-
options;
|
|
228
|
-
spinnerFrames;
|
|
229
|
-
spinnerIndex = 0;
|
|
230
|
-
timer = null;
|
|
231
|
-
lastRenderedLines = 0;
|
|
232
|
-
symbols = getSymbols(supportsUnicode());
|
|
233
|
-
sigintHandler = null;
|
|
234
|
-
filledChar;
|
|
235
|
-
emptyChar;
|
|
236
|
-
constructor(options = {}) {
|
|
237
|
-
this.options = {
|
|
238
|
-
barWidth: options.barWidth,
|
|
239
|
-
showPercent: options.showPercent ?? true,
|
|
240
|
-
showCount: options.showCount ?? true,
|
|
241
|
-
spinnerInterval: options.spinnerInterval ?? 80
|
|
242
|
-
};
|
|
243
|
-
this.spinnerFrames = supportsUnicode() ? ["◐", "◓", "◑", "◒"] : ["-", "\\", "|", "/"];
|
|
244
|
-
const unicode = supportsUnicode();
|
|
245
|
-
this.filledChar = unicode ? "█" : "#";
|
|
246
|
-
this.emptyChar = unicode ? "░" : "-";
|
|
247
|
-
}
|
|
248
|
-
start() {
|
|
249
|
-
if (!isInteractive())
|
|
250
|
-
return this;
|
|
251
|
-
hideCursor();
|
|
252
|
-
this.setupSignalHandler();
|
|
253
|
-
this.timer = setInterval(() => {
|
|
254
|
-
if (this.bars.size > 0 && [...this.bars.values()].some((b) => b.status === "active")) {
|
|
255
|
-
this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
|
|
256
|
-
this.render();
|
|
257
|
-
}
|
|
258
|
-
}, this.options.spinnerInterval);
|
|
259
|
-
return this;
|
|
260
|
-
}
|
|
261
|
-
add(config) {
|
|
262
|
-
const state = {
|
|
263
|
-
id: config.id,
|
|
264
|
-
label: config.label,
|
|
265
|
-
total: config.total ?? 100,
|
|
266
|
-
current: config.current ?? 0,
|
|
267
|
-
status: "active",
|
|
268
|
-
startTime: Date.now()
|
|
269
|
-
};
|
|
270
|
-
this.bars.set(config.id, state);
|
|
271
|
-
if (!this.barOrder.includes(config.id)) {
|
|
272
|
-
this.barOrder.push(config.id);
|
|
273
|
-
}
|
|
274
|
-
if (!isInteractive()) {
|
|
275
|
-
console.log(`${this.symbols.bullet} ${config.label}`);
|
|
276
|
-
} else {
|
|
277
|
-
this.render();
|
|
278
|
-
}
|
|
279
|
-
return this;
|
|
280
|
-
}
|
|
281
|
-
update(id, current, label) {
|
|
282
|
-
const bar = this.bars.get(id);
|
|
283
|
-
if (!bar)
|
|
284
|
-
return this;
|
|
285
|
-
bar.current = Math.min(current, bar.total);
|
|
286
|
-
if (label !== undefined)
|
|
287
|
-
bar.label = label;
|
|
288
|
-
if (isInteractive()) {
|
|
289
|
-
this.render();
|
|
290
|
-
}
|
|
291
|
-
return this;
|
|
292
|
-
}
|
|
293
|
-
increment(id, amount = 1) {
|
|
294
|
-
const bar = this.bars.get(id);
|
|
295
|
-
if (!bar)
|
|
296
|
-
return this;
|
|
297
|
-
return this.update(id, bar.current + amount);
|
|
298
|
-
}
|
|
299
|
-
complete(id, label) {
|
|
300
|
-
const bar = this.bars.get(id);
|
|
301
|
-
if (!bar)
|
|
302
|
-
return this;
|
|
303
|
-
bar.status = "completed";
|
|
304
|
-
bar.current = bar.total;
|
|
305
|
-
if (label !== undefined)
|
|
306
|
-
bar.label = label;
|
|
307
|
-
if (!isInteractive()) {
|
|
308
|
-
console.log(`${colors.success(this.symbols.success)} ${bar.label}`);
|
|
309
|
-
} else {
|
|
310
|
-
this.render();
|
|
311
|
-
}
|
|
312
|
-
return this;
|
|
313
|
-
}
|
|
314
|
-
fail(id, label) {
|
|
315
|
-
const bar = this.bars.get(id);
|
|
316
|
-
if (!bar)
|
|
317
|
-
return this;
|
|
318
|
-
bar.status = "failed";
|
|
319
|
-
if (label !== undefined)
|
|
320
|
-
bar.label = label;
|
|
321
|
-
if (!isInteractive()) {
|
|
322
|
-
console.log(`${colors.error(this.symbols.error)} ${bar.label}`);
|
|
323
|
-
} else {
|
|
324
|
-
this.render();
|
|
325
|
-
}
|
|
326
|
-
return this;
|
|
327
|
-
}
|
|
328
|
-
remove(id) {
|
|
329
|
-
this.bars.delete(id);
|
|
330
|
-
this.barOrder = this.barOrder.filter((i) => i !== id);
|
|
331
|
-
if (isInteractive()) {
|
|
332
|
-
this.render();
|
|
333
|
-
}
|
|
334
|
-
return this;
|
|
335
|
-
}
|
|
336
|
-
get(id) {
|
|
337
|
-
return this.bars.get(id);
|
|
338
|
-
}
|
|
339
|
-
get allDone() {
|
|
340
|
-
if (this.bars.size === 0)
|
|
341
|
-
return true;
|
|
342
|
-
return [...this.bars.values()].every((b) => b.status !== "active");
|
|
343
|
-
}
|
|
344
|
-
stop() {
|
|
345
|
-
if (this.timer) {
|
|
346
|
-
clearInterval(this.timer);
|
|
347
|
-
this.timer = null;
|
|
348
|
-
}
|
|
349
|
-
this.cleanup();
|
|
350
|
-
return this;
|
|
351
|
-
}
|
|
352
|
-
render() {
|
|
353
|
-
if (!isTTY())
|
|
354
|
-
return;
|
|
355
|
-
this.clearOutput();
|
|
356
|
-
const width = getTerminalWidth();
|
|
357
|
-
const lines = [];
|
|
358
|
-
for (const id of this.barOrder) {
|
|
359
|
-
const bar = this.bars.get(id);
|
|
360
|
-
if (!bar)
|
|
361
|
-
continue;
|
|
362
|
-
const line = this.renderBar(bar, width);
|
|
363
|
-
lines.push(line);
|
|
364
|
-
}
|
|
365
|
-
if (lines.length > 0) {
|
|
366
|
-
process.stdout.write(lines.join(`
|
|
367
|
-
`));
|
|
368
|
-
this.lastRenderedLines = lines.length;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
renderBar(bar, termWidth) {
|
|
372
|
-
const parts = [];
|
|
373
|
-
let symbol;
|
|
374
|
-
switch (bar.status) {
|
|
375
|
-
case "completed":
|
|
376
|
-
symbol = colors.success(this.symbols.success);
|
|
377
|
-
break;
|
|
378
|
-
case "failed":
|
|
379
|
-
symbol = colors.error(this.symbols.error);
|
|
380
|
-
break;
|
|
381
|
-
default:
|
|
382
|
-
symbol = colors.primary(this.spinnerFrames[this.spinnerIndex]);
|
|
383
|
-
}
|
|
384
|
-
parts.push(symbol);
|
|
385
|
-
parts.push(bar.label);
|
|
386
|
-
const suffixParts = [];
|
|
387
|
-
if (this.options.showPercent) {
|
|
388
|
-
const pct = bar.total === 0 ? 0 : Math.round(bar.current / bar.total * 100);
|
|
389
|
-
suffixParts.push(`${pct}%`);
|
|
390
|
-
}
|
|
391
|
-
if (this.options.showCount) {
|
|
392
|
-
suffixParts.push(`${bar.current}/${bar.total}`);
|
|
393
|
-
}
|
|
394
|
-
const suffix = suffixParts.length > 0 ? ` ${suffixParts.join(" ")}` : "";
|
|
395
|
-
const labelLen = stripAnsi(parts.join(" ")).length + 1;
|
|
396
|
-
const bracketLen = 2;
|
|
397
|
-
const suffixLen = stripAnsi(suffix).length;
|
|
398
|
-
const minBarWidth = 10;
|
|
399
|
-
const availableWidth = termWidth - labelLen - bracketLen - suffixLen - 1;
|
|
400
|
-
const barWidth = this.options.barWidth ?? Math.max(minBarWidth, Math.min(30, availableWidth));
|
|
401
|
-
const filledWidth = Math.round(bar.current / bar.total * barWidth);
|
|
402
|
-
const emptyWidth = barWidth - filledWidth;
|
|
403
|
-
const barViz = `[${this.filledChar.repeat(filledWidth)}${this.emptyChar.repeat(emptyWidth)}]`;
|
|
404
|
-
parts.push(barViz);
|
|
405
|
-
return truncate(parts.join(" ") + suffix, termWidth);
|
|
406
|
-
}
|
|
407
|
-
clearOutput() {
|
|
408
|
-
if (!isTTY() || this.lastRenderedLines === 0)
|
|
409
|
-
return;
|
|
410
|
-
for (let i = 0;i < this.lastRenderedLines; i++) {
|
|
411
|
-
if (i > 0)
|
|
412
|
-
process.stdout.write(cursor.up(1));
|
|
413
|
-
clearLine();
|
|
414
|
-
}
|
|
415
|
-
this.lastRenderedLines = 0;
|
|
416
|
-
}
|
|
417
|
-
setupSignalHandler() {
|
|
418
|
-
this.sigintHandler = () => {
|
|
419
|
-
this.cleanup();
|
|
420
|
-
process.exit(130);
|
|
421
|
-
};
|
|
422
|
-
process.on("SIGINT", this.sigintHandler);
|
|
423
|
-
}
|
|
424
|
-
cleanup() {
|
|
425
|
-
if (this.sigintHandler) {
|
|
426
|
-
process.removeListener("SIGINT", this.sigintHandler);
|
|
427
|
-
this.sigintHandler = null;
|
|
428
|
-
}
|
|
429
|
-
showCursor();
|
|
430
|
-
if (isTTY() && this.lastRenderedLines > 0) {
|
|
431
|
-
process.stdout.write(`
|
|
432
|
-
`);
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
class ProgressBar {
|
|
437
|
-
total;
|
|
438
|
-
current;
|
|
439
|
-
label;
|
|
440
|
-
width;
|
|
441
|
-
showPercent;
|
|
442
|
-
showCount;
|
|
443
|
-
showETA;
|
|
444
|
-
filledChar;
|
|
445
|
-
emptyChar;
|
|
446
|
-
startTime = null;
|
|
447
|
-
lastRender = "";
|
|
448
|
-
symbols = getSymbols(supportsUnicode());
|
|
449
|
-
sigintHandler = null;
|
|
450
|
-
isComplete = false;
|
|
451
|
-
constructor(options = {}) {
|
|
452
|
-
this.total = options.total ?? 100;
|
|
453
|
-
this.current = options.current ?? 0;
|
|
454
|
-
this.label = options.label ?? "";
|
|
455
|
-
this.width = options.width;
|
|
456
|
-
this.showPercent = options.showPercent ?? true;
|
|
457
|
-
this.showCount = options.showCount ?? true;
|
|
458
|
-
this.showETA = options.showETA ?? true;
|
|
459
|
-
const unicode = supportsUnicode();
|
|
460
|
-
this.filledChar = options.chars?.filled ?? (unicode ? "█" : "#");
|
|
461
|
-
this.emptyChar = options.chars?.empty ?? (unicode ? "░" : "-");
|
|
462
|
-
}
|
|
463
|
-
start(label) {
|
|
464
|
-
if (label !== undefined)
|
|
465
|
-
this.label = label;
|
|
466
|
-
this.startTime = Date.now();
|
|
467
|
-
this.current = 0;
|
|
468
|
-
this.isComplete = false;
|
|
469
|
-
if (!isInteractive()) {
|
|
470
|
-
console.log(`${this.symbols.bullet} ${this.label}`);
|
|
471
|
-
return this;
|
|
472
|
-
}
|
|
473
|
-
hideCursor();
|
|
474
|
-
this.setupSignalHandler();
|
|
475
|
-
this.render();
|
|
476
|
-
return this;
|
|
477
|
-
}
|
|
478
|
-
update(current) {
|
|
479
|
-
this.current = Math.min(current, this.total);
|
|
480
|
-
if (isInteractive()) {
|
|
481
|
-
this.render();
|
|
482
|
-
}
|
|
483
|
-
return this;
|
|
484
|
-
}
|
|
485
|
-
increment(amount = 1) {
|
|
486
|
-
return this.update(this.current + amount);
|
|
487
|
-
}
|
|
488
|
-
setLabel(label) {
|
|
489
|
-
this.label = label;
|
|
490
|
-
if (isInteractive()) {
|
|
491
|
-
this.render();
|
|
492
|
-
}
|
|
493
|
-
return this;
|
|
494
|
-
}
|
|
495
|
-
setTotal(total) {
|
|
496
|
-
this.total = total;
|
|
497
|
-
if (isInteractive()) {
|
|
498
|
-
this.render();
|
|
499
|
-
}
|
|
500
|
-
return this;
|
|
501
|
-
}
|
|
502
|
-
complete(label) {
|
|
503
|
-
if (label !== undefined)
|
|
504
|
-
this.label = label;
|
|
505
|
-
this.current = this.total;
|
|
506
|
-
this.isComplete = true;
|
|
507
|
-
if (!isInteractive()) {
|
|
508
|
-
console.log(`${colors.success(this.symbols.success)} ${this.label}`);
|
|
509
|
-
} else {
|
|
510
|
-
clearLine();
|
|
511
|
-
process.stdout.write(`${colors.success(this.symbols.success)} ${this.label}
|
|
512
|
-
`);
|
|
513
|
-
}
|
|
514
|
-
this.cleanup();
|
|
515
|
-
return this;
|
|
516
|
-
}
|
|
517
|
-
fail(label) {
|
|
518
|
-
if (label !== undefined)
|
|
519
|
-
this.label = label;
|
|
520
|
-
this.isComplete = true;
|
|
521
|
-
if (!isInteractive()) {
|
|
522
|
-
console.log(`${colors.error(this.symbols.error)} ${this.label}`);
|
|
523
|
-
} else {
|
|
524
|
-
clearLine();
|
|
525
|
-
process.stdout.write(`${colors.error(this.symbols.error)} ${this.label}
|
|
526
|
-
`);
|
|
527
|
-
}
|
|
528
|
-
this.cleanup();
|
|
529
|
-
return this;
|
|
530
|
-
}
|
|
531
|
-
get percentage() {
|
|
532
|
-
return this.total === 0 ? 0 : Math.round(this.current / this.total * 100);
|
|
533
|
-
}
|
|
534
|
-
get isDone() {
|
|
535
|
-
return this.isComplete || this.current >= this.total;
|
|
536
|
-
}
|
|
537
|
-
render() {
|
|
538
|
-
if (!isTTY())
|
|
539
|
-
return;
|
|
540
|
-
const termWidth = getTerminalWidth();
|
|
541
|
-
const parts = [];
|
|
542
|
-
if (this.label) {
|
|
543
|
-
parts.push(this.label);
|
|
544
|
-
}
|
|
545
|
-
const suffixParts = [];
|
|
546
|
-
if (this.showPercent) {
|
|
547
|
-
suffixParts.push(`${this.percentage}%`);
|
|
548
|
-
}
|
|
549
|
-
if (this.showCount) {
|
|
550
|
-
suffixParts.push(`${this.current}/${this.total}`);
|
|
551
|
-
}
|
|
552
|
-
if (this.showETA && this.startTime) {
|
|
553
|
-
const eta = this.calculateETA();
|
|
554
|
-
if (eta)
|
|
555
|
-
suffixParts.push(eta);
|
|
556
|
-
}
|
|
557
|
-
const suffix = suffixParts.length > 0 ? ` ${suffixParts.join(" ")}` : "";
|
|
558
|
-
const labelLen = this.label ? stripAnsi(this.label).length + 1 : 0;
|
|
559
|
-
const bracketLen = 2;
|
|
560
|
-
const suffixLen = stripAnsi(suffix).length;
|
|
561
|
-
const minBarWidth = 10;
|
|
562
|
-
const availableWidth = termWidth - labelLen - bracketLen - suffixLen - 1;
|
|
563
|
-
const barWidth = this.width ?? Math.max(minBarWidth, Math.min(40, availableWidth));
|
|
564
|
-
const filledWidth = Math.round(this.current / this.total * barWidth);
|
|
565
|
-
const emptyWidth = barWidth - filledWidth;
|
|
566
|
-
const bar = `[${this.filledChar.repeat(filledWidth)}${this.emptyChar.repeat(emptyWidth)}]`;
|
|
567
|
-
parts.push(bar);
|
|
568
|
-
const line = truncate(parts.join(" ") + suffix, termWidth);
|
|
569
|
-
if (line !== this.lastRender) {
|
|
570
|
-
clearLine();
|
|
571
|
-
process.stdout.write(line);
|
|
572
|
-
this.lastRender = line;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
calculateETA() {
|
|
576
|
-
if (!this.startTime || this.current === 0)
|
|
577
|
-
return null;
|
|
578
|
-
const elapsed = Date.now() - this.startTime;
|
|
579
|
-
if (elapsed < 1000)
|
|
580
|
-
return null;
|
|
581
|
-
const rate = this.current / elapsed;
|
|
582
|
-
const remaining = this.total - this.current;
|
|
583
|
-
const etaMs = remaining / rate;
|
|
584
|
-
return `ETA ${formatDuration(etaMs)}`;
|
|
585
|
-
}
|
|
586
|
-
setupSignalHandler() {
|
|
587
|
-
this.sigintHandler = () => {
|
|
588
|
-
this.cleanup();
|
|
589
|
-
process.exit(130);
|
|
590
|
-
};
|
|
591
|
-
process.on("SIGINT", this.sigintHandler);
|
|
592
|
-
}
|
|
593
|
-
cleanup() {
|
|
594
|
-
if (this.sigintHandler) {
|
|
595
|
-
process.removeListener("SIGINT", this.sigintHandler);
|
|
596
|
-
this.sigintHandler = null;
|
|
597
|
-
}
|
|
598
|
-
showCursor();
|
|
599
|
-
}
|
|
600
|
-
}
|
|
260
|
+
// src/utils/progress/spinner.ts
|
|
601
261
|
var spinnerColors = {
|
|
602
|
-
cyan:
|
|
603
|
-
yellow:
|
|
604
|
-
green:
|
|
605
|
-
red:
|
|
606
|
-
magenta:
|
|
607
|
-
blue:
|
|
608
|
-
white:
|
|
262
|
+
cyan: chalk3.cyan,
|
|
263
|
+
yellow: chalk3.yellow,
|
|
264
|
+
green: chalk3.green,
|
|
265
|
+
red: chalk3.red,
|
|
266
|
+
magenta: chalk3.magenta,
|
|
267
|
+
blue: chalk3.blue,
|
|
268
|
+
white: chalk3.white
|
|
609
269
|
};
|
|
610
270
|
var FRAME_SETS = {
|
|
611
271
|
dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
@@ -625,13 +285,11 @@ class Spinner {
|
|
|
625
285
|
symbols = getSymbols(supportsUnicode());
|
|
626
286
|
lastRenderedLines = 0;
|
|
627
287
|
sigintHandler = null;
|
|
628
|
-
animate;
|
|
629
288
|
constructor(options = {}) {
|
|
630
289
|
this.label = options.label ?? "";
|
|
631
290
|
this.detail = options.detail;
|
|
632
291
|
this.interval = options.interval ?? 80;
|
|
633
292
|
this.colorFn = spinnerColors[options.color ?? "cyan"];
|
|
634
|
-
this.animate = options.animate ?? true;
|
|
635
293
|
const style = options.style ?? "circle";
|
|
636
294
|
this.frames = supportsUnicode() ? FRAME_SETS[style] : ASCII_FRAME_SET;
|
|
637
295
|
}
|
|
@@ -643,7 +301,7 @@ class Spinner {
|
|
|
643
301
|
this.state = "spinning";
|
|
644
302
|
this.frameIndex = 0;
|
|
645
303
|
this.lastRenderedLines = 0;
|
|
646
|
-
if (!isInteractive()
|
|
304
|
+
if (!isInteractive()) {
|
|
647
305
|
console.log(`${this.symbols.bullet} ${this.label}`);
|
|
648
306
|
return this;
|
|
649
307
|
}
|
|
@@ -701,12 +359,14 @@ class Spinner {
|
|
|
701
359
|
this.timer = null;
|
|
702
360
|
}
|
|
703
361
|
this.state = state;
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
362
|
+
if (!isInteractive()) {
|
|
363
|
+
const symbol = state === "success" ? this.symbols.success : this.symbols.error;
|
|
364
|
+
const colorFn = state === "success" ? colors.success : colors.error;
|
|
707
365
|
console.log(`${colorFn(symbol)} ${this.label}`);
|
|
708
366
|
} else {
|
|
709
367
|
this.clearOutput();
|
|
368
|
+
const symbol = state === "success" ? this.symbols.success : this.symbols.error;
|
|
369
|
+
const colorFn = state === "success" ? colors.success : colors.error;
|
|
710
370
|
process.stdout.write(`${colorFn(symbol)} ${this.label}
|
|
711
371
|
`);
|
|
712
372
|
}
|
|
@@ -756,206 +416,7 @@ ${detailLine}`);
|
|
|
756
416
|
function spinner(label, options) {
|
|
757
417
|
return new Spinner({ ...options, label }).start();
|
|
758
418
|
}
|
|
759
|
-
|
|
760
|
-
class StepProgress {
|
|
761
|
-
steps = [];
|
|
762
|
-
showNumbers;
|
|
763
|
-
spinnerInterval;
|
|
764
|
-
spinnerFrames;
|
|
765
|
-
spinnerIndex = 0;
|
|
766
|
-
timer = null;
|
|
767
|
-
symbols = getSymbols(supportsUnicode());
|
|
768
|
-
lastRenderedLines = 0;
|
|
769
|
-
sigintHandler = null;
|
|
770
|
-
constructor(options = {}) {
|
|
771
|
-
this.showNumbers = options.showNumbers ?? true;
|
|
772
|
-
this.spinnerInterval = options.spinnerInterval ?? 80;
|
|
773
|
-
this.spinnerFrames = supportsUnicode() ? ["◐", "◓", "◑", "◒"] : ["-", "\\", "|", "/"];
|
|
774
|
-
if (options.steps) {
|
|
775
|
-
this.steps = options.steps.map((label) => ({ label, status: "pending" }));
|
|
776
|
-
}
|
|
777
|
-
}
|
|
778
|
-
start() {
|
|
779
|
-
if (!isInteractive())
|
|
780
|
-
return this;
|
|
781
|
-
hideCursor();
|
|
782
|
-
this.setupSignalHandler();
|
|
783
|
-
this.render();
|
|
784
|
-
this.timer = setInterval(() => {
|
|
785
|
-
if (this.steps.some((s) => s.status === "active")) {
|
|
786
|
-
this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
|
|
787
|
-
this.render();
|
|
788
|
-
}
|
|
789
|
-
}, this.spinnerInterval);
|
|
790
|
-
return this;
|
|
791
|
-
}
|
|
792
|
-
addStep(label) {
|
|
793
|
-
this.steps.push({ label, status: "pending" });
|
|
794
|
-
if (isInteractive())
|
|
795
|
-
this.render();
|
|
796
|
-
return this;
|
|
797
|
-
}
|
|
798
|
-
startStep(index) {
|
|
799
|
-
if (index >= 0 && index < this.steps.length) {
|
|
800
|
-
this.steps[index].status = "active";
|
|
801
|
-
this.steps[index].startTime = Date.now();
|
|
802
|
-
if (isInteractive()) {
|
|
803
|
-
this.render();
|
|
804
|
-
} else {
|
|
805
|
-
const step = this.steps[index];
|
|
806
|
-
const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
|
|
807
|
-
console.log(`${this.symbols.bullet} ${prefix2}${step.label}...`);
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
return this;
|
|
811
|
-
}
|
|
812
|
-
completeStep(index) {
|
|
813
|
-
if (index >= 0 && index < this.steps.length) {
|
|
814
|
-
this.steps[index].status = "completed";
|
|
815
|
-
this.steps[index].endTime = Date.now();
|
|
816
|
-
if (isInteractive()) {
|
|
817
|
-
this.render();
|
|
818
|
-
} else {
|
|
819
|
-
const step = this.steps[index];
|
|
820
|
-
const duration = this.getStepDuration(step);
|
|
821
|
-
const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
|
|
822
|
-
console.log(`${colors.success(this.symbols.success)} ${prefix2}${step.label}${duration}`);
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
return this;
|
|
826
|
-
}
|
|
827
|
-
failStep(index) {
|
|
828
|
-
if (index >= 0 && index < this.steps.length) {
|
|
829
|
-
this.steps[index].status = "failed";
|
|
830
|
-
this.steps[index].endTime = Date.now();
|
|
831
|
-
if (isInteractive()) {
|
|
832
|
-
this.render();
|
|
833
|
-
} else {
|
|
834
|
-
const step = this.steps[index];
|
|
835
|
-
const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
|
|
836
|
-
console.log(`${colors.error(this.symbols.error)} ${prefix2}${step.label}`);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
return this;
|
|
840
|
-
}
|
|
841
|
-
skipStep(index) {
|
|
842
|
-
if (index >= 0 && index < this.steps.length) {
|
|
843
|
-
this.steps[index].status = "skipped";
|
|
844
|
-
if (isInteractive())
|
|
845
|
-
this.render();
|
|
846
|
-
}
|
|
847
|
-
return this;
|
|
848
|
-
}
|
|
849
|
-
async run(tasks) {
|
|
850
|
-
this.steps = tasks.map((t) => ({ label: t.label, status: "pending" }));
|
|
851
|
-
this.start();
|
|
852
|
-
const results = [];
|
|
853
|
-
let failed = false;
|
|
854
|
-
for (let i = 0;i < tasks.length; i++) {
|
|
855
|
-
if (failed) {
|
|
856
|
-
this.skipStep(i);
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
this.startStep(i);
|
|
860
|
-
try {
|
|
861
|
-
results.push(await tasks[i].task());
|
|
862
|
-
this.completeStep(i);
|
|
863
|
-
} catch {
|
|
864
|
-
this.failStep(i);
|
|
865
|
-
failed = true;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
this.stop();
|
|
869
|
-
return { results, failed };
|
|
870
|
-
}
|
|
871
|
-
stop() {
|
|
872
|
-
if (this.timer) {
|
|
873
|
-
clearInterval(this.timer);
|
|
874
|
-
this.timer = null;
|
|
875
|
-
}
|
|
876
|
-
this.cleanup();
|
|
877
|
-
return this;
|
|
878
|
-
}
|
|
879
|
-
get currentStepIndex() {
|
|
880
|
-
const activeIdx = this.steps.findIndex((s) => s.status === "active");
|
|
881
|
-
if (activeIdx >= 0)
|
|
882
|
-
return activeIdx;
|
|
883
|
-
return this.steps.findIndex((s) => s.status === "pending");
|
|
884
|
-
}
|
|
885
|
-
render() {
|
|
886
|
-
if (!isTTY())
|
|
887
|
-
return;
|
|
888
|
-
if (this.lastRenderedLines > 0) {
|
|
889
|
-
moveCursorUp(this.lastRenderedLines - 1);
|
|
890
|
-
for (let i = 0;i < this.lastRenderedLines; i++) {
|
|
891
|
-
clearLine();
|
|
892
|
-
if (i < this.lastRenderedLines - 1) {
|
|
893
|
-
process.stdout.write(cursor.down(1));
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
moveCursorUp(this.lastRenderedLines - 1);
|
|
897
|
-
}
|
|
898
|
-
const width = getTerminalWidth();
|
|
899
|
-
const lines = [];
|
|
900
|
-
for (let i = 0;i < this.steps.length; i++) {
|
|
901
|
-
const step = this.steps[i];
|
|
902
|
-
const prefix2 = this.showNumbers ? `[${i + 1}/${this.steps.length}] ` : "";
|
|
903
|
-
const duration = this.getStepDuration(step);
|
|
904
|
-
let symbol;
|
|
905
|
-
let text;
|
|
906
|
-
switch (step.status) {
|
|
907
|
-
case "completed":
|
|
908
|
-
symbol = colors.success(this.symbols.success);
|
|
909
|
-
text = `${prefix2}${step.label}${duration}`;
|
|
910
|
-
break;
|
|
911
|
-
case "failed":
|
|
912
|
-
symbol = colors.error(this.symbols.error);
|
|
913
|
-
text = `${prefix2}${step.label}`;
|
|
914
|
-
break;
|
|
915
|
-
case "active":
|
|
916
|
-
symbol = colors.primary(this.spinnerFrames[this.spinnerIndex]);
|
|
917
|
-
text = `${prefix2}${step.label}`;
|
|
918
|
-
break;
|
|
919
|
-
case "skipped":
|
|
920
|
-
symbol = colors.muted(this.symbols.bullet);
|
|
921
|
-
text = colors.muted(`${prefix2}${step.label} (skipped)`);
|
|
922
|
-
break;
|
|
923
|
-
default:
|
|
924
|
-
symbol = colors.muted("○");
|
|
925
|
-
text = colors.muted(`${prefix2}${step.label}`);
|
|
926
|
-
}
|
|
927
|
-
lines.push(truncate(`${symbol} ${text}`, width));
|
|
928
|
-
}
|
|
929
|
-
process.stdout.write(lines.join(`
|
|
930
|
-
`));
|
|
931
|
-
this.lastRenderedLines = lines.length;
|
|
932
|
-
}
|
|
933
|
-
getStepDuration(step) {
|
|
934
|
-
if (step.startTime && step.endTime) {
|
|
935
|
-
const ms = step.endTime - step.startTime;
|
|
936
|
-
return colors.muted(` (${formatDuration(ms)})`);
|
|
937
|
-
}
|
|
938
|
-
return "";
|
|
939
|
-
}
|
|
940
|
-
setupSignalHandler() {
|
|
941
|
-
this.sigintHandler = () => {
|
|
942
|
-
this.cleanup();
|
|
943
|
-
process.exit(130);
|
|
944
|
-
};
|
|
945
|
-
process.on("SIGINT", this.sigintHandler);
|
|
946
|
-
}
|
|
947
|
-
cleanup() {
|
|
948
|
-
if (this.sigintHandler) {
|
|
949
|
-
process.removeListener("SIGINT", this.sigintHandler);
|
|
950
|
-
this.sigintHandler = null;
|
|
951
|
-
}
|
|
952
|
-
showCursor();
|
|
953
|
-
if (isTTY() && this.lastRenderedLines > 0) {
|
|
954
|
-
process.stdout.write(`
|
|
955
|
-
`);
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
}
|
|
419
|
+
// src/utils/progress/summary.ts
|
|
959
420
|
class Summary {
|
|
960
421
|
items = [];
|
|
961
422
|
title;
|
|
@@ -1078,58 +539,6 @@ class Summary {
|
|
|
1078
539
|
function summary(options) {
|
|
1079
540
|
return new Summary(options);
|
|
1080
541
|
}
|
|
1081
|
-
|
|
1082
|
-
// src/utils/filter-options.ts
|
|
1083
|
-
import { mergeFilters, parseListFlag } from "@doccov/sdk";
|
|
1084
|
-
import chalk3 from "chalk";
|
|
1085
|
-
var parseVisibilityFlag = (value) => {
|
|
1086
|
-
if (!value)
|
|
1087
|
-
return;
|
|
1088
|
-
const validTags = ["public", "beta", "alpha", "internal"];
|
|
1089
|
-
const parsed = parseListFlag(value);
|
|
1090
|
-
if (!parsed)
|
|
1091
|
-
return;
|
|
1092
|
-
const result = [];
|
|
1093
|
-
for (const tag of parsed) {
|
|
1094
|
-
const lower = tag.toLowerCase();
|
|
1095
|
-
if (validTags.includes(lower)) {
|
|
1096
|
-
result.push(lower);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
return result.length > 0 ? result : undefined;
|
|
1100
|
-
};
|
|
1101
|
-
var formatList = (label, values) => `${label}: ${values.map((value) => chalk3.cyan(value)).join(", ")}`;
|
|
1102
|
-
var mergeFilterOptions = (config, cliOptions) => {
|
|
1103
|
-
const messages = [];
|
|
1104
|
-
if (config?.include) {
|
|
1105
|
-
messages.push(formatList("include filters from config", config.include));
|
|
1106
|
-
}
|
|
1107
|
-
if (config?.exclude) {
|
|
1108
|
-
messages.push(formatList("exclude filters from config", config.exclude));
|
|
1109
|
-
}
|
|
1110
|
-
if (cliOptions.include) {
|
|
1111
|
-
messages.push(formatList("apply include filters from CLI", cliOptions.include));
|
|
1112
|
-
}
|
|
1113
|
-
if (cliOptions.exclude) {
|
|
1114
|
-
messages.push(formatList("apply exclude filters from CLI", cliOptions.exclude));
|
|
1115
|
-
}
|
|
1116
|
-
if (cliOptions.visibility) {
|
|
1117
|
-
messages.push(formatList("apply visibility filter from CLI", cliOptions.visibility));
|
|
1118
|
-
}
|
|
1119
|
-
const resolved = mergeFilters(config, cliOptions);
|
|
1120
|
-
if (!resolved.include && !resolved.exclude && !cliOptions.visibility) {
|
|
1121
|
-
return { messages };
|
|
1122
|
-
}
|
|
1123
|
-
const source = resolved.source === "override" ? "cli" : resolved.source;
|
|
1124
|
-
return {
|
|
1125
|
-
include: resolved.include,
|
|
1126
|
-
exclude: resolved.exclude,
|
|
1127
|
-
visibility: cliOptions.visibility,
|
|
1128
|
-
source,
|
|
1129
|
-
messages
|
|
1130
|
-
};
|
|
1131
|
-
};
|
|
1132
|
-
|
|
1133
542
|
// src/utils/validation.ts
|
|
1134
543
|
function clampPercentage(value, fallback = 80) {
|
|
1135
544
|
if (Number.isNaN(value))
|
|
@@ -1157,7 +566,7 @@ import {
|
|
|
1157
566
|
previewForgottenExportFixes,
|
|
1158
567
|
serializeJSDoc
|
|
1159
568
|
} from "@doccov/sdk";
|
|
1160
|
-
import
|
|
569
|
+
import chalk4 from "chalk";
|
|
1161
570
|
|
|
1162
571
|
// src/commands/check/utils.ts
|
|
1163
572
|
import * as fs from "node:fs";
|
|
@@ -1231,13 +640,13 @@ async function handleFixes(openpkg, doccov, options, deps) {
|
|
|
1231
640
|
}
|
|
1232
641
|
const { fixable, nonFixable } = categorizeDrifts(allDrifts.map((d) => d.drift));
|
|
1233
642
|
if (fixable.length === 0) {
|
|
1234
|
-
log(
|
|
643
|
+
log(chalk4.yellow(`Found ${nonFixable.length} drift issue(s), but none are auto-fixable.`));
|
|
1235
644
|
return { fixedDriftKeys, editsApplied: 0, filesModified: 0, forgottenExportsFixed: 0 };
|
|
1236
645
|
}
|
|
1237
646
|
log("");
|
|
1238
|
-
log(
|
|
647
|
+
log(chalk4.bold(`Found ${fixable.length} fixable issue(s)`));
|
|
1239
648
|
if (nonFixable.length > 0) {
|
|
1240
|
-
log(
|
|
649
|
+
log(chalk4.gray(`(${nonFixable.length} non-fixable issue(s) skipped)`));
|
|
1241
650
|
}
|
|
1242
651
|
log("");
|
|
1243
652
|
const groupedDrifts = groupByExport(allDrifts.filter((d) => fixable.includes(d.drift)));
|
|
@@ -1270,20 +679,20 @@ async function handleFixes(openpkg, doccov, options, deps) {
|
|
|
1270
679
|
const applyResult = await applyEdits(edits);
|
|
1271
680
|
if (applyResult.errors.length > 0) {
|
|
1272
681
|
for (const err of applyResult.errors) {
|
|
1273
|
-
error(
|
|
682
|
+
error(chalk4.red(` ${err.file}: ${err.error}`));
|
|
1274
683
|
}
|
|
1275
684
|
}
|
|
1276
685
|
const totalFixes = Array.from(editsByFile.values()).reduce((sum, fileEdits) => sum + fileEdits.reduce((s, e) => s + e.fixes.length, 0), 0);
|
|
1277
686
|
log("");
|
|
1278
|
-
log(
|
|
687
|
+
log(chalk4.green(`✓ Applied ${totalFixes} fix(es) to ${applyResult.filesModified} file(s)`));
|
|
1279
688
|
for (const [filePath, fileEdits] of editsByFile) {
|
|
1280
689
|
const relativePath = path3.relative(targetDir, filePath);
|
|
1281
690
|
const fixCount = fileEdits.reduce((s, e) => s + e.fixes.length, 0);
|
|
1282
|
-
log(
|
|
691
|
+
log(chalk4.dim(` ${relativePath} (${fixCount} fixes)`));
|
|
1283
692
|
}
|
|
1284
693
|
if (options.healthScore !== undefined) {
|
|
1285
694
|
log("");
|
|
1286
|
-
log(
|
|
695
|
+
log(chalk4.cyan("Run doccov check again to see updated health score"));
|
|
1287
696
|
}
|
|
1288
697
|
return {
|
|
1289
698
|
fixedDriftKeys,
|
|
@@ -1310,7 +719,7 @@ async function handleForgottenExportFixes(doccov, options, deps) {
|
|
|
1310
719
|
return { fixesApplied: 0, filesModified: 0 };
|
|
1311
720
|
}
|
|
1312
721
|
log("");
|
|
1313
|
-
log(
|
|
722
|
+
log(chalk4.bold(`Found ${fixes.length} forgotten export(s) to fix`));
|
|
1314
723
|
if (isPreview) {
|
|
1315
724
|
displayForgottenExportPreview(fixes, targetDir, log);
|
|
1316
725
|
return { fixesApplied: 0, filesModified: 0 };
|
|
@@ -1318,12 +727,12 @@ async function handleForgottenExportFixes(doccov, options, deps) {
|
|
|
1318
727
|
const result = await applyForgottenExportFixes(fixes);
|
|
1319
728
|
if (result.errors.length > 0) {
|
|
1320
729
|
for (const err of result.errors) {
|
|
1321
|
-
error(
|
|
730
|
+
error(chalk4.red(` ${err.file}: ${err.error}`));
|
|
1322
731
|
}
|
|
1323
732
|
}
|
|
1324
733
|
if (result.fixesApplied > 0) {
|
|
1325
734
|
log("");
|
|
1326
|
-
log(
|
|
735
|
+
log(chalk4.green(`✓ Added ${result.fixesApplied} export(s) to ${result.filesModified} file(s)`));
|
|
1327
736
|
const grouped = new Map;
|
|
1328
737
|
for (const fix of fixes) {
|
|
1329
738
|
const relativePath = path3.relative(targetDir, fix.targetFile);
|
|
@@ -1332,45 +741,45 @@ async function handleForgottenExportFixes(doccov, options, deps) {
|
|
|
1332
741
|
grouped.set(relativePath, types);
|
|
1333
742
|
}
|
|
1334
743
|
for (const [file, types] of grouped) {
|
|
1335
|
-
log(
|
|
744
|
+
log(chalk4.dim(` ${file}: ${types.join(", ")}`));
|
|
1336
745
|
}
|
|
1337
746
|
}
|
|
1338
747
|
return { fixesApplied: result.fixesApplied, filesModified: result.filesModified };
|
|
1339
748
|
}
|
|
1340
749
|
function displayForgottenExportPreview(fixes, targetDir, log) {
|
|
1341
|
-
log(
|
|
750
|
+
log(chalk4.bold("Preview - forgotten exports that would be added:"));
|
|
1342
751
|
log("");
|
|
1343
752
|
const previews = previewForgottenExportFixes(fixes);
|
|
1344
753
|
for (const [filePath, preview] of previews) {
|
|
1345
754
|
const relativePath = path3.relative(targetDir, filePath);
|
|
1346
|
-
log(
|
|
755
|
+
log(chalk4.cyan(`${relativePath}:${preview.insertLine + 1}`));
|
|
1347
756
|
log("");
|
|
1348
757
|
for (const stmt of preview.statements) {
|
|
1349
|
-
log(
|
|
758
|
+
log(chalk4.green(` + ${stmt}`));
|
|
1350
759
|
}
|
|
1351
760
|
log("");
|
|
1352
761
|
}
|
|
1353
|
-
log(
|
|
1354
|
-
log(
|
|
762
|
+
log(chalk4.yellow(`${fixes.length} export(s) would be added.`));
|
|
763
|
+
log(chalk4.gray("Run with --fix to apply these changes."));
|
|
1355
764
|
}
|
|
1356
765
|
function generateEditForExport(exp, drifts, targetDir, log) {
|
|
1357
766
|
if (!exp.source?.file) {
|
|
1358
|
-
log(
|
|
767
|
+
log(chalk4.gray(` Skipping ${exp.name}: no source location`));
|
|
1359
768
|
return null;
|
|
1360
769
|
}
|
|
1361
770
|
if (exp.source.file.endsWith(".d.ts")) {
|
|
1362
|
-
log(
|
|
771
|
+
log(chalk4.gray(` Skipping ${exp.name}: declaration file`));
|
|
1363
772
|
return null;
|
|
1364
773
|
}
|
|
1365
774
|
const filePath = path3.resolve(targetDir, exp.source.file);
|
|
1366
775
|
if (!fs2.existsSync(filePath)) {
|
|
1367
|
-
log(
|
|
776
|
+
log(chalk4.gray(` Skipping ${exp.name}: file not found`));
|
|
1368
777
|
return null;
|
|
1369
778
|
}
|
|
1370
779
|
const sourceFile = createSourceFile(filePath);
|
|
1371
780
|
const location = findJSDocLocation(sourceFile, exp.name, exp.source.line);
|
|
1372
781
|
if (!location) {
|
|
1373
|
-
log(
|
|
782
|
+
log(chalk4.gray(` Skipping ${exp.name}: could not find declaration`));
|
|
1374
783
|
return null;
|
|
1375
784
|
}
|
|
1376
785
|
let existingPatch = {};
|
|
@@ -1395,13 +804,13 @@ function generateEditForExport(exp, drifts, targetDir, log) {
|
|
|
1395
804
|
return { filePath, edit, fixes, existingPatch };
|
|
1396
805
|
}
|
|
1397
806
|
function displayPreview(editsByFile, targetDir, log) {
|
|
1398
|
-
log(
|
|
807
|
+
log(chalk4.bold("Preview - changes that would be made:"));
|
|
1399
808
|
log("");
|
|
1400
809
|
for (const [filePath, fileEdits] of editsByFile) {
|
|
1401
810
|
const relativePath = path3.relative(targetDir, filePath);
|
|
1402
811
|
for (const { export: exp, edit, fixes } of fileEdits) {
|
|
1403
|
-
log(
|
|
1404
|
-
log(
|
|
812
|
+
log(chalk4.cyan(`${relativePath}:${edit.startLine + 1}`));
|
|
813
|
+
log(chalk4.bold(` ${exp.name}`));
|
|
1405
814
|
log("");
|
|
1406
815
|
if (edit.hasExisting && edit.existingJSDoc) {
|
|
1407
816
|
const oldLines = edit.existingJSDoc.split(`
|
|
@@ -1409,26 +818,26 @@ function displayPreview(editsByFile, targetDir, log) {
|
|
|
1409
818
|
const newLines = edit.newJSDoc.split(`
|
|
1410
819
|
`);
|
|
1411
820
|
for (const line of oldLines) {
|
|
1412
|
-
log(
|
|
821
|
+
log(chalk4.red(` - ${line}`));
|
|
1413
822
|
}
|
|
1414
823
|
for (const line of newLines) {
|
|
1415
|
-
log(
|
|
824
|
+
log(chalk4.green(` + ${line}`));
|
|
1416
825
|
}
|
|
1417
826
|
} else {
|
|
1418
827
|
const newLines = edit.newJSDoc.split(`
|
|
1419
828
|
`);
|
|
1420
829
|
for (const line of newLines) {
|
|
1421
|
-
log(
|
|
830
|
+
log(chalk4.green(` + ${line}`));
|
|
1422
831
|
}
|
|
1423
832
|
}
|
|
1424
833
|
log("");
|
|
1425
|
-
log(
|
|
834
|
+
log(chalk4.dim(` Fixes: ${fixes.map((f) => f.description).join(", ")}`));
|
|
1426
835
|
log("");
|
|
1427
836
|
}
|
|
1428
837
|
}
|
|
1429
838
|
const totalFixes = Array.from(editsByFile.values()).reduce((sum, fileEdits) => sum + fileEdits.reduce((s, e) => s + e.fixes.length, 0), 0);
|
|
1430
|
-
log(
|
|
1431
|
-
log(
|
|
839
|
+
log(chalk4.yellow(`${totalFixes} fix(es) across ${editsByFile.size} file(s) would be applied.`));
|
|
840
|
+
log(chalk4.gray("Run with --fix to apply these changes."));
|
|
1432
841
|
}
|
|
1433
842
|
|
|
1434
843
|
// src/commands/check/output.ts
|
|
@@ -1885,7 +1294,7 @@ function computeStats(openpkg, doccov) {
|
|
|
1885
1294
|
import * as fs3 from "node:fs";
|
|
1886
1295
|
import * as path5 from "node:path";
|
|
1887
1296
|
import { DEFAULT_REPORT_DIR, getReportPath } from "@doccov/sdk";
|
|
1888
|
-
import
|
|
1297
|
+
import chalk5 from "chalk";
|
|
1889
1298
|
function writeReport(options) {
|
|
1890
1299
|
const { format, content, outputPath, cwd = process.cwd(), silent = false } = options;
|
|
1891
1300
|
const reportPath = outputPath ? path5.resolve(cwd, outputPath) : path5.resolve(cwd, getReportPath(format));
|
|
@@ -1896,7 +1305,7 @@ function writeReport(options) {
|
|
|
1896
1305
|
fs3.writeFileSync(reportPath, content);
|
|
1897
1306
|
const relativePath = path5.relative(cwd, reportPath);
|
|
1898
1307
|
if (!silent) {
|
|
1899
|
-
console.log(
|
|
1308
|
+
console.log(chalk5.green(`✓ Wrote ${format} report to ${relativePath}`));
|
|
1900
1309
|
}
|
|
1901
1310
|
return { path: reportPath, format, relativePath };
|
|
1902
1311
|
}
|
|
@@ -1943,7 +1352,7 @@ function displayHealthTree(health, log) {
|
|
|
1943
1352
|
const completenessLabel = missingTotal > 0 ? `${health.completeness.score}% (${missingTotal} missing docs)` : `${health.completeness.score}%`;
|
|
1944
1353
|
const completenessColor = getHealthColor(getHealthStatus(health.completeness.score));
|
|
1945
1354
|
log(`${tree.branch} ${colors.muted("completeness")} ${completenessColor(completenessLabel)}`);
|
|
1946
|
-
const accuracyLabel = health.accuracy.
|
|
1355
|
+
const accuracyLabel = health.accuracy.fixable > 0 ? `${health.accuracy.score}% (${health.accuracy.fixable} fixable with --fix)` : `${health.accuracy.score}%`;
|
|
1947
1356
|
const accuracyColor = getHealthColor(getHealthStatus(health.accuracy.score));
|
|
1948
1357
|
const lastBranch = !health.examples ? tree.corner : tree.branch;
|
|
1949
1358
|
log(`${lastBranch} ${colors.muted("accuracy")} ${accuracyColor(accuracyLabel)}`);
|
|
@@ -1992,6 +1401,7 @@ function displayTextOutput(options, deps) {
|
|
|
1992
1401
|
warnBelowApiSurface,
|
|
1993
1402
|
driftExports,
|
|
1994
1403
|
typecheckErrors,
|
|
1404
|
+
runtimeErrors,
|
|
1995
1405
|
staleRefs,
|
|
1996
1406
|
specWarnings,
|
|
1997
1407
|
specInfos,
|
|
@@ -2011,6 +1421,7 @@ function displayTextOutput(options, deps) {
|
|
|
2011
1421
|
const apiSurfaceFailed = minApiSurface !== undefined && apiSurfaceScore < minApiSurface;
|
|
2012
1422
|
const apiSurfaceWarn = warnBelowApiSurface !== undefined && apiSurfaceScore < warnBelowApiSurface && !apiSurfaceFailed;
|
|
2013
1423
|
const hasTypecheckErrors = typecheckErrors.length > 0;
|
|
1424
|
+
const hasRuntimeErrors = runtimeErrors > 0;
|
|
2014
1425
|
if (specWarnings.length > 0 || specInfos.length > 0) {
|
|
2015
1426
|
log("");
|
|
2016
1427
|
for (const diag of specWarnings) {
|
|
@@ -2105,7 +1516,7 @@ function displayTextOutput(options, deps) {
|
|
|
2105
1516
|
}
|
|
2106
1517
|
}
|
|
2107
1518
|
log("");
|
|
2108
|
-
const failed = healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasStaleRefs;
|
|
1519
|
+
const failed = healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasRuntimeErrors || hasStaleRefs;
|
|
2109
1520
|
if (!failed) {
|
|
2110
1521
|
const thresholdParts = [];
|
|
2111
1522
|
thresholdParts.push(`health ${healthScore}% ≥ ${minHealth}%`);
|
|
@@ -2130,6 +1541,9 @@ function displayTextOutput(options, deps) {
|
|
|
2130
1541
|
if (hasTypecheckErrors) {
|
|
2131
1542
|
log(colors.error(`${sym.error} ${typecheckErrors.length} example type errors`));
|
|
2132
1543
|
}
|
|
1544
|
+
if (hasRuntimeErrors) {
|
|
1545
|
+
log(colors.error(`${sym.error} ${runtimeErrors} example runtime errors`));
|
|
1546
|
+
}
|
|
2133
1547
|
if (hasStaleRefs) {
|
|
2134
1548
|
log(colors.error(`${sym.error} ${staleRefs.length} stale references in docs`));
|
|
2135
1549
|
}
|
|
@@ -2147,6 +1561,7 @@ function handleNonTextOutput(options, deps) {
|
|
|
2147
1561
|
minHealth,
|
|
2148
1562
|
minApiSurface,
|
|
2149
1563
|
typecheckErrors,
|
|
1564
|
+
runtimeErrors,
|
|
2150
1565
|
limit,
|
|
2151
1566
|
stdout,
|
|
2152
1567
|
outputPath,
|
|
@@ -2183,7 +1598,8 @@ function handleNonTextOutput(options, deps) {
|
|
|
2183
1598
|
const apiSurfaceScore = doccov.apiSurface?.completeness ?? 100;
|
|
2184
1599
|
const apiSurfaceFailed = minApiSurface !== undefined && apiSurfaceScore < minApiSurface;
|
|
2185
1600
|
const hasTypecheckErrors = typecheckErrors.length > 0;
|
|
2186
|
-
|
|
1601
|
+
const hasRuntimeErrors = runtimeErrors > 0;
|
|
1602
|
+
return !(healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasRuntimeErrors);
|
|
2187
1603
|
}
|
|
2188
1604
|
function displayApiSurfaceOutput(doccov, deps) {
|
|
2189
1605
|
const { log } = deps;
|
|
@@ -2356,7 +1772,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2356
1772
|
};
|
|
2357
1773
|
const resolvedFilters = mergeFilterOptions(config, cliFilters);
|
|
2358
1774
|
if (resolvedFilters.visibility) {
|
|
2359
|
-
log(
|
|
1775
|
+
log(chalk6.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
|
|
2360
1776
|
}
|
|
2361
1777
|
const resolveExternalTypes = !options.skipResolve;
|
|
2362
1778
|
const analyzer = createDocCov({
|
|
@@ -2433,6 +1849,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2433
1849
|
minHealth,
|
|
2434
1850
|
minApiSurface,
|
|
2435
1851
|
typecheckErrors,
|
|
1852
|
+
runtimeErrors: runtimeDrifts.length,
|
|
2436
1853
|
limit: parseInt(options.limit, 10) || 20,
|
|
2437
1854
|
stdout: options.stdout,
|
|
2438
1855
|
outputPath: options.output,
|
|
@@ -2452,6 +1869,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2452
1869
|
warnBelowApiSurface,
|
|
2453
1870
|
driftExports,
|
|
2454
1871
|
typecheckErrors,
|
|
1872
|
+
runtimeErrors: runtimeDrifts.length,
|
|
2455
1873
|
staleRefs,
|
|
2456
1874
|
specWarnings,
|
|
2457
1875
|
specInfos,
|
|
@@ -2461,7 +1879,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2461
1879
|
process.exit(1);
|
|
2462
1880
|
}
|
|
2463
1881
|
} catch (commandError) {
|
|
2464
|
-
error(
|
|
1882
|
+
error(chalk6.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
2465
1883
|
process.exit(1);
|
|
2466
1884
|
}
|
|
2467
1885
|
});
|
|
@@ -2479,7 +1897,7 @@ import {
|
|
|
2479
1897
|
parseMarkdownFiles as parseMarkdownFiles2
|
|
2480
1898
|
} from "@doccov/sdk";
|
|
2481
1899
|
import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
|
|
2482
|
-
import
|
|
1900
|
+
import chalk7 from "chalk";
|
|
2483
1901
|
import { glob as glob2 } from "glob";
|
|
2484
1902
|
var defaultDependencies2 = {
|
|
2485
1903
|
readFileSync: fs4.readFileSync,
|
|
@@ -2554,9 +1972,9 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2554
1972
|
}, null, 2));
|
|
2555
1973
|
} else {
|
|
2556
1974
|
log("");
|
|
2557
|
-
log(
|
|
1975
|
+
log(chalk7.bold("Semver Recommendation"));
|
|
2558
1976
|
log(` Current version: ${currentVersion}`);
|
|
2559
|
-
log(` Recommended: ${
|
|
1977
|
+
log(` Recommended: ${chalk7.cyan(nextVersion)} (${chalk7.yellow(recommendation.bump.toUpperCase())})`);
|
|
2560
1978
|
log(` Reason: ${recommendation.reason}`);
|
|
2561
1979
|
}
|
|
2562
1980
|
return;
|
|
@@ -2586,8 +2004,8 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2586
2004
|
silent: true
|
|
2587
2005
|
});
|
|
2588
2006
|
}
|
|
2589
|
-
const cacheNote = fromCache ?
|
|
2590
|
-
log(
|
|
2007
|
+
const cacheNote = fromCache ? chalk7.cyan(" (cached)") : "";
|
|
2008
|
+
log(chalk7.dim(`Report: ${jsonPath}`) + cacheNote);
|
|
2591
2009
|
}
|
|
2592
2010
|
break;
|
|
2593
2011
|
case "json": {
|
|
@@ -2645,18 +2063,18 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2645
2063
|
checks
|
|
2646
2064
|
});
|
|
2647
2065
|
if (failures.length > 0) {
|
|
2648
|
-
log(
|
|
2066
|
+
log(chalk7.red(`
|
|
2649
2067
|
✗ Check failed`));
|
|
2650
2068
|
for (const f of failures) {
|
|
2651
|
-
log(
|
|
2069
|
+
log(chalk7.red(` - ${f}`));
|
|
2652
2070
|
}
|
|
2653
2071
|
process.exitCode = 1;
|
|
2654
2072
|
} else if (options.strict || minCoverage !== undefined || maxDrift !== undefined) {
|
|
2655
|
-
log(
|
|
2073
|
+
log(chalk7.green(`
|
|
2656
2074
|
✓ All checks passed`));
|
|
2657
2075
|
}
|
|
2658
2076
|
} catch (commandError) {
|
|
2659
|
-
error(
|
|
2077
|
+
error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
2660
2078
|
process.exitCode = 1;
|
|
2661
2079
|
}
|
|
2662
2080
|
});
|
|
@@ -2683,7 +2101,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
|
|
|
2683
2101
|
if (!docsPatterns || docsPatterns.length === 0) {
|
|
2684
2102
|
if (config?.docs?.include) {
|
|
2685
2103
|
docsPatterns = config.docs.include;
|
|
2686
|
-
log(
|
|
2104
|
+
log(chalk7.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
|
|
2687
2105
|
}
|
|
2688
2106
|
}
|
|
2689
2107
|
if (docsPatterns && docsPatterns.length > 0) {
|
|
@@ -2706,37 +2124,37 @@ function loadSpec(filePath, readFileSync3) {
|
|
|
2706
2124
|
}
|
|
2707
2125
|
function printSummary(diff, baseName, headName, fromCache, log) {
|
|
2708
2126
|
log("");
|
|
2709
|
-
const cacheIndicator = fromCache ?
|
|
2710
|
-
log(
|
|
2127
|
+
const cacheIndicator = fromCache ? chalk7.cyan(" (cached)") : "";
|
|
2128
|
+
log(chalk7.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
|
|
2711
2129
|
log("─".repeat(40));
|
|
2712
2130
|
log("");
|
|
2713
|
-
const coverageColor = diff.coverageDelta > 0 ?
|
|
2131
|
+
const coverageColor = diff.coverageDelta > 0 ? chalk7.green : diff.coverageDelta < 0 ? chalk7.red : chalk7.gray;
|
|
2714
2132
|
const coverageSign = diff.coverageDelta > 0 ? "+" : "";
|
|
2715
2133
|
log(` Coverage: ${diff.oldCoverage}% → ${diff.newCoverage}% ${coverageColor(`(${coverageSign}${diff.coverageDelta}%)`)}`);
|
|
2716
2134
|
const breakingCount = diff.breaking.length;
|
|
2717
2135
|
const highSeverity = diff.categorizedBreaking?.filter((c) => c.severity === "high").length ?? 0;
|
|
2718
2136
|
if (breakingCount > 0) {
|
|
2719
|
-
const severityNote = highSeverity > 0 ?
|
|
2720
|
-
log(` Breaking: ${
|
|
2137
|
+
const severityNote = highSeverity > 0 ? chalk7.red(` (${highSeverity} high severity)`) : "";
|
|
2138
|
+
log(` Breaking: ${chalk7.red(breakingCount)} changes${severityNote}`);
|
|
2721
2139
|
} else {
|
|
2722
|
-
log(` Breaking: ${
|
|
2140
|
+
log(` Breaking: ${chalk7.green("0")} changes`);
|
|
2723
2141
|
}
|
|
2724
2142
|
const newCount = diff.nonBreaking.length;
|
|
2725
2143
|
const undocCount = diff.newUndocumented.length;
|
|
2726
2144
|
if (newCount > 0) {
|
|
2727
|
-
const undocNote = undocCount > 0 ?
|
|
2728
|
-
log(` New: ${
|
|
2145
|
+
const undocNote = undocCount > 0 ? chalk7.yellow(` (${undocCount} undocumented)`) : "";
|
|
2146
|
+
log(` New: ${chalk7.green(newCount)} exports${undocNote}`);
|
|
2729
2147
|
}
|
|
2730
2148
|
if (diff.driftIntroduced > 0 || diff.driftResolved > 0) {
|
|
2731
2149
|
const parts = [];
|
|
2732
2150
|
if (diff.driftIntroduced > 0)
|
|
2733
|
-
parts.push(
|
|
2151
|
+
parts.push(chalk7.red(`+${diff.driftIntroduced}`));
|
|
2734
2152
|
if (diff.driftResolved > 0)
|
|
2735
|
-
parts.push(
|
|
2153
|
+
parts.push(chalk7.green(`-${diff.driftResolved}`));
|
|
2736
2154
|
log(` Drift: ${parts.join(", ")}`);
|
|
2737
2155
|
}
|
|
2738
2156
|
const recommendation = recommendSemverBump(diff);
|
|
2739
|
-
const bumpColor = recommendation.bump === "major" ?
|
|
2157
|
+
const bumpColor = recommendation.bump === "major" ? chalk7.red : recommendation.bump === "minor" ? chalk7.yellow : chalk7.green;
|
|
2740
2158
|
log(` Semver: ${bumpColor(recommendation.bump.toUpperCase())} (${recommendation.reason})`);
|
|
2741
2159
|
log("");
|
|
2742
2160
|
}
|
|
@@ -2819,7 +2237,7 @@ function printGitHubAnnotations(diff, log) {
|
|
|
2819
2237
|
// src/commands/init.ts
|
|
2820
2238
|
import * as fs5 from "node:fs";
|
|
2821
2239
|
import * as path7 from "node:path";
|
|
2822
|
-
import
|
|
2240
|
+
import chalk8 from "chalk";
|
|
2823
2241
|
var defaultDependencies3 = {
|
|
2824
2242
|
fileExists: fs5.existsSync,
|
|
2825
2243
|
writeFileSync: fs5.writeFileSync,
|
|
@@ -2837,7 +2255,7 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2837
2255
|
const cwd = path7.resolve(options.cwd);
|
|
2838
2256
|
const existing = findExistingConfig(cwd, fileExists2);
|
|
2839
2257
|
if (existing) {
|
|
2840
|
-
error(
|
|
2258
|
+
error(chalk8.red(`A DocCov config already exists at ${path7.relative(cwd, existing) || "./doccov.config.*"}.`));
|
|
2841
2259
|
process.exitCode = 1;
|
|
2842
2260
|
return;
|
|
2843
2261
|
}
|
|
@@ -2846,7 +2264,7 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2846
2264
|
const fileName = `doccov.config.${targetFormat}`;
|
|
2847
2265
|
const outputPath = path7.join(cwd, fileName);
|
|
2848
2266
|
if (fileExists2(outputPath)) {
|
|
2849
|
-
error(
|
|
2267
|
+
error(chalk8.red(`Cannot create ${fileName}; file already exists.`));
|
|
2850
2268
|
process.exitCode = 1;
|
|
2851
2269
|
return;
|
|
2852
2270
|
}
|
|
@@ -2867,16 +2285,16 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2867
2285
|
}
|
|
2868
2286
|
const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync4);
|
|
2869
2287
|
log("");
|
|
2870
|
-
log(
|
|
2288
|
+
log(chalk8.bold("Add this badge to your README:"));
|
|
2871
2289
|
log("");
|
|
2872
2290
|
if (repoInfo) {
|
|
2873
|
-
log(
|
|
2291
|
+
log(chalk8.cyan(`[](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
|
|
2874
2292
|
} else {
|
|
2875
|
-
log(
|
|
2876
|
-
log(
|
|
2293
|
+
log(chalk8.cyan(`[](https://doccov.dev/OWNER/REPO)`));
|
|
2294
|
+
log(chalk8.dim(" Replace OWNER/REPO with your GitHub repo"));
|
|
2877
2295
|
}
|
|
2878
2296
|
log("");
|
|
2879
|
-
log(
|
|
2297
|
+
log(chalk8.dim("Run `doccov check` to verify your documentation coverage"));
|
|
2880
2298
|
});
|
|
2881
2299
|
}
|
|
2882
2300
|
var findExistingConfig = (cwd, fileExists2) => {
|
|
@@ -3015,7 +2433,7 @@ import {
|
|
|
3015
2433
|
normalize,
|
|
3016
2434
|
validateSpec
|
|
3017
2435
|
} from "@openpkg-ts/spec";
|
|
3018
|
-
import
|
|
2436
|
+
import chalk9 from "chalk";
|
|
3019
2437
|
var defaultDependencies4 = {
|
|
3020
2438
|
createDocCov: (options) => new DocCov2(options),
|
|
3021
2439
|
writeFileSync: fs6.writeFileSync,
|
|
@@ -3028,7 +2446,7 @@ function getArrayLength(value) {
|
|
|
3028
2446
|
function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
|
|
3029
2447
|
const location = diagnostic.location;
|
|
3030
2448
|
const relativePath = location?.file ? path8.relative(baseDir, location.file) || location.file : undefined;
|
|
3031
|
-
const locationText = location && relativePath ?
|
|
2449
|
+
const locationText = location && relativePath ? chalk9.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
|
|
3032
2450
|
const locationPrefix = locationText ? `${locationText} ` : "";
|
|
3033
2451
|
return `${prefix2} ${locationPrefix}${diagnostic.message}`;
|
|
3034
2452
|
}
|
|
@@ -3052,7 +2470,7 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3052
2470
|
config = await loadDocCovConfig(targetDir);
|
|
3053
2471
|
} catch (configError) {
|
|
3054
2472
|
spin.fail("Failed to load config");
|
|
3055
|
-
error(
|
|
2473
|
+
error(chalk9.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
|
|
3056
2474
|
process.exit(1);
|
|
3057
2475
|
}
|
|
3058
2476
|
const cliFilters = {
|
|
@@ -3085,9 +2503,9 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3085
2503
|
const validation = validateSpec(normalized);
|
|
3086
2504
|
if (!validation.ok) {
|
|
3087
2505
|
spin.fail("Validation failed");
|
|
3088
|
-
error(
|
|
2506
|
+
error(chalk9.red("Spec failed schema validation"));
|
|
3089
2507
|
for (const err of validation.errors) {
|
|
3090
|
-
error(
|
|
2508
|
+
error(chalk9.red(`schema: ${err.instancePath || "/"} ${err.message}`));
|
|
3091
2509
|
}
|
|
3092
2510
|
process.exit(1);
|
|
3093
2511
|
}
|
|
@@ -3102,9 +2520,9 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3102
2520
|
const doccovValidation = validateDocCovSpec(doccovSpec);
|
|
3103
2521
|
if (!doccovValidation.ok) {
|
|
3104
2522
|
spin.fail("DocCov validation failed");
|
|
3105
|
-
error(
|
|
2523
|
+
error(chalk9.red("DocCov spec failed schema validation"));
|
|
3106
2524
|
for (const err of doccovValidation.errors) {
|
|
3107
|
-
error(
|
|
2525
|
+
error(chalk9.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
|
|
3108
2526
|
}
|
|
3109
2527
|
process.exit(1);
|
|
3110
2528
|
}
|
|
@@ -3124,12 +2542,12 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3124
2542
|
const doccovPath = path8.join(outputDir, "doccov.json");
|
|
3125
2543
|
writeFileSync4(doccovPath, JSON.stringify(doccovSpec, null, 2));
|
|
3126
2544
|
spin.success(`Generated ${options.output}/`);
|
|
3127
|
-
log(
|
|
3128
|
-
log(
|
|
2545
|
+
log(chalk9.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
|
|
2546
|
+
log(chalk9.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
|
|
3129
2547
|
} else {
|
|
3130
2548
|
spin.success(`Generated ${options.output}/openpkg.json`);
|
|
3131
|
-
log(
|
|
3132
|
-
log(
|
|
2549
|
+
log(chalk9.gray(` ${getArrayLength(normalized.exports)} exports`));
|
|
2550
|
+
log(chalk9.gray(` ${getArrayLength(normalized.types)} types`));
|
|
3133
2551
|
}
|
|
3134
2552
|
}
|
|
3135
2553
|
const isFullGenerationInfo = (gen) => {
|
|
@@ -3141,62 +2559,62 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3141
2559
|
const pm = await detectPackageManager(fileSystem);
|
|
3142
2560
|
const buildCmd = pm.name === "npm" ? "npm run build" : `${pm.name} run build`;
|
|
3143
2561
|
log("");
|
|
3144
|
-
log(
|
|
3145
|
-
log(
|
|
2562
|
+
log(chalk9.yellow("⚠ Runtime extraction requested but no schemas extracted."));
|
|
2563
|
+
log(chalk9.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
|
|
3146
2564
|
}
|
|
3147
2565
|
if (options.verbose && fullGen) {
|
|
3148
2566
|
log("");
|
|
3149
|
-
log(
|
|
3150
|
-
log(
|
|
3151
|
-
log(
|
|
3152
|
-
log(
|
|
3153
|
-
log(
|
|
3154
|
-
log(
|
|
3155
|
-
log(
|
|
2567
|
+
log(chalk9.bold("Generation Info"));
|
|
2568
|
+
log(chalk9.gray(` Timestamp: ${fullGen.timestamp}`));
|
|
2569
|
+
log(chalk9.gray(` Generator: ${fullGen.generator.name}@${fullGen.generator.version}`));
|
|
2570
|
+
log(chalk9.gray(` Entry point: ${fullGen.analysis.entryPoint}`));
|
|
2571
|
+
log(chalk9.gray(` Detected via: ${fullGen.analysis.entryPointSource}`));
|
|
2572
|
+
log(chalk9.gray(` Declaration only: ${fullGen.analysis.isDeclarationOnly ? "yes" : "no"}`));
|
|
2573
|
+
log(chalk9.gray(` External types: ${fullGen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
|
|
3156
2574
|
if (fullGen.analysis.maxTypeDepth) {
|
|
3157
|
-
log(
|
|
2575
|
+
log(chalk9.gray(` Max type depth: ${fullGen.analysis.maxTypeDepth}`));
|
|
3158
2576
|
}
|
|
3159
2577
|
if (fullGen.analysis.schemaExtraction) {
|
|
3160
2578
|
const se = fullGen.analysis.schemaExtraction;
|
|
3161
|
-
log(
|
|
2579
|
+
log(chalk9.gray(` Schema extraction: ${se.method}`));
|
|
3162
2580
|
if (se.runtimeCount) {
|
|
3163
|
-
log(
|
|
2581
|
+
log(chalk9.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
|
|
3164
2582
|
}
|
|
3165
2583
|
}
|
|
3166
2584
|
log("");
|
|
3167
|
-
log(
|
|
3168
|
-
log(
|
|
2585
|
+
log(chalk9.bold("Environment"));
|
|
2586
|
+
log(chalk9.gray(` node_modules: ${fullGen.environment.hasNodeModules ? "found" : "not found"}`));
|
|
3169
2587
|
if (fullGen.environment.packageManager) {
|
|
3170
|
-
log(
|
|
2588
|
+
log(chalk9.gray(` Package manager: ${fullGen.environment.packageManager}`));
|
|
3171
2589
|
}
|
|
3172
2590
|
if (fullGen.environment.isMonorepo) {
|
|
3173
|
-
log(
|
|
2591
|
+
log(chalk9.gray(` Monorepo: yes`));
|
|
3174
2592
|
}
|
|
3175
2593
|
if (fullGen.environment.targetPackage) {
|
|
3176
|
-
log(
|
|
2594
|
+
log(chalk9.gray(` Target package: ${fullGen.environment.targetPackage}`));
|
|
3177
2595
|
}
|
|
3178
2596
|
if (fullGen.issues.length > 0) {
|
|
3179
2597
|
log("");
|
|
3180
|
-
log(
|
|
2598
|
+
log(chalk9.bold("Issues"));
|
|
3181
2599
|
for (const issue of fullGen.issues) {
|
|
3182
|
-
const prefix2 = issue.severity === "error" ?
|
|
2600
|
+
const prefix2 = issue.severity === "error" ? chalk9.red(">") : issue.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
|
|
3183
2601
|
log(`${prefix2} [${issue.code}] ${issue.message}`);
|
|
3184
2602
|
if (issue.suggestion) {
|
|
3185
|
-
log(
|
|
2603
|
+
log(chalk9.gray(` ${issue.suggestion}`));
|
|
3186
2604
|
}
|
|
3187
2605
|
}
|
|
3188
2606
|
}
|
|
3189
2607
|
}
|
|
3190
2608
|
if (options.showDiagnostics && result.diagnostics.length > 0) {
|
|
3191
2609
|
log("");
|
|
3192
|
-
log(
|
|
2610
|
+
log(chalk9.bold("Diagnostics"));
|
|
3193
2611
|
for (const diagnostic of result.diagnostics) {
|
|
3194
|
-
const prefix2 = diagnostic.severity === "error" ?
|
|
2612
|
+
const prefix2 = diagnostic.severity === "error" ? chalk9.red(">") : diagnostic.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
|
|
3195
2613
|
log(formatDiagnosticOutput(prefix2, diagnostic, targetDir));
|
|
3196
2614
|
}
|
|
3197
2615
|
}
|
|
3198
2616
|
} catch (commandError) {
|
|
3199
|
-
error(
|
|
2617
|
+
error(chalk9.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
3200
2618
|
process.exit(1);
|
|
3201
2619
|
}
|
|
3202
2620
|
});
|
|
@@ -3214,7 +2632,7 @@ import {
|
|
|
3214
2632
|
renderSparkline,
|
|
3215
2633
|
saveSnapshot
|
|
3216
2634
|
} from "@doccov/sdk";
|
|
3217
|
-
import
|
|
2635
|
+
import chalk10 from "chalk";
|
|
3218
2636
|
function formatDate(timestamp) {
|
|
3219
2637
|
const date = new Date(timestamp);
|
|
3220
2638
|
return date.toLocaleDateString("en-US", {
|
|
@@ -3227,19 +2645,19 @@ function formatDate(timestamp) {
|
|
|
3227
2645
|
}
|
|
3228
2646
|
function getColorForScore(score) {
|
|
3229
2647
|
if (score >= 90)
|
|
3230
|
-
return
|
|
2648
|
+
return chalk10.green;
|
|
3231
2649
|
if (score >= 70)
|
|
3232
|
-
return
|
|
2650
|
+
return chalk10.yellow;
|
|
3233
2651
|
if (score >= 50)
|
|
3234
|
-
return
|
|
3235
|
-
return
|
|
2652
|
+
return chalk10.hex("#FFA500");
|
|
2653
|
+
return chalk10.red;
|
|
3236
2654
|
}
|
|
3237
2655
|
function formatSnapshot(snapshot) {
|
|
3238
2656
|
const color = getColorForScore(snapshot.coverageScore);
|
|
3239
2657
|
const date = formatDate(snapshot.timestamp);
|
|
3240
2658
|
const version = snapshot.version ? ` v${snapshot.version}` : "";
|
|
3241
|
-
const commit = snapshot.commit ?
|
|
3242
|
-
return `${
|
|
2659
|
+
const commit = snapshot.commit ? chalk10.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
|
|
2660
|
+
return `${chalk10.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version}${commit}`;
|
|
3243
2661
|
}
|
|
3244
2662
|
function formatWeekDate(timestamp) {
|
|
3245
2663
|
const date = new Date(timestamp);
|
|
@@ -3247,10 +2665,10 @@ function formatWeekDate(timestamp) {
|
|
|
3247
2665
|
}
|
|
3248
2666
|
function formatVelocity(velocity) {
|
|
3249
2667
|
if (velocity > 0)
|
|
3250
|
-
return
|
|
2668
|
+
return chalk10.green(`+${velocity}%/day`);
|
|
3251
2669
|
if (velocity < 0)
|
|
3252
|
-
return
|
|
3253
|
-
return
|
|
2670
|
+
return chalk10.red(`${velocity}%/day`);
|
|
2671
|
+
return chalk10.gray("0%/day");
|
|
3254
2672
|
}
|
|
3255
2673
|
function registerTrendsCommand(program) {
|
|
3256
2674
|
program.command("trends").description("Show coverage trends over time").option("--cwd <dir>", "Working directory", process.cwd()).option("-n, --limit <count>", "Number of snapshots to show", "10").option("--prune <count>", "Prune history to keep only N snapshots").option("--record", "Record current coverage to history").option("--json", "Output as JSON").option("--extended", "Show extended trend analysis (velocity, projections)").option("--weekly", "Show weekly summary breakdown").action(async (options) => {
|
|
@@ -3259,14 +2677,14 @@ function registerTrendsCommand(program) {
|
|
|
3259
2677
|
const keepCount = parseInt(options.prune, 10);
|
|
3260
2678
|
if (!Number.isNaN(keepCount)) {
|
|
3261
2679
|
const deleted = pruneHistory(cwd, keepCount);
|
|
3262
|
-
console.log(
|
|
2680
|
+
console.log(chalk10.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
|
|
3263
2681
|
}
|
|
3264
2682
|
return;
|
|
3265
2683
|
}
|
|
3266
2684
|
if (options.record) {
|
|
3267
2685
|
const specPath = path9.resolve(cwd, "openpkg.json");
|
|
3268
2686
|
if (!fs7.existsSync(specPath)) {
|
|
3269
|
-
console.error(
|
|
2687
|
+
console.error(chalk10.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
|
|
3270
2688
|
process.exit(1);
|
|
3271
2689
|
}
|
|
3272
2690
|
const spin = spinner("Recording coverage snapshot");
|
|
@@ -3279,21 +2697,21 @@ function registerTrendsCommand(program) {
|
|
|
3279
2697
|
console.log(formatSnapshot(trend.current));
|
|
3280
2698
|
if (trend.delta !== undefined) {
|
|
3281
2699
|
const deltaStr = formatDelta2(trend.delta);
|
|
3282
|
-
const deltaColor = trend.delta > 0 ?
|
|
3283
|
-
console.log(
|
|
2700
|
+
const deltaColor = trend.delta > 0 ? chalk10.green : trend.delta < 0 ? chalk10.red : chalk10.gray;
|
|
2701
|
+
console.log(chalk10.gray("Change from previous:"), deltaColor(deltaStr));
|
|
3284
2702
|
}
|
|
3285
2703
|
return;
|
|
3286
2704
|
} catch (error) {
|
|
3287
2705
|
spin.fail("Failed to record snapshot");
|
|
3288
|
-
console.error(
|
|
2706
|
+
console.error(chalk10.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
|
|
3289
2707
|
process.exit(1);
|
|
3290
2708
|
}
|
|
3291
2709
|
}
|
|
3292
2710
|
const snapshots = loadSnapshots(cwd);
|
|
3293
2711
|
const limit = parseInt(options.limit ?? "10", 10);
|
|
3294
2712
|
if (snapshots.length === 0) {
|
|
3295
|
-
console.log(
|
|
3296
|
-
console.log(
|
|
2713
|
+
console.log(chalk10.yellow("No coverage history found."));
|
|
2714
|
+
console.log(chalk10.gray("Run `doccov trends --record` to save the current coverage."));
|
|
3297
2715
|
return;
|
|
3298
2716
|
}
|
|
3299
2717
|
if (options.json) {
|
|
@@ -3308,17 +2726,17 @@ function registerTrendsCommand(program) {
|
|
|
3308
2726
|
}
|
|
3309
2727
|
const sparklineData = snapshots.slice(0, 10).map((s) => s.coverageScore).reverse();
|
|
3310
2728
|
const sparkline = renderSparkline(sparklineData);
|
|
3311
|
-
console.log(
|
|
3312
|
-
console.log(
|
|
3313
|
-
console.log(
|
|
2729
|
+
console.log(chalk10.bold("Coverage Trends"));
|
|
2730
|
+
console.log(chalk10.gray(`Package: ${snapshots[0].package}`));
|
|
2731
|
+
console.log(chalk10.gray(`Sparkline: ${sparkline}`));
|
|
3314
2732
|
console.log("");
|
|
3315
2733
|
if (snapshots.length >= 2) {
|
|
3316
2734
|
const oldest = snapshots[snapshots.length - 1];
|
|
3317
2735
|
const newest = snapshots[0];
|
|
3318
2736
|
const overallDelta = newest.coverageScore - oldest.coverageScore;
|
|
3319
2737
|
const deltaStr = formatDelta2(overallDelta);
|
|
3320
|
-
const deltaColor = overallDelta > 0 ?
|
|
3321
|
-
console.log(
|
|
2738
|
+
const deltaColor = overallDelta > 0 ? chalk10.green : overallDelta < 0 ? chalk10.red : chalk10.gray;
|
|
2739
|
+
console.log(chalk10.gray("Overall trend:"), deltaColor(deltaStr), chalk10.gray(`(${snapshots.length} snapshots)`));
|
|
3322
2740
|
console.log("");
|
|
3323
2741
|
}
|
|
3324
2742
|
if (options.extended) {
|
|
@@ -3328,55 +2746,55 @@ function registerTrendsCommand(program) {
|
|
|
3328
2746
|
const specContent = fs7.readFileSync(specPath, "utf-8");
|
|
3329
2747
|
const spec = JSON.parse(specContent);
|
|
3330
2748
|
const extended = getExtendedTrend(spec, cwd);
|
|
3331
|
-
console.log(
|
|
3332
|
-
console.log(
|
|
2749
|
+
console.log(chalk10.bold("Extended Analysis"));
|
|
2750
|
+
console.log(chalk10.gray("90-day retention"));
|
|
3333
2751
|
console.log("");
|
|
3334
2752
|
console.log(" Velocity:");
|
|
3335
2753
|
console.log(` 7-day: ${formatVelocity(extended.velocity7d)}`);
|
|
3336
2754
|
console.log(` 30-day: ${formatVelocity(extended.velocity30d)}`);
|
|
3337
2755
|
console.log(` 90-day: ${formatVelocity(extended.velocity90d)}`);
|
|
3338
2756
|
console.log("");
|
|
3339
|
-
const projColor = extended.projected30d >= extended.trend.current.coverageScore ?
|
|
2757
|
+
const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk10.green : chalk10.red;
|
|
3340
2758
|
console.log(` Projected (30d): ${projColor(`${extended.projected30d}%`)}`);
|
|
3341
|
-
console.log(` All-time high: ${
|
|
3342
|
-
console.log(` All-time low: ${
|
|
2759
|
+
console.log(` All-time high: ${chalk10.green(`${extended.allTimeHigh}%`)}`);
|
|
2760
|
+
console.log(` All-time low: ${chalk10.red(`${extended.allTimeLow}%`)}`);
|
|
3343
2761
|
if (extended.dataRange) {
|
|
3344
2762
|
const startDate = formatWeekDate(extended.dataRange.start);
|
|
3345
2763
|
const endDate = formatWeekDate(extended.dataRange.end);
|
|
3346
|
-
console.log(
|
|
2764
|
+
console.log(chalk10.gray(` Data range: ${startDate} - ${endDate}`));
|
|
3347
2765
|
}
|
|
3348
2766
|
console.log("");
|
|
3349
2767
|
if (options.weekly && extended.weeklySummaries.length > 0) {
|
|
3350
|
-
console.log(
|
|
2768
|
+
console.log(chalk10.bold("Weekly Summary"));
|
|
3351
2769
|
const weekLimit = Math.min(extended.weeklySummaries.length, 8);
|
|
3352
2770
|
for (let i = 0;i < weekLimit; i++) {
|
|
3353
2771
|
const week = extended.weeklySummaries[i];
|
|
3354
2772
|
const weekStart = formatWeekDate(week.weekStart);
|
|
3355
2773
|
const weekEnd = formatWeekDate(week.weekEnd);
|
|
3356
|
-
const deltaColor = week.delta > 0 ?
|
|
2774
|
+
const deltaColor = week.delta > 0 ? chalk10.green : week.delta < 0 ? chalk10.red : chalk10.gray;
|
|
3357
2775
|
const deltaStr = week.delta > 0 ? `+${week.delta}%` : `${week.delta}%`;
|
|
3358
2776
|
console.log(` ${weekStart} - ${weekEnd}: ${week.avgCoverage}% avg ${deltaColor(deltaStr)} (${week.snapshotCount} snapshots)`);
|
|
3359
2777
|
}
|
|
3360
2778
|
if (extended.weeklySummaries.length > weekLimit) {
|
|
3361
|
-
console.log(
|
|
2779
|
+
console.log(chalk10.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
|
|
3362
2780
|
}
|
|
3363
2781
|
console.log("");
|
|
3364
2782
|
}
|
|
3365
2783
|
} catch {
|
|
3366
|
-
console.log(
|
|
2784
|
+
console.log(chalk10.yellow("Could not load openpkg.json for extended analysis"));
|
|
3367
2785
|
console.log("");
|
|
3368
2786
|
}
|
|
3369
2787
|
}
|
|
3370
2788
|
}
|
|
3371
|
-
console.log(
|
|
2789
|
+
console.log(chalk10.bold("History"));
|
|
3372
2790
|
const displaySnapshots = snapshots.slice(0, limit);
|
|
3373
2791
|
for (let i = 0;i < displaySnapshots.length; i++) {
|
|
3374
2792
|
const snapshot = displaySnapshots[i];
|
|
3375
|
-
const prefix2 = i === 0 ?
|
|
2793
|
+
const prefix2 = i === 0 ? chalk10.cyan("→") : " ";
|
|
3376
2794
|
console.log(`${prefix2} ${formatSnapshot(snapshot)}`);
|
|
3377
2795
|
}
|
|
3378
2796
|
if (snapshots.length > limit) {
|
|
3379
|
-
console.log(
|
|
2797
|
+
console.log(chalk10.gray(` ... and ${snapshots.length - limit} more`));
|
|
3380
2798
|
}
|
|
3381
2799
|
});
|
|
3382
2800
|
}
|