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