@driftdev/cli 0.35.1 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/drift.js CHANGED
@@ -175286,6 +175286,25 @@ function validateConfig(raw) {
175286
175286
  if (docs.exclude !== undefined && (!Array.isArray(docs.exclude) || !docs.exclude.every((i) => typeof i === "string"))) {
175287
175287
  errors.push('"docs.exclude" must be an array of strings');
175288
175288
  }
175289
+ if (docs.remote !== undefined) {
175290
+ if (!Array.isArray(docs.remote)) {
175291
+ errors.push('"docs.remote" must be an array');
175292
+ } else {
175293
+ for (let i = 0;i < docs.remote.length; i++) {
175294
+ const target = docs.remote[i];
175295
+ if (typeof target !== "object" || target === null) {
175296
+ errors.push(`"docs.remote[${i}]" must be an object`);
175297
+ continue;
175298
+ }
175299
+ if (typeof target.repo !== "string" || !/^[^/]+\/[^/]+$/.test(target.repo)) {
175300
+ errors.push(`"docs.remote[${i}].repo" must be in "owner/repo" format`);
175301
+ }
175302
+ if (target.branch !== undefined && typeof target.branch !== "string") {
175303
+ errors.push(`"docs.remote[${i}].branch" must be a string`);
175304
+ }
175305
+ }
175306
+ }
175307
+ }
175289
175308
  }
175290
175309
  }
175291
175310
  if (errors.length > 0)
@@ -175651,14 +175670,17 @@ function renderBatchLint(data) {
175651
175670
  function renderBatchList(data) {
175652
175671
  const lines = [""];
175653
175672
  const rows = data.packages;
175673
+ const columnLabel = data.filter === "undocumented" ? "UNDOCUMENTED" : "EXPORTS";
175654
175674
  const nameW = Math.max(7, ...rows.map((r) => r.name.length));
175655
- lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray("EXPORTS")}`));
175675
+ lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray(columnLabel)}`));
175656
175676
  for (const r of rows) {
175657
- lines.push(indent(`${pad(r.name, nameW)} ${r.count}`));
175677
+ const countStr = data.filter === "undocumented" ? r.count === 0 ? c.green("0") : c.yellow(String(r.count)) : String(r.count);
175678
+ lines.push(indent(`${pad(r.name, nameW)} ${countStr}`));
175658
175679
  }
175659
175680
  const total = rows.reduce((s, r) => s + r.count, 0);
175660
175681
  lines.push("");
175661
- lines.push(indent(`${c.bold("Total")}: ${total} exports across ${rows.length} packages`));
175682
+ const label = data.filter === "undocumented" ? "undocumented exports" : "exports";
175683
+ lines.push(indent(`${c.bold("Total")}: ${total} ${label} across ${rows.length} packages`));
175662
175684
  lines.push("");
175663
175685
  return lines.join(`
175664
175686
  `);
@@ -176013,7 +176035,7 @@ function getPackageInfo(cwd) {
176013
176035
  }
176014
176036
  }
176015
176037
  function registerHealthCommand(program) {
176016
- program.command("health [entry]", { isDefault: true }).description("Show documentation health score (default command)").option("--min <n>", "Minimum health threshold (exit 1 if below)").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (entry, options) => {
176038
+ program.command("health [entry]").description("Show documentation health score (default command)").option("--min <n>", "Minimum health threshold (exit 1 if below)").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (entry, options) => {
176017
176039
  const startTime = Date.now();
176018
176040
  const version = getVersion();
176019
176041
  try {
@@ -176517,6 +176539,7 @@ import { existsSync as existsSync17, readFileSync as readFileSync20 } from "node
176517
176539
  import * as path24 from "node:path";
176518
176540
  import { fileURLToPath as fileURLToPath5 } from "node:url";
176519
176541
  import { computeDrift as computeDrift2 } from "@driftdev/sdk";
176542
+ import { diffSpec as diffSpec6, categorizeBreakingChanges as categorizeBreakingChanges4 } from "@openpkg-ts/spec";
176520
176543
 
176521
176544
  // src/formatters/ci.ts
176522
176545
  function renderCi(data) {
@@ -176771,20 +176794,104 @@ function filterChangedPackages(allDirs, changedFiles) {
176771
176794
  return changedFiles.some((f) => prefix === "" || f.startsWith(prefix));
176772
176795
  });
176773
176796
  }
176774
- function buildMarkdownTable(results, pass) {
176797
+ function buildPRComment(results, pass, commit) {
176775
176798
  const lines = [];
176776
- lines.push(`## Drift CI Results
176799
+ const multi = results.length > 1;
176800
+ lines.push(`## Drift CI
176777
176801
  `);
176778
176802
  lines.push("| Package | Exports | Coverage | Lint | Status |");
176779
176803
  lines.push("|---------|---------|----------|------|--------|");
176780
176804
  for (const r of results) {
176781
- const cov = r.coveragePass ? `${r.coverage}%` : `${r.coverage}% ❌`;
176782
- const lint = r.lintPass ? `${r.lintIssues} issues` : `${r.lintIssues} issues ❌`;
176783
- const status = r.pass ? "" : "";
176805
+ const cov = r.coveragePass ? `${r.coverage}%` : `${r.coverage}% :x:`;
176806
+ const lint = r.lintPass ? `${r.lintIssues}` : `${r.lintIssues} :x:`;
176807
+ const status = r.pass ? ":white_check_mark:" : ":x:";
176784
176808
  lines.push(`| ${r.name} | ${r.exports} | ${cov} | ${lint} | ${status} |`);
176785
176809
  }
176810
+ const apiEntries = [];
176811
+ for (const r of results) {
176812
+ if (!r.diff)
176813
+ continue;
176814
+ const { breaking, added, changed } = r.diff;
176815
+ if (breaking.length === 0 && added.length === 0 && changed.length === 0)
176816
+ continue;
176817
+ const pkgLines = [];
176818
+ if (multi)
176819
+ pkgLines.push(`
176820
+ **${r.name}**`);
176821
+ for (const name of added)
176822
+ pkgLines.push(`- :heavy_plus_sign: Added: \`${name}\``);
176823
+ for (const name of changed)
176824
+ pkgLines.push(`- :pencil2: Changed: \`${name}\``);
176825
+ for (const b of breaking)
176826
+ pkgLines.push(`- :x: Removed: \`${b.name}\``);
176827
+ apiEntries.push(pkgLines.join(`
176828
+ `));
176829
+ }
176830
+ if (apiEntries.length > 0) {
176831
+ const total = results.reduce((s, r) => s + (r.diff ? r.diff.breaking.length + r.diff.added.length + r.diff.changed.length : 0), 0);
176832
+ lines.push("");
176833
+ lines.push(`<details>
176834
+ <summary>API Changes (${total})</summary>
176835
+ `);
176836
+ lines.push(apiEntries.join(`
176837
+ `));
176838
+ lines.push(`
176839
+ </details>`);
176840
+ }
176841
+ const breakingEntries = [];
176842
+ for (const r of results) {
176843
+ if (!r.diff?.breaking.length)
176844
+ continue;
176845
+ const pkgLines = [];
176846
+ if (multi)
176847
+ pkgLines.push(`
176848
+ **${r.name}**`);
176849
+ for (const b of r.diff.breaking) {
176850
+ pkgLines.push(`- \`${b.name}\`${b.reason ? `: ${b.reason}` : ""}`);
176851
+ }
176852
+ breakingEntries.push(pkgLines.join(`
176853
+ `));
176854
+ }
176855
+ if (breakingEntries.length > 0) {
176856
+ const total = results.reduce((s, r) => s + (r.diff?.breaking.length ?? 0), 0);
176857
+ lines.push("");
176858
+ lines.push(`<details>
176859
+ <summary>Breaking Changes (${total})</summary>
176860
+ `);
176861
+ lines.push(breakingEntries.join(`
176862
+ `));
176863
+ lines.push(`
176864
+ </details>`);
176865
+ }
176866
+ const undocEntries = [];
176867
+ for (const r of results) {
176868
+ if (!r.undocumented?.length)
176869
+ continue;
176870
+ const pkgLines = [];
176871
+ if (multi)
176872
+ pkgLines.push(`
176873
+ **${r.name}**`);
176874
+ for (const name of r.undocumented)
176875
+ pkgLines.push(`- \`${name}\``);
176876
+ undocEntries.push(pkgLines.join(`
176877
+ `));
176878
+ }
176879
+ if (undocEntries.length > 0) {
176880
+ const total = results.reduce((s, r) => s + (r.undocumented?.length ?? 0), 0);
176881
+ lines.push("");
176882
+ lines.push(`<details>
176883
+ <summary>Undocumented Exports (${total})</summary>
176884
+ `);
176885
+ lines.push(undocEntries.join(`
176886
+ `));
176887
+ lines.push(`
176888
+ </details>`);
176889
+ }
176786
176890
  lines.push("");
176787
- lines.push(pass ? "**All checks passed.**" : "**Some checks failed.**");
176891
+ lines.push("---");
176892
+ const ts18 = new Date().toISOString();
176893
+ const sha = commit ?? "";
176894
+ lines.push(`*[Drift](https://github.com/driftdev/drift) · ${ts18}${sha ? ` · ${sha}` : ""}*`);
176788
176895
  return lines.join(`
176789
176896
  `);
176790
176897
  }
@@ -176852,6 +176959,24 @@ function registerCiCommand(program) {
176852
176959
  lintIssues += drifts.length;
176853
176960
  }
176854
176961
  const lintPass = config.lint === false || lintIssues === 0;
176962
+ let diff;
176963
+ let undocumented;
176964
+ if (gh.isPR && gh.baseRef) {
176965
+ try {
176966
+ const relEntry = path24.relative(cwd, detectEntry(absDir));
176967
+ const oldSpec = await extractSpecFromRef(`origin/${gh.baseRef}`, relEntry, cwd);
176968
+ const diffResult = diffSpec6(oldSpec, spec);
176969
+ const breaking = categorizeBreakingChanges4(diffResult.breaking, oldSpec, spec);
176970
+ diff = {
176971
+ breaking: breaking.map((b) => ({ name: b.name, reason: b.reason })),
176972
+ added: diffResult.nonBreaking,
176973
+ changed: diffResult.docsOnly
176974
+ };
176975
+ } catch {}
176976
+ const undoc = exports.filter((e) => !e.description?.trim()).map((e) => e.name);
176977
+ if (undoc.length > 0)
176978
+ undocumented = undoc;
176979
+ }
176855
176980
  results.push({
176856
176981
  name,
176857
176982
  coverage,
@@ -176859,7 +176984,9 @@ function registerCiCommand(program) {
176859
176984
  lintIssues,
176860
176985
  lintPass,
176861
176986
  exports: total,
176862
- pass: coveragePass && lintPass
176987
+ pass: coveragePass && lintPass,
176988
+ diff,
176989
+ undocumented
176863
176990
  });
176864
176991
  } catch {
176865
176992
  results.push({
@@ -176897,7 +177024,7 @@ function registerCiCommand(program) {
176897
177024
  });
176898
177025
  } catch {}
176899
177026
  if (gh.isPR && gh.token && gh.repository) {
176900
- const md = buildMarkdownTable(results, allPass);
177027
+ const md = buildPRComment(results, allPass, commit);
176901
177028
  writeStepSummary(md);
176902
177029
  const prNumber = getPRNumber(gh.eventPath);
176903
177030
  if (prNumber) {
@@ -177244,7 +177371,7 @@ function registerExamplesCommand(program) {
177244
177371
  import { readFileSync as readFileSync23 } from "node:fs";
177245
177372
  import * as path27 from "node:path";
177246
177373
  import { fileURLToPath as fileURLToPath8 } from "node:url";
177247
- import { diffSpec as diffSpec6, categorizeBreakingChanges as categorizeBreakingChanges4 } from "@openpkg-ts/spec";
177374
+ import { diffSpec as diffSpec7, categorizeBreakingChanges as categorizeBreakingChanges5 } from "@openpkg-ts/spec";
177248
177375
 
177249
177376
  // src/formatters/diff.ts
177250
177377
  function renderDiff(data) {
@@ -177310,8 +177437,8 @@ function registerDiffCommand(program) {
177310
177437
  try {
177311
177438
  const args = [oldPath, newPath].filter(Boolean);
177312
177439
  const { oldSpec, newSpec } = await resolveSpecs({ args, ...options });
177313
- const diff = diffSpec6(oldSpec, newSpec);
177314
- const breaking = categorizeBreakingChanges4(diff.breaking, oldSpec, newSpec);
177440
+ const diff = diffSpec7(oldSpec, newSpec);
177441
+ const breaking = categorizeBreakingChanges5(diff.breaking, oldSpec, newSpec);
177315
177442
  const data = {
177316
177443
  breaking,
177317
177444
  added: diff.nonBreaking,
@@ -177831,7 +177958,7 @@ function flattenConfig(obj, prefix = "") {
177831
177958
  }
177832
177959
  function registerConfigCommand(program) {
177833
177960
  const cmd = program.command("config").description("Manage drift configuration");
177834
- cmd.command("list").description("Show all config values").action(() => {
177961
+ cmd.command("list").alias("show").description("Show all config values").action(() => {
177835
177962
  const startTime = Date.now();
177836
177963
  const version = getVersion12();
177837
177964
  try {
@@ -178032,10 +178159,11 @@ function renderInit(data) {
178032
178159
  const lines = [""];
178033
178160
  lines.push(indent(`${data.isMonorepo ? "Monorepo" : "Project"} scan ${c.gray(`${data.packages.length} package${data.packages.length === 1 ? "" : "s"}`)}`));
178034
178161
  lines.push("");
178035
- const header = ["PACKAGE", "ENTRY", "EXPORTS", "COVERAGE"];
178162
+ const header = ["Package", "Exports", "Coverage", "Health"];
178036
178163
  const rows = data.packages.map((pkg) => {
178037
- const color = coverageColor(pkg.coverage);
178038
- return [pkg.name, pkg.entry, String(pkg.exports), color(`${pkg.coverage}%`)];
178164
+ const covColor = coverageColor(pkg.coverage);
178165
+ const healthColor = coverageColor(pkg.health);
178166
+ return [pkg.name, String(pkg.exports), covColor(`${pkg.coverage}%`), healthColor(`${pkg.health}%`)];
178039
178167
  });
178040
178168
  lines.push(indent(table([header, ...rows])));
178041
178169
  lines.push("");
@@ -178050,6 +178178,277 @@ function renderInit(data) {
178050
178178
  `);
178051
178179
  }
178052
178180
 
178181
+ // src/utils/progress/spinner.ts
178182
+ import chalk3 from "chalk";
178183
+
178184
+ // src/utils/progress/colors.ts
178185
+ import chalk2 from "chalk";
178186
+ var colors = {
178187
+ success: chalk2.green,
178188
+ error: chalk2.red,
178189
+ warning: chalk2.yellow,
178190
+ info: chalk2.cyan,
178191
+ muted: chalk2.gray,
178192
+ bold: chalk2.bold,
178193
+ dim: chalk2.dim,
178194
+ underline: chalk2.underline,
178195
+ primary: chalk2.cyan,
178196
+ secondary: chalk2.magenta,
178197
+ path: chalk2.cyan,
178198
+ number: chalk2.yellow,
178199
+ code: chalk2.gray
178200
+ };
178201
+ var symbols = {
178202
+ success: "✓",
178203
+ error: "✗",
178204
+ warning: "⚠",
178205
+ info: "ℹ",
178206
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
178207
+ bullet: "•",
178208
+ arrow: "→",
178209
+ arrowRight: "›",
178210
+ line: "─",
178211
+ corner: "└",
178212
+ vertical: "│",
178213
+ horizontalLine: "─"
178214
+ };
178215
+ var asciiSymbols = {
178216
+ success: "+",
178217
+ error: "x",
178218
+ warning: "!",
178219
+ info: "i",
178220
+ spinner: ["-", "\\", "|", "/"],
178221
+ bullet: "*",
178222
+ arrow: "->",
178223
+ arrowRight: ">",
178224
+ line: "-",
178225
+ corner: "\\",
178226
+ vertical: "|",
178227
+ horizontalLine: "-"
178228
+ };
178229
+ function getSymbols(unicodeSupport = true) {
178230
+ return unicodeSupport ? symbols : asciiSymbols;
178231
+ }
178232
+ var prefix = {
178233
+ success: colors.success(symbols.success),
178234
+ error: colors.error(symbols.error),
178235
+ warning: colors.warning(symbols.warning),
178236
+ info: colors.info(symbols.info)
178237
+ };
178238
+
178239
+ // src/utils/progress/utils.ts
178240
+ function isTTY2() {
178241
+ return Boolean(process.stdout.isTTY);
178242
+ }
178243
+ function isCI() {
178244
+ return Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL || process.env.BUILDKITE || process.env.TEAMCITY_VERSION || process.env.TF_BUILD || process.env.CODEBUILD_BUILD_ID || process.env.BITBUCKET_BUILD_NUMBER);
178245
+ }
178246
+ function isInteractive() {
178247
+ return isTTY2() && !isCI();
178248
+ }
178249
+ function supportsUnicode() {
178250
+ if (process.platform === "win32") {
178251
+ return Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
178252
+ }
178253
+ return process.env.TERM !== "linux";
178254
+ }
178255
+ var MIN_TERMINAL_WIDTH = 40;
178256
+ var DEFAULT_TERMINAL_WIDTH = 80;
178257
+ function getTerminalWidth() {
178258
+ const width = process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
178259
+ return Math.max(width, MIN_TERMINAL_WIDTH);
178260
+ }
178261
+ var cursor = {
178262
+ hide: "\x1B[?25l",
178263
+ show: "\x1B[?25h",
178264
+ up: (n = 1) => `\x1B[${n}A`,
178265
+ down: (n = 1) => `\x1B[${n}B`,
178266
+ forward: (n = 1) => `\x1B[${n}C`,
178267
+ back: (n = 1) => `\x1B[${n}D`,
178268
+ left: "\x1B[G",
178269
+ clearLine: "\x1B[2K",
178270
+ clearDown: "\x1B[J",
178271
+ save: "\x1B[s",
178272
+ restore: "\x1B[u"
178273
+ };
178274
+ function clearLine() {
178275
+ if (isTTY2()) {
178276
+ process.stdout.write(cursor.clearLine + cursor.left);
178277
+ }
178278
+ }
178279
+ function hideCursor() {
178280
+ if (isTTY2()) {
178281
+ process.stdout.write(cursor.hide);
178282
+ }
178283
+ }
178284
+ function showCursor() {
178285
+ if (isTTY2()) {
178286
+ process.stdout.write(cursor.show);
178287
+ }
178288
+ }
178289
+ function truncate(text, maxLength) {
178290
+ if (text.length <= maxLength)
178291
+ return text;
178292
+ return `${text.slice(0, maxLength - 1)}…`;
178293
+ }
178294
+
178295
+ // src/utils/progress/spinner.ts
178296
+ var spinnerColors = {
178297
+ cyan: chalk3.cyan,
178298
+ yellow: chalk3.yellow,
178299
+ green: chalk3.green,
178300
+ red: chalk3.red,
178301
+ magenta: chalk3.magenta,
178302
+ blue: chalk3.blue,
178303
+ white: chalk3.white
178304
+ };
178305
+ var FRAME_SETS = {
178306
+ dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
178307
+ circle: ["◐", "◓", "◑", "◒"]
178308
+ };
178309
+ var ASCII_FRAME_SET = ["-", "\\", "|", "/"];
178310
+
178311
+ class Spinner {
178312
+ label;
178313
+ detail;
178314
+ frames;
178315
+ interval;
178316
+ colorFn;
178317
+ frameIndex = 0;
178318
+ timer = null;
178319
+ state = "stopped";
178320
+ symbols = getSymbols(supportsUnicode());
178321
+ lastRenderedLines = 0;
178322
+ sigintHandler = null;
178323
+ constructor(options = {}) {
178324
+ this.label = options.label ?? "";
178325
+ this.detail = options.detail;
178326
+ this.interval = options.interval ?? 80;
178327
+ this.colorFn = spinnerColors[options.color ?? "cyan"];
178328
+ const style = options.style ?? "circle";
178329
+ this.frames = supportsUnicode() ? FRAME_SETS[style] : ASCII_FRAME_SET;
178330
+ }
178331
+ start(label) {
178332
+ if (label !== undefined)
178333
+ this.label = label;
178334
+ if (this.state === "spinning")
178335
+ return this;
178336
+ this.state = "spinning";
178337
+ this.frameIndex = 0;
178338
+ this.lastRenderedLines = 0;
178339
+ if (!isInteractive()) {
178340
+ console.log(`${this.symbols.bullet} ${this.label}`);
178341
+ return this;
178342
+ }
178343
+ hideCursor();
178344
+ this.setupSignalHandler();
178345
+ this.render();
178346
+ this.timer = setInterval(() => {
178347
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
178348
+ this.render();
178349
+ }, this.interval);
178350
+ return this;
178351
+ }
178352
+ stop() {
178353
+ if (this.timer) {
178354
+ clearInterval(this.timer);
178355
+ this.timer = null;
178356
+ }
178357
+ this.state = "stopped";
178358
+ this.clearOutput();
178359
+ this.cleanup();
178360
+ return this;
178361
+ }
178362
+ success(label) {
178363
+ if (label !== undefined)
178364
+ this.label = label;
178365
+ this.finish("success");
178366
+ return this;
178367
+ }
178368
+ fail(label) {
178369
+ if (label !== undefined)
178370
+ this.label = label;
178371
+ this.finish("error");
178372
+ return this;
178373
+ }
178374
+ update(label) {
178375
+ this.label = label;
178376
+ if (this.state === "spinning" && isInteractive()) {
178377
+ this.render();
178378
+ }
178379
+ return this;
178380
+ }
178381
+ setDetail(detail) {
178382
+ this.detail = detail;
178383
+ if (this.state === "spinning" && isInteractive()) {
178384
+ this.render();
178385
+ }
178386
+ return this;
178387
+ }
178388
+ get isSpinning() {
178389
+ return this.state === "spinning";
178390
+ }
178391
+ finish(state) {
178392
+ if (this.timer) {
178393
+ clearInterval(this.timer);
178394
+ this.timer = null;
178395
+ }
178396
+ this.state = state;
178397
+ if (!isInteractive()) {
178398
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
178399
+ const colorFn = state === "success" ? colors.success : colors.error;
178400
+ console.log(`${colorFn(symbol)} ${this.label}`);
178401
+ } else {
178402
+ this.clearOutput();
178403
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
178404
+ const colorFn = state === "success" ? colors.success : colors.error;
178405
+ process.stdout.write(`${colorFn(symbol)} ${this.label}
178406
+ `);
178407
+ }
178408
+ this.cleanup();
178409
+ }
178410
+ render() {
178411
+ if (!isTTY2())
178412
+ return;
178413
+ this.clearOutput();
178414
+ const frame = this.colorFn(this.frames[this.frameIndex]);
178415
+ const width = getTerminalWidth();
178416
+ const mainLine = truncate(`${frame} ${this.label}`, width);
178417
+ process.stdout.write(mainLine);
178418
+ let lines = 1;
178419
+ if (this.detail) {
178420
+ const detailLine = truncate(` ${colors.muted(this.detail)}`, width);
178421
+ process.stdout.write(`
178422
+ ${detailLine}`);
178423
+ lines = 2;
178424
+ }
178425
+ this.lastRenderedLines = lines;
178426
+ }
178427
+ clearOutput() {
178428
+ if (!isTTY2())
178429
+ return;
178430
+ for (let i = 0;i < this.lastRenderedLines; i++) {
178431
+ if (i > 0)
178432
+ process.stdout.write(cursor.up(1));
178433
+ clearLine();
178434
+ }
178435
+ }
178436
+ setupSignalHandler() {
178437
+ this.sigintHandler = () => {
178438
+ this.cleanup();
178439
+ process.exit(130);
178440
+ };
178441
+ process.on("SIGINT", this.sigintHandler);
178442
+ }
178443
+ cleanup() {
178444
+ if (this.sigintHandler) {
178445
+ process.removeListener("SIGINT", this.sigintHandler);
178446
+ this.sigintHandler = null;
178447
+ }
178448
+ showCursor();
178449
+ }
178450
+ }
178451
+
178053
178452
  // src/commands/init.ts
178054
178453
  var __dirname15 = path33.dirname(fileURLToPath14(import.meta.url));
178055
178454
  function getVersion14() {
@@ -178084,7 +178483,8 @@ async function scanPackage(cwd, pkgDir) {
178084
178483
  documented++;
178085
178484
  }
178086
178485
  const coverage = total > 0 ? Math.round(documented / total * 100) : 100;
178087
- return { name, entry: path33.relative(cwd, entryFile), exports: total, coverage };
178486
+ const health = Math.round(coverage * 0.5 + 100 * 0.5);
178487
+ return { name, entry: path33.relative(cwd, entryFile), exports: total, coverage, health };
178088
178488
  } catch {
178089
178489
  return null;
178090
178490
  }
@@ -178097,7 +178497,7 @@ function generateConfig(packages) {
178097
178497
  };
178098
178498
  }
178099
178499
  function registerInitCommand(program) {
178100
- program.command("init").description("Scan project, generate global drift config").action(async () => {
178500
+ program.command("init").description("Scan project, generate global drift config").option("--project", "Write to drift.config.json in cwd instead of global config").action(async (opts) => {
178101
178501
  const startTime = Date.now();
178102
178502
  const version = getVersion14();
178103
178503
  const cwd = process.cwd();
@@ -178105,21 +178505,27 @@ function registerInitCommand(program) {
178105
178505
  const workspaces = detectWorkspaces(cwd);
178106
178506
  const isMonorepo = workspaces !== null;
178107
178507
  const packageDirs = isMonorepo ? resolveGlobs(cwd, workspaces) : ["."];
178508
+ const spin = new Spinner({ style: "dots" });
178509
+ spin.start("Scanning packages…");
178108
178510
  const packages = [];
178109
178511
  for (const dir of packageDirs) {
178512
+ spin.update(`Scanning ${dir}…`);
178110
178513
  const result = await scanPackage(cwd, dir);
178111
178514
  if (result)
178112
178515
  packages.push(result);
178113
178516
  }
178517
+ spin.success(`Scanned ${packages.length} package${packages.length === 1 ? "" : "s"}`);
178114
178518
  if (packages.length === 0) {
178115
178519
  formatError("init", "No TypeScript packages found", startTime, version);
178116
178520
  return;
178117
178521
  }
178118
178522
  const config = generateConfig(packages);
178119
- const globalDir = getGlobalDir();
178120
- if (!existsSync19(globalDir))
178121
- mkdirSync7(globalDir, { recursive: true });
178122
- const configPath = getGlobalConfigPath();
178523
+ const configPath = opts.project ? path33.resolve(cwd, "drift.config.json") : getGlobalConfigPath();
178524
+ if (!opts.project) {
178525
+ const globalDir = getGlobalDir();
178526
+ if (!existsSync19(globalDir))
178527
+ mkdirSync7(globalDir, { recursive: true });
178528
+ }
178123
178529
  writeFileSync7(configPath, JSON.stringify(config, null, 2) + `
178124
178530
  `);
178125
178531
  ensureProjectDir(cwd);
@@ -178521,6 +178927,13 @@ function renderList(data) {
178521
178927
  lines.push(` ${c.bold(`${total} exports`)}`);
178522
178928
  }
178523
178929
  lines.push("");
178930
+ if (total === 0 && data.filter) {
178931
+ const msg = data.filter === "undocumented" ? `${c.green(sym.ok)} All exports are documented` : `${c.green(sym.ok)} No drifted exports found`;
178932
+ lines.push(indent(msg));
178933
+ lines.push("");
178934
+ return lines.join(`
178935
+ `);
178936
+ }
178524
178937
  if (!data.search) {
178525
178938
  const kindCounts = new Map;
178526
178939
  for (const exp of exports) {
@@ -178577,9 +178990,14 @@ function registerListCommand(program) {
178577
178990
  const rows = [];
178578
178991
  for (const pkg of packages) {
178579
178992
  const res = await listExports({ entryFile: pkg.entry });
178580
- rows.push({ name: pkg.name, count: res.exports.length });
178993
+ let filtered = res.exports;
178994
+ if (options.undocumented) {
178995
+ filtered = filtered.filter((e) => !e.description || e.description.trim().length === 0);
178996
+ }
178997
+ rows.push({ name: pkg.name, count: filtered.length });
178581
178998
  }
178582
- formatOutput("list", { packages: rows }, startTime, version, renderBatchList);
178999
+ const filter2 = options.undocumented ? "undocumented" : undefined;
179000
+ formatOutput("list", { packages: rows, filter: filter2 }, startTime, version, renderBatchList);
178583
179001
  return;
178584
179002
  }
178585
179003
  let entryFile;
@@ -178616,6 +179034,7 @@ function registerListCommand(program) {
178616
179034
  const matches = fuzzySearch(searchTerm, exports);
178617
179035
  exports = matches.map((m) => exports.find((e) => e.name === m.name));
178618
179036
  }
179037
+ const filter = options.undocumented ? "undocumented" : options.drifted ? "drifted" : undefined;
178619
179038
  const data = {
178620
179039
  exports: exports.map((e) => ({
178621
179040
  name: e.name,
@@ -178624,7 +179043,8 @@ function registerListCommand(program) {
178624
179043
  ...e.deprecated ? { deprecated: true } : {}
178625
179044
  })),
178626
179045
  ...searchTerm ? { search: searchTerm } : {},
178627
- showAll: !!options.full
179046
+ showAll: !!options.full,
179047
+ ...filter ? { filter } : {}
178628
179048
  };
178629
179049
  formatOutput("list", data, startTime, version, renderList);
178630
179050
  } catch (err) {
@@ -178966,7 +179386,7 @@ function registerReportCommand(program) {
178966
179386
  import { readFileSync as readFileSync36 } from "node:fs";
178967
179387
  import * as path40 from "node:path";
178968
179388
  import { fileURLToPath as fileURLToPath20 } from "node:url";
178969
- import { diffSpec as diffSpec7, recommendSemverBump as recommendSemverBump3 } from "@openpkg-ts/spec";
179389
+ import { diffSpec as diffSpec8, recommendSemverBump as recommendSemverBump3 } from "@openpkg-ts/spec";
178970
179390
 
178971
179391
  // src/formatters/semver.ts
178972
179392
  function renderSemver(data) {
@@ -178995,7 +179415,7 @@ function registerSemverCommand(program) {
178995
179415
  try {
178996
179416
  const args = [oldPath, newPath].filter(Boolean);
178997
179417
  const { oldSpec, newSpec } = await resolveSpecs({ args, ...options });
178998
- const diff = diffSpec7(oldSpec, newSpec);
179418
+ const diff = diffSpec8(oldSpec, newSpec);
178999
179419
  const recommendation = recommendSemverBump3(diff);
179000
179420
  const data = {
179001
179421
  bump: recommendation.bump,
@@ -179156,6 +179576,12 @@ if (process.argv.includes("--capabilities")) {
179156
179576
  `);
179157
179577
  process.exit(0);
179158
179578
  }
179579
+ var userArgs = process.argv.slice(2).filter((a) => !a.startsWith("-"));
179580
+ if (userArgs.length === 0) {
179581
+ const { configPath } = loadConfig();
179582
+ const subcommand = configPath ? "health" : "init";
179583
+ process.argv.splice(2, 0, subcommand);
179584
+ }
179159
179585
  program.parseAsync().catch(() => {
179160
179586
  process.exit(1);
179161
179587
  });
package/package.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "name": "@driftdev/cli",
3
- "version": "0.35.1",
4
- "description": "DocCov CLI - Documentation coverage and drift detection for TypeScript",
3
+ "version": "0.37.0",
4
+ "description": "Drift CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "cli",
8
8
  "documentation",
9
- "doccov",
9
+ "drift",
10
10
  "docs-coverage",
11
11
  "drift-detection"
12
12
  ],
13
- "homepage": "https://github.com/doccov/doccov#readme",
13
+ "homepage": "https://github.com/driftdev/drift#readme",
14
14
  "repository": {
15
15
  "type": "git",
16
- "url": "git+https://github.com/doccov/doccov.git",
16
+ "url": "git+https://github.com/driftdev/drift.git",
17
17
  "directory": "packages/cli"
18
18
  },
19
19
  "license": "MIT",
@@ -28,10 +28,6 @@
28
28
  ".": {
29
29
  "import": "./dist/index.js",
30
30
  "types": "./dist/index.d.ts"
31
- },
32
- "./config": {
33
- "import": "./dist/config/index.js",
34
- "types": "./dist/config/index.d.ts"
35
31
  }
36
32
  },
37
33
  "scripts": {
@@ -47,8 +43,8 @@
47
43
  "dist"
48
44
  ],
49
45
  "dependencies": {
50
- "@driftdev/sdk": "^0.35.1",
51
- "@driftdev/spec": "^0.35.1",
46
+ "@driftdev/sdk": "^0.36.0",
47
+ "@driftdev/spec": "^0.36.0",
52
48
  "@openpkg-ts/spec": "^0.37.0",
53
49
  "chalk": "^5.4.1",
54
50
  "commander": "^14.0.0"
@@ -1,9 +0,0 @@
1
- import { DocCovConfig as DocCovConfig2, DocCovConfigInput, DocsConfig } from "@driftdev/sdk";
2
- import { DocCovConfig } from "@driftdev/sdk";
3
- declare const DRIFT_CONFIG_FILENAMES: readonly ["drift.config.ts", "drift.config.mts", "drift.config.js", "drift.config.mjs"];
4
- interface LoadedDriftTsConfig extends DocCovConfig {
5
- filePath: string;
6
- }
7
- declare const loadDriftTsConfig: (cwd: string) => Promise<LoadedDriftTsConfig | null>;
8
- declare const defineConfig: (config: DocCovConfigInput) => DocCovConfigInput;
9
- export { loadDriftTsConfig, defineConfig, LoadedDriftTsConfig, DocsConfig, DocCovConfigInput, DocCovConfig2 as DocCovConfig, DRIFT_CONFIG_FILENAMES };
@@ -1,108 +0,0 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
19
- var __export = (target, all) => {
20
- for (var name in all)
21
- __defProp(target, name, {
22
- get: all[name],
23
- enumerable: true,
24
- configurable: true,
25
- set: (newValue) => all[name] = () => newValue
26
- });
27
- };
28
- var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
30
-
31
- // src/config/drift-ts-config.ts
32
- import { access } from "node:fs/promises";
33
- import path from "node:path";
34
- import { pathToFileURL } from "node:url";
35
- import { docCovConfigSchema, normalizeConfig } from "@driftdev/sdk";
36
- var DRIFT_CONFIG_FILENAMES = [
37
- "drift.config.ts",
38
- "drift.config.mts",
39
- "drift.config.js",
40
- "drift.config.mjs"
41
- ];
42
- var fileExists = async (filePath) => {
43
- try {
44
- await access(filePath);
45
- return true;
46
- } catch {
47
- return false;
48
- }
49
- };
50
- var findConfigFile = async (cwd) => {
51
- let current = path.resolve(cwd);
52
- const { root } = path.parse(current);
53
- while (true) {
54
- for (const candidate of DRIFT_CONFIG_FILENAMES) {
55
- const candidatePath = path.join(current, candidate);
56
- if (await fileExists(candidatePath)) {
57
- return candidatePath;
58
- }
59
- }
60
- if (current === root) {
61
- return null;
62
- }
63
- current = path.dirname(current);
64
- }
65
- };
66
- var importConfigModule = async (absolutePath) => {
67
- const fileUrl = pathToFileURL(absolutePath);
68
- fileUrl.searchParams.set("t", Date.now().toString());
69
- const module = await import(fileUrl.href);
70
- return module?.default ?? module?.config ?? module;
71
- };
72
- var formatIssues = (issues) => issues.map((issue) => `- ${issue}`).join(`
73
- `);
74
- var loadDriftTsConfig = async (cwd) => {
75
- const configPath = await findConfigFile(cwd);
76
- if (!configPath) {
77
- return null;
78
- }
79
- let rawConfig;
80
- try {
81
- rawConfig = await importConfigModule(configPath);
82
- } catch (error) {
83
- const message = error instanceof Error ? error.message : String(error);
84
- throw new Error(`Failed to load drift config at ${configPath}: ${message}`);
85
- }
86
- const parsed = docCovConfigSchema.safeParse(rawConfig);
87
- if (!parsed.success) {
88
- const issues = parsed.error.issues.map((issue) => {
89
- const pathLabel = issue.path.length > 0 ? issue.path.join(".") : "(root)";
90
- return `${pathLabel}: ${issue.message}`;
91
- });
92
- throw new Error(`Invalid drift configuration at ${configPath}.
93
- ${formatIssues(issues)}`);
94
- }
95
- const normalized = normalizeConfig(parsed.data);
96
- return {
97
- filePath: configPath,
98
- ...normalized
99
- };
100
- };
101
-
102
- // src/config/index.ts
103
- var defineConfig = (config) => config;
104
- export {
105
- loadDriftTsConfig,
106
- defineConfig,
107
- DRIFT_CONFIG_FILENAMES
108
- };