Switch to double quotes and reformat codebase with Biome
This commit is contained in:
@@ -4,49 +4,40 @@
|
||||
* Renders structured data as aligned ASCII tables with optional color support.
|
||||
*/
|
||||
|
||||
import chalk, { type ChalkInstance } from 'chalk'
|
||||
import type {
|
||||
AnyCommandResult,
|
||||
ColumnDef,
|
||||
OutputOptions,
|
||||
OutputSchema,
|
||||
} from './types.js'
|
||||
import chalk, { type ChalkInstance } from "chalk";
|
||||
import type { AnyCommandResult, ColumnDef, OutputOptions, OutputSchema } from "./types.js";
|
||||
|
||||
// ANSI escape code regex for stripping colors when measuring width
|
||||
const ANSI_REGEX =
|
||||
// eslint-disable-next-line no-control-regex
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g
|
||||
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
||||
|
||||
/** Strip ANSI escape codes from a string */
|
||||
function stripAnsi(str: string): string {
|
||||
return str.replace(ANSI_REGEX, '')
|
||||
return str.replace(ANSI_REGEX, "");
|
||||
}
|
||||
|
||||
/** Get visible string length (excluding ANSI codes) */
|
||||
function visibleLength(str: string): number {
|
||||
return stripAnsi(str).length
|
||||
return stripAnsi(str).length;
|
||||
}
|
||||
|
||||
/** Pad a cell to the specified width with alignment */
|
||||
function padCell(
|
||||
cell: string,
|
||||
width: number,
|
||||
align: 'left' | 'right' | 'center'
|
||||
): string {
|
||||
const visible = visibleLength(cell)
|
||||
const padding = Math.max(0, width - visible)
|
||||
function padCell(cell: string, width: number, align: "left" | "right" | "center"): string {
|
||||
const visible = visibleLength(cell);
|
||||
const padding = Math.max(0, width - visible);
|
||||
|
||||
switch (align) {
|
||||
case 'right':
|
||||
return ' '.repeat(padding) + cell
|
||||
case 'center': {
|
||||
const left = Math.floor(padding / 2)
|
||||
const right = padding - left
|
||||
return ' '.repeat(left) + cell + ' '.repeat(right)
|
||||
case "right":
|
||||
return " ".repeat(padding) + cell;
|
||||
case "center": {
|
||||
const left = Math.floor(padding / 2);
|
||||
const right = padding - left;
|
||||
return " ".repeat(left) + cell + " ".repeat(right);
|
||||
}
|
||||
case 'left':
|
||||
case "left":
|
||||
default:
|
||||
return cell + ' '.repeat(padding)
|
||||
return cell + " ".repeat(padding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,15 +56,15 @@ function applyColor(str: string, colorName: string): string {
|
||||
grey: chalk.grey,
|
||||
dim: chalk.dim,
|
||||
bold: chalk.bold,
|
||||
}
|
||||
};
|
||||
|
||||
const colorFn = colorMap[colorName]
|
||||
return colorFn ? colorFn(str) : str
|
||||
const colorFn = colorMap[colorName];
|
||||
return colorFn ? colorFn(str) : str;
|
||||
}
|
||||
|
||||
/** Extract value from item using field definition */
|
||||
function getValue<T>(item: T, field: keyof T | ((item: T) => unknown)): unknown {
|
||||
return typeof field === 'function' ? field(item) : item[field]
|
||||
return typeof field === "function" ? field(item) : item[field];
|
||||
}
|
||||
|
||||
/** Render a single table row */
|
||||
@@ -81,106 +72,99 @@ function renderRow<T>(
|
||||
item: T,
|
||||
columns: ColumnDef<T>[],
|
||||
widths: number[],
|
||||
options: OutputOptions
|
||||
options: OutputOptions,
|
||||
): string {
|
||||
return columns
|
||||
.map((col, colIndex) => {
|
||||
const value = getValue(item, col.field)
|
||||
let cell = String(value ?? '')
|
||||
const width = widths[colIndex]
|
||||
const value = getValue(item, col.field);
|
||||
let cell = String(value ?? "");
|
||||
const width = widths[colIndex];
|
||||
|
||||
// Apply color if enabled
|
||||
if (col.color && !options.noColor) {
|
||||
const colorName = col.color(value, item)
|
||||
const colorName = col.color(value, item);
|
||||
if (colorName) {
|
||||
cell = applyColor(cell, colorName)
|
||||
cell = applyColor(cell, colorName);
|
||||
}
|
||||
}
|
||||
|
||||
return padCell(cell, width ?? 0, col.align ?? 'left')
|
||||
return padCell(cell, width ?? 0, col.align ?? "left");
|
||||
})
|
||||
.join(' ')
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
/** Render header row */
|
||||
function renderHeader<T>(
|
||||
columns: ColumnDef<T>[],
|
||||
widths: number[],
|
||||
options: OutputOptions
|
||||
options: OutputOptions,
|
||||
): string {
|
||||
const headerRow = columns
|
||||
.map((col, i) => padCell(col.header, widths[i] ?? 0, col.align ?? 'left'))
|
||||
.join(' ')
|
||||
.map((col, i) => padCell(col.header, widths[i] ?? 0, col.align ?? "left"))
|
||||
.join(" ");
|
||||
|
||||
return options.noColor ? headerRow : chalk.bold(headerRow)
|
||||
return options.noColor ? headerRow : chalk.bold(headerRow);
|
||||
}
|
||||
|
||||
/** Calculate column widths based on content and hints */
|
||||
function calculateWidths<T>(
|
||||
data: T[],
|
||||
columns: ColumnDef<T>[],
|
||||
includeHeaders: boolean
|
||||
): number[] {
|
||||
function calculateWidths<T>(data: T[], columns: ColumnDef<T>[], includeHeaders: boolean): number[] {
|
||||
return columns.map((col) => {
|
||||
// Start with header width if including headers
|
||||
let maxWidth = includeHeaders ? col.header.length : 0
|
||||
let maxWidth = includeHeaders ? col.header.length : 0;
|
||||
|
||||
// Check all data values
|
||||
for (const item of data) {
|
||||
const value = getValue(item, col.field)
|
||||
const str = String(value ?? '')
|
||||
maxWidth = Math.max(maxWidth, visibleLength(str))
|
||||
const value = getValue(item, col.field);
|
||||
const str = String(value ?? "");
|
||||
maxWidth = Math.max(maxWidth, visibleLength(str));
|
||||
}
|
||||
|
||||
// Apply width hint if specified (minimum width)
|
||||
if (col.width) {
|
||||
maxWidth = Math.max(maxWidth, col.width)
|
||||
maxWidth = Math.max(maxWidth, col.width);
|
||||
}
|
||||
|
||||
return maxWidth
|
||||
})
|
||||
return maxWidth;
|
||||
});
|
||||
}
|
||||
|
||||
/** Render a list result as a table */
|
||||
export function renderTable<T>(
|
||||
result: AnyCommandResult<T>,
|
||||
options: OutputOptions
|
||||
): string {
|
||||
const { schema } = result
|
||||
const data = result.type === 'list' ? result.data : [result.data]
|
||||
export function renderTable<T>(result: AnyCommandResult<T>, options: OutputOptions): string {
|
||||
const { schema } = result;
|
||||
const data = result.type === "list" ? result.data : [result.data];
|
||||
|
||||
if (data.length === 0) {
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
|
||||
const columns = schema.columns as ColumnDef<T>[]
|
||||
const includeHeaders = !options.noHeaders
|
||||
const widths = calculateWidths(data, columns, includeHeaders)
|
||||
const columns = schema.columns as ColumnDef<T>[];
|
||||
const includeHeaders = !options.noHeaders;
|
||||
const widths = calculateWidths(data, columns, includeHeaders);
|
||||
|
||||
const lines: string[] = []
|
||||
const lines: string[] = [];
|
||||
|
||||
// Add header row
|
||||
if (includeHeaders) {
|
||||
lines.push(renderHeader(columns, widths, options))
|
||||
lines.push(renderHeader(columns, widths, options));
|
||||
}
|
||||
|
||||
// Add data rows
|
||||
for (const item of data) {
|
||||
lines.push(renderRow(item, columns, widths, options))
|
||||
lines.push(renderRow(item, columns, widths, options));
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/** Render just a table header (for streaming) */
|
||||
export function renderTableHeader<T>(
|
||||
schema: OutputSchema<T>,
|
||||
options: OutputOptions,
|
||||
widths?: number[]
|
||||
widths?: number[],
|
||||
): string {
|
||||
const columns = schema.columns
|
||||
const actualWidths = widths ?? columns.map((col) => col.width ?? col.header.length)
|
||||
return renderHeader(columns, actualWidths, options)
|
||||
const columns = schema.columns;
|
||||
const actualWidths = widths ?? columns.map((col) => col.width ?? col.header.length);
|
||||
return renderHeader(columns, actualWidths, options);
|
||||
}
|
||||
|
||||
/** Render just a table row (for streaming) */
|
||||
@@ -188,9 +172,9 @@ export function renderTableRow<T>(
|
||||
item: T,
|
||||
schema: OutputSchema<T>,
|
||||
options: OutputOptions,
|
||||
widths?: number[]
|
||||
widths?: number[],
|
||||
): string {
|
||||
const columns = schema.columns
|
||||
const actualWidths = widths ?? columns.map((col) => col.width ?? col.header.length)
|
||||
return renderRow(item, columns, actualWidths, options)
|
||||
const columns = schema.columns;
|
||||
const actualWidths = widths ?? columns.map((col) => col.width ?? col.header.length);
|
||||
return renderRow(item, columns, actualWidths, options);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user