UNPKG

3.18 kBJavaScriptView Raw
1"use strict";
2
3const O = Object
4 , { first, strlen } = require ('printable-characters') // handles ANSI codes and invisible characters
5 , limit = (s, n) => (first (s, n - 1) + '…')
6
7const asColumns = (rows, cfg_) => {
8
9 const
10
11 zip = (arrs, f) => arrs.reduce ((a, b) => b.map ((b, i) => [...a[i] || [], b]), []).map (args => f (...args)),
12
13 /* Convert cell data to string (converting multiline text to singleline) */
14
15 cells = rows.map (r => r.map (c => c.replace (/\n/g, '\\n'))),
16
17 /* Compute column widths (per row) and max widths (per column) */
18
19 cellWidths = cells.map (r => r.map (strlen)),
20 maxWidths = zip (cellWidths, Math.max),
21
22 /* Default config */
23
24 cfg = O.assign ({
25 delimiter: ' ',
26 minColumnWidths: maxWidths.map (x => 0),
27 maxTotalWidth: 0 }, cfg_),
28
29 delimiterLength = strlen (cfg.delimiter),
30
31 /* Project desired column widths, taking maxTotalWidth and minColumnWidths in account. */
32
33 totalWidth = maxWidths.reduce ((a, b) => a + b, 0),
34 relativeWidths = maxWidths.map (w => w / totalWidth),
35 maxTotalWidth = cfg.maxTotalWidth - (delimiterLength * (maxWidths.length - 1)),
36 excessWidth = Math.max (0, totalWidth - maxTotalWidth),
37 computedWidths = zip ([cfg.minColumnWidths, maxWidths, relativeWidths],
38 (min, max, relative) => Math.max (min, Math.floor (max - excessWidth * relative))),
39
40 /* This is how many symbols we should pad or cut (per column). */
41
42 restCellWidths = cellWidths.map (widths => zip ([computedWidths, widths], (a, b) => a - b))
43
44 /* Perform final composition. */
45
46 return zip ([cells, restCellWidths], (a, b) =>
47 zip ([a, b], (str, w) => (w >= 0)
48 ? (cfg.right ? (' '.repeat (w) + str) : (str + ' '.repeat (w)))
49 : (limit (str, strlen (str) + w))).join (cfg.delimiter))
50}
51
52const asTable = cfg => O.assign (arr => {
53
54/* Print arrays */
55
56 if (arr[0] && Array.isArray (arr[0]))
57 return asColumns (
58 arr.map (
59 r => r.map (
60 (c, i) => (c === undefined) ? '' : cfg.print(c, i)
61 )
62 ),
63 cfg
64 ).join ('\n')
65
66/* Print objects */
67
68 const colNames = [...new Set ([].concat (...arr.map (O.keys)))],
69 columns = [
70 colNames.map (cfg.title),
71 ...arr.map (
72 o => colNames.map (
73 key => (o[key] === undefined) ? '' : cfg.print(o[key], key)
74 )
75 )
76 ],
77 lines = asColumns (columns, cfg)
78
79 return [lines[0], cfg.dash.repeat (strlen (lines[0])), ...lines.slice (1)].join ('\n')
80
81}, cfg, {
82
83 configure: newConfig => asTable (O.assign ({}, cfg, newConfig)),
84})
85
86module.exports = asTable ({
87
88 maxTotalWidth: Number.MAX_SAFE_INTEGER,
89 print: String,
90 title: String,
91 dash: '-',
92 right: false
93})