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