@nacelle/rich-text-utils
Version:
A set of Typescript types and helpers to work with Rich Text fields.
127 lines • 5.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.genericHtmlRender = exports.render = exports.transformNode = exports.defaultMetaTransformer = exports.markToTagName = exports.renderRule = exports.RenderError = void 0;
const guards_1 = require("./guards");
const array_flatten_1 = require("array-flatten");
class RenderError extends Error {
constructor(message, node) {
super(message);
this.node = node;
Object.setPrototypeOf(this, RenderError.prototype);
}
}
exports.RenderError = RenderError;
const renderRule = (guard, transform) => ({
appliable: guard,
apply: (ctx) => transform(ctx)
});
exports.renderRule = renderRule;
function markToTagName(mark) {
switch (mark) {
case 'emphasis':
return 'em';
case 'underline':
return 'u';
case 'strikethrough':
return 'del';
case 'highlight':
return 'mark';
default:
return mark;
}
}
exports.markToTagName = markToTagName;
const defaultMetaTransformer = ({ meta }) => {
const attributes = {};
meta.forEach((entry) => {
if (['target', 'title', 'rel'].includes(entry.id)) {
attributes[entry.id] = entry.value;
}
});
return attributes;
};
exports.defaultMetaTransformer = defaultMetaTransformer;
function transformNode(adapter, node, key, ancestors, renderRules) {
const children = guards_1.hasChildren(node)
? array_flatten_1.flatten(node.children
.map((node, index) => transformNode(adapter, node, `t-${index}`, [...ancestors, node], renderRules))
.filter((x) => !!x))
: undefined;
const matchingTransform = renderRules.find((transform) => transform.appliable(node));
if (matchingTransform) {
return matchingTransform.apply({ adapter, node, children, key, ancestors });
}
else {
throw new RenderError(`Don't know how to render a node with type "${node.type}". Please specify a custom renderRule for it!`, node);
}
}
exports.transformNode = transformNode;
function render(adapter, richTextOrNode, renderRules) {
if (!richTextOrNode) {
return null;
}
const result = transformNode(adapter, guards_1.isRichText(richTextOrNode)
? richTextOrNode.value.document
: guards_1.isDocument(richTextOrNode)
? richTextOrNode.document
: richTextOrNode, 't-0', [], renderRules);
return result;
}
exports.render = render;
function genericHtmlRender(adapter, richTextOrNode, customRules, metaTransformer = exports.defaultMetaTransformer) {
return render(adapter, richTextOrNode, [
...customRules,
exports.renderRule(guards_1.isRoot, ({ adapter: { renderFragment }, key, children }) => {
return renderFragment(children, key);
}),
exports.renderRule(guards_1.isParagraph, ({ adapter: { renderNode }, key, children }) => {
return renderNode('p', { key }, children);
}),
exports.renderRule(guards_1.isList, ({ adapter: { renderNode }, node, key, children }) => {
return renderNode(node.style === 'bulleted' ? 'ul' : 'ol', { key }, children);
}),
exports.renderRule(guards_1.isListItem, ({ adapter: { renderNode }, key, children }) => {
return renderNode('li', { key }, children);
}),
exports.renderRule(guards_1.isBlockquote, ({ adapter: { renderNode }, key, node, children }) => {
const childrenWithAttribution = node.attribution
? [
...(children || []),
renderNode(`footer`, { key: 'footer' }, node.attribution),
]
: children;
return renderNode('blockquote', { key }, childrenWithAttribution);
}),
exports.renderRule(guards_1.isCode, ({ adapter: { renderNode, renderText }, key, node }) => {
return renderNode('pre', { key, 'data-language': node.language }, renderNode('code', null, renderText(node.code)));
}),
exports.renderRule(guards_1.isLink, ({ adapter: { renderNode }, key, children, node }) => {
const meta = node.meta ? metaTransformer({ node, meta: node.meta }) : {};
return renderNode('a', Object.assign(Object.assign({}, (meta || {})), { key, href: node.url }), children);
}),
exports.renderRule(guards_1.isThematicBreak, ({ adapter: { renderNode }, key }) => {
return renderNode('hr', { key });
}),
exports.renderRule(guards_1.isHeading, ({ node, adapter: { renderNode }, children, key }) => {
return renderNode(`h${node.level}`, { key }, children);
}),
exports.renderRule(guards_1.isSpan, ({ adapter: { renderNode, renderText }, key, node }) => {
const marks = node.marks || [];
const lines = node.value.split(/\n/);
const textWithNewlinesConvertedToBr = lines.length > 0
? lines.slice(1).reduce((acc, line, index) => {
return acc.concat([
renderNode('br', { key: `${key}-br-${index}` }),
renderText(line, `${key}-line-${index}`),
]);
}, [renderText(lines[0], `${key}-line-first`)])
: renderText(node.value, key);
return marks.reduce((children, mark) => {
return renderNode(markToTagName(mark), { key }, children);
}, textWithNewlinesConvertedToBr);
}),
]);
}
exports.genericHtmlRender = genericHtmlRender;
;
//# sourceMappingURL=render.js.map