UNPKG

debt-collector

Version:

a nodejs tool to identify, track and mesure technical debt

220 lines (219 loc) 7.34 kB
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime"; import { Box, Text } from 'ink'; import crypto from 'crypto'; function sha1(obj) { // Convert object to string const str = JSON.stringify(obj); // Create SHA1 hash using Node's crypto module return crypto.createHash('sha1').update(str).digest('hex'); } /* Table */ export default function Table(props) { /* Config */ /** * Merges provided configuration with defaults. */ const getConfig = () => { return { data: props.data, columns: props.columns || getDataKeys(), padding: props.padding || 1, header: props.header || Header, cell: props.cell || Cell, skeleton: props.skeleton || Skeleton, }; }; /** * Gets all keyes used in data by traversing through the data. */ const getDataKeys = () => { let keys = new Set(); // Collect all the keys. for (const data of props.data) { for (const key in data) { keys.add(key); } } return Array.from(keys); }; /** * Calculates the width of each column by finding * the longest value in a cell of a particular column. * * Returns a list of column names and their widths. */ const getColumns = () => { const config = getConfig(); const { columns, padding } = config; const widths = columns.map((key) => { const header = String(key).length; /* Get the width of each cell in the column */ const data = props.data.map((data) => { const value = data[key]; if (value == undefined || value == null) return 0; return String(value).length; }); const width = Math.max(...data, header) + padding * 2; /* Construct a cell */ return { column: key, width: width, key: String(key), }; }); return widths; }; /** * Returns a (data) row representing the headings. */ const getHeadings = () => { const config = getConfig(); const { columns } = config; const headings = columns.reduce((acc, column) => ({ ...acc, [column]: column }), {}); return headings; }; /* Rendering utilities */ const config = getConfig(); // The top most line in the table. const header = row({ cell: config.skeleton, padding: config.padding, skeleton: { component: config.skeleton, // chars line: '─', left: '┌', right: '┐', cross: '┬', }, }); // The line with column names. const heading = row({ cell: config.header, padding: config.padding, skeleton: { component: config.skeleton, // chars line: ' ', left: '│', right: '│', cross: '│', }, }); // The line that separates rows. const separator = row({ cell: config.skeleton, padding: config.padding, skeleton: { component: config.skeleton, // chars line: '─', left: '├', right: '┤', cross: '┼', }, }); // The row with the data. const data = row({ cell: config.cell, padding: config.padding, skeleton: { component: config.skeleton, // chars line: ' ', left: '│', right: '│', cross: '│', }, }); // The bottom most line of the table. const footer = row({ cell: config.skeleton, padding: config.padding, skeleton: { component: config.skeleton, // chars line: '─', left: '└', right: '┘', cross: '┴', }, }); /* Data */ const columns = getColumns(); const headings = getHeadings(); /** * Render the table line by line. */ return (_jsxs(Box, { flexDirection: "column", children: [header({ key: 'header', columns, data: {} }), heading({ key: 'heading', columns, data: headings }), props.data.map((row, index) => { // Calculate the hash of the row based on its value and position const key = `row-${sha1(row)}-${index}`; // Construct a row. return (_jsxs(Box, { flexDirection: "column", children: [separator({ key: `separator-${key}`, columns, data: {} }), data({ key: `data-${key}`, columns, data: row })] }, key)); }), footer({ key: 'footer', columns, data: {} })] })); } /** * Constructs a Row element from the configuration. */ function row(config) { /* This is a component builder. We return a function. */ const skeleton = config.skeleton; /* Row */ return (props) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(skeleton.component, { children: skeleton.left }), ...intersperse((i) => { const key = `${props.key}-hseparator-${i}`; // The horizontal separator. return (_jsx(skeleton.component, { children: skeleton.cross }, key)); }, // Values. props.columns.map((column, colI) => { // content const value = props.data[column.column]; if (value == undefined || value == null) { const key = `${props.key}-empty-${column.key}`; return (_jsx(config.cell, { column: colI, children: skeleton.line.repeat(column.width) }, key)); } else { const key = `${props.key}-cell-${column.key}`; // margins const ml = config.padding; const mr = column.width - String(value).length - config.padding; return ( /* prettier-ignore */ _jsx(config.cell, { column: colI, children: `${skeleton.line.repeat(ml)}${String(value)}${skeleton.line.repeat(mr)}` }, key)); } })), _jsx(skeleton.component, { children: skeleton.right })] })); } /** * Renders the header of a table. */ export function Header(props) { return (_jsx(Text, { bold: true, color: "blue", children: props.children })); } /** * Renders a cell in the table. */ export function Cell(props) { return _jsx(Text, { children: props.children }); } /** * Redners the scaffold of the table. */ export function Skeleton(props) { return _jsx(Text, { bold: true, children: props.children }); } /* Utility functions */ /** * Intersperses a list of elements with another element. */ function intersperse(intersperser, elements) { // Intersparse by reducing from left. let interspersed = elements.reduce((acc, element, index) => { // Only add element if it's the first one. if (acc.length === 0) return [element]; // Add the intersparser as well otherwise. return [...acc, intersperser(index), element]; }, []); return interspersed; }