fumadocs-openapi
Version:
Generate MDX docs for your OpenAPI spec
95 lines (94 loc) • 3.54 kB
JavaScript
import { dump } from 'js-yaml';
import Slugger from 'github-slugger';
import { idToTitle } from '../utils/id-to-title.js';
export function generateDocument(input, processed, pageProps, options, context) {
const { frontmatter, includeDescription = false, addGeneratedComment = true, } = options;
const out = [];
const extend = frontmatter?.(options.title, options.description, context);
const page = {
...pageProps,
document: options.inlineDocument ? processed.downloaded : input,
};
let meta;
if (page.operations?.length === 1) {
const operation = page.operations[0];
meta = {
method: operation.method.toUpperCase(),
route: operation.path,
};
}
const data = generateStaticData(processed.document, page);
const banner = dump({
title: options.title,
description: !includeDescription ? options.description : undefined,
full: true,
...extend,
_openapi: {
...meta,
...data,
...extend?._openapi,
},
}).trim();
if (banner.length > 0)
out.push(`---\n${banner}\n---`);
if (addGeneratedComment !== false) {
let commentContent = 'This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again.';
if (typeof addGeneratedComment === 'string') {
commentContent = addGeneratedComment;
}
commentContent = commentContent.replaceAll('/', '\\/');
out.push(`{/* ${commentContent} */}`);
}
const imports = options.imports
?.map((item) => `import { ${item.names.join(', ')} } from ${JSON.stringify(item.from)};`)
.join('\n');
if (imports) {
out.push(imports);
}
if (options.description && includeDescription)
out.push(options.description);
out.push(pageContent(page));
return out.join('\n\n');
}
function generateStaticData(dereferenced, props) {
const slugger = new Slugger();
const toc = [];
const structuredData = { headings: [], contents: [] };
for (const item of props.operations ?? []) {
const operation = dereferenced.paths?.[item.path]?.[item.method];
if (!operation)
continue;
if (props.hasHead && operation.operationId) {
const title = operation.summary ??
(operation.operationId ? idToTitle(operation.operationId) : item.path);
const id = slugger.slug(title);
toc.push({
depth: 2,
title,
url: `#${id}`,
});
structuredData.headings.push({
content: title,
id,
});
}
if (operation.description)
structuredData.contents.push({
content: operation.description,
heading: structuredData.headings.at(-1)?.id,
});
}
return { toc, structuredData };
}
function pageContent(props) {
// filter extra properties in props
const operations = (props.operations ?? []).map((item) => ({
path: item.path,
method: item.method,
}));
const webhooks = (props.webhooks ?? []).map((item) => ({
name: item.name,
method: item.method,
}));
return `<APIPage document={${JSON.stringify(props.document)}} operations={${JSON.stringify(operations)}} webhooks={${JSON.stringify(webhooks)}} hasHead={${JSON.stringify(props.hasHead)}} />`;
}