@yozora/core-html-renderer
Version:
Yozora renderer in html
236 lines (212 loc) • 8.66 kB
JavaScript
;
var prism = require('prismjs');
var ast = require('@yozora/ast');
var sanitize = require('sanitize-html');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var prism__default = /*#__PURE__*/_interopDefault(prism);
var sanitize__default = /*#__PURE__*/_interopDefault(sanitize);
const renderBlockquote = (node, context) => {
const children = context.renderChildren(node.children);
return `<blockquote class="yozora-blockquote">${children}</blockquote>`;
};
const renderBreak = () => {
return '<br class="yozora-break" />';
};
const renderCode = (node, context) => {
const { value, lang } = node;
const highlightedCode = lang != null && prism__default.default.languages[lang] != null
? prism__default.default.highlight(value, prism__default.default.languages[lang], lang)
: context.sanitize(value);
return `<pre class="yozora-code"><code>${highlightedCode}</code></pre>`;
};
const renderDelete = (node, context) => {
const children = context.renderChildren(node.children);
return `<del class="yozora-delete">${children}</del>`;
};
const renderEmphasis = (node, context) => {
const children = context.renderChildren(node.children);
return `<em class="yozora-emphasis">${children}</em>`;
};
const renderHeading = (node, context) => {
const depth = Number(node.depth);
const H = `h${depth}`;
const children = context.renderChildren(node.children);
return `<${H} class="yozora-heading">${children}</${H}>`;
};
const renderImage = (node, context) => {
const url = context.sanitize(node.url);
const alt = context.sanitize(node.alt);
const title = context.sanitize(node.title || alt);
return `<img class="yozora-image" alt="${alt}" src="${url}" title="${title}" />`;
};
const renderImageReference = (node, context) => {
const definition = context.getDefinition(node.identifier);
if (definition == null)
return '';
const url = context.sanitize(definition.url);
const alt = context.sanitize(definition.label);
const title = context.sanitize(definition.title || alt);
return `<img class="yozora-image" alt="${alt}" src="${url}" title="${title}" />`;
};
const renderInlineCode = (node, context) => {
const value = context.sanitize(node.value);
return `<pre class="yozora-inline-code"><code>${value}</code></pre>`;
};
const renderLink = (node, context) => {
const url = context.sanitize(node.url);
const title = context.sanitize(node.title || url);
const children = context.renderChildren(node.children);
return `<a class="yozora-link" href="${url}" title="${title}" target="_blank" rel="noopener,noreferrer">${children}</a>`;
};
const renderLinkReference = (node, context) => {
const definition = context.getDefinition(node.identifier);
if (definition == null)
return '';
const url = context.sanitize(definition.url);
const title = context.sanitize(definition.title || url);
const children = context.renderChildren(node.children);
return `<a class="yozora-link" href="${url}" title="${title}" target="_blank" rel="noopener,noreferrer">${children}</a>`;
};
const renderList = (node, context) => {
const children = context.renderChildren(node.children);
if (node.ordered) {
const { start = 1 } = node;
return `<ol class="yozora-list" start="${Number(start)}">${children}</ol>`;
}
return `<ul class="yozora-list">${children}</ul>`;
};
const renderListItem = (node, context) => {
const children = context.renderChildren(node.children);
let checkbox = '';
if (node.status != null) {
switch (node.status) {
case 'todo':
checkbox = '<input disabled="" type="checkbox" /> ';
break;
case 'done':
checkbox = '<input checked="" disabled="" type="checkbox" /> ';
break;
}
}
return `<li class="yozora-list-item">${checkbox}${children}</li>`;
};
const renderParagraph = (node, context) => {
const children = context.renderChildren(node.children);
return `<p class="yozora-paragraph">${children}</p>`;
};
const renderStrong = (node, context) => {
const children = context.renderChildren(node.children);
return `<strong class="yozora-strong">${children}</strong>`;
};
const ColumnAlignTypeMap = {
left: 'left',
right: 'right',
center: 'center',
};
const renderTable = (node, context) => {
const aligns = node.columns.map(col => {
if (col.align == null)
return null;
return ColumnAlignTypeMap[col.align.toLowerCase()] ?? null;
});
const [tHeadRow, ...tBodyRows] = node.children;
const thead = '<thead class="yozora-table__thead"><tr class="yozora-table-row">' +
tHeadRow.children
.map((th, i) => {
const align = aligns[i] == null ? '' : ` align="${aligns[i]}"`;
const ths = context.renderChildren(th.children);
return `<th class="yozora-table-cell"${align}>${ths}</th>`;
})
.join('') +
'</tr></thead>';
const tbody = '<tbody class="yozora-table__tbody">' +
tBodyRows
.map((row) => {
const tds = row.children
.map((td, i) => {
const align = aligns[i] == null ? '' : ` align="${aligns[i]}"`;
const ths = context.renderChildren(td.children);
return `<td class="yozora-table-cell"${align}>${ths}</td>`;
})
.join('');
return `<tr class="yozora-table-row">${tds}</tr>`;
})
.join('') +
'</tbody>';
return `<table class="yozora-table">${thead}${tbody}</table>`;
};
const renderText = (node, context) => {
const value = context.sanitize(node.value);
return `<span class="yozora-text">${value}</span>`;
};
const renderThematicBreak = () => {
return '<hr class="yozora-thematic-break" />';
};
const defaultRendererMap = {
[ast.BlockquoteType]: renderBlockquote,
[ast.BreakType]: renderBreak,
[ast.CodeType]: renderCode,
[ast.DefinitionType]: () => '',
[ast.DeleteType]: renderDelete,
[ast.EmphasisType]: renderEmphasis,
[ast.HeadingType]: renderHeading,
[ast.HtmlType]: () => '',
[ast.ImageType]: renderImage,
[ast.ImageReferenceType]: renderImageReference,
[ast.InlineCodeType]: renderInlineCode,
[ast.LinkType]: renderLink,
[ast.LinkReferenceType]: renderLinkReference,
[ast.ListType]: renderList,
[ast.ListItemType]: renderListItem,
[ast.ParagraphType]: renderParagraph,
[ast.StrongType]: renderStrong,
[ast.TableType]: renderTable,
[ast.TextType]: renderText,
[ast.ThematicBreakType]: renderThematicBreak,
_fallback: node => {
console.warn(`Cannot find render for \`${node.type}\` type node:`, node);
return '';
},
};
function createNodeRendererContext(definitionMap, footnoteDefinitionMap, rendererMap = defaultRendererMap) {
const context = {
sanitize: value => sanitize__default.default(value, { allowedTags: [] }),
getDefinition: (identifier) => definitionMap[identifier],
getFootnoteDefinition: (identifier) => footnoteDefinitionMap[identifier],
renderChildren: (nodes) => {
if (nodes == null || nodes.length < 1)
return '';
return nodes
.map(node => {
const renderNode = rendererMap[node.type];
if (renderNode === undefined) {
console.warn(`Cannot find renderer for node ${node.type}`);
return '';
}
return renderNode(node, context);
})
.join('');
},
};
return context;
}
exports.createNodeRendererContext = createNodeRendererContext;
exports.defaultRendererMap = defaultRendererMap;
exports.renderBlockquote = renderBlockquote;
exports.renderBreak = renderBreak;
exports.renderCode = renderCode;
exports.renderDelete = renderDelete;
exports.renderEmphasis = renderEmphasis;
exports.renderHeading = renderHeading;
exports.renderImage = renderImage;
exports.renderImageReference = renderImageReference;
exports.renderInlineCode = renderInlineCode;
exports.renderLink = renderLink;
exports.renderLinkReference = renderLinkReference;
exports.renderList = renderList;
exports.renderListItem = renderListItem;
exports.renderParagraph = renderParagraph;
exports.renderStrong = renderStrong;
exports.renderTable = renderTable;
exports.renderText = renderText;
exports.renderThematicBreak = renderThematicBreak;