very-small-parser
Version:
A very small Markdown, HTML, and CSS parser.
152 lines (151 loc) • 6.08 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.toText = void 0;
const toText_1 = require("../../html/toText");
const toText_2 = require("../inline/toText");
const toTextInlineChildren = (children) => {
if (!children)
return '';
let str = '';
const length = children.length;
for (let i = 0; i < length; i++)
str += (0, toText_2.toText)(children[i]);
return str;
};
const toTextBlockChildren = (children, separator = '\n\n') => {
if (!children)
return '';
let str = '';
const length = children.length;
for (let i = 0; i < length; i++)
str += (str ? separator : '') + (0, exports.toText)(children[i]);
return str;
};
const toText = (node) => {
if (Array.isArray(node))
return toTextBlockChildren(node);
const block = node;
const type = block.type;
switch (type) {
case 'paragraph':
return toTextInlineChildren(block.children);
case 'code': {
return '```' + (block.lang || '') + (block.meta ? ' ' + block.meta : '') + '\n' + block.value + '\n```';
}
case 'heading': {
const depth = block.depth;
const prefix = '#'.repeat(depth);
return prefix + ' ' + toTextInlineChildren(block.children);
}
case 'blockquote': {
const indent = block.spoiler ? '>! ' : '> ';
return indent + toTextBlockChildren(block.children).replace(/\n/g, '\n' + indent);
}
case 'list': {
const { ordered, start, spread } = block;
const bullet = ordered ? (start || 1) + '. ' : '- ';
const separator = spread ? '\n\n' : '\n';
const children = block.children;
const last = children.length - 1;
let str = '';
for (let i = 0; i <= last; i++) {
const item = children[i];
const itemSeparator = item.spread ? '\n\n' : '\n';
const content = toTextBlockChildren(item.children, itemSeparator).replace(/\n/g, '\n ');
const checked = item.checked;
if (typeof checked === 'boolean')
str += (checked ? '- [x]' : '- [ ]') + ' ' + content;
else
str += bullet + content;
if (i !== last)
str += separator;
}
return str;
}
case 'thematicBreak':
return '---';
case 'table': {
const { align, children: rows } = block;
const texts = [];
const columnSizes = Array.from({ length: align.length }, () => 1);
const columnLength = align.length;
const rowLength = rows.length;
let totalSize = 1 * columnLength;
// Compute column sizes and pre-format cell texts
for (let i = 0; i < rowLength; i++) {
const row = rows[i];
const textRow = [];
const cells = row.children;
texts.push(textRow);
for (let j = 0; j < columnLength; j++) {
const cell = cells[j];
const text = toTextInlineChildren(cell.children);
textRow.push(text);
const size = text.length;
if (size > columnSizes[j]) {
totalSize += size - columnSizes[j];
columnSizes[j] = size;
}
}
}
const isWide = totalSize > 200;
// Format cells
for (let i = 0; i < rowLength; i++) {
const row = texts[i];
for (let j = 0; j < columnLength; j++) {
const alignment = align[j];
const size = columnSizes[j];
let txt = row[j];
const length = txt.length;
const leftPadding = alignment === 'right' ? size - length : alignment === 'center' ? Math.ceil((size - length) / 2) : 0;
if (!isWide)
txt = row[j].padStart(leftPadding + length, ' ').padEnd(size, ' ');
texts[i][j] = ' ' + txt + ' ';
}
}
// Format first row (header)
let str = '|' + texts[0].join('|') + '|\n';
// Format header separator
for (let j = 0; j < columnLength; j++) {
const alignment = align[j];
const txt = isWide ? '-' : '-'.repeat(columnSizes[j]);
str +=
'|' +
(alignment === 'center' || alignment === 'left' ? ':' : '-') +
txt +
(alignment === 'center' || alignment === 'right' ? ':' : '-');
}
str += '|';
// Format remaining rows
for (let i = 1; i < rowLength; i++)
str += '\n|' + texts[i].join('|') + '|';
return str;
}
case 'definition': {
const { label, url, title } = block;
let str = '[' + label + ']: ';
if (!url || url.includes('"'))
str += '<' + url + '>';
else
str += url;
if (title) {
str += title.length + str.length > 80 ? '\n ' : ' ';
const hasDoubleQuote = title.includes('"');
str += hasDoubleQuote ? '(' + title + ')' : '"' + title + '"';
}
return str;
}
case 'footnoteDefinition': {
const { label, children } = block;
return '[^' + label + ']: ' + toTextBlockChildren(children).replace(/\n/g, '\n ');
}
case 'math':
return '$$\n' + block.value + '\n$$';
case 'element':
return (0, toText_1.toText)(block);
case '': // newline
return '\n\n';
}
return toTextBlockChildren(block.children ?? []);
};
exports.toText = toText;