UNPKG

@swell/cli

Version:

Swell's command line interface/utility

129 lines (128 loc) 4.33 kB
import { titleize } from 'inflection'; import { createStream, getBorderCharacters, } from 'table'; import { Output } from './output.js'; // the terminal width used in column display // leave 5% for padding const MAX_WIDTH = Math.floor((process.stdout.columns || 80) * 0.95); // the default column width const DEFAULT_COL_WIDTH = 10; export class TableOutput extends Output { stream; constructor(columns, pretty) { super(columns, pretty); const extraWidths = this.computeExtraWidths(); const config = { border: getBorderCharacters('void'), columnCount: columns.length, columnDefault: { width: DEFAULT_COL_WIDTH }, columns: columns.map((c) => this.prepareColumnDisplay(c, extraWidths[c])), drawVerticalLine: () => false, }; // create the output stream and write the header this.stream = createStream(config); this.writeHeader(); } prepareData(log) { return this.columns.map((c) => { // for each column, get the value from the log item and prepare it for // display in the table, i.e. go through TableLoggedItem before returning const tableLog = new TableLoggedItem(log, this.pretty); const preparedLog = tableLog.prepareData(); return preparedLog[c]; }); } write(row) { this.stream.write(row); } computeExtraWidths() { const extraWidths = {}; // compute the initial width of the table // eslint-disable-next-line unicorn/no-array-reduce const initialWidth = this.columns.reduce((acc, col) => { const display = this.displayOptions(col); return acc + (display.width || display.minWidth || DEFAULT_COL_WIDTH); }, 0); const availableWidth = MAX_WIDTH - initialWidth; const minWidthCols = this.columns.filter((col) => this.displayOptions(col).minWidth); // if there is extra width available, split it across all columns that have // minWidth if (availableWidth > 0 && minWidthCols.length > 0) { const extraWidthPerCol = Math.floor(availableWidth / minWidthCols.length); for (const col of minWidthCols) { extraWidths[col] = extraWidthPerCol; } } return extraWidths; } displayOptions(column) { // an extension of the column def in https://www.npmjs.com/package/table // for config.columns const columns = { data: { minWidth: 40, wrapWord: !this.pretty, }, date: { title: 'Date UTC', width: 24, }, req: { width: 24, }, request: { minWidth: 40, }, status: { width: 6, }, time: { width: 6, }, }; return columns[column]; } prepareColumnDisplay(column, extraWidth) { const display = this.displayOptions(column); const { minWidth, wrapWord } = display; let { width } = display; if (minWidth) { width = minWidth; } if (width && extraWidth) { width += extraWidth; } return { width, wrapWord }; } writeHeader() { this.stream.write(this.columns.map((c) => this.displayOptions(c)?.title || titleize(c))); } } /** * A layer between the log item and the table output that handles the mapping of * log data to the columns specifically for the table output. */ class TableLoggedItem { logItem; pretty = false; constructor(logItem, pretty) { this.logItem = logItem; this.pretty = pretty; } prepareData() { this.logItem.data = this.prepareLogItemData(); return this.logItem; } prepareLogItemData() { let logData = this.logItem.data; try { if (this.pretty && typeof logData === 'string') { logData = JSON.parse(logData); logData = JSON.stringify(logData, null, 2); } } catch { // do nothing, JSON parsing errors } return logData; } }