fumadocs-openapi 5.2.1 → 5.3.0

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.js CHANGED
@@ -5,10 +5,21 @@ import Slugger from 'github-slugger';
5
5
  import { mkdir, writeFile } from 'node:fs/promises';
6
6
  import fg from 'fast-glob';
7
7
 
8
- function createMethod(method, operation) {
8
+ function noRef(v) {
9
+ return v;
10
+ }
11
+
12
+ /**
13
+ * Summarize method endpoint information
14
+ */ function createMethod(method, path, operation) {
9
15
  return {
16
+ description: path.description,
17
+ summary: path.summary,
10
18
  ...operation,
11
- parameters: operation.parameters ?? [],
19
+ parameters: [
20
+ ...noRef(operation.parameters ?? []),
21
+ ...noRef(path.parameters ?? [])
22
+ ],
12
23
  method: method.toUpperCase()
13
24
  };
14
25
  }
@@ -25,13 +36,13 @@ const methodKeys = [
25
36
  * Build the route information of tags, use `.get('all')` to get all entries
26
37
  */ function buildRoutes(document) {
27
38
  const map = new Map();
28
- for (const [path, value] of Object.entries(document.paths)){
29
- if (!value) continue;
39
+ for (const [path, pathItem] of Object.entries(document.paths)){
40
+ if (!pathItem) continue;
30
41
  const methodMap = new Map();
31
42
  for (const methodKey of methodKeys){
32
- const operation = value[methodKey];
43
+ const operation = pathItem[methodKey];
33
44
  if (!operation) continue;
34
- const info = createMethod(methodKey, operation);
45
+ const info = createMethod(methodKey, pathItem, operation);
35
46
  const tags = operation.tags ?? [];
36
47
  for (const tag of [
37
48
  ...tags,
@@ -45,7 +56,7 @@ const methodKeys = [
45
56
  for (const [tag, methods] of methodMap.entries()){
46
57
  const list = map.get(tag) ?? [];
47
58
  list.push({
48
- ...value,
59
+ ...pathItem,
49
60
  path,
50
61
  methods
51
62
  });
@@ -134,7 +134,7 @@ function generateBody(method, schema) {
134
134
  });
135
135
  }
136
136
 
137
- function getSampleRequest$2(endpoint) {
137
+ function getSampleRequest$3(endpoint) {
138
138
  const s = [];
139
139
  s.push(`curl -X ${endpoint.method} "${endpoint.url}"`);
140
140
  for (const param of endpoint.parameters){
@@ -161,7 +161,7 @@ function getSampleRequest$2(endpoint) {
161
161
  return s.flatMap((v, i)=>v.split('\n').map((line)=>i > 0 ? ` ${line}` : line).join('\n')).join(' \\\n');
162
162
  }
163
163
 
164
- function getSampleRequest$1(endpoint) {
164
+ function getSampleRequest$2(endpoint) {
165
165
  const s = [];
166
166
  const options = new Map();
167
167
  const headers = new Map();
@@ -192,7 +192,7 @@ function getSampleRequest$1(endpoint) {
192
192
  return s.join('\n\n');
193
193
  }
194
194
 
195
- function getSampleRequest(endpoint) {
195
+ function getSampleRequest$1(endpoint) {
196
196
  const imports = [
197
197
  'fmt',
198
198
  'net/http',
@@ -244,6 +244,32 @@ ${Array.from(variables.entries()).map(([k, v])=>` ${k} := ${v}`).join('\n')}
244
244
  }`;
245
245
  }
246
246
 
247
+ function getSampleRequest(endpoint) {
248
+ const headers = new Map();
249
+ const cookies = new Map();
250
+ const variables = new Map();
251
+ for (const param of endpoint.parameters){
252
+ if (param.in === 'header') headers.set(param.name, param.sample);
253
+ if (param.in === 'cookie') cookies.set(param.name, param.sample);
254
+ }
255
+ if (headers.size > 0) {
256
+ variables.set('headers', JSON.stringify(Object.fromEntries(headers.entries()), null, 2));
257
+ }
258
+ if (cookies.size > 0) {
259
+ variables.set('cookies', JSON.stringify(Object.fromEntries(cookies.entries()), null, 2));
260
+ }
261
+ if (endpoint.body) {
262
+ variables.set(endpoint.body.mediaType === 'multipart/form-data' ? 'data' : 'json', JSON.stringify(endpoint.body.sample, null, 2));
263
+ }
264
+ return `import requests
265
+
266
+ url = ${JSON.stringify(endpoint.url)}
267
+ ${Array.from(variables.entries()).map(([k, v])=>`${k} = ${v}`).join('\n')}
268
+ response = requests.request("${endpoint.method}", url${variables.size > 0 ? `, ${Array.from(variables.keys()).map((k)=>`${k}=${k}`).join(', ')}` : ''})
269
+
270
+ print(response.text)`;
271
+ }
272
+
247
273
  async function getTypescriptSchema(endpoint, code) {
248
274
  if (code in endpoint.responses) {
249
275
  return compile(endpoint.responses[code].schema, 'Response', {
@@ -443,7 +469,6 @@ function heading(depth, child, ctx) {
443
469
  }
444
470
 
445
471
  const keys = {
446
- example: 'Example',
447
472
  default: 'Default',
448
473
  minimum: 'Minimum',
449
474
  maximum: 'Maximum',
@@ -461,17 +486,6 @@ function Schema({ name, schema, ctx }) {
461
486
  const stack = ctx.stack ?? [];
462
487
  const { renderer } = ctx.render;
463
488
  const child = [];
464
- function field(key, value) {
465
- child.push(/*#__PURE__*/ jsxs("span", {
466
- children: [
467
- key,
468
- ": ",
469
- /*#__PURE__*/ jsx("code", {
470
- children: value
471
- })
472
- ]
473
- }, key));
474
- }
475
489
  // object type
476
490
  if (isObject(schema) && parseObject) {
477
491
  const { additionalProperties, properties } = schema;
@@ -510,15 +524,32 @@ function Schema({ name, schema, ctx }) {
510
524
  if (schema.description) child.push(/*#__PURE__*/ jsx(Markdown, {
511
525
  text: schema.description
512
526
  }, "description"));
527
+ const fields = [];
513
528
  for (const [key, value] of Object.entries(keys)){
514
529
  if (key in schema) {
515
- field(value, JSON.stringify(schema[key]));
530
+ fields.push({
531
+ key: value,
532
+ value: JSON.stringify(schema[key])
533
+ });
516
534
  }
517
535
  }
518
- // enum types
519
536
  if (schema.enum) {
520
- field('Value in', schema.enum.map((value)=>JSON.stringify(value)).join(' | '));
537
+ fields.push({
538
+ key: 'Value in',
539
+ value: schema.enum.map((value)=>JSON.stringify(value)).join(' | ')
540
+ });
521
541
  }
542
+ if (fields.length > 0) child.push(/*#__PURE__*/ jsx("p", {
543
+ children: fields.map((field)=>/*#__PURE__*/ jsxs("span", {
544
+ children: [
545
+ field.key,
546
+ ": ",
547
+ /*#__PURE__*/ jsx("code", {
548
+ children: field.value
549
+ })
550
+ ]
551
+ }, field.key))
552
+ }, "fields"));
522
553
  if (isObject(schema) && !parseObject) {
523
554
  child.push(/*#__PURE__*/ jsx(renderer.ObjectCollapsible, {
524
555
  name: "Attributes",
@@ -537,7 +568,7 @@ function Schema({ name, schema, ctx }) {
537
568
  name: name,
538
569
  children: /*#__PURE__*/ jsx(Schema, {
539
570
  name: name,
540
- schema: combineSchema(schema.allOf.map(noRef)),
571
+ schema: combineSchema(noRef(schema.allOf)),
541
572
  ctx: {
542
573
  ...ctx,
543
574
  parseObject: true,
@@ -578,6 +609,7 @@ function Schema({ name, schema, ctx }) {
578
609
  return /*#__PURE__*/ jsx(renderer.Property, {
579
610
  name: name,
580
611
  type: getSchemaType(schema, ctx),
612
+ required: ctx.required,
581
613
  deprecated: schema.deprecated,
582
614
  children: child
583
615
  });
@@ -601,7 +633,7 @@ function Schema({ name, schema, ctx }) {
601
633
  Object.assign(result.additionalProperties, s.additionalProperties);
602
634
  }
603
635
  if (s.allOf) {
604
- add(combineSchema(s.allOf.map(noRef)));
636
+ add(combineSchema(noRef(s.allOf)));
605
637
  }
606
638
  }
607
639
  schema.forEach(add);
@@ -750,19 +782,24 @@ async function APIExample({ method, endpoint, ctx }) {
750
782
  const samples = dedupe([
751
783
  {
752
784
  label: 'cURL',
753
- source: getSampleRequest$2(endpoint),
785
+ source: getSampleRequest$3(endpoint),
754
786
  lang: 'bash'
755
787
  },
756
788
  {
757
789
  label: 'JavaScript',
758
- source: getSampleRequest$1(endpoint),
790
+ source: getSampleRequest$2(endpoint),
759
791
  lang: 'js'
760
792
  },
761
793
  {
762
794
  label: 'Go',
763
- source: getSampleRequest(endpoint),
795
+ source: getSampleRequest$1(endpoint),
764
796
  lang: 'go'
765
797
  },
798
+ {
799
+ label: 'Python',
800
+ source: getSampleRequest(endpoint),
801
+ lang: 'python'
802
+ },
766
803
  ...ctx.generateCodeSamples ? await ctx.generateCodeSamples(endpoint) : [],
767
804
  ...method['x-codeSamples'] ?? []
768
805
  ]).filter((item)=>item.source !== undefined);
@@ -931,14 +968,36 @@ async function ResponseTabs({ endpoint, operation, ctx: { renderer, generateType
931
968
  });
932
969
  }
933
970
 
934
- function createMethod(method, operation) {
971
+ /**
972
+ * Summarize method endpoint information
973
+ */ function createMethod(method, path, operation) {
935
974
  return {
975
+ description: path.description,
976
+ summary: path.summary,
936
977
  ...operation,
937
- parameters: operation.parameters ?? [],
978
+ parameters: [
979
+ ...noRef(operation.parameters ?? []),
980
+ ...noRef(path.parameters ?? [])
981
+ ],
938
982
  method: method.toUpperCase()
939
983
  };
940
984
  }
941
985
 
986
+ const sharedTransformers = [
987
+ {
988
+ name: 'fumadocs:pre-process',
989
+ line (hast) {
990
+ if (hast.children.length === 0) {
991
+ // Keep the empty lines when using grid layout
992
+ hast.children.push({
993
+ type: 'text',
994
+ value: ' '
995
+ });
996
+ }
997
+ }
998
+ }
999
+ ];
1000
+
942
1001
  const highlighter = await createHighlighter({
943
1002
  themes: Object.values(bundledThemes),
944
1003
  langs: Object.values(bundledLanguages)
@@ -951,7 +1010,8 @@ function CodeBlock({ code, lang, ...props }) {
951
1010
  themes: {
952
1011
  light: 'github-light',
953
1012
  dark: 'github-dark'
954
- }
1013
+ },
1014
+ transformers: sharedTransformers
955
1015
  });
956
1016
  }, [
957
1017
  code,
@@ -1016,9 +1076,11 @@ async function APIPage(props) {
1016
1076
  return /*#__PURE__*/ jsx(ctx.renderer.Root, {
1017
1077
  baseUrl: ctx.baseUrl,
1018
1078
  children: operations.map((item)=>{
1019
- const operation = document.paths[item.path]?.[item.method];
1079
+ const pathItem = document.paths[item.path];
1080
+ if (!pathItem) return null;
1081
+ const operation = pathItem[item.method];
1020
1082
  if (!operation) return null;
1021
- const method = createMethod(item.method, operation);
1083
+ const method = createMethod(item.method, pathItem, operation);
1022
1084
  return /*#__PURE__*/ jsx(Operation, {
1023
1085
  method: method,
1024
1086
  path: item.path,
@@ -64,7 +64,7 @@ function useSchemaContext() {
64
64
  return ctx;
65
65
  }
66
66
 
67
- const APIPlayground = dynamic(()=>import('./playground-client-itvkUjgt.js').then((mod)=>mod.APIPlayground));
67
+ const APIPlayground = dynamic(()=>import('./playground-client-CthJtmMz.js').then((mod)=>mod.APIPlayground));
68
68
  function Root({ children, baseUrl, className, ...props }) {
69
69
  return /*#__PURE__*/ jsx("div", {
70
70
  className: cn('flex flex-col gap-24 text-sm text-fd-muted-foreground', className),
package/dist/ui/index.js CHANGED
@@ -3,8 +3,8 @@ import { cn } from 'fumadocs-ui/components/api';
3
3
  import { Fragment } from 'react';
4
4
  import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
5
5
  import { cva } from 'class-variance-authority';
6
- import { C as CopyRouteButton } from './client-client-34yX5eij.js';
7
- export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-34yX5eij.js';
6
+ import { C as CopyRouteButton } from './client-client-CgPBB3ii.js';
7
+ export { A as APIPlayground, R as Root, u as useSchemaContext } from './client-client-CgPBB3ii.js';
8
8
 
9
9
  const badgeVariants = cva('rounded border px-1.5 py-1 text-xs font-medium leading-[12px]', {
10
10
  variants: {
@@ -6,7 +6,7 @@ import { FormProvider, Controller, useFormContext, useFieldArray, useForm, useWa
6
6
  import useSWRImmutable from 'swr/immutable';
7
7
  import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';
8
8
  import { cn, buttonVariants } from 'fumadocs-ui/components/api';
9
- import { u as useSchemaContext, a as useApiContext, S as SchemaContext } from './client-client-34yX5eij.js';
9
+ import { u as useSchemaContext, a as useApiContext, S as SchemaContext } from './client-client-CgPBB3ii.js';
10
10
  import { Slot } from '@radix-ui/react-slot';
11
11
  import { cva } from 'class-variance-authority';
12
12
  import { CircleCheckIcon, CircleXIcon, ChevronDown, ChevronUp, Check, Trash2, Plus } from 'lucide-react';
@@ -809,6 +809,21 @@ function ArrayInput({ fieldName, field, ...props }) {
809
809
  });
810
810
  }
811
811
 
812
+ const sharedTransformers = [
813
+ {
814
+ name: 'fumadocs:pre-process',
815
+ line (hast) {
816
+ if (hast.children.length === 0) {
817
+ // Keep the empty lines when using grid layout
818
+ hast.children.push({
819
+ type: 'text',
820
+ value: ' '
821
+ });
822
+ }
823
+ }
824
+ }
825
+ ];
826
+
812
827
  function CodeBlock({ code, lang = 'json', ...props }) {
813
828
  const { highlighter } = useApiContext();
814
829
  const [html, setHtml] = useState('');
@@ -820,7 +835,8 @@ function CodeBlock({ code, lang = 'json', ...props }) {
820
835
  themes: {
821
836
  light: 'github-light',
822
837
  dark: 'github-dark'
823
- }
838
+ },
839
+ transformers: sharedTransformers
824
840
  });
825
841
  setHtml(themedHtml);
826
842
  }, [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fumadocs-openapi",
3
- "version": "5.2.1",
3
+ "version": "5.3.0",
4
4
  "description": "Generate MDX docs for your OpenAPI spec",
5
5
  "keywords": [
6
6
  "NextJs",
@@ -60,8 +60,8 @@
60
60
  "remark": "^15.0.0",
61
61
  "shiki": "^1.13.0",
62
62
  "swr": "^2.2.5",
63
- "fumadocs-core": "13.3.0",
64
- "fumadocs-ui": "13.3.0"
63
+ "fumadocs-core": "13.3.2",
64
+ "fumadocs-ui": "13.3.2"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@types/js-yaml": "^4.0.9",