@doccov/cli 0.28.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +216 -807
- 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
|
}
|
|
@@ -2356,7 +1765,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2356
1765
|
};
|
|
2357
1766
|
const resolvedFilters = mergeFilterOptions(config, cliFilters);
|
|
2358
1767
|
if (resolvedFilters.visibility) {
|
|
2359
|
-
log(
|
|
1768
|
+
log(chalk6.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
|
|
2360
1769
|
}
|
|
2361
1770
|
const resolveExternalTypes = !options.skipResolve;
|
|
2362
1771
|
const analyzer = createDocCov({
|
|
@@ -2461,7 +1870,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
2461
1870
|
process.exit(1);
|
|
2462
1871
|
}
|
|
2463
1872
|
} catch (commandError) {
|
|
2464
|
-
error(
|
|
1873
|
+
error(chalk6.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
2465
1874
|
process.exit(1);
|
|
2466
1875
|
}
|
|
2467
1876
|
});
|
|
@@ -2479,7 +1888,7 @@ import {
|
|
|
2479
1888
|
parseMarkdownFiles as parseMarkdownFiles2
|
|
2480
1889
|
} from "@doccov/sdk";
|
|
2481
1890
|
import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
|
|
2482
|
-
import
|
|
1891
|
+
import chalk7 from "chalk";
|
|
2483
1892
|
import { glob as glob2 } from "glob";
|
|
2484
1893
|
var defaultDependencies2 = {
|
|
2485
1894
|
readFileSync: fs4.readFileSync,
|
|
@@ -2554,9 +1963,9 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2554
1963
|
}, null, 2));
|
|
2555
1964
|
} else {
|
|
2556
1965
|
log("");
|
|
2557
|
-
log(
|
|
1966
|
+
log(chalk7.bold("Semver Recommendation"));
|
|
2558
1967
|
log(` Current version: ${currentVersion}`);
|
|
2559
|
-
log(` Recommended: ${
|
|
1968
|
+
log(` Recommended: ${chalk7.cyan(nextVersion)} (${chalk7.yellow(recommendation.bump.toUpperCase())})`);
|
|
2560
1969
|
log(` Reason: ${recommendation.reason}`);
|
|
2561
1970
|
}
|
|
2562
1971
|
return;
|
|
@@ -2586,8 +1995,8 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2586
1995
|
silent: true
|
|
2587
1996
|
});
|
|
2588
1997
|
}
|
|
2589
|
-
const cacheNote = fromCache ?
|
|
2590
|
-
log(
|
|
1998
|
+
const cacheNote = fromCache ? chalk7.cyan(" (cached)") : "";
|
|
1999
|
+
log(chalk7.dim(`Report: ${jsonPath}`) + cacheNote);
|
|
2591
2000
|
}
|
|
2592
2001
|
break;
|
|
2593
2002
|
case "json": {
|
|
@@ -2645,18 +2054,18 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
2645
2054
|
checks
|
|
2646
2055
|
});
|
|
2647
2056
|
if (failures.length > 0) {
|
|
2648
|
-
log(
|
|
2057
|
+
log(chalk7.red(`
|
|
2649
2058
|
✗ Check failed`));
|
|
2650
2059
|
for (const f of failures) {
|
|
2651
|
-
log(
|
|
2060
|
+
log(chalk7.red(` - ${f}`));
|
|
2652
2061
|
}
|
|
2653
2062
|
process.exitCode = 1;
|
|
2654
2063
|
} else if (options.strict || minCoverage !== undefined || maxDrift !== undefined) {
|
|
2655
|
-
log(
|
|
2064
|
+
log(chalk7.green(`
|
|
2656
2065
|
✓ All checks passed`));
|
|
2657
2066
|
}
|
|
2658
2067
|
} catch (commandError) {
|
|
2659
|
-
error(
|
|
2068
|
+
error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
2660
2069
|
process.exitCode = 1;
|
|
2661
2070
|
}
|
|
2662
2071
|
});
|
|
@@ -2683,7 +2092,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
|
|
|
2683
2092
|
if (!docsPatterns || docsPatterns.length === 0) {
|
|
2684
2093
|
if (config?.docs?.include) {
|
|
2685
2094
|
docsPatterns = config.docs.include;
|
|
2686
|
-
log(
|
|
2095
|
+
log(chalk7.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
|
|
2687
2096
|
}
|
|
2688
2097
|
}
|
|
2689
2098
|
if (docsPatterns && docsPatterns.length > 0) {
|
|
@@ -2706,37 +2115,37 @@ function loadSpec(filePath, readFileSync3) {
|
|
|
2706
2115
|
}
|
|
2707
2116
|
function printSummary(diff, baseName, headName, fromCache, log) {
|
|
2708
2117
|
log("");
|
|
2709
|
-
const cacheIndicator = fromCache ?
|
|
2710
|
-
log(
|
|
2118
|
+
const cacheIndicator = fromCache ? chalk7.cyan(" (cached)") : "";
|
|
2119
|
+
log(chalk7.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
|
|
2711
2120
|
log("─".repeat(40));
|
|
2712
2121
|
log("");
|
|
2713
|
-
const coverageColor = diff.coverageDelta > 0 ?
|
|
2122
|
+
const coverageColor = diff.coverageDelta > 0 ? chalk7.green : diff.coverageDelta < 0 ? chalk7.red : chalk7.gray;
|
|
2714
2123
|
const coverageSign = diff.coverageDelta > 0 ? "+" : "";
|
|
2715
2124
|
log(` Coverage: ${diff.oldCoverage}% → ${diff.newCoverage}% ${coverageColor(`(${coverageSign}${diff.coverageDelta}%)`)}`);
|
|
2716
2125
|
const breakingCount = diff.breaking.length;
|
|
2717
2126
|
const highSeverity = diff.categorizedBreaking?.filter((c) => c.severity === "high").length ?? 0;
|
|
2718
2127
|
if (breakingCount > 0) {
|
|
2719
|
-
const severityNote = highSeverity > 0 ?
|
|
2720
|
-
log(` Breaking: ${
|
|
2128
|
+
const severityNote = highSeverity > 0 ? chalk7.red(` (${highSeverity} high severity)`) : "";
|
|
2129
|
+
log(` Breaking: ${chalk7.red(breakingCount)} changes${severityNote}`);
|
|
2721
2130
|
} else {
|
|
2722
|
-
log(` Breaking: ${
|
|
2131
|
+
log(` Breaking: ${chalk7.green("0")} changes`);
|
|
2723
2132
|
}
|
|
2724
2133
|
const newCount = diff.nonBreaking.length;
|
|
2725
2134
|
const undocCount = diff.newUndocumented.length;
|
|
2726
2135
|
if (newCount > 0) {
|
|
2727
|
-
const undocNote = undocCount > 0 ?
|
|
2728
|
-
log(` New: ${
|
|
2136
|
+
const undocNote = undocCount > 0 ? chalk7.yellow(` (${undocCount} undocumented)`) : "";
|
|
2137
|
+
log(` New: ${chalk7.green(newCount)} exports${undocNote}`);
|
|
2729
2138
|
}
|
|
2730
2139
|
if (diff.driftIntroduced > 0 || diff.driftResolved > 0) {
|
|
2731
2140
|
const parts = [];
|
|
2732
2141
|
if (diff.driftIntroduced > 0)
|
|
2733
|
-
parts.push(
|
|
2142
|
+
parts.push(chalk7.red(`+${diff.driftIntroduced}`));
|
|
2734
2143
|
if (diff.driftResolved > 0)
|
|
2735
|
-
parts.push(
|
|
2144
|
+
parts.push(chalk7.green(`-${diff.driftResolved}`));
|
|
2736
2145
|
log(` Drift: ${parts.join(", ")}`);
|
|
2737
2146
|
}
|
|
2738
2147
|
const recommendation = recommendSemverBump(diff);
|
|
2739
|
-
const bumpColor = recommendation.bump === "major" ?
|
|
2148
|
+
const bumpColor = recommendation.bump === "major" ? chalk7.red : recommendation.bump === "minor" ? chalk7.yellow : chalk7.green;
|
|
2740
2149
|
log(` Semver: ${bumpColor(recommendation.bump.toUpperCase())} (${recommendation.reason})`);
|
|
2741
2150
|
log("");
|
|
2742
2151
|
}
|
|
@@ -2819,7 +2228,7 @@ function printGitHubAnnotations(diff, log) {
|
|
|
2819
2228
|
// src/commands/init.ts
|
|
2820
2229
|
import * as fs5 from "node:fs";
|
|
2821
2230
|
import * as path7 from "node:path";
|
|
2822
|
-
import
|
|
2231
|
+
import chalk8 from "chalk";
|
|
2823
2232
|
var defaultDependencies3 = {
|
|
2824
2233
|
fileExists: fs5.existsSync,
|
|
2825
2234
|
writeFileSync: fs5.writeFileSync,
|
|
@@ -2837,7 +2246,7 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2837
2246
|
const cwd = path7.resolve(options.cwd);
|
|
2838
2247
|
const existing = findExistingConfig(cwd, fileExists2);
|
|
2839
2248
|
if (existing) {
|
|
2840
|
-
error(
|
|
2249
|
+
error(chalk8.red(`A DocCov config already exists at ${path7.relative(cwd, existing) || "./doccov.config.*"}.`));
|
|
2841
2250
|
process.exitCode = 1;
|
|
2842
2251
|
return;
|
|
2843
2252
|
}
|
|
@@ -2846,7 +2255,7 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2846
2255
|
const fileName = `doccov.config.${targetFormat}`;
|
|
2847
2256
|
const outputPath = path7.join(cwd, fileName);
|
|
2848
2257
|
if (fileExists2(outputPath)) {
|
|
2849
|
-
error(
|
|
2258
|
+
error(chalk8.red(`Cannot create ${fileName}; file already exists.`));
|
|
2850
2259
|
process.exitCode = 1;
|
|
2851
2260
|
return;
|
|
2852
2261
|
}
|
|
@@ -2867,16 +2276,16 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
2867
2276
|
}
|
|
2868
2277
|
const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync4);
|
|
2869
2278
|
log("");
|
|
2870
|
-
log(
|
|
2279
|
+
log(chalk8.bold("Add this badge to your README:"));
|
|
2871
2280
|
log("");
|
|
2872
2281
|
if (repoInfo) {
|
|
2873
|
-
log(
|
|
2282
|
+
log(chalk8.cyan(`[](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
|
|
2874
2283
|
} else {
|
|
2875
|
-
log(
|
|
2876
|
-
log(
|
|
2284
|
+
log(chalk8.cyan(`[](https://doccov.dev/OWNER/REPO)`));
|
|
2285
|
+
log(chalk8.dim(" Replace OWNER/REPO with your GitHub repo"));
|
|
2877
2286
|
}
|
|
2878
2287
|
log("");
|
|
2879
|
-
log(
|
|
2288
|
+
log(chalk8.dim("Run `doccov check` to verify your documentation coverage"));
|
|
2880
2289
|
});
|
|
2881
2290
|
}
|
|
2882
2291
|
var findExistingConfig = (cwd, fileExists2) => {
|
|
@@ -3015,7 +2424,7 @@ import {
|
|
|
3015
2424
|
normalize,
|
|
3016
2425
|
validateSpec
|
|
3017
2426
|
} from "@openpkg-ts/spec";
|
|
3018
|
-
import
|
|
2427
|
+
import chalk9 from "chalk";
|
|
3019
2428
|
var defaultDependencies4 = {
|
|
3020
2429
|
createDocCov: (options) => new DocCov2(options),
|
|
3021
2430
|
writeFileSync: fs6.writeFileSync,
|
|
@@ -3028,7 +2437,7 @@ function getArrayLength(value) {
|
|
|
3028
2437
|
function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
|
|
3029
2438
|
const location = diagnostic.location;
|
|
3030
2439
|
const relativePath = location?.file ? path8.relative(baseDir, location.file) || location.file : undefined;
|
|
3031
|
-
const locationText = location && relativePath ?
|
|
2440
|
+
const locationText = location && relativePath ? chalk9.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
|
|
3032
2441
|
const locationPrefix = locationText ? `${locationText} ` : "";
|
|
3033
2442
|
return `${prefix2} ${locationPrefix}${diagnostic.message}`;
|
|
3034
2443
|
}
|
|
@@ -3052,7 +2461,7 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3052
2461
|
config = await loadDocCovConfig(targetDir);
|
|
3053
2462
|
} catch (configError) {
|
|
3054
2463
|
spin.fail("Failed to load config");
|
|
3055
|
-
error(
|
|
2464
|
+
error(chalk9.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
|
|
3056
2465
|
process.exit(1);
|
|
3057
2466
|
}
|
|
3058
2467
|
const cliFilters = {
|
|
@@ -3085,9 +2494,9 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3085
2494
|
const validation = validateSpec(normalized);
|
|
3086
2495
|
if (!validation.ok) {
|
|
3087
2496
|
spin.fail("Validation failed");
|
|
3088
|
-
error(
|
|
2497
|
+
error(chalk9.red("Spec failed schema validation"));
|
|
3089
2498
|
for (const err of validation.errors) {
|
|
3090
|
-
error(
|
|
2499
|
+
error(chalk9.red(`schema: ${err.instancePath || "/"} ${err.message}`));
|
|
3091
2500
|
}
|
|
3092
2501
|
process.exit(1);
|
|
3093
2502
|
}
|
|
@@ -3102,9 +2511,9 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3102
2511
|
const doccovValidation = validateDocCovSpec(doccovSpec);
|
|
3103
2512
|
if (!doccovValidation.ok) {
|
|
3104
2513
|
spin.fail("DocCov validation failed");
|
|
3105
|
-
error(
|
|
2514
|
+
error(chalk9.red("DocCov spec failed schema validation"));
|
|
3106
2515
|
for (const err of doccovValidation.errors) {
|
|
3107
|
-
error(
|
|
2516
|
+
error(chalk9.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
|
|
3108
2517
|
}
|
|
3109
2518
|
process.exit(1);
|
|
3110
2519
|
}
|
|
@@ -3124,12 +2533,12 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3124
2533
|
const doccovPath = path8.join(outputDir, "doccov.json");
|
|
3125
2534
|
writeFileSync4(doccovPath, JSON.stringify(doccovSpec, null, 2));
|
|
3126
2535
|
spin.success(`Generated ${options.output}/`);
|
|
3127
|
-
log(
|
|
3128
|
-
log(
|
|
2536
|
+
log(chalk9.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
|
|
2537
|
+
log(chalk9.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
|
|
3129
2538
|
} else {
|
|
3130
2539
|
spin.success(`Generated ${options.output}/openpkg.json`);
|
|
3131
|
-
log(
|
|
3132
|
-
log(
|
|
2540
|
+
log(chalk9.gray(` ${getArrayLength(normalized.exports)} exports`));
|
|
2541
|
+
log(chalk9.gray(` ${getArrayLength(normalized.types)} types`));
|
|
3133
2542
|
}
|
|
3134
2543
|
}
|
|
3135
2544
|
const isFullGenerationInfo = (gen) => {
|
|
@@ -3141,62 +2550,62 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
3141
2550
|
const pm = await detectPackageManager(fileSystem);
|
|
3142
2551
|
const buildCmd = pm.name === "npm" ? "npm run build" : `${pm.name} run build`;
|
|
3143
2552
|
log("");
|
|
3144
|
-
log(
|
|
3145
|
-
log(
|
|
2553
|
+
log(chalk9.yellow("⚠ Runtime extraction requested but no schemas extracted."));
|
|
2554
|
+
log(chalk9.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
|
|
3146
2555
|
}
|
|
3147
2556
|
if (options.verbose && fullGen) {
|
|
3148
2557
|
log("");
|
|
3149
|
-
log(
|
|
3150
|
-
log(
|
|
3151
|
-
log(
|
|
3152
|
-
log(
|
|
3153
|
-
log(
|
|
3154
|
-
log(
|
|
3155
|
-
log(
|
|
2558
|
+
log(chalk9.bold("Generation Info"));
|
|
2559
|
+
log(chalk9.gray(` Timestamp: ${fullGen.timestamp}`));
|
|
2560
|
+
log(chalk9.gray(` Generator: ${fullGen.generator.name}@${fullGen.generator.version}`));
|
|
2561
|
+
log(chalk9.gray(` Entry point: ${fullGen.analysis.entryPoint}`));
|
|
2562
|
+
log(chalk9.gray(` Detected via: ${fullGen.analysis.entryPointSource}`));
|
|
2563
|
+
log(chalk9.gray(` Declaration only: ${fullGen.analysis.isDeclarationOnly ? "yes" : "no"}`));
|
|
2564
|
+
log(chalk9.gray(` External types: ${fullGen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
|
|
3156
2565
|
if (fullGen.analysis.maxTypeDepth) {
|
|
3157
|
-
log(
|
|
2566
|
+
log(chalk9.gray(` Max type depth: ${fullGen.analysis.maxTypeDepth}`));
|
|
3158
2567
|
}
|
|
3159
2568
|
if (fullGen.analysis.schemaExtraction) {
|
|
3160
2569
|
const se = fullGen.analysis.schemaExtraction;
|
|
3161
|
-
log(
|
|
2570
|
+
log(chalk9.gray(` Schema extraction: ${se.method}`));
|
|
3162
2571
|
if (se.runtimeCount) {
|
|
3163
|
-
log(
|
|
2572
|
+
log(chalk9.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
|
|
3164
2573
|
}
|
|
3165
2574
|
}
|
|
3166
2575
|
log("");
|
|
3167
|
-
log(
|
|
3168
|
-
log(
|
|
2576
|
+
log(chalk9.bold("Environment"));
|
|
2577
|
+
log(chalk9.gray(` node_modules: ${fullGen.environment.hasNodeModules ? "found" : "not found"}`));
|
|
3169
2578
|
if (fullGen.environment.packageManager) {
|
|
3170
|
-
log(
|
|
2579
|
+
log(chalk9.gray(` Package manager: ${fullGen.environment.packageManager}`));
|
|
3171
2580
|
}
|
|
3172
2581
|
if (fullGen.environment.isMonorepo) {
|
|
3173
|
-
log(
|
|
2582
|
+
log(chalk9.gray(` Monorepo: yes`));
|
|
3174
2583
|
}
|
|
3175
2584
|
if (fullGen.environment.targetPackage) {
|
|
3176
|
-
log(
|
|
2585
|
+
log(chalk9.gray(` Target package: ${fullGen.environment.targetPackage}`));
|
|
3177
2586
|
}
|
|
3178
2587
|
if (fullGen.issues.length > 0) {
|
|
3179
2588
|
log("");
|
|
3180
|
-
log(
|
|
2589
|
+
log(chalk9.bold("Issues"));
|
|
3181
2590
|
for (const issue of fullGen.issues) {
|
|
3182
|
-
const prefix2 = issue.severity === "error" ?
|
|
2591
|
+
const prefix2 = issue.severity === "error" ? chalk9.red(">") : issue.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
|
|
3183
2592
|
log(`${prefix2} [${issue.code}] ${issue.message}`);
|
|
3184
2593
|
if (issue.suggestion) {
|
|
3185
|
-
log(
|
|
2594
|
+
log(chalk9.gray(` ${issue.suggestion}`));
|
|
3186
2595
|
}
|
|
3187
2596
|
}
|
|
3188
2597
|
}
|
|
3189
2598
|
}
|
|
3190
2599
|
if (options.showDiagnostics && result.diagnostics.length > 0) {
|
|
3191
2600
|
log("");
|
|
3192
|
-
log(
|
|
2601
|
+
log(chalk9.bold("Diagnostics"));
|
|
3193
2602
|
for (const diagnostic of result.diagnostics) {
|
|
3194
|
-
const prefix2 = diagnostic.severity === "error" ?
|
|
2603
|
+
const prefix2 = diagnostic.severity === "error" ? chalk9.red(">") : diagnostic.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
|
|
3195
2604
|
log(formatDiagnosticOutput(prefix2, diagnostic, targetDir));
|
|
3196
2605
|
}
|
|
3197
2606
|
}
|
|
3198
2607
|
} catch (commandError) {
|
|
3199
|
-
error(
|
|
2608
|
+
error(chalk9.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
3200
2609
|
process.exit(1);
|
|
3201
2610
|
}
|
|
3202
2611
|
});
|
|
@@ -3214,7 +2623,7 @@ import {
|
|
|
3214
2623
|
renderSparkline,
|
|
3215
2624
|
saveSnapshot
|
|
3216
2625
|
} from "@doccov/sdk";
|
|
3217
|
-
import
|
|
2626
|
+
import chalk10 from "chalk";
|
|
3218
2627
|
function formatDate(timestamp) {
|
|
3219
2628
|
const date = new Date(timestamp);
|
|
3220
2629
|
return date.toLocaleDateString("en-US", {
|
|
@@ -3227,19 +2636,19 @@ function formatDate(timestamp) {
|
|
|
3227
2636
|
}
|
|
3228
2637
|
function getColorForScore(score) {
|
|
3229
2638
|
if (score >= 90)
|
|
3230
|
-
return
|
|
2639
|
+
return chalk10.green;
|
|
3231
2640
|
if (score >= 70)
|
|
3232
|
-
return
|
|
2641
|
+
return chalk10.yellow;
|
|
3233
2642
|
if (score >= 50)
|
|
3234
|
-
return
|
|
3235
|
-
return
|
|
2643
|
+
return chalk10.hex("#FFA500");
|
|
2644
|
+
return chalk10.red;
|
|
3236
2645
|
}
|
|
3237
2646
|
function formatSnapshot(snapshot) {
|
|
3238
2647
|
const color = getColorForScore(snapshot.coverageScore);
|
|
3239
2648
|
const date = formatDate(snapshot.timestamp);
|
|
3240
2649
|
const version = snapshot.version ? ` v${snapshot.version}` : "";
|
|
3241
|
-
const commit = snapshot.commit ?
|
|
3242
|
-
return `${
|
|
2650
|
+
const commit = snapshot.commit ? chalk10.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
|
|
2651
|
+
return `${chalk10.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version}${commit}`;
|
|
3243
2652
|
}
|
|
3244
2653
|
function formatWeekDate(timestamp) {
|
|
3245
2654
|
const date = new Date(timestamp);
|
|
@@ -3247,10 +2656,10 @@ function formatWeekDate(timestamp) {
|
|
|
3247
2656
|
}
|
|
3248
2657
|
function formatVelocity(velocity) {
|
|
3249
2658
|
if (velocity > 0)
|
|
3250
|
-
return
|
|
2659
|
+
return chalk10.green(`+${velocity}%/day`);
|
|
3251
2660
|
if (velocity < 0)
|
|
3252
|
-
return
|
|
3253
|
-
return
|
|
2661
|
+
return chalk10.red(`${velocity}%/day`);
|
|
2662
|
+
return chalk10.gray("0%/day");
|
|
3254
2663
|
}
|
|
3255
2664
|
function registerTrendsCommand(program) {
|
|
3256
2665
|
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 +2668,14 @@ function registerTrendsCommand(program) {
|
|
|
3259
2668
|
const keepCount = parseInt(options.prune, 10);
|
|
3260
2669
|
if (!Number.isNaN(keepCount)) {
|
|
3261
2670
|
const deleted = pruneHistory(cwd, keepCount);
|
|
3262
|
-
console.log(
|
|
2671
|
+
console.log(chalk10.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
|
|
3263
2672
|
}
|
|
3264
2673
|
return;
|
|
3265
2674
|
}
|
|
3266
2675
|
if (options.record) {
|
|
3267
2676
|
const specPath = path9.resolve(cwd, "openpkg.json");
|
|
3268
2677
|
if (!fs7.existsSync(specPath)) {
|
|
3269
|
-
console.error(
|
|
2678
|
+
console.error(chalk10.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
|
|
3270
2679
|
process.exit(1);
|
|
3271
2680
|
}
|
|
3272
2681
|
const spin = spinner("Recording coverage snapshot");
|
|
@@ -3279,21 +2688,21 @@ function registerTrendsCommand(program) {
|
|
|
3279
2688
|
console.log(formatSnapshot(trend.current));
|
|
3280
2689
|
if (trend.delta !== undefined) {
|
|
3281
2690
|
const deltaStr = formatDelta2(trend.delta);
|
|
3282
|
-
const deltaColor = trend.delta > 0 ?
|
|
3283
|
-
console.log(
|
|
2691
|
+
const deltaColor = trend.delta > 0 ? chalk10.green : trend.delta < 0 ? chalk10.red : chalk10.gray;
|
|
2692
|
+
console.log(chalk10.gray("Change from previous:"), deltaColor(deltaStr));
|
|
3284
2693
|
}
|
|
3285
2694
|
return;
|
|
3286
2695
|
} catch (error) {
|
|
3287
2696
|
spin.fail("Failed to record snapshot");
|
|
3288
|
-
console.error(
|
|
2697
|
+
console.error(chalk10.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
|
|
3289
2698
|
process.exit(1);
|
|
3290
2699
|
}
|
|
3291
2700
|
}
|
|
3292
2701
|
const snapshots = loadSnapshots(cwd);
|
|
3293
2702
|
const limit = parseInt(options.limit ?? "10", 10);
|
|
3294
2703
|
if (snapshots.length === 0) {
|
|
3295
|
-
console.log(
|
|
3296
|
-
console.log(
|
|
2704
|
+
console.log(chalk10.yellow("No coverage history found."));
|
|
2705
|
+
console.log(chalk10.gray("Run `doccov trends --record` to save the current coverage."));
|
|
3297
2706
|
return;
|
|
3298
2707
|
}
|
|
3299
2708
|
if (options.json) {
|
|
@@ -3308,17 +2717,17 @@ function registerTrendsCommand(program) {
|
|
|
3308
2717
|
}
|
|
3309
2718
|
const sparklineData = snapshots.slice(0, 10).map((s) => s.coverageScore).reverse();
|
|
3310
2719
|
const sparkline = renderSparkline(sparklineData);
|
|
3311
|
-
console.log(
|
|
3312
|
-
console.log(
|
|
3313
|
-
console.log(
|
|
2720
|
+
console.log(chalk10.bold("Coverage Trends"));
|
|
2721
|
+
console.log(chalk10.gray(`Package: ${snapshots[0].package}`));
|
|
2722
|
+
console.log(chalk10.gray(`Sparkline: ${sparkline}`));
|
|
3314
2723
|
console.log("");
|
|
3315
2724
|
if (snapshots.length >= 2) {
|
|
3316
2725
|
const oldest = snapshots[snapshots.length - 1];
|
|
3317
2726
|
const newest = snapshots[0];
|
|
3318
2727
|
const overallDelta = newest.coverageScore - oldest.coverageScore;
|
|
3319
2728
|
const deltaStr = formatDelta2(overallDelta);
|
|
3320
|
-
const deltaColor = overallDelta > 0 ?
|
|
3321
|
-
console.log(
|
|
2729
|
+
const deltaColor = overallDelta > 0 ? chalk10.green : overallDelta < 0 ? chalk10.red : chalk10.gray;
|
|
2730
|
+
console.log(chalk10.gray("Overall trend:"), deltaColor(deltaStr), chalk10.gray(`(${snapshots.length} snapshots)`));
|
|
3322
2731
|
console.log("");
|
|
3323
2732
|
}
|
|
3324
2733
|
if (options.extended) {
|
|
@@ -3328,55 +2737,55 @@ function registerTrendsCommand(program) {
|
|
|
3328
2737
|
const specContent = fs7.readFileSync(specPath, "utf-8");
|
|
3329
2738
|
const spec = JSON.parse(specContent);
|
|
3330
2739
|
const extended = getExtendedTrend(spec, cwd);
|
|
3331
|
-
console.log(
|
|
3332
|
-
console.log(
|
|
2740
|
+
console.log(chalk10.bold("Extended Analysis"));
|
|
2741
|
+
console.log(chalk10.gray("90-day retention"));
|
|
3333
2742
|
console.log("");
|
|
3334
2743
|
console.log(" Velocity:");
|
|
3335
2744
|
console.log(` 7-day: ${formatVelocity(extended.velocity7d)}`);
|
|
3336
2745
|
console.log(` 30-day: ${formatVelocity(extended.velocity30d)}`);
|
|
3337
2746
|
console.log(` 90-day: ${formatVelocity(extended.velocity90d)}`);
|
|
3338
2747
|
console.log("");
|
|
3339
|
-
const projColor = extended.projected30d >= extended.trend.current.coverageScore ?
|
|
2748
|
+
const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk10.green : chalk10.red;
|
|
3340
2749
|
console.log(` Projected (30d): ${projColor(`${extended.projected30d}%`)}`);
|
|
3341
|
-
console.log(` All-time high: ${
|
|
3342
|
-
console.log(` All-time low: ${
|
|
2750
|
+
console.log(` All-time high: ${chalk10.green(`${extended.allTimeHigh}%`)}`);
|
|
2751
|
+
console.log(` All-time low: ${chalk10.red(`${extended.allTimeLow}%`)}`);
|
|
3343
2752
|
if (extended.dataRange) {
|
|
3344
2753
|
const startDate = formatWeekDate(extended.dataRange.start);
|
|
3345
2754
|
const endDate = formatWeekDate(extended.dataRange.end);
|
|
3346
|
-
console.log(
|
|
2755
|
+
console.log(chalk10.gray(` Data range: ${startDate} - ${endDate}`));
|
|
3347
2756
|
}
|
|
3348
2757
|
console.log("");
|
|
3349
2758
|
if (options.weekly && extended.weeklySummaries.length > 0) {
|
|
3350
|
-
console.log(
|
|
2759
|
+
console.log(chalk10.bold("Weekly Summary"));
|
|
3351
2760
|
const weekLimit = Math.min(extended.weeklySummaries.length, 8);
|
|
3352
2761
|
for (let i = 0;i < weekLimit; i++) {
|
|
3353
2762
|
const week = extended.weeklySummaries[i];
|
|
3354
2763
|
const weekStart = formatWeekDate(week.weekStart);
|
|
3355
2764
|
const weekEnd = formatWeekDate(week.weekEnd);
|
|
3356
|
-
const deltaColor = week.delta > 0 ?
|
|
2765
|
+
const deltaColor = week.delta > 0 ? chalk10.green : week.delta < 0 ? chalk10.red : chalk10.gray;
|
|
3357
2766
|
const deltaStr = week.delta > 0 ? `+${week.delta}%` : `${week.delta}%`;
|
|
3358
2767
|
console.log(` ${weekStart} - ${weekEnd}: ${week.avgCoverage}% avg ${deltaColor(deltaStr)} (${week.snapshotCount} snapshots)`);
|
|
3359
2768
|
}
|
|
3360
2769
|
if (extended.weeklySummaries.length > weekLimit) {
|
|
3361
|
-
console.log(
|
|
2770
|
+
console.log(chalk10.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
|
|
3362
2771
|
}
|
|
3363
2772
|
console.log("");
|
|
3364
2773
|
}
|
|
3365
2774
|
} catch {
|
|
3366
|
-
console.log(
|
|
2775
|
+
console.log(chalk10.yellow("Could not load openpkg.json for extended analysis"));
|
|
3367
2776
|
console.log("");
|
|
3368
2777
|
}
|
|
3369
2778
|
}
|
|
3370
2779
|
}
|
|
3371
|
-
console.log(
|
|
2780
|
+
console.log(chalk10.bold("History"));
|
|
3372
2781
|
const displaySnapshots = snapshots.slice(0, limit);
|
|
3373
2782
|
for (let i = 0;i < displaySnapshots.length; i++) {
|
|
3374
2783
|
const snapshot = displaySnapshots[i];
|
|
3375
|
-
const prefix2 = i === 0 ?
|
|
2784
|
+
const prefix2 = i === 0 ? chalk10.cyan("→") : " ";
|
|
3376
2785
|
console.log(`${prefix2} ${formatSnapshot(snapshot)}`);
|
|
3377
2786
|
}
|
|
3378
2787
|
if (snapshots.length > limit) {
|
|
3379
|
-
console.log(
|
|
2788
|
+
console.log(chalk10.gray(` ... and ${snapshots.length - limit} more`));
|
|
3380
2789
|
}
|
|
3381
2790
|
});
|
|
3382
2791
|
}
|