All files / lib/internal cli_table.js

100% Statements 95/95
93.33% Branches 14/15
100% Functions 2/2
100% Lines 95/95

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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