lowlight
virtual DOMやSSR環境でsyntax highlightするときに便利
API Reference
cdn.skypack.devも同様
サンプル
test.jsconst { render, html, useState, useEffect } = htmPreact;
render(html`<${() => {
const [json, setJson] = useState("Loading modules...");
useEffect(() => {
const code = new URLSearchParams(location.search).get("code");
if (!code) {
setJson("No source file specified.");
return;
}
(async () => {
const [
{ lowlight },
{ toHtml },
] = await Promise.all([
import("https://esm.sh/lowlight@2.8.1"),
import("https://esm.sh/hast-util-to-html@8.0.4"),
]);
setJson("Loading Code...");
const res = await fetch(code);
const tree = lowlight.highlightAuto(await res.text());
setJson(JSON.stringify(tree, null, 2));
//setJson(toHtml(tree));
})().catch((e)=>alert(JSON.stringify(e)));
}, []);
return html`<style>pre{white-space:pre-wrap}</style><pre><code>${json}</code></pre>`;
}} />`, document.body);
うまく分割できたっぽい
test2.jsconst { render, html, useState, useEffect } = htmPreact;
/** 空文字のnodeなら`false`を返す */
const isEmptyNode = (node) =>
(node.type === "comment" || node.type === "text") && node.value === "";
/** 与えられたhastを1行ごとに分割する
*
* @param node 分割したいhast
* @return 分割したhastを1行ずつ返す
*/
function* splitNode(node) {
switch (node.type) {
case "root":
yield* splitNode({
type: "element",
tagName: "div",
children: node.children,
});
return;
case "doctype":
yield node;
return;
case "comment":
case "text":
yield* node.value.split("\n").map(
(value) => ({ type: node.type, value })
);
return;
case "element": {
if (node.children.length === 0) {
yield node;
return;
}
let prev;
const { children, ...rest } = node;
for (const child of children) {
let counter = 0;
for (const splitted of splitNode(child)) {
if (prev && counter > 0) {
yield prev;
prev = undefined;
}
counter++;
const isEmpty = isEmptyNode(splitted);
if (prev) {
if (isEmpty) continue;
prev.children.push(splitted);
continue;
}
prev = { ...rest, children: isEmpty ? [] : [splitted] };
}
}
if (prev) yield prev;
return;
}
}
}
render(html`<${() => {
const [json, setJson] = useState("Loading modules...");
useEffect(() => {
const code = new URLSearchParams(location.search).get("code");
if (!code) {
setJson("No source file specified.");
return;
}
(async () => {
const [
{ lowlight },
{ toHtml },
] = await Promise.all([
import("https://esm.sh/lowlight@2.8.1"),
import("https://esm.sh/hast-util-to-html@8.0.4"),
]);
setJson("Loading Code...");
const res = await fetch(code);
const tree = lowlight.highlightAuto(await res.text());
setJson(JSON.stringify([...splitNode(tree)], null, 2));
/*setJson(toHtml(
{ type: "root", children: [...splitNode(tree)] }
));*/
})().catch((e)=>alert(JSON.stringify(e)));
}, []);
return html`<style>pre{white-space:pre-wrap}</style><pre><code>${json}</code></pre>`;
}} />`, document.body);