Skip to content

Commit c74d109

Browse files
committed
Update CLI format
1 parent e4884fb commit c74d109

4 files changed

Lines changed: 109 additions & 13 deletions

File tree

packages/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@changespage/cli",
3-
"version": "0.1.0",
3+
"version": "0.2.0",
44
"type": "module",
55
"bin": {
66
"chp": "./dist/index.js"

packages/cli/src/commands/posts.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Command } from "commander";
22
import { ApiClient } from "../client.js";
33
import { getSecretKey } from "../config.js";
4+
import { output } from "../formatter.js";
45

56
function readStdin(): Promise<string | null> {
67
if (process.stdin.isTTY) {
@@ -31,15 +32,6 @@ function getClient(cmd: Command): ApiClient {
3132
});
3233
}
3334

34-
function output(data: unknown, cmd: Command) {
35-
const opts = cmd.optsWithGlobals();
36-
if (opts.pretty) {
37-
console.log(JSON.stringify(data, null, 2));
38-
} else {
39-
console.log(JSON.stringify(data));
40-
}
41-
}
42-
4335
function parseTags(tags: string): string[] {
4436
return tags.split(",").map((t) => t.trim());
4537
}
@@ -51,7 +43,7 @@ export function registerPostsCommand(program: Command) {
5143
.command("list")
5244
.description("List posts")
5345
.option("--status <status>", "Filter by status (draft|published|archived)")
54-
.option("--limit <n>", "Max number of posts", "20")
46+
.option("--limit <n>", "Max number of posts", "5")
5547
.option("--offset <n>", "Offset for pagination", "0")
5648
.action(async function (this: Command) {
5749
const client = getClient(this);
@@ -145,6 +137,6 @@ export function registerPostsCommand(program: Command) {
145137
.action(async function (this: Command, id: string) {
146138
const client = getClient(this);
147139
await client.deletePost(id);
148-
console.log(JSON.stringify({ deleted: true, id }));
140+
output({ deleted: true, id }, this);
149141
});
150142
}

packages/cli/src/formatter.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import { Command } from "commander";
2+
3+
const isTTY = process.stdout.isTTY ?? false;
4+
const BOLD = isTTY ? "\x1b[1m" : "";
5+
const DIM = isTTY ? "\x1b[2m" : "";
6+
const CYAN = isTTY ? "\x1b[36m" : "";
7+
const RESET = isTTY ? "\x1b[0m" : "";
8+
9+
const DATE_KEYS = new Set(["created_at", "updated_at", "publish_at", "publication_date"]);
10+
11+
function formatValue(value: unknown, key?: string): string {
12+
if (value === null || value === undefined) return "";
13+
if (Array.isArray(value)) return value.join(", ");
14+
if (typeof value === "object") return JSON.stringify(value);
15+
if (key && DATE_KEYS.has(key) && typeof value === "string") {
16+
const d = new Date(value);
17+
if (!isNaN(d.getTime())) return d.toLocaleString();
18+
}
19+
return String(value);
20+
}
21+
22+
function wrapText(str: string, width: number, indent: number): string {
23+
if (width < 10) width = 10;
24+
if (str.length <= width) return str;
25+
26+
const lines: string[] = [];
27+
const pad = " ".repeat(indent);
28+
let remaining = str;
29+
30+
while (remaining.length > 0) {
31+
const max = lines.length === 0 ? width : width;
32+
if (remaining.length <= max) {
33+
lines.push(remaining);
34+
break;
35+
}
36+
let breakAt = remaining.lastIndexOf(" ", max);
37+
if (breakAt <= 0) breakAt = max;
38+
lines.push(remaining.slice(0, breakAt));
39+
remaining = remaining.slice(breakAt).trimStart();
40+
}
41+
42+
return lines.join("\n" + pad);
43+
}
44+
45+
function formatList(rows: Record<string, unknown>[]) {
46+
const termWidth = process.stdout.columns ?? 80;
47+
48+
for (let i = 0; i < rows.length; i++) {
49+
const entries = Object.entries(rows[i]).filter(([, v]) => v !== null && v !== undefined && v !== "");
50+
const maxKeyLen = entries.reduce((max, [k]) => Math.max(max, k.length), 0);
51+
const valueWidth = termWidth - maxKeyLen - 3;
52+
53+
const indent = maxKeyLen + 3;
54+
for (const [key, value] of entries) {
55+
const label = `${BOLD}${CYAN}${key.padEnd(maxKeyLen)}${RESET}`;
56+
const formatted = wrapText(formatValue(value, key), Math.max(valueWidth, 20), indent);
57+
console.log(`${label}${DIM} : ${RESET}${formatted}`);
58+
}
59+
60+
if (i < rows.length - 1) {
61+
console.log(`${DIM}${"─".repeat(Math.min(termWidth, 60))}${RESET}`);
62+
}
63+
}
64+
}
65+
66+
function formatKeyValue(obj: Record<string, unknown>) {
67+
const termWidth = process.stdout.columns ?? 80;
68+
const entries = Object.entries(obj).filter(([, v]) => v !== null && v !== undefined && v !== "");
69+
const maxKeyLen = entries.reduce((max, [k]) => Math.max(max, k.length), 0);
70+
const valueWidth = termWidth - maxKeyLen - 3;
71+
const indent = maxKeyLen + 3;
72+
73+
for (const [key, value] of entries) {
74+
const label = `${BOLD}${CYAN}${key.padEnd(maxKeyLen)}${RESET}`;
75+
const formatted = wrapText(formatValue(value, key), Math.max(valueWidth, 20), indent);
76+
console.log(`${label}${DIM} : ${RESET}${formatted}`);
77+
}
78+
}
79+
80+
export function output(data: unknown, cmd: Command) {
81+
const opts = cmd.optsWithGlobals();
82+
if (opts.json) {
83+
console.log(JSON.stringify(data));
84+
return;
85+
}
86+
87+
if (data === null || data === undefined) return;
88+
89+
if (Array.isArray(data)) {
90+
if (data.length === 0) {
91+
console.log(DIM + "(no results)" + RESET);
92+
return;
93+
}
94+
formatList(data);
95+
return;
96+
}
97+
98+
if (typeof data === "object") {
99+
formatKeyValue(data as Record<string, unknown>);
100+
return;
101+
}
102+
103+
console.log(String(data));
104+
}

packages/cli/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ program
99
.description("CLI for changes.page")
1010
.version("0.1.0")
1111
.option("--secret-key <key>", "Page secret key")
12-
.option("--pretty", "Pretty-print JSON output");
12+
.option("--json", "Output raw JSON");
1313

1414
registerConfigureCommand(program);
1515
registerPostsCommand(program);

0 commit comments

Comments
 (0)