UNPKG

@podlite/to-jsx

Version:

Render Podlite markup as React JSX components

102 lines 3.63 kB
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