1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 |
- "use strict";
- const O = Object
- , { first, strlen } = require ('printable-characters') // handles ANSI codes and invisible characters
- , limit = (s, n) => (first (s, n - 1) + '…')
- const asColumns = (rows, cfg_) => {
-
- const
- zip = (arrs, f) => arrs.reduce ((a, b) => b.map ((b, i) => [...a[i] || [], b]), []).map (args => f (...args)),
- /* Convert cell data to string (converting multiline text to singleline) */
- cells = rows.map (r => r.map (c => c.replace (/\n/g, '\\n'))),
- /* Compute column widths (per row) and max widths (per column) */
- cellWidths = cells.map (r => r.map (strlen)),
- maxWidths = zip (cellWidths, Math.max),
- /* Default config */
- cfg = O.assign ({
- delimiter: ' ',
- minColumnWidths: maxWidths.map (x => 0),
- maxTotalWidth: 0 }, cfg_),
- delimiterLength = strlen (cfg.delimiter),
- /* Project desired column widths, taking maxTotalWidth and minColumnWidths in account. */
- totalWidth = maxWidths.reduce ((a, b) => a + b, 0),
- relativeWidths = maxWidths.map (w => w / totalWidth),
- maxTotalWidth = cfg.maxTotalWidth - (delimiterLength * (maxWidths.length - 1)),
- excessWidth = Math.max (0, totalWidth - maxTotalWidth),
- computedWidths = zip ([cfg.minColumnWidths, maxWidths, relativeWidths],
- (min, max, relative) => Math.max (min, Math.floor (max - excessWidth * relative))),
- /* This is how many symbols we should pad or cut (per column). */
- restCellWidths = cellWidths.map (widths => zip ([computedWidths, widths], (a, b) => a - b))
- /* Perform final composition. */
- return zip ([cells, restCellWidths], (a, b) =>
- zip ([a, b], (str, w) => (w >= 0)
- ? (cfg.right ? (' '.repeat (w) + str) : (str + ' '.repeat (w)))
- : (limit (str, strlen (str) + w))).join (cfg.delimiter))
- }
- const asTable = cfg => O.assign (arr => {
- /* Print arrays */
- if (arr[0] && Array.isArray (arr[0])) {
- return asColumns (arr.map (r => r.map (
- (c, i) => (c === undefined) ? '' : cfg.print (c, i)
- )
- ),
- cfg).join ('\n')
- }
- /* Print objects */
- const colNames = [...new Set ([].concat (...arr.map (O.keys)))],
- columns = [colNames.map (cfg.title),
- ...arr.map (o => colNames.map (
- key => (o[key] === undefined) ? '' : cfg.print (o[key], key)
- )
- )
- ],
- lines = asColumns (columns, cfg)
- return (cfg.dash ? [lines[0], cfg.dash.repeat (strlen (lines[0])), ...lines.slice (1)] : lines).join ('\n')
- }, cfg, {
- configure: newConfig => asTable (O.assign ({}, cfg, newConfig)),
- })
- module.exports = asTable ({
- maxTotalWidth: Number.MAX_SAFE_INTEGER,
- print: String,
- title: String,
- dash: '-',
- right: false
- })
|