@podlite/to-jsx
Version:
Render Podlite markup as React JSX components
102 lines • 3.63 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { makeAttrs } from '@podlite/schema';
import { codeToThemedHtml } from './shiki';
const fcodeToTag = {
B: 'strong',
I: 'em',
C: 'code',
U: 'u',
K: 'kbd',
};
export const extractPlainAndDecorations = (nodes) => {
const decorations = [];
let plain = '';
const walk = (input) => {
if (input == null)
return;
if (typeof input === 'string') {
plain += input;
return;
}
if (Array.isArray(input)) {
for (const child of input)
walk(child);
return;
}
const node = input;
if (node.type === 'text' && typeof node.value === 'string') {
plain += node.value;
return;
}
if (node.type === 'verbatim' && typeof node.value === 'string') {
plain += node.value;
return;
}
if (node.type === 'fcode' && typeof node.name === 'string') {
const tagName = fcodeToTag[node.name] || 'span';
const start = plain.length;
walk(node.content);
const end = plain.length;
if (end > start) {
decorations.push({
start,
end,
tagName,
properties: { class: `fc-${node.name}` },
});
}
return;
}
if (node.content !== undefined) {
walk(node.content);
}
else if (typeof node.text === 'string') {
plain += node.text;
}
};
walk(nodes);
return { plain, decorations };
};
const HighlightedCode = React.memo(({ node, children, keyProp, ctx, id, wrap = 'pre-code' }) => {
const conf = makeAttrs(node, ctx);
const caption = conf.exists('caption') ? conf.getFirstValue('caption') : null;
const lang = conf.getFirstValue('lang');
const [html, setHtml] = useState(null);
useEffect(() => {
if (!lang)
return;
let cancelled = false;
const highlight = async () => {
try {
const isDark = typeof document !== 'undefined' && document.body && document.body.className.toLowerCase().includes('dark');
const { plain, decorations } = extractPlainAndDecorations(node.content);
const result = await codeToThemedHtml({
code: plain,
language: lang,
theme: isDark ? 'dark' : 'light',
decorations,
});
if (!cancelled)
setHtml(result);
}
catch (e) {
console.error('[podlite] shiki highlight error:', e);
}
};
highlight();
return () => {
cancelled = true;
};
}, [lang, node.content]);
const codeBody = html ? (React.createElement("code", { key: keyProp, className: "shiki", dangerouslySetInnerHTML: { __html: html } })) : (React.createElement("pre", { key: `${keyProp}-pre`, id: id },
React.createElement("code", { key: keyProp }, children)));
if (wrap === 'block') {
return (React.createElement("div", { className: "code-block", key: `${keyProp}-code-div` },
codeBody,
caption ? (React.createElement("div", { key: `${keyProp}-caption`, className: "caption" }, caption)) : null));
}
return codeBody;
});
HighlightedCode.displayName = 'HighlightedCode';
export default HighlightedCode;
//# sourceMappingURL=HighlightedCode.js.map