fumadocs-openapi 3.1.0 → 3.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -55,7 +55,9 @@ interface GenerateOptions {
55
55
  from: string;
56
56
  }[];
57
57
  /**
58
- * Customise frontmatter
58
+ * Customise frontmatter.
59
+ *
60
+ * A `full: true` property will be added by default.
59
61
  */
60
62
  frontmatter?: (title: string, description: string | undefined) => Record<string, unknown>;
61
63
  renderer?: Partial<Renderer>;
@@ -64,7 +66,12 @@ interface GenerateTagOutput {
64
66
  tag: string;
65
67
  content: string;
66
68
  }
69
+ interface GenerateOperationOutput {
70
+ id: string;
71
+ content: string;
72
+ }
67
73
  declare function generate(pathOrDocument: string | OpenAPIV3.Document, options?: GenerateOptions): Promise<string>;
74
+ declare function generateOperations(pathOrDocument: string | OpenAPIV3.Document, options?: GenerateOptions): Promise<GenerateOperationOutput[]>;
68
75
  declare function generateTags(pathOrDocument: string | OpenAPIV3.Document, options?: GenerateOptions): Promise<GenerateTagOutput[]>;
69
76
 
70
77
  interface Config extends GenerateOptions {
@@ -83,7 +90,7 @@ interface Config extends GenerateOptions {
83
90
  *
84
91
  * @defaultValue file
85
92
  */
86
- per?: 'tag' | 'file';
93
+ per?: 'tag' | 'file' | 'operation';
87
94
  /**
88
95
  * Specify name for output file
89
96
  */
@@ -110,4 +117,4 @@ interface RenderContext {
110
117
 
111
118
  declare function createElement(name: string, props: object, ...child: string[]): string;
112
119
 
113
- export { type APIInfoProps, type Config, type GenerateOptions, type GenerateTagOutput, type MethodInformation, type ObjectCollapsibleProps, type PropertyProps, type RenderContext, type Renderer, type RequestProps, type ResponseProps, type ResponsesProps, type RouteInformation, createElement, defaultRenderer, generate, generateFiles, generateTags };
120
+ export { type APIInfoProps, type Config, type GenerateOperationOutput, type GenerateOptions, type GenerateTagOutput, type MethodInformation, type ObjectCollapsibleProps, type PropertyProps, type RenderContext, type Renderer, type RequestProps, type ResponseProps, type ResponsesProps, type RouteInformation, createElement, defaultRenderer, generate, generateFiles, generateOperations, generateTags };
package/dist/index.js CHANGED
@@ -72,6 +72,9 @@ function codeblock({ language, title }, child) {
72
72
  "```"
73
73
  ].join("\n");
74
74
  }
75
+ function heading(depth, child) {
76
+ return `${"#".repeat(depth)} ${child.trim()}`;
77
+ }
75
78
 
76
79
  // src/render/renderer.ts
77
80
  var defaultRenderer = {
@@ -99,6 +102,7 @@ function renderPage(title, description, content, options) {
99
102
  const banner = dump({
100
103
  title,
101
104
  description,
105
+ full: true,
102
106
  ...options.frontmatter?.(title, description)
103
107
  }).trim();
104
108
  const finalImports = (options.imports ?? [
@@ -375,23 +379,27 @@ function getSchemaType(schema) {
375
379
  }
376
380
 
377
381
  // src/render/operation.ts
378
- async function renderOperation(path, method, ctx) {
382
+ async function renderOperation(path, method, ctx, noTitle = false) {
383
+ let level = 2;
379
384
  const body = noRef(method.requestBody);
380
385
  const security = method.security ?? ctx.document.security;
381
386
  const info = [];
382
387
  const example = [];
383
388
  const title = method.summary ?? method.operationId;
384
- if (title) info.push(`## ${title}`);
389
+ if (title && !noTitle) {
390
+ info.push(heading(level, title));
391
+ level++;
392
+ }
385
393
  if (method.description) info.push(p(method.description));
386
394
  if (security) {
387
- info.push("### Authorization");
395
+ info.push(heading(level, "Authorization"));
388
396
  info.push(getAuthSection(security, ctx));
389
397
  }
390
398
  if (body) {
391
399
  const bodySchema = getPreferredMedia(body.content)?.schema;
392
400
  if (!bodySchema) throw new Error();
393
401
  info.push(
394
- `### Request Body${!body.required ? " (Optional)" : ""}`,
402
+ heading(level, `Request Body ${!body.required ? "(Optional)" : ""}`),
395
403
  p(body.description),
396
404
  schemaElement("body", noRef(bodySchema), {
397
405
  parseObject: true,
@@ -435,7 +443,7 @@ async function renderOperation(path, method, ctx) {
435
443
  parameterGroups.set(groupName, group);
436
444
  }
437
445
  for (const [group, parameters] of Array.from(parameterGroups.entries())) {
438
- info.push(`### ${group}`, ...parameters);
446
+ info.push(heading(level, group), ...parameters);
439
447
  }
440
448
  info.push(getResponseTable(method));
441
449
  example.push(
@@ -584,6 +592,29 @@ async function generate(pathOrDocument, options = {}) {
584
592
  options
585
593
  );
586
594
  }
595
+ async function generateOperations(pathOrDocument, options = {}) {
596
+ const document = await Parser.dereference(pathOrDocument);
597
+ const routes = buildRoutes(document).get("all") ?? [];
598
+ const ctx = getContext(document, options);
599
+ return await Promise.all(
600
+ routes.flatMap((route) => {
601
+ return route.methods.map(async (method) => {
602
+ const content = renderPage(
603
+ method.summary ?? method.method,
604
+ method.description,
605
+ [await renderOperation(route.path, method, ctx, true)],
606
+ options
607
+ );
608
+ if (!method.operationId)
609
+ throw new Error("Operation ID is required for generating docs.");
610
+ return {
611
+ id: method.operationId,
612
+ content
613
+ };
614
+ });
615
+ })
616
+ );
617
+ }
587
618
  async function generateTags(pathOrDocument, options = {}) {
588
619
  const document = await Parser.dereference(pathOrDocument);
589
620
  const tags = Array.from(buildRoutes(document).entries());
@@ -640,10 +671,24 @@ async function generateFiles({
640
671
  console.log(`Generated: ${outPath}`);
641
672
  return;
642
673
  }
674
+ if (per === "operation") {
675
+ const results2 = await generateOperations(path, options);
676
+ await Promise.all(
677
+ results2.map(async (result) => {
678
+ const outPath = join(
679
+ outputDir,
680
+ filename,
681
+ `${getName(result.id)}.mdx`
682
+ );
683
+ await write(outPath, result.content);
684
+ console.log(`Generated: ${outPath}`);
685
+ })
686
+ );
687
+ }
643
688
  const results = await generateTags(path, options);
644
689
  for (const result of results) {
645
690
  let tagName = result.tag;
646
- tagName = nameFn?.("tag", tagName) ?? tagName.toLowerCase().replace(/\s+/g, "-");
691
+ tagName = nameFn?.("tag", tagName) ?? getName(tagName);
647
692
  const outPath = join(outputDir, filename, `${tagName}.mdx`);
648
693
  await write(outPath, result.content);
649
694
  console.log(`Generated: ${outPath}`);
@@ -651,6 +696,12 @@ async function generateFiles({
651
696
  })
652
697
  );
653
698
  }
699
+ function getName(s) {
700
+ return s.replace(
701
+ /[A-Z]/g,
702
+ (match, idx) => idx === 0 ? match : `-${match.toLowerCase()}`
703
+ ).replace(/\s+/g, "-").toLowerCase();
704
+ }
654
705
  async function write(path, content) {
655
706
  await mkdir(dirname(path), { recursive: true });
656
707
  await writeFile(path, content);
@@ -660,5 +711,6 @@ export {
660
711
  defaultRenderer,
661
712
  generate,
662
713
  generateFiles,
714
+ generateOperations,
663
715
  generateTags
664
716
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "NextJs",