1'use strict'; 2 3const stringWidth = require('string-width'); 4const stripAnsi = require('strip-ansi'); 5 6const concat = Array.prototype.concat; 7const defaults = { 8 character: ' ', 9 newline: '\n', 10 padding: 2, 11 sort: true, 12 width: 0, 13}; 14 15function byPlainText(a, b) { 16 const plainA = stripAnsi(a); 17 const plainB = stripAnsi(b); 18 19 if (plainA === plainB) { 20 return 0; 21 } 22 23 if (plainA > plainB) { 24 return 1; 25 } 26 27 return -1; 28} 29 30function makeArray() { 31 return []; 32} 33 34function makeList(count) { 35 return Array.apply(null, Array(count)); 36} 37 38function padCell(fullWidth, character, value) { 39 const valueWidth = stringWidth(value); 40 const filler = makeList(fullWidth - valueWidth + 1); 41 42 return value + filler.join(character); 43} 44 45function toRows(rows, cell, i) { 46 rows[i % rows.length].push(cell); 47 48 return rows; 49} 50 51function toString(arr) { 52 return arr.join(''); 53} 54 55function columns(values, options) { 56 values = concat.apply([], values); 57 options = Object.assign({}, defaults, options); 58 59 let cells = values.filter(Boolean).map(String); 60 61 if (options.sort !== false) { 62 cells = cells.sort(byPlainText); 63 } 64 65 const termWidth = options.width || process.stdout.columns; 66 const cellWidth = 67 Math.max.apply(null, cells.map(stringWidth)) + options.padding; 68 const columnCount = Math.floor(termWidth / cellWidth) || 1; 69 const rowCount = Math.ceil(cells.length / columnCount) || 1; 70 71 if (columnCount === 1) { 72 return cells.join(options.newline); 73 } 74 75 return cells 76 .map(padCell.bind(null, cellWidth, options.character)) 77 .reduce(toRows, makeList(rowCount).map(makeArray)) 78 .map(toString) 79 .join(options.newline); 80} 81 82module.exports = columns; 83