'use strict'; const { Writable } = require('stream'); const MIN_CELL_WIDTH = 15; class TablePrinter { constructor(opts) { this.opts = opts; this._hasWritten = false; } push(csv) { const lines = csv.split(this.opts.eol); if (!lines.length) return; if (!this._hasWritten) this.setColumnWidths(lines[0]); const top = this._hasWritten ? this.middleLine : this.topLine; this.print(top, lines); this._hasWritten = true; } end(csv) { let lines = csv.split(this.opts.eol); if (!this._hasWritten) this.setColumnWidths(lines[0]); const top = this._hasWritten ? this.middleLine : this.topLine; this.print(top, lines, this.bottomLine); } printCSV(csv) { this.end(csv); } setColumnWidths(line) { this.colWidths = line .split(this.opts.delimiter) .map(elem => Math.max(elem.length * 2, MIN_CELL_WIDTH)); this.topLine = `┌${this.colWidths.map(i => '─'.repeat(i)).join('┬')}┐`; this.middleLine = `├${this.colWidths.map(i => '─'.repeat(i)).join('┼')}┤`; this.bottomLine = `└${this.colWidths.map(i => '─'.repeat(i)).join('┴')}┘`; } print(top, lines, bottom) { const table = `${top}\n` + lines .map(row => this.formatRow(row)) .join(`\n${this.middleLine}\n`) + (bottom ? `\n${bottom}` : ''); // eslint-disable-next-line no-console console.log(table); } formatRow(row) { const wrappedRow = row .split(this.opts.delimiter) .map((cell, i) => cell.match(new RegExp(`(.{1,${this.colWidths[i] - 2}})`, 'g')) || []); const height = wrappedRow.reduce((acc, cell) => Math.max(acc, cell.length), 0); const processedCells = wrappedRow .map((cell, i) => this.formatCell(cell, height, this.colWidths[i])); return Array(height).fill('') .map((_, i) => `│${processedCells.map(cell => cell[i]).join('│')}│`) .join('\n'); } formatCell(content, heigth, width) { const paddedContent = this.padCellHorizontally(content, width); return this.padCellVertically(paddedContent, heigth, width); } padCellVertically(content, heigth, width) { const vertPad = heigth - content.length; const vertPadTop = Math.ceil(vertPad / 2); const vertPadBottom = vertPad - vertPadTop; const emptyLine = ' '.repeat(width); return [ ...Array(vertPadTop).fill(emptyLine), ...content, ...Array(vertPadBottom).fill(emptyLine) ]; } padCellHorizontally(content, width) { return content.map((line) => { const horPad = width - line.length - 2; return ` ${line}${' '.repeat(horPad)} `; }); } writeStream() { let csv = ''; const table = this; return new Writable({ write(chunk, encoding, callback) { csv += chunk.toString(); const index = csv.lastIndexOf(table.opts.eol); let lines = csv.substring(0, index); csv = csv.substring(index + 1); if (lines) table.push(lines); callback(); }, final() { table.end(csv); } }); } } module.exports = TablePrinter;