fumadocs-openapi 2.0.5 → 3.0.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.d.ts +60 -20
- package/dist/index.js +185 -171
- package/package.json +3 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,28 +1,66 @@
|
|
|
1
1
|
import { OpenAPIV3 } from 'openapi-types';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
interface ResponsesProps {
|
|
4
|
+
items: string[];
|
|
5
|
+
}
|
|
6
|
+
interface ResponseProps {
|
|
7
|
+
value: string;
|
|
8
|
+
}
|
|
9
|
+
interface APIInfoProps {
|
|
10
|
+
method: string;
|
|
11
|
+
route: string;
|
|
12
|
+
}
|
|
13
|
+
interface PropertyProps {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
deprecated?: boolean;
|
|
18
|
+
}
|
|
19
|
+
interface ObjectCollapsibleProps {
|
|
20
|
+
name: string;
|
|
21
|
+
}
|
|
22
|
+
interface Renderer {
|
|
23
|
+
Root: (child: string[]) => string;
|
|
24
|
+
API: (child: string[]) => string;
|
|
25
|
+
APIInfo: (props: APIInfoProps, child: string[]) => string;
|
|
26
|
+
APIExample: (child: string[]) => string;
|
|
27
|
+
Responses: (props: ResponsesProps, child: string[]) => string;
|
|
28
|
+
Response: (props: ResponseProps, child: string[]) => string;
|
|
29
|
+
ResponseTypes: (child: string[]) => string;
|
|
30
|
+
ExampleResponse: (json: string) => string;
|
|
31
|
+
TypeScriptResponse: (code: string) => string;
|
|
32
|
+
/**
|
|
33
|
+
* Collapsible to show object schemas
|
|
34
|
+
*/
|
|
35
|
+
ObjectCollapsible: (props: ObjectCollapsibleProps, child: string[]) => string;
|
|
36
|
+
Property: (props: PropertyProps, child: string[]) => string;
|
|
37
|
+
}
|
|
38
|
+
declare const defaultRenderer: Renderer;
|
|
39
|
+
|
|
4
40
|
interface GenerateOptions {
|
|
5
|
-
tag?: string;
|
|
6
41
|
/**
|
|
7
|
-
* The
|
|
42
|
+
* The imports of your MDX components.
|
|
8
43
|
*
|
|
9
|
-
*
|
|
44
|
+
* If not specified, import required components from `fumadocs-ui/components/api`.
|
|
10
45
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
46
|
+
imports?: {
|
|
47
|
+
names: string[];
|
|
48
|
+
from: string;
|
|
49
|
+
}[];
|
|
50
|
+
/**
|
|
51
|
+
* Customise frontmatter
|
|
52
|
+
*/
|
|
53
|
+
frontmatter?: (title: string, description: string | undefined) => Record<string, unknown>;
|
|
54
|
+
renderer?: Partial<Renderer>;
|
|
13
55
|
}
|
|
14
|
-
interface
|
|
15
|
-
|
|
16
|
-
imports: string[];
|
|
56
|
+
interface GenerateTagOutput {
|
|
57
|
+
tag: string;
|
|
17
58
|
content: string;
|
|
18
59
|
}
|
|
19
60
|
declare function generate(pathOrDocument: string | OpenAPIV3.Document, options?: GenerateOptions): Promise<string>;
|
|
20
|
-
declare function generateTags(pathOrDocument: string | OpenAPIV3.Document, options?:
|
|
21
|
-
tag: string;
|
|
22
|
-
content: string;
|
|
23
|
-
}[]>;
|
|
61
|
+
declare function generateTags(pathOrDocument: string | OpenAPIV3.Document, options?: GenerateOptions): Promise<GenerateTagOutput[]>;
|
|
24
62
|
|
|
25
|
-
interface Config {
|
|
63
|
+
interface Config extends GenerateOptions {
|
|
26
64
|
/**
|
|
27
65
|
* Schema files
|
|
28
66
|
*/
|
|
@@ -43,13 +81,9 @@ interface Config {
|
|
|
43
81
|
* Specify name for output file
|
|
44
82
|
*/
|
|
45
83
|
name?: (type: 'file' | 'tag', name: string) => string;
|
|
46
|
-
/**
|
|
47
|
-
* Modify output file
|
|
48
|
-
*/
|
|
49
|
-
render?: NonNullable<GenerateOptions['render']>;
|
|
50
84
|
cwd?: string;
|
|
51
85
|
}
|
|
52
|
-
declare function generateFiles({ input, output, name: nameFn, per,
|
|
86
|
+
declare function generateFiles({ input, output, name: nameFn, per, cwd, ...options }: Config): Promise<void>;
|
|
53
87
|
|
|
54
88
|
interface RouteInformation {
|
|
55
89
|
path: string;
|
|
@@ -61,5 +95,11 @@ interface MethodInformation extends OpenAPIV3.OperationObject {
|
|
|
61
95
|
parameters: OpenAPIV3.ParameterObject[];
|
|
62
96
|
method: string;
|
|
63
97
|
}
|
|
98
|
+
interface RenderContext {
|
|
99
|
+
renderer: Renderer;
|
|
100
|
+
baseUrl: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare function createElement(name: string, props: object, ...child: string[]): string;
|
|
64
104
|
|
|
65
|
-
export { type Config, type GenerateOptions, type MethodInformation, type RouteInformation,
|
|
105
|
+
export { type APIInfoProps, type Config, type GenerateOptions, type GenerateTagOutput, type MethodInformation, type ObjectCollapsibleProps, type PropertyProps, type RenderContext, type Renderer, type ResponseProps, type ResponsesProps, type RouteInformation, createElement, defaultRenderer, generate, generateFiles, generateTags };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,60 @@
|
|
|
1
1
|
// src/generate.ts
|
|
2
2
|
import Parser from "@apidevtools/json-schema-ref-parser";
|
|
3
3
|
|
|
4
|
+
// src/build-routes.ts
|
|
5
|
+
var methodKeys = [
|
|
6
|
+
"get",
|
|
7
|
+
"post",
|
|
8
|
+
"patch",
|
|
9
|
+
"delete",
|
|
10
|
+
"head",
|
|
11
|
+
"put"
|
|
12
|
+
];
|
|
13
|
+
function buildRoutes(document) {
|
|
14
|
+
const map = /* @__PURE__ */ new Map();
|
|
15
|
+
for (const [path, value] of Object.entries(document.paths)) {
|
|
16
|
+
if (!value) continue;
|
|
17
|
+
const methodMap = /* @__PURE__ */ new Map();
|
|
18
|
+
for (const methodKey of methodKeys) {
|
|
19
|
+
const operation = value[methodKey];
|
|
20
|
+
if (!operation) continue;
|
|
21
|
+
const info = buildOperation(methodKey, operation);
|
|
22
|
+
const tags = operation.tags ?? [];
|
|
23
|
+
for (const tag of [...tags, "all"]) {
|
|
24
|
+
const list = methodMap.get(tag) ?? [];
|
|
25
|
+
list.push(info);
|
|
26
|
+
methodMap.set(tag, list);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const [tag, methods] of methodMap.entries()) {
|
|
30
|
+
const list = map.get(tag) ?? [];
|
|
31
|
+
list.push({
|
|
32
|
+
...value,
|
|
33
|
+
path,
|
|
34
|
+
methods
|
|
35
|
+
});
|
|
36
|
+
map.set(tag, list);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return map;
|
|
40
|
+
}
|
|
41
|
+
function buildOperation(method, operation) {
|
|
42
|
+
return {
|
|
43
|
+
...operation,
|
|
44
|
+
parameters: operation.parameters ?? [],
|
|
45
|
+
method: method.toUpperCase()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/render/page.ts
|
|
50
|
+
import { dump } from "js-yaml";
|
|
51
|
+
|
|
4
52
|
// src/render/element.ts
|
|
5
53
|
function createElement(name, props, ...child) {
|
|
6
54
|
const s = [];
|
|
7
55
|
const params = Object.entries(props).map(([key, value]) => `${key}={${JSON.stringify(value)}}`).join(" ");
|
|
8
56
|
s.push(params.length > 0 ? `<${name} ${params}>` : `<${name}>`);
|
|
9
|
-
s.push(...child);
|
|
57
|
+
s.push(...child.filter((v) => v.length > 0));
|
|
10
58
|
s.push(`</${name}>`);
|
|
11
59
|
return s.join("\n\n");
|
|
12
60
|
}
|
|
@@ -20,42 +68,53 @@ function span(child) {
|
|
|
20
68
|
function codeblock({ language, title }, child) {
|
|
21
69
|
return [
|
|
22
70
|
title ? `\`\`\`${language} title=${JSON.stringify(title)}` : `\`\`\`${language}`,
|
|
23
|
-
child,
|
|
71
|
+
child.trim(),
|
|
24
72
|
"```"
|
|
25
73
|
].join("\n");
|
|
26
74
|
}
|
|
27
75
|
|
|
28
|
-
// src/render/
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
76
|
+
// src/render/renderer.ts
|
|
77
|
+
var defaultRenderer = {
|
|
78
|
+
Root: (child) => createElement("Root", {}, ...child),
|
|
79
|
+
API: (child) => createElement("API", {}, ...child),
|
|
80
|
+
APIInfo: (props, child) => createElement("APIInfo", props, ...child),
|
|
81
|
+
APIExample: (child) => createElement("APIExample", {}, ...child),
|
|
82
|
+
Responses: (props, child) => createElement("Responses", props, ...child),
|
|
83
|
+
Response: (props, child) => createElement("Response", props, ...child),
|
|
84
|
+
ResponseTypes: (child) => createElement("ResponseTypes", {}, ...child),
|
|
85
|
+
ExampleResponse: (json) => createElement("ExampleResponse", {}, codeblock({ language: "json" }, json)),
|
|
86
|
+
TypeScriptResponse: (code) => createElement(
|
|
87
|
+
"TypeScriptResponse",
|
|
88
|
+
{},
|
|
89
|
+
codeblock({ language: "ts" }, code)
|
|
90
|
+
),
|
|
91
|
+
Property: (props, child) => createElement("Property", props, ...child),
|
|
92
|
+
ObjectCollapsible: (props, child) => createElement("ObjectCollapsible", props, ...child)
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// src/render/page.ts
|
|
96
|
+
function renderPage(title, description, content, options) {
|
|
97
|
+
const banner = dump({
|
|
98
|
+
title,
|
|
99
|
+
description,
|
|
100
|
+
...options.frontmatter?.(title, description)
|
|
101
|
+
}).trim();
|
|
102
|
+
const finalImports = (options.imports ?? [
|
|
103
|
+
{
|
|
104
|
+
names: Object.keys(defaultRenderer),
|
|
105
|
+
from: "fumadocs-ui/components/api"
|
|
106
|
+
}
|
|
107
|
+
]).map(
|
|
108
|
+
(item) => `import { ${item.names.join(", ")} } from ${JSON.stringify(item.from)};`
|
|
109
|
+
).join("\n");
|
|
110
|
+
const Root = options.renderer?.Root ?? defaultRenderer.Root;
|
|
111
|
+
return `---
|
|
112
|
+
${banner}
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
${finalImports}
|
|
116
|
+
|
|
117
|
+
${Root(content)}`;
|
|
59
118
|
}
|
|
60
119
|
|
|
61
120
|
// src/samples/index.ts
|
|
@@ -75,7 +134,7 @@ function getValue(value) {
|
|
|
75
134
|
}
|
|
76
135
|
|
|
77
136
|
// src/samples/index.ts
|
|
78
|
-
function createEndpoint(path, method, baseUrl
|
|
137
|
+
function createEndpoint(path, method, baseUrl) {
|
|
79
138
|
const params = [];
|
|
80
139
|
const responses = {};
|
|
81
140
|
for (const param of method.parameters) {
|
|
@@ -184,22 +243,26 @@ var keys = {
|
|
|
184
243
|
function isObject(schema) {
|
|
185
244
|
return schema.type === "object" || schema.properties !== void 0;
|
|
186
245
|
}
|
|
187
|
-
function schemaElement(name, schema,
|
|
246
|
+
function schemaElement(name, schema, ctx) {
|
|
188
247
|
if (schema.readOnly && !ctx.readOnly) return "";
|
|
189
248
|
if (schema.writeOnly && !ctx.writeOnly) return "";
|
|
249
|
+
const { renderer } = ctx.render;
|
|
190
250
|
const child = [];
|
|
191
251
|
function field(key, value) {
|
|
192
252
|
child.push(span(`${key}: \`${value}\``));
|
|
193
253
|
}
|
|
194
|
-
if (isObject(schema) && parseObject) {
|
|
254
|
+
if (isObject(schema) && ctx.parseObject) {
|
|
195
255
|
const { additionalProperties, properties } = schema;
|
|
196
256
|
if (additionalProperties) {
|
|
197
257
|
if (additionalProperties === true) {
|
|
198
258
|
child.push(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
259
|
+
renderer.Property(
|
|
260
|
+
{
|
|
261
|
+
name: "[key: string]",
|
|
262
|
+
type: "any"
|
|
263
|
+
},
|
|
264
|
+
[]
|
|
265
|
+
)
|
|
203
266
|
);
|
|
204
267
|
} else {
|
|
205
268
|
child.push(
|
|
@@ -235,28 +298,25 @@ function schemaElement(name, schema, { parseObject, ...ctx }) {
|
|
|
235
298
|
);
|
|
236
299
|
}
|
|
237
300
|
const resolved = resolveObjectType(schema);
|
|
238
|
-
if (resolved && !parseObject) {
|
|
301
|
+
if (resolved && !ctx.parseObject) {
|
|
239
302
|
child.push(
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
})
|
|
248
|
-
)
|
|
249
|
-
)
|
|
303
|
+
renderer.ObjectCollapsible({ name }, [
|
|
304
|
+
schemaElement(name, resolved, {
|
|
305
|
+
...ctx,
|
|
306
|
+
parseObject: true,
|
|
307
|
+
required: false
|
|
308
|
+
})
|
|
309
|
+
])
|
|
250
310
|
);
|
|
251
311
|
}
|
|
252
|
-
return
|
|
312
|
+
return renderer.Property(
|
|
253
313
|
{
|
|
254
314
|
name,
|
|
255
315
|
type: getSchemaType(schema),
|
|
256
316
|
required: ctx.required,
|
|
257
317
|
deprecated: schema.deprecated
|
|
258
318
|
},
|
|
259
|
-
|
|
319
|
+
child
|
|
260
320
|
);
|
|
261
321
|
}
|
|
262
322
|
function resolveObjectType(schema) {
|
|
@@ -284,7 +344,7 @@ function getSchemaType(schema) {
|
|
|
284
344
|
}
|
|
285
345
|
|
|
286
346
|
// src/render/operation.ts
|
|
287
|
-
async function renderOperation(path, method,
|
|
347
|
+
async function renderOperation(path, method, ctx) {
|
|
288
348
|
const info = [];
|
|
289
349
|
const example = [];
|
|
290
350
|
const title = method.summary ?? method.operationId;
|
|
@@ -301,12 +361,13 @@ async function renderOperation(path, method, baseUrl) {
|
|
|
301
361
|
parseObject: true,
|
|
302
362
|
readOnly: method.method === "GET",
|
|
303
363
|
writeOnly: method.method !== "GET",
|
|
304
|
-
required: body.required ?? false
|
|
364
|
+
required: body.required ?? false,
|
|
365
|
+
render: ctx
|
|
305
366
|
})
|
|
306
367
|
);
|
|
307
368
|
}
|
|
308
369
|
const parameterGroups = /* @__PURE__ */ new Map();
|
|
309
|
-
const endpoint = createEndpoint(path, method, baseUrl);
|
|
370
|
+
const endpoint = createEndpoint(path, method, ctx.baseUrl);
|
|
310
371
|
for (const param of method.parameters) {
|
|
311
372
|
const schema = noRef(
|
|
312
373
|
param.schema ?? getPreferredMedia(param.content ?? {})?.schema
|
|
@@ -323,7 +384,8 @@ async function renderOperation(path, method, baseUrl) {
|
|
|
323
384
|
parseObject: false,
|
|
324
385
|
readOnly: method.method === "GET",
|
|
325
386
|
writeOnly: method.method !== "GET",
|
|
326
|
-
required: param.required ?? false
|
|
387
|
+
required: param.required ?? false,
|
|
388
|
+
render: ctx
|
|
327
389
|
}
|
|
328
390
|
);
|
|
329
391
|
const groupName = {
|
|
@@ -343,11 +405,11 @@ async function renderOperation(path, method, baseUrl) {
|
|
|
343
405
|
example.push(
|
|
344
406
|
codeblock({ language: "bash", title: "curl" }, getSampleRequest(endpoint))
|
|
345
407
|
);
|
|
346
|
-
example.push(await getResponseTabs(endpoint, method));
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
);
|
|
408
|
+
example.push(await getResponseTabs(endpoint, method, ctx));
|
|
409
|
+
return ctx.renderer.API([
|
|
410
|
+
ctx.renderer.APIInfo({ method: method.method, route: path }, info),
|
|
411
|
+
ctx.renderer.APIExample(example)
|
|
412
|
+
]);
|
|
351
413
|
}
|
|
352
414
|
function getResponseTable(operation) {
|
|
353
415
|
const table = [];
|
|
@@ -358,131 +420,85 @@ function getResponseTable(operation) {
|
|
|
358
420
|
});
|
|
359
421
|
return table.join("\n");
|
|
360
422
|
}
|
|
361
|
-
async function getResponseTabs(endpoint, operation) {
|
|
423
|
+
async function getResponseTabs(endpoint, operation, { renderer }) {
|
|
362
424
|
const items = [];
|
|
363
425
|
const child = [];
|
|
364
|
-
for (const
|
|
426
|
+
for (const code of Object.keys(operation.responses)) {
|
|
365
427
|
const example = getExampleResponse(endpoint, code);
|
|
366
428
|
const ts = await getTypescript(endpoint, code);
|
|
367
429
|
const description = code in endpoint.responses ? endpoint.responses[code].schema.description : void 0;
|
|
368
430
|
if (example && ts) {
|
|
369
431
|
items.push(code);
|
|
370
432
|
child.push(
|
|
371
|
-
|
|
372
|
-
{ value: code },
|
|
433
|
+
renderer.Response({ value: code }, [
|
|
373
434
|
p(description),
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
)
|
|
380
|
-
)
|
|
381
|
-
)
|
|
435
|
+
renderer.ResponseTypes([
|
|
436
|
+
renderer.ExampleResponse(example),
|
|
437
|
+
renderer.TypeScriptResponse(ts)
|
|
438
|
+
])
|
|
439
|
+
])
|
|
382
440
|
);
|
|
383
441
|
}
|
|
384
442
|
}
|
|
385
443
|
if (items.length === 0) return "";
|
|
386
|
-
return
|
|
444
|
+
return renderer.Responses(
|
|
387
445
|
{
|
|
388
446
|
items
|
|
389
447
|
},
|
|
390
|
-
|
|
448
|
+
child
|
|
391
449
|
);
|
|
392
450
|
}
|
|
393
451
|
|
|
394
452
|
// src/generate.ts
|
|
395
|
-
async function dereference(pathOrDocument) {
|
|
396
|
-
return await Parser.dereference(pathOrDocument);
|
|
397
|
-
}
|
|
398
453
|
async function generate(pathOrDocument, options = {}) {
|
|
399
|
-
const document = await dereference(pathOrDocument);
|
|
400
|
-
const
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
if (!value) throw new Error("Invalid schema");
|
|
404
|
-
const methodKeys = [
|
|
405
|
-
"get",
|
|
406
|
-
"post",
|
|
407
|
-
"patch",
|
|
408
|
-
"delete",
|
|
409
|
-
"head",
|
|
410
|
-
"put"
|
|
411
|
-
];
|
|
412
|
-
const methods = [];
|
|
413
|
-
for (const methodKey of methodKeys) {
|
|
414
|
-
const operation = value[methodKey];
|
|
415
|
-
if (!operation) continue;
|
|
416
|
-
if (tag && !operation.tags?.includes(tag.name)) continue;
|
|
417
|
-
methods.push(buildOperation(methodKey, operation));
|
|
418
|
-
}
|
|
419
|
-
return {
|
|
420
|
-
...value,
|
|
421
|
-
path: key,
|
|
422
|
-
methods
|
|
423
|
-
};
|
|
424
|
-
}
|
|
425
|
-
);
|
|
426
|
-
const serverUrl = document.servers?.[0].url;
|
|
427
|
-
const s = [];
|
|
454
|
+
const document = await Parser.dereference(pathOrDocument);
|
|
455
|
+
const routes = buildRoutes(document).get("all") ?? [];
|
|
456
|
+
const ctx = getContext(document, options);
|
|
457
|
+
const child = [];
|
|
428
458
|
for (const route of routes) {
|
|
429
459
|
for (const method of route.methods) {
|
|
430
|
-
|
|
460
|
+
child.push(await renderOperation(route.path, method, ctx));
|
|
431
461
|
}
|
|
432
462
|
}
|
|
433
|
-
return
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
463
|
+
return renderPage(
|
|
464
|
+
document.info.title,
|
|
465
|
+
document.info.description,
|
|
466
|
+
child,
|
|
437
467
|
options
|
|
438
468
|
);
|
|
439
469
|
}
|
|
440
470
|
async function generateTags(pathOrDocument, options = {}) {
|
|
441
|
-
const document = await dereference(pathOrDocument);
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
frontmatter: result.frontmatter ?? [
|
|
460
|
-
"---",
|
|
461
|
-
title && `title: ${title}`,
|
|
462
|
-
description && `description: |
|
|
463
|
-
${description.split("\n").join("\n ")}
|
|
464
|
-
`,
|
|
465
|
-
"---"
|
|
466
|
-
].filter(Boolean).join("\n"),
|
|
467
|
-
imports: result.imports ?? [
|
|
468
|
-
`import { Root, API, APIInfo, APIExample, Property } from '${componentsImportPath}'`,
|
|
469
|
-
`import { Tabs, Tab } from 'fumadocs-ui/components/tabs'`,
|
|
470
|
-
`import { Accordion, Accordions } from 'fumadocs-ui/components/accordion';`
|
|
471
|
-
],
|
|
472
|
-
content: result.content ?? content
|
|
473
|
-
};
|
|
474
|
-
return [rendered.frontmatter, rendered.imports.join("\n"), rendered.content].filter(Boolean).join("\n\n");
|
|
471
|
+
const document = await Parser.dereference(pathOrDocument);
|
|
472
|
+
const tags = Array.from(buildRoutes(document).entries());
|
|
473
|
+
const ctx = getContext(document, options);
|
|
474
|
+
return await Promise.all(
|
|
475
|
+
tags.filter(([tag]) => tag !== "all").map(async ([tag, routes]) => {
|
|
476
|
+
const info = document.tags?.find((t) => t.name === tag);
|
|
477
|
+
const child = [];
|
|
478
|
+
for (const route of routes) {
|
|
479
|
+
for (const method of route.methods) {
|
|
480
|
+
child.push(await renderOperation(route.path, method, ctx));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
tag,
|
|
485
|
+
content: renderPage(tag, info?.description, child, options)
|
|
486
|
+
};
|
|
487
|
+
})
|
|
488
|
+
);
|
|
475
489
|
}
|
|
476
|
-
function
|
|
490
|
+
function getContext(document, options) {
|
|
477
491
|
return {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
492
|
+
renderer: {
|
|
493
|
+
...defaultRenderer,
|
|
494
|
+
...options.renderer
|
|
495
|
+
},
|
|
496
|
+
baseUrl: document.servers?.[0].url ?? "https://example.com"
|
|
481
497
|
};
|
|
482
498
|
}
|
|
483
499
|
|
|
484
500
|
// src/generate-file.ts
|
|
485
|
-
import {
|
|
501
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
486
502
|
import { dirname, join, parse } from "node:path";
|
|
487
503
|
import fg from "fast-glob";
|
|
488
504
|
async function generateFiles({
|
|
@@ -490,13 +506,10 @@ async function generateFiles({
|
|
|
490
506
|
output,
|
|
491
507
|
name: nameFn,
|
|
492
508
|
per = "file",
|
|
493
|
-
|
|
494
|
-
|
|
509
|
+
cwd = process.cwd(),
|
|
510
|
+
...options
|
|
495
511
|
}) {
|
|
496
512
|
const outputDir = join(cwd, output);
|
|
497
|
-
const options = {
|
|
498
|
-
render: render2
|
|
499
|
-
};
|
|
500
513
|
const resolvedInputs = await fg.glob(input, { absolute: true, cwd });
|
|
501
514
|
await Promise.all(
|
|
502
515
|
resolvedInputs.map(async (path) => {
|
|
@@ -505,27 +518,28 @@ async function generateFiles({
|
|
|
505
518
|
if (per === "file") {
|
|
506
519
|
const outPath = join(outputDir, `${filename}.mdx`);
|
|
507
520
|
const result = await generate(path, options);
|
|
508
|
-
write(outPath, result);
|
|
521
|
+
await write(outPath, result);
|
|
522
|
+
console.log(`Generated: ${outPath}`);
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const results = await generateTags(path, options);
|
|
526
|
+
for (const result of results) {
|
|
527
|
+
let tagName = result.tag;
|
|
528
|
+
tagName = nameFn?.("tag", tagName) ?? tagName.toLowerCase().replace(/\s+/g, "-");
|
|
529
|
+
const outPath = join(outputDir, filename, `${tagName}.mdx`);
|
|
530
|
+
await write(outPath, result.content);
|
|
509
531
|
console.log(`Generated: ${outPath}`);
|
|
510
|
-
} else {
|
|
511
|
-
const results = await generateTags(path, options);
|
|
512
|
-
results.forEach((result) => {
|
|
513
|
-
let tagName = result.tag;
|
|
514
|
-
tagName = nameFn?.("tag", tagName) ?? tagName.toLowerCase().replace(/\s+/g, "-");
|
|
515
|
-
const outPath = join(outputDir, filename, `${tagName}.mdx`);
|
|
516
|
-
write(outPath, result.content);
|
|
517
|
-
console.log(`Generated: ${outPath}`);
|
|
518
|
-
});
|
|
519
532
|
}
|
|
520
533
|
})
|
|
521
534
|
);
|
|
522
535
|
}
|
|
523
|
-
function write(path, content) {
|
|
524
|
-
|
|
525
|
-
|
|
536
|
+
async function write(path, content) {
|
|
537
|
+
await mkdir(dirname(path), { recursive: true });
|
|
538
|
+
await writeFile(path, content);
|
|
526
539
|
}
|
|
527
540
|
export {
|
|
528
|
-
|
|
541
|
+
createElement,
|
|
542
|
+
defaultRenderer,
|
|
529
543
|
generate,
|
|
530
544
|
generateFiles,
|
|
531
545
|
generateTags
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fumadocs-openapi",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Generate MDX docs for your OpenAPI spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"NextJs",
|
|
@@ -19,10 +19,12 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@apidevtools/json-schema-ref-parser": "^11.6.4",
|
|
21
21
|
"fast-glob": "^3.3.1",
|
|
22
|
+
"js-yaml": "^4.1.0",
|
|
22
23
|
"json-schema-to-typescript": "^14.0.5",
|
|
23
24
|
"openapi-sampler": "^1.5.1"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
27
|
+
"@types/js-yaml": "^4.0.9",
|
|
26
28
|
"@types/node": "18.17.5",
|
|
27
29
|
"@types/openapi-sampler": "^1.0.3",
|
|
28
30
|
"openapi-types": "^12.1.3",
|