From fda3d4b7ec1533a2a844aaacc17fe43edafdb534 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov Date: Wed, 13 May 2026 00:25:35 +0200 Subject: [PATCH] feat(cli): add print-command-tree build-time script --- packages/cli/src/print-command-tree.test.ts | 23 ++++++++++++ packages/cli/src/print-command-tree.ts | 39 +++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 packages/cli/src/print-command-tree.test.ts create mode 100644 packages/cli/src/print-command-tree.ts diff --git a/packages/cli/src/print-command-tree.test.ts b/packages/cli/src/print-command-tree.test.ts new file mode 100644 index 00000000..003d467d --- /dev/null +++ b/packages/cli/src/print-command-tree.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest'; +import { renderKtxCommandTree } from './print-command-tree.js'; + +describe('renderKtxCommandTree', () => { + it('renders an indented tree rooted at "ktx" with known top-level commands', () => { + const output = renderKtxCommandTree(); + + const lines = output.split('\n'); + expect(lines[0]).toMatch(/^ktx( |$|\s-)/); + + const topLevel = lines.filter((line) => /^ {2}\S/.test(line)).map((line) => line.trim().split(' ')[0]); + + for (const expected of ['setup', 'connection', 'ingest', 'sl', 'dev']) { + expect(topLevel).toContain(expected); + } + }); + + it('ends with a single trailing newline', () => { + const output = renderKtxCommandTree(); + expect(output.endsWith('\n')).toBe(true); + expect(output.endsWith('\n\n')).toBe(false); + }); +}); diff --git a/packages/cli/src/print-command-tree.ts b/packages/cli/src/print-command-tree.ts new file mode 100644 index 00000000..2ede889c --- /dev/null +++ b/packages/cli/src/print-command-tree.ts @@ -0,0 +1,39 @@ +import { fileURLToPath } from 'node:url'; +import { buildKtxProgram } from './cli-program.js'; +import type { KtxCliIo, KtxCliPackageInfo } from './cli-runtime.js'; +import { formatCommandTree, walkCommandTree } from './command-tree.js'; + +function silentIo(): KtxCliIo { + return { + stdout: { isTTY: false, columns: 80, write: () => {} }, + stderr: { write: () => {} }, + }; +} + +function stubPackageInfo(): KtxCliPackageInfo { + return { name: '@ktx/cli', version: '0.0.0-docs', contextPackageName: '@ktx/context' }; +} + +export function renderKtxCommandTree(): string { + const program = buildKtxProgram({ + io: silentIo(), + deps: {}, + packageInfo: stubPackageInfo(), + runInit: async () => 0, + }); + return formatCommandTree(walkCommandTree(program)); +} + +export function main(stdout: { write(chunk: string): void }): void { + stdout.write(renderKtxCommandTree()); +} + +const invokedAsScript = + typeof process !== 'undefined' && + Array.isArray(process.argv) && + process.argv[1] !== undefined && + fileURLToPath(import.meta.url) === process.argv[1]; + +if (invokedAsScript) { + main(process.stdout); +}