• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3const {
4  MathCeil,
5  MathMax,
6  ObjectPrototypeHasOwnProperty,
7} = primordials;
8
9const { getStringWidth } = require('internal/util/inspect');
10
11// The use of Unicode characters below is the only non-comment use of non-ASCII
12// Unicode characters in Node.js built-in modules. If they are ever removed or
13// rewritten with \u escapes, then a test will need to be (re-)added to Node.js
14// core to verify that Unicode characters work in built-ins. Otherwise,
15// consumers using Unicode in _third_party_main.js will run into problems.
16// Refs: https://github.com/nodejs/node/issues/10673
17const tableChars = {
18  /* eslint-disable node-core/non-ascii-character */
19  middleMiddle: '─',
20  rowMiddle: '┼',
21  topRight: '┐',
22  topLeft: '┌',
23  leftMiddle: '├',
24  topMiddle: '┬',
25  bottomRight: '┘',
26  bottomLeft: '└',
27  bottomMiddle: '┴',
28  rightMiddle: '┤',
29  left: '│ ',
30  right: ' │',
31  middle: ' │ ',
32  /* eslint-enable node-core/non-ascii-character */
33};
34
35const renderRow = (row, columnWidths) => {
36  let out = tableChars.left;
37  for (let i = 0; i < row.length; i++) {
38    const cell = row[i];
39    const len = getStringWidth(cell);
40    const needed = (columnWidths[i] - len) / 2;
41    // round(needed) + ceil(needed) will always add up to the amount
42    // of spaces we need while also left justifying the output.
43    out += `${' '.repeat(needed)}${cell}${' '.repeat(MathCeil(needed))}`;
44    if (i !== row.length - 1)
45      out += tableChars.middle;
46  }
47  out += tableChars.right;
48  return out;
49};
50
51const table = (head, columns) => {
52  const rows = [];
53  const columnWidths = head.map((h) => getStringWidth(h));
54  const longestColumn = columns.reduce((n, a) => MathMax(n, a.length), 0);
55
56  for (let i = 0; i < head.length; i++) {
57    const column = columns[i];
58    for (let j = 0; j < longestColumn; j++) {
59      if (rows[j] === undefined)
60        rows[j] = [];
61      const value = rows[j][i] =
62        ObjectPrototypeHasOwnProperty(column, j) ? column[j] : '';
63      const width = columnWidths[i] || 0;
64      const counted = getStringWidth(value);
65      columnWidths[i] = MathMax(width, counted);
66    }
67  }
68
69  const divider = columnWidths.map((i) =>
70    tableChars.middleMiddle.repeat(i + 2));
71
72  let result = `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` +
73               `${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` +
74               `${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` +
75               `${tableChars.rightMiddle}\n`;
76
77  for (const row of rows)
78    result += `${renderRow(row, columnWidths)}\n`;
79
80  result += `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` +
81            tableChars.bottomRight;
82
83  return result;
84};
85
86module.exports = table;
87