UNPKG

@f-fjs/tidy-markdown

Version:
300 lines 10.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const indent_1 = __importDefault(require("indent")); const parse5_1 = require("parse5"); const language_code_rewrites_json_1 = __importDefault(require("./language-code-rewrites.json")); const node_1 = require("./node"); const tables_1 = require("./tables"); const tree_adapter_1 = __importDefault(require("./tree-adapter")); const utils_1 = require("./utils"); const CODE_HIGHLIGHT_REGEX = /(?:highlight highlight|lang(?:uage)?)-(\S+)/; const { insertTextBefore, insertText, isTextNode } = tree_adapter_1.default; function indentChildren(node) { let allChildrenAreElements = true; if (utils_1.isParentNode(node)) for (const child of Array.from(node.childNodes)) { if (isTextNode(child)) { allChildrenAreElements = false; } } if (allChildrenAreElements) { if (utils_1.isParentNode(node)) Array.from(node.childNodes) .forEach(child => insertTextBefore(node, '\n ', child)); return insertText(node, '\n'); } } // TODO: handle indenting nested children // regex taken from https://github.com/chjj/marked/blob/8f9d0b/lib/marked.js#L452 function isValidLink(link) { return /.+(?:@|:\/).+/.test(link); } const fallback = () => true; /** * This array holds a set of "converters" that process DOM nodes and output * Markdown. The `filter` property determines what nodes the converter is run * on. The `replacement` function takes the content of the node and the node * itself and returns a string of Markdown. The `surroundingBlankLines` option * determines whether or not the block should have a blank line before and after * it. Converters are matched to nodes starting from the top of the converters * list and testing each one downwards. * @type {Array} */ exports.Converters = new Array({ filter(node) { return node_1.isConverterNode(node.parentNode) && node.parentNode._converter.filter === fallback; }, surroundingBlankLines: false, replacement(_content, node) { indentChildren(node); return ''; } }, { filter: 'p', surroundingBlankLines: true, replacement(content) { return content; } }, { filter: ['td', 'th'], surroundingBlankLines: false, replacement(content) { return content; } }, { filter: ['tbody', 'thead', 'tr'], surroundingBlankLines: false, replacement() { return ''; } }, { filter: ['del', 's', 'strike'], surroundingBlankLines: false, replacement(content) { return `~~${content}~~`; } }, { filter: ['em', 'i'], surroundingBlankLines: false, replacement(content) { if (content.indexOf('_') >= 0) { return `*${content.replace(/\*/g, '\\*')}*`; } else { return `_${content.replace(/_/g, '\\_')}_`; } } }, { filter: ['strong', 'b'], surroundingBlankLines: false, replacement(content) { return `**${content.replace(/\*/g, '\\*')}**`; } }, { filter: 'br', surroundingBlankLines: false, trailingWhitespace(node) { if (utils_1.isElement(node.nextSibling) && node.nextSibling.tagName === 'br') return ''; return '\n'; }, replacement() { return '<br>'; } }, { filter: 'a', surroundingBlankLines: false, replacement(content, node, links) { const refUrl = utils_1.getAttribute(node, 'href') || ''; const refTitle = utils_1.getAttribute(node, 'title'); const referenceLink = links.find(({ url, title }) => url == refUrl && title == refTitle); if (referenceLink) { if (content.toLowerCase() === referenceLink.name) { return `[${content}]`; } else { return `[${content}][${referenceLink.name}]`; } } else if (refTitle) { return `[${content}](${refUrl} \"${refTitle}\")`; } else if (isValidLink(refUrl) && (content === refUrl || content === refUrl.replace(/^mailto:/, ''))) { return `<${content}>`; } else { return `[${content}](${refUrl})`; } } }, { filter(node) { // Ignore img nodes that have custom styling or other attributes return utils_1.isElement(node) && node.tagName === 'img' && utils_1.noExtraAttributes(node, 'alt', 'src', 'title'); }, surroundingBlankLines: false, replacement(_content, node, links) { const alt = utils_1.getAttribute(node, 'alt') || ''; const refUrl = utils_1.getAttribute(node, 'src') || ''; const refTitle = utils_1.getAttribute(node, 'title'); const referenceLink = links.find(({ url, title }) => url == refUrl && title == refTitle); if (referenceLink) { if (alt.toLowerCase() === referenceLink.name) { return `![${alt}]`; } else { return `![${alt}][${referenceLink.name}]`; } } else if (refTitle) { return `![${alt}](${refUrl} \"${refTitle}\")`; } else { return `![${alt}](${refUrl})`; } } }, { filter(node) { return node.type === 'checkbox' && utils_1.isElement(node.parentNode) && node.parentNode.tagName === 'li'; }, surroundingBlankLines: false, replacement(_content, node) { return (node['checked'] ? '[x]' : '[ ]') + ' '; } }, { filter: 'table', surroundingBlankLines: true, replacement(_content, node) { const { alignments, rows } = tables_1.extractRows(node); const columnWidths = tables_1.getColumnWidths(rows); // const totalCols = rows[0].length const out = [ tables_1.formatRow(rows[0], alignments, columnWidths), tables_1.formatHeaderSeparator(alignments, columnWidths), ...rows.slice(1).map(row => tables_1.formatRow(row, alignments, columnWidths)) ]; return out.join('\n'); } }, { filter: 'pre', surroundingBlankLines: true, replacement(content, node) { var _a, _b, _c, _d; let language; if (utils_1.isParentNode(node)) { const first = node.childNodes[0]; if (utils_1.isElement(first) && first.tagName === 'code') { language = (_b = (_a = utils_1.getAttribute(node.childNodes[0], 'class')) === null || _a === void 0 ? void 0 : _a.match(CODE_HIGHLIGHT_REGEX)) === null || _b === void 0 ? void 0 : _b[1]; } } if (language == null && utils_1.isElement(node.parentNode) && node.parentNode.tagName === 'div') { language = (_d = (_c = utils_1.getAttribute(node.parentNode, 'class')) === null || _c === void 0 ? void 0 : _c.match(CODE_HIGHLIGHT_REGEX)) === null || _d === void 0 ? void 0 : _d[1]; } if (language != null) { language = language.toLowerCase(); if (language_code_rewrites_json_1.default[language] != null) { language = language_code_rewrites_json_1.default[language]; } } return utils_1.delimitCode(`${language || ''}\n${content}\n`, '```'); } }, { filter: 'code', surroundingBlankLines: false, replacement(content, node) { if ((utils_1.isElement(node.parentNode) && node.parentNode.tagName) !== 'pre') { return utils_1.delimitCode(content, '`'); // inline code } else { // code that we'll handle once it reaches the pre tag. we only bother // passing it through this converter to avoid it being serialized before // it gets to the pre tag return content; } } }, { filter(node) { return utils_1.isElement(node) && node.tagName === 'div' && CODE_HIGHLIGHT_REGEX.test(node['className']); }, surroundingBlankLines: true, replacement(content) { return content; } }, { filter: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], surroundingBlankLines: true, replacement(content, node) { utils_1.assertIsElement(node); const hLevel = parseInt(node.tagName.charAt(1), 10); return `${'#'.repeat(hLevel)} ${content}`; } }, { filter: 'hr', surroundingBlankLines: true, replacement() { return '-'.repeat(80); } }, { filter: 'blockquote', surroundingBlankLines: true, replacement(content) { return indent_1.default(content, '> '); } }, { filter: 'li', surroundingBlankLines: false, trailingWhitespace: '\n', replacement(content, node) { if (content.indexOf('\n') >= 0) { // the indent here is for all the lines after the first, so we only need // do it if there's a linebreak in the content content = indent_1.default(content, ' ').trimLeft(); } const parent = node.parentNode; const prefix = utils_1.isElement(parent) && parent.tagName === 'ol' ? parent.childNodes.indexOf(node) + 1 + '. ' : '- '; return prefix + content; } }, { filter: ['ul', 'ol'], surroundingBlankLines(node) { let p = node; while (p.parentNode) { p = p.parentNode; if (utils_1.isElement(p) && p.tagName === 'li') break; } return !p ? true : { leading: '\n', trailing: '' }; }, replacement(content, node) { let p = node; while (p.parentNode) { p = p.parentNode; if (utils_1.isElement(p) && p.tagName === 'li') break; } if (utils_1.isElement(p === null || p === void 0 ? void 0 : p.parentNode) && p.parentNode.tagName === 'ol' && !(utils_1.isElement(node) && node.tagName === 'ol')) { content = indent_1.default(content, ' '); } return content.trimRight(); } }, { filter: '_comment', replacement(content) { return `<!-- ${content} -->`; } }, { filter: fallback, surroundingBlankLines: true, replacement(_content, node) { indentChildren(node); return parse5_1.serialize({ children: [node], nodeName: '#document-fragment', quirksMode: false }, { treeAdapter: tree_adapter_1.default }); } }); //# sourceMappingURL=converters.js.map