113 lines
3.9 KiB
JavaScript
113 lines
3.9 KiB
JavaScript
|
|
import { unified } from "unified";
|
||
|
|
import remarkParse from "remark-parse";
|
||
|
|
import remark2rehype from "remark-rehype";
|
||
|
|
import { parseFrontMatter } from "remark-mdc";
|
||
|
|
import { defu } from "defu";
|
||
|
|
import { nodeTextContent } from "../utils/node.js";
|
||
|
|
import { useProcessorPlugins } from "./utils/plugins.js";
|
||
|
|
import { defaults } from "./options.js";
|
||
|
|
import { generateToc } from "./toc.js";
|
||
|
|
import { compileHast } from "./compiler.js";
|
||
|
|
let moduleOptions;
|
||
|
|
let generatedMdcConfigs;
|
||
|
|
export const createMarkdownParser = async (inlineOptions = {}) => {
|
||
|
|
if (!moduleOptions) {
|
||
|
|
moduleOptions = await import(
|
||
|
|
"#mdc-imports"
|
||
|
|
/* @vite-ignore */
|
||
|
|
).catch(() => ({}));
|
||
|
|
}
|
||
|
|
if (!generatedMdcConfigs) {
|
||
|
|
generatedMdcConfigs = await import(
|
||
|
|
"#mdc-configs"
|
||
|
|
/* @vite-ignore */
|
||
|
|
).then((r) => r.getMdcConfigs()).catch(() => []);
|
||
|
|
}
|
||
|
|
const mdcConfigs = [
|
||
|
|
...generatedMdcConfigs || [],
|
||
|
|
...inlineOptions.configs || []
|
||
|
|
];
|
||
|
|
if (inlineOptions.highlight != null && inlineOptions.highlight != false && inlineOptions.highlight.highlighter !== void 0 && typeof inlineOptions.highlight.highlighter !== "function") {
|
||
|
|
if (process.dev)
|
||
|
|
console.warn("[@nuxtjs/mdc] `highlighter` passed to `parseMarkdown` is should be a function, but got " + JSON.stringify(inlineOptions.highlight.highlighter) + ", ignored.");
|
||
|
|
inlineOptions = {
|
||
|
|
...inlineOptions,
|
||
|
|
highlight: {
|
||
|
|
...inlineOptions.highlight
|
||
|
|
}
|
||
|
|
};
|
||
|
|
delete inlineOptions.highlight.highlighter;
|
||
|
|
}
|
||
|
|
const options = defu(inlineOptions, {
|
||
|
|
remark: { plugins: moduleOptions?.remarkPlugins },
|
||
|
|
rehype: { plugins: moduleOptions?.rehypePlugins },
|
||
|
|
highlight: moduleOptions?.highlight
|
||
|
|
}, defaults);
|
||
|
|
if (options.rehype?.plugins?.highlight) {
|
||
|
|
options.rehype.plugins.highlight.options = {
|
||
|
|
...options.rehype.plugins.highlight.options || {},
|
||
|
|
...options.highlight || {}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
let processor = unified();
|
||
|
|
for (const config of mdcConfigs) {
|
||
|
|
processor = await config.unified?.pre?.(processor) || processor;
|
||
|
|
}
|
||
|
|
processor.use(remarkParse);
|
||
|
|
for (const config of mdcConfigs) {
|
||
|
|
processor = await config.unified?.remark?.(processor) || processor;
|
||
|
|
}
|
||
|
|
await useProcessorPlugins(processor, options.remark?.plugins);
|
||
|
|
processor.use(remark2rehype, options.rehype?.options);
|
||
|
|
for (const config of mdcConfigs) {
|
||
|
|
processor = await config.unified?.rehype?.(processor) || processor;
|
||
|
|
}
|
||
|
|
await useProcessorPlugins(processor, options.rehype?.plugins);
|
||
|
|
processor.use(compileHast, options);
|
||
|
|
for (const config of mdcConfigs) {
|
||
|
|
processor = await config.unified?.post?.(processor) || processor;
|
||
|
|
}
|
||
|
|
return async function parse(md, { fileOptions } = {}) {
|
||
|
|
const { content, data: frontmatter } = await parseFrontMatter(md);
|
||
|
|
const processedFile = await processor.process({ ...fileOptions, value: content, data: frontmatter });
|
||
|
|
const result = processedFile.result;
|
||
|
|
const data = Object.assign(
|
||
|
|
contentHeading(result.body),
|
||
|
|
frontmatter,
|
||
|
|
processedFile?.data || {}
|
||
|
|
);
|
||
|
|
let toc;
|
||
|
|
if (data.toc !== false) {
|
||
|
|
const tocOption = defu(data.toc || {}, options.toc);
|
||
|
|
toc = generateToc(result.body, tocOption);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
data,
|
||
|
|
body: result.body,
|
||
|
|
excerpt: result.excerpt,
|
||
|
|
toc
|
||
|
|
};
|
||
|
|
};
|
||
|
|
};
|
||
|
|
export const parseMarkdown = async (md, markdownParserOptions = {}, parseOptions = {}) => {
|
||
|
|
const parser = await createMarkdownParser(markdownParserOptions);
|
||
|
|
return parser(md, parseOptions);
|
||
|
|
};
|
||
|
|
export function contentHeading(body) {
|
||
|
|
let title = "";
|
||
|
|
let description = "";
|
||
|
|
const children = body.children.filter((node) => node.type === "element" && node.tag !== "hr");
|
||
|
|
if (children.length && children[0].tag === "h1") {
|
||
|
|
const node = children.shift();
|
||
|
|
title = nodeTextContent(node);
|
||
|
|
}
|
||
|
|
if (children.length && children[0].tag === "p") {
|
||
|
|
const node = children.shift();
|
||
|
|
description = nodeTextContent(node);
|
||
|
|
}
|
||
|
|
return {
|
||
|
|
title,
|
||
|
|
description
|
||
|
|
};
|
||
|
|
}
|