mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
34a9bff2b3
- NEW: src/interceptor.ts — unified XHR/Fetch interceptor (was duplicated 3x) - NEW: src/version.ts — centralized PKG_VERSION (was duplicated 2x) - NEW: src/constants.ts — shared VOLATILE_PARAMS, FIELD_ROLES etc. - NEW: src/engine.test.ts, src/registry.test.ts — 14 new unit tests - browser.ts: use shared normalizeEval, interceptor, withTimeoutMs, PKG_VERSION - intercept.ts, tap.ts: use shared interceptor generators - cascade.ts: extract shared buildFetchProbeJs (90% dedup) - engine.ts: use InternalCliCommand (no more 'as any' casts) - executor.ts: remove all 15 'as StepHandler' type casts - runtime.ts: add withTimeoutMs, IBrowserFactory interface - registry.ts: add InternalCliCommand type for internal fields - validate.ts: add pipeline step name validation - output.ts: remove 'null as any' and '.filter(() => true)' hacks - explore.ts, synthesize.ts: use shared constants - docs: fix V2EX commands (3→6), SKILL.md version, verify example Tests: 88 passed (was 74), tsc --noEmit: 0 errors
97 lines
2.8 KiB
TypeScript
97 lines
2.8 KiB
TypeScript
/**
|
|
* Output formatting: table, JSON, Markdown, CSV, YAML.
|
|
*/
|
|
|
|
import chalk from 'chalk';
|
|
import Table from 'cli-table3';
|
|
import yaml from 'js-yaml';
|
|
|
|
export interface RenderOptions {
|
|
fmt?: string;
|
|
columns?: string[];
|
|
title?: string;
|
|
elapsed?: number;
|
|
source?: string;
|
|
}
|
|
|
|
export function render(data: any, opts: RenderOptions = {}): void {
|
|
const fmt = opts.fmt ?? 'table';
|
|
if (data === null || data === undefined) {
|
|
console.log(data);
|
|
return;
|
|
}
|
|
switch (fmt) {
|
|
case 'json': renderJson(data); break;
|
|
case 'md': case 'markdown': renderMarkdown(data, opts); break;
|
|
case 'csv': renderCsv(data, opts); break;
|
|
case 'yaml': case 'yml': renderYaml(data); break;
|
|
default: renderTable(data, opts); break;
|
|
}
|
|
}
|
|
|
|
function renderTable(data: any, opts: RenderOptions): void {
|
|
const rows: any[] = Array.isArray(data) ? data : [data];
|
|
if (!rows.length) { console.log(chalk.dim('(no data)')); return; }
|
|
const columns = opts.columns ?? Object.keys(rows[0]);
|
|
|
|
const header = columns.map(c => capitalize(c));
|
|
const table = new Table({
|
|
head: header.map(h => chalk.bold(h)),
|
|
style: { head: [], border: [] },
|
|
wordWrap: true,
|
|
wrapOnWordBoundary: true,
|
|
});
|
|
|
|
for (const row of rows) {
|
|
table.push(columns.map(c => {
|
|
const v = row[c];
|
|
return v === null || v === undefined ? '' : String(v);
|
|
}));
|
|
}
|
|
|
|
console.log();
|
|
if (opts.title) console.log(chalk.dim(` ${opts.title}`));
|
|
console.log(table.toString());
|
|
const footer: string[] = [];
|
|
footer.push(`${rows.length} items`);
|
|
if (opts.elapsed) footer.push(`${opts.elapsed.toFixed(1)}s`);
|
|
if (opts.source) footer.push(opts.source);
|
|
console.log(chalk.dim(footer.join(' · ')));
|
|
}
|
|
|
|
function renderJson(data: any): void {
|
|
console.log(JSON.stringify(data, null, 2));
|
|
}
|
|
|
|
function renderMarkdown(data: any, opts: RenderOptions): void {
|
|
const rows: any[] = Array.isArray(data) ? data : [data];
|
|
if (!rows.length) return;
|
|
const columns = opts.columns ?? Object.keys(rows[0]);
|
|
console.log('| ' + columns.join(' | ') + ' |');
|
|
console.log('| ' + columns.map(() => '---').join(' | ') + ' |');
|
|
for (const row of rows) {
|
|
console.log('| ' + columns.map(c => String(row[c] ?? '')).join(' | ') + ' |');
|
|
}
|
|
}
|
|
|
|
function renderCsv(data: any, opts: RenderOptions): void {
|
|
const rows: any[] = Array.isArray(data) ? data : [data];
|
|
if (!rows.length) return;
|
|
const columns = opts.columns ?? Object.keys(rows[0]);
|
|
console.log(columns.join(','));
|
|
for (const row of rows) {
|
|
console.log(columns.map(c => {
|
|
const v = String(row[c] ?? '');
|
|
return v.includes(',') || v.includes('"') ? `"${v.replace(/"/g, '""')}"` : v;
|
|
}).join(','));
|
|
}
|
|
}
|
|
|
|
function renderYaml(data: any): void {
|
|
console.log(yaml.dump(data, { sortKeys: false, lineWidth: 120, noRefs: true }));
|
|
}
|
|
|
|
function capitalize(s: string): string {
|
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
}
|