UNPKG

@f-fjs/tidy-markdown

Version:
158 lines 6.5 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const wcwidth_1 = __importDefault(require("wcwidth")); const node_1 = require("./node"); const tree_adapter_1 = __importDefault(require("./tree-adapter")); const utils_1 = require("./utils"); function pad(text, length, char = ' ') { if (char == null) { char = ' '; } const invert = typeof text === 'number'; if (invert) { [length, text] = Array.from([text, length]); } text = text.toString(); let res = ''; const padlength = length - wcwidth_1.default(text); res += char.repeat(padlength); if (invert) { return res + text; } else { return text + res; } } /** * Determines the alignment for a table cell by reading the style attribute * @return {String|null} One of 'right', 'left', 'center', or null */ function getCellAlignment(node) { var _a, _b; return ((_b = (_a = utils_1.getAttribute(node, 'style')) === null || _a === void 0 ? void 0 : _a.match(/text-align:\s*(right|left|center)/)) === null || _b === void 0 ? void 0 : _b[1]) || null; } /** * Join an array of cells (columns) from a single row. * @param {String[]} columns * @return {String} The joined row. */ function joinColumns(columns) { if (columns.length > 1) { return columns.join(' | '); } else { // use a leading pipe for single column tables, otherwise the output won't // render as a table return `| ${columns[0]}`; } } function extractColumns(row) { const columns = new Array(); const alignments = new Array(); if (utils_1.isParentNode(row)) row.childNodes .forEach(column => { // we don't care if it's a `th` or `td` because we cannot represent the // difference in markdown anyway - the first row always represents the // headers if (utils_1.isElement(column) && ['th', 'td'].includes(column.tagName) && node_1.isConverterNode(column)) { columns.push(column._replacement); alignments.push(getCellAlignment(column)); } else if (tree_adapter_1.default.isTextNode(column)) { throw new Error(`Cannot handle ${utils_1.isElement(column) && column.tagName} in table row`); } }); return { columns, alignments }; } function extractRows(node) { const alignments = new Array(); const rows = new Array(); const inqueue = [node]; while (inqueue.length > 0) { const elem = inqueue.shift(); if (utils_1.isParentNode(elem)) elem.childNodes.forEach(child => { if (utils_1.isElement(child) && child.tagName === 'tr') { const row = extractColumns(child); rows.push(row.columns); // alignments in markdown are column-wide, so after the first row we just // want to make sure there aren't any conflicting values within a single // column for (let i = 0; i < row.alignments.length; i++) { const alignment = row.alignments[i]; if (i + 1 > alignments.length) { // if all previous rows were shorter, or if we are at the beginning // of the table, then we need to populate the alignments array alignments.push(alignment); } if (alignment !== alignments[i]) { throw new Error(`Alignment in a table column ${i} is not consistent`); } } } else if (tree_adapter_1.default.isElementNode(child)) { inqueue.push(child); } }); } // when there are more alignments than headers (from columns that extend beyond // the headers), and those alignments aren't doing anything, it looks better to // remove them while (alignments.length > rows[0].length && alignments.slice(-1)[0] === null) { alignments.pop(); } return { alignments, rows }; } exports.extractRows = extractRows; function formatRow(row, alignments, columnWidths) { // apply padding around each cell for alignment and column width row = row.map((column, i) => { switch (alignments[i]) { case 'right': return pad(columnWidths[i], column); case 'center': // rounding causes a bias to the left because we can't have half a char const whitespace = columnWidths[i] - column.length; const leftPadded = pad(Math.floor(whitespace / 2) + column.length, column); return pad(leftPadded, Math.ceil(whitespace / 2) + leftPadded.length); default: // left is the default alignment when formatting return pad(column, columnWidths[i]); } }); // trimRight is to remove any trailing whitespace added by the padding return joinColumns(row).trimRight(); } exports.formatRow = formatRow; function formatHeaderSeparator(alignments, columnWidths) { const columns = alignments.map((alignment, i) => { switch (alignment) { case 'center': return `:${pad('', columnWidths[i] - 2, '-')}:`; case 'left': return `:${pad('', columnWidths[i] - 1, '-')}`; case 'right': return pad('', columnWidths[i] - 1, '-') + ':'; default: return pad('', columnWidths[i], '-'); } }); return joinColumns(columns); } exports.formatHeaderSeparator = formatHeaderSeparator; function getColumnWidths(rows) { const columnWidths = rows[0].map((row) => (row || '').length); rows.forEach((row) => { row.forEach((column, i) => { if (i < columnWidths.length) columnWidths[i] = Math.max(wcwidth_1.default(column), columnWidths[i]); }); }); return columnWidths; } exports.getColumnWidths = getColumnWidths; //# sourceMappingURL=tables.js.map