All files / src format-table.js

15.68% Statements 8/51
100% Branches 1/1
0% Functions 0/2
15.68% Lines 8/51

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 52193x 193x 193x 193x 193x                             193x 193x                                                           193x  
import charRegex from 'char-regex';
import { stripVTControlCharacters } from 'node:util';
 
const SEPARATOR = '  ';
 
function stringLength(string) {
  if (!string) {
    return 0;
  }
  const stringWithoutAnsi = stripVTControlCharacters(string);
  if (!stringWithoutAnsi) {
    return 0;
  }
  const stringWithSplitEmojis = stringWithoutAnsi.match(charRegex());
  if (!stringWithSplitEmojis) {
    return 0;
  }
  return stringWithSplitEmojis.length;
}
 
export const formatTable = (data, columnWidth = []) => {
  const fixedWidthPlaceholder = columnWidth.map((item) => {
    return typeof item === 'number' ? ' '.repeat(item) : item;
  });

  const columnSizes = [fixedWidthPlaceholder, ...data]
    .map((row) => row.map((cell) => String(cell)))
    .reduce((acc, row) => {
      row.forEach((cell, index) => {
        const cellLength = stringLength(cell);
        acc[index] = Math.max(acc[index] ?? 0, cellLength);
      });
      return acc;
    }, []);

  return data
    .map((row) => row.map((cell) => String(cell)))
    .map((row) => {
      return row
        .map((cell, index) => {
          const isLastColumn = index === row.length - 1;
          if (isLastColumn) {
            return cell;
          }
          const rightPaddingLength = columnSizes[index] - stringLength(cell) ?? 0;
          return cell + ' '.repeat(rightPaddingLength);
        })
        .join(SEPARATOR);
    })
    .join('\n');
};