@scalar/code-highlight
Version:
Central methods and themes for code highlighting in Scalar projects
113 lines (112 loc) • 3.4 kB
JavaScript
import rehypeExternalLinks from "rehype-external-links";
import rehypeFormat from "rehype-format";
import rehypeRaw from "rehype-raw";
import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
import rehypeStringify from "rehype-stringify";
import remarkGfm from "remark-gfm";
import remarkParse from "remark-parse";
import remarkRehype from "remark-rehype";
import remarkStringify from "remark-stringify";
import { unified } from "unified";
import { SKIP, visit } from "unist-util-visit";
import { standardLanguages } from "../languages/index.js";
import { rehypeAlert } from "../rehype-alert/index.js";
import { rehypeHighlight } from "../rehype-highlight/index.js";
const transformNodes = (options, ..._ignored) => (tree) => {
if (!options?.transform || !options?.type) {
return;
}
visit(tree, options?.type, (node) => {
options?.transform ? options?.transform(node) : node;
return SKIP;
});
return;
};
function htmlFromMarkdown(markdown, options) {
const removeTags = options?.removeTags ?? [];
const tagNames = [...defaultSchema.tagNames ?? [], ...options?.allowTags ?? []].filter(
(t) => !removeTags.includes(t)
);
const html = unified().use(remarkParse).use(remarkGfm).use(transformNodes, {
transform: options?.transform,
type: options?.transformType
}).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeAlert).use(rehypeRaw).use(rehypeSanitize, {
...defaultSchema,
// Don't prefix the heading ids
clobberPrefix: "",
// Makes it even more strict
tagNames,
attributes: {
...defaultSchema.attributes,
abbr: ["title"],
// Allow alert classes
div: ["class", ["className", /^markdown-alert(-.*)?$/]]
}
}).use(rehypeHighlight, {
languages: standardLanguages,
// Enable auto detection
detect: true
}).use(rehypeExternalLinks, { target: "_blank" }).use(rehypeFormat).use(rehypeStringify).processSync(markdown);
return html.toString();
}
function getMarkdownAst(markdown) {
return unified().use(remarkParse).use(remarkGfm).parse(markdown);
}
function getHeadings(markdown, depth = 1) {
const tree = getMarkdownAst(markdown);
const nodes = [];
visit(tree, "heading", (node) => {
const text = findTextInHeading(node);
if (text) {
nodes.push({ depth: node.depth ?? depth, value: text.value });
}
});
return nodes;
}
function findTextInHeading(node) {
if (node.type === "text") {
return node;
}
if ("children" in node && node.children) {
for (const child of node.children) {
const text = findTextInHeading(child);
if (text) {
return text;
}
}
}
return null;
}
function splitContent(markdown) {
const tree = getMarkdownAst(markdown);
const sections = [];
let nodes = [];
tree.children?.forEach((node) => {
if (node.type === "heading") {
if (nodes.length) {
sections.push(nodes);
}
sections.push([node]);
nodes = [];
} else {
nodes.push(node);
}
});
if (nodes.length) {
sections.push(nodes);
}
return sections.map((section) => createDocument(section));
}
function createDocument(nodes) {
const markdown = unified().use(remarkStringify).use(remarkGfm).stringify({
type: "root",
children: nodes
});
return markdown.trim();
}
export {
getHeadings,
htmlFromMarkdown,
splitContent
};
//# sourceMappingURL=markdown.js.map