@mintlify/scraping 4.0.1072 → 4.0.1073
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/__test__/openApiTagHierarchy.test.ts +286 -0
- package/bin/openapi/common.d.ts +5 -3
- package/bin/openapi/common.js +160 -149
- package/bin/openapi/common.js.map +1 -1
- package/bin/openapi/generateOpenApiPages.js +16 -5
- package/bin/openapi/generateOpenApiPages.js.map +1 -1
- package/bin/openapi/generateOpenApiPagesForDocsConfig.js +8 -3
- package/bin/openapi/generateOpenApiPagesForDocsConfig.js.map +1 -1
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/openapi/common.ts +198 -171
- package/src/openapi/generateOpenApiPages.ts +23 -5
- package/src/openapi/generateOpenApiPagesForDocsConfig.ts +14 -3
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { generateOpenApiPages as generateEditorPages } from '@mintlify/common';
|
|
2
|
+
import type { OpenAPIV3_2 } from '@mintlify/validation';
|
|
3
|
+
import type { OpenAPI } from 'openapi-types';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
|
|
6
|
+
import { generateOpenApiPages } from '../src/openapi/generateOpenApiPages.js';
|
|
7
|
+
import { generateOpenApiPagesForDocsConfig } from '../src/openapi/generateOpenApiPagesForDocsConfig.js';
|
|
8
|
+
|
|
9
|
+
const endpoint = (tags: string[], summary: string) => ({
|
|
10
|
+
get: { tags, summary, responses: { '200': { description: 'OK' } } },
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
type Tag = { name: string; parent?: string; summary?: string; kind?: string };
|
|
14
|
+
|
|
15
|
+
function document() {
|
|
16
|
+
const tags: Tag[] = [
|
|
17
|
+
{ name: 'books', parent: 'products', summary: 'Books' },
|
|
18
|
+
{ name: 'products', parent: 'store', summary: 'Products' },
|
|
19
|
+
{ name: 'store', summary: 'Store' },
|
|
20
|
+
];
|
|
21
|
+
return {
|
|
22
|
+
openapi: '3.2.0',
|
|
23
|
+
info: { title: 'Store', version: '1' },
|
|
24
|
+
tags,
|
|
25
|
+
paths: {
|
|
26
|
+
'/books': endpoint(['books'], 'List books'),
|
|
27
|
+
'/products': endpoint(['products'], 'List products'),
|
|
28
|
+
},
|
|
29
|
+
webhooks: {
|
|
30
|
+
bookCreated: {
|
|
31
|
+
post: {
|
|
32
|
+
tags: ['books'],
|
|
33
|
+
summary: 'Book created',
|
|
34
|
+
responses: { '200': { description: 'OK' } },
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('3.2 methods', () => {
|
|
42
|
+
it('generates pages for query and additionalOperations with the wire method in the meta tag', async () => {
|
|
43
|
+
const base = document();
|
|
44
|
+
const spec: OpenAPIV3_2.Document = {
|
|
45
|
+
...base,
|
|
46
|
+
paths: {
|
|
47
|
+
...base.paths,
|
|
48
|
+
'/books': {
|
|
49
|
+
...base.paths['/books'],
|
|
50
|
+
query: {
|
|
51
|
+
tags: ['books'],
|
|
52
|
+
summary: 'Search books',
|
|
53
|
+
responses: { '200': { description: 'OK' } },
|
|
54
|
+
},
|
|
55
|
+
additionalOperations: {
|
|
56
|
+
PURGE: {
|
|
57
|
+
tags: ['books'],
|
|
58
|
+
summary: 'Purge books',
|
|
59
|
+
responses: { '204': { description: 'Purged' } },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const { pagesAcc } = await generateOpenApiPagesForDocsConfig(spec as OpenAPI.Document, {
|
|
66
|
+
writeFiles: false,
|
|
67
|
+
outDir: 'api-reference',
|
|
68
|
+
});
|
|
69
|
+
expect(Object.values(pagesAcc).map((page) => [page.href, page.openapi])).toEqual(
|
|
70
|
+
expect.arrayContaining([
|
|
71
|
+
['/api-reference/books/search-books', 'query /books'],
|
|
72
|
+
['/api-reference/books/purge-books', 'PURGE /books'],
|
|
73
|
+
])
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe.each([
|
|
79
|
+
['docs.json', generateOpenApiPagesForDocsConfig],
|
|
80
|
+
['mint.json', generateOpenApiPages],
|
|
81
|
+
] as const)('%s tag hierarchy', (_name, generate) => {
|
|
82
|
+
it('generates nested paths and webhooks with matching decorated navigation and stable URLs', async () => {
|
|
83
|
+
const spec = document();
|
|
84
|
+
const result = await generate(spec, {
|
|
85
|
+
writeFiles: false,
|
|
86
|
+
outDir: 'api-reference',
|
|
87
|
+
});
|
|
88
|
+
expect(result.nav).toEqual([
|
|
89
|
+
{
|
|
90
|
+
group: 'Store',
|
|
91
|
+
pages: [
|
|
92
|
+
{
|
|
93
|
+
group: 'Products',
|
|
94
|
+
pages: [
|
|
95
|
+
{
|
|
96
|
+
group: 'Books',
|
|
97
|
+
pages: ['api-reference/books/list-books', 'api-reference/books/book-created'],
|
|
98
|
+
},
|
|
99
|
+
'api-reference/products/list-products',
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
expect(result.decoratedNav).toMatchObject([
|
|
106
|
+
{
|
|
107
|
+
group: 'Store',
|
|
108
|
+
pages: [
|
|
109
|
+
{
|
|
110
|
+
group: 'Products',
|
|
111
|
+
pages: [
|
|
112
|
+
{
|
|
113
|
+
group: 'Books',
|
|
114
|
+
pages: [
|
|
115
|
+
{ href: '/api-reference/books/list-books' },
|
|
116
|
+
{ href: '/api-reference/books/book-created' },
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{ href: '/api-reference/products/list-products' },
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
]);
|
|
125
|
+
const flat = await generate(
|
|
126
|
+
{ ...spec, tags: spec.tags.map(({ name }) => ({ name })), openapi: '3.1.0' },
|
|
127
|
+
{ writeFiles: false, outDir: 'api-reference' }
|
|
128
|
+
);
|
|
129
|
+
expect(Object.keys(result.pagesAcc)).toEqual(Object.keys(flat.pagesAcc));
|
|
130
|
+
expect(
|
|
131
|
+
generateEditorPages({ spec, specFilePath: 'openapi.yaml' })
|
|
132
|
+
.map((page) => page.href)
|
|
133
|
+
.sort()
|
|
134
|
+
).toEqual(
|
|
135
|
+
Object.values(result.pagesAcc)
|
|
136
|
+
.map((page) => page.href)
|
|
137
|
+
.sort()
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('does not merge distinct tags with identical labels', async () => {
|
|
142
|
+
const spec = {
|
|
143
|
+
...document(),
|
|
144
|
+
webhooks: {},
|
|
145
|
+
tags: [
|
|
146
|
+
{ name: 'books', parent: 'store', summary: 'Items' },
|
|
147
|
+
{ name: 'products', parent: 'store', summary: 'Items' },
|
|
148
|
+
{ name: 'store' },
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
const { nav } = await generate(spec, { writeFiles: false });
|
|
152
|
+
expect(nav).toEqual([
|
|
153
|
+
{
|
|
154
|
+
group: 'store',
|
|
155
|
+
pages: [
|
|
156
|
+
{ group: 'Items', pages: ['books/list-books'] },
|
|
157
|
+
{ group: 'Items', pages: ['products/list-products'] },
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('keeps filenames unique when different tag names produce the same slug', async () => {
|
|
164
|
+
const spec = {
|
|
165
|
+
...document(),
|
|
166
|
+
webhooks: {},
|
|
167
|
+
tags: [{ name: 'book.s' }, { name: 'books' }],
|
|
168
|
+
paths: {
|
|
169
|
+
'/first': endpoint(['book.s'], 'List'),
|
|
170
|
+
'/second': endpoint(['books'], 'List'),
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
const { pagesAcc } = await generate(spec, {
|
|
174
|
+
writeFiles: false,
|
|
175
|
+
outDir: 'api-reference',
|
|
176
|
+
});
|
|
177
|
+
expect(Object.keys(pagesAcc)).toEqual([
|
|
178
|
+
'api-reference/books/list',
|
|
179
|
+
'api-reference/books/list-1',
|
|
180
|
+
]);
|
|
181
|
+
expect(
|
|
182
|
+
generateEditorPages({ spec, specFilePath: 'openapi.yaml' }).map((page) => page.href)
|
|
183
|
+
).toEqual(['/api-reference/books/list', '/api-reference/books/list-1']);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('omits groups containing only hidden or excluded operations', async () => {
|
|
187
|
+
const spec = document();
|
|
188
|
+
const hiddenSpec = {
|
|
189
|
+
...spec,
|
|
190
|
+
webhooks: {},
|
|
191
|
+
paths: {
|
|
192
|
+
'/books': { get: { ...spec.paths['/books'].get, 'x-hidden': true } },
|
|
193
|
+
'/products': { get: { ...spec.paths['/products'].get, 'x-excluded': true } },
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
const { nav, pagesAcc } = await generate(hiddenSpec, {
|
|
197
|
+
writeFiles: false,
|
|
198
|
+
});
|
|
199
|
+
expect(nav).toEqual([]);
|
|
200
|
+
expect(Object.keys(pagesAcc)).toEqual(['books/list-books']);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('supports webhook-only specs and rejects specs with no operations', async () => {
|
|
204
|
+
const { paths: _paths, ...spec } = document();
|
|
205
|
+
const result = await generate(spec, { writeFiles: false });
|
|
206
|
+
expect(Object.keys(result.pagesAcc)).toEqual(['books/book-created']);
|
|
207
|
+
await expect(generate({ ...spec, webhooks: {} }, { writeFiles: false })).rejects.toThrow(
|
|
208
|
+
'No paths or webhooks'
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('turns kind: badge tags into the page tag pill without grouping by them', async () => {
|
|
213
|
+
const spec = document();
|
|
214
|
+
spec.tags.push(
|
|
215
|
+
{ name: 'beta', kind: 'badge', summary: 'Beta' },
|
|
216
|
+
{ name: 'internal', kind: 'badge', summary: 'Internal: staff only' }
|
|
217
|
+
);
|
|
218
|
+
spec.paths['/books'].get.tags = ['books', 'beta'];
|
|
219
|
+
spec.paths['/products'].get.tags = ['products', 'internal', 'beta'];
|
|
220
|
+
spec.webhooks.bookCreated.post.tags = ['books', 'beta'];
|
|
221
|
+
const written: Record<string, string> = {};
|
|
222
|
+
const result = await generate(spec, {
|
|
223
|
+
writeFiles: true,
|
|
224
|
+
writeFile: (path, contents) => {
|
|
225
|
+
written[path] = contents;
|
|
226
|
+
},
|
|
227
|
+
outDir: 'api-reference',
|
|
228
|
+
});
|
|
229
|
+
expect(result.pagesAcc['api-reference/books/list-books']?.tag).toBe('Beta');
|
|
230
|
+
expect(result.pagesAcc['api-reference/products/list-products']?.tag).toBe(
|
|
231
|
+
'Internal: staff only'
|
|
232
|
+
);
|
|
233
|
+
expect(result.pagesAcc['api-reference/books/book-created']?.tag).toBe('Beta');
|
|
234
|
+
expect(written['api-reference/books/list-books.mdx']).toContain('\ntag: Beta\n');
|
|
235
|
+
expect(written['api-reference/products/list-products.mdx']).toContain(
|
|
236
|
+
"\ntag: 'Internal: staff only'\n"
|
|
237
|
+
);
|
|
238
|
+
expect(result.nav[0]).toMatchObject({ group: 'Store' });
|
|
239
|
+
expect(JSON.stringify(result.nav)).not.toContain('Beta');
|
|
240
|
+
|
|
241
|
+
const editorPages = generateEditorPages({
|
|
242
|
+
spec,
|
|
243
|
+
specFilePath: 'openapi.yaml',
|
|
244
|
+
outputDirectory: 'api-reference',
|
|
245
|
+
});
|
|
246
|
+
expect(editorPages.map((page) => page.xMintMetadata?.tag)).toEqual([
|
|
247
|
+
'Beta',
|
|
248
|
+
'Internal: staff only',
|
|
249
|
+
'Beta',
|
|
250
|
+
]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('files a page under its navigation tag when a badge tag is listed first', async () => {
|
|
254
|
+
const spec = document();
|
|
255
|
+
spec.tags.push({ name: 'beta', kind: 'badge', summary: 'Beta' });
|
|
256
|
+
spec.paths['/books'].get.tags = ['beta', 'books'];
|
|
257
|
+
spec.webhooks.bookCreated.post.tags = ['beta', 'books'];
|
|
258
|
+
const result = await generate(spec, { writeFiles: false, outDir: 'api-reference' });
|
|
259
|
+
expect(Object.keys(result.pagesAcc)).toEqual(
|
|
260
|
+
expect.arrayContaining(['api-reference/books/list-books', 'api-reference/books/book-created'])
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const editorPages = generateEditorPages({
|
|
264
|
+
spec,
|
|
265
|
+
specFilePath: 'openapi.yaml',
|
|
266
|
+
outputDirectory: 'api-reference',
|
|
267
|
+
});
|
|
268
|
+
expect(editorPages.map((page) => page.href)).toEqual(
|
|
269
|
+
expect.arrayContaining([
|
|
270
|
+
'/api-reference/books/list-books',
|
|
271
|
+
'/api-reference/books/book-created',
|
|
272
|
+
])
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it('lets x-mint metadata tag override a badge tag', async () => {
|
|
277
|
+
const spec = document();
|
|
278
|
+
spec.tags.push({ name: 'beta', kind: 'badge' });
|
|
279
|
+
Object.assign(spec.paths['/books'].get, {
|
|
280
|
+
tags: ['books', 'beta'],
|
|
281
|
+
'x-mint': { metadata: { tag: 'PREVIEW' } },
|
|
282
|
+
});
|
|
283
|
+
const result = await generate(spec, { writeFiles: false });
|
|
284
|
+
expect(result.pagesAcc['books/list-books']?.tag).toBe('PREVIEW');
|
|
285
|
+
});
|
|
286
|
+
});
|
package/bin/openapi/common.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type LoadedOverlay } from '@mintlify/common';
|
|
1
|
+
import { createOpenApiBadgeResolver, createOpenApiTagResolver, type LoadedOverlay } from '@mintlify/common';
|
|
2
2
|
import type { DecoratedNavigationPage, PageMetaTags } from '@mintlify/models';
|
|
3
3
|
import { XMint } from '@mintlify/validation';
|
|
4
4
|
import { OpenAPI, OpenAPIV3 } from 'openapi-types';
|
|
@@ -35,6 +35,8 @@ export type GenerateOpenApiPagesOptions = {
|
|
|
35
35
|
mountPath?: string;
|
|
36
36
|
overwrite?: boolean;
|
|
37
37
|
localSchema?: boolean;
|
|
38
|
+
resolveTags?: ReturnType<typeof createOpenApiTagResolver>;
|
|
39
|
+
resolveBadge?: ReturnType<typeof createOpenApiBadgeResolver>;
|
|
38
40
|
writeFile?: (filename: string, content: string) => void | Promise<void>;
|
|
39
41
|
overlays?: readonly LoadedOverlay[];
|
|
40
42
|
};
|
|
@@ -45,8 +47,8 @@ export type OpenApiPageGenerationResult<N, DN> = {
|
|
|
45
47
|
pagesAcc: Record<string, DecoratedNavigationPage>;
|
|
46
48
|
isUrl: boolean;
|
|
47
49
|
};
|
|
48
|
-
export declare function processOpenApiPath<N, DN>(path: string, pathItemObject: OpenAPIV3.PathItemObject<OpenApiExtensions>, schema: OpenAPI.Document, nav: N, decoratedNav: DN, writePromises: Promise<void>[], pagesAcc: Record<string, DecoratedNavigationPage>, options: GenerateOpenApiPagesOptions, findNavGroup: (nav: any, groupName?: string) => any): string[];
|
|
49
|
-
export declare function processOpenApiWebhook<N, DN>(webhook: string, webhookObject: OpenAPIV3.PathItemObject<OpenApiExtensions>, schema: OpenAPI.Document, nav: N, decoratedNav: DN, writePromises: Promise<void>[], pagesAcc: Record<string, DecoratedNavigationPage>, options: GenerateOpenApiPagesOptions, findNavGroup: (nav: any, groupName?: string) => any): string[];
|
|
50
|
+
export declare function processOpenApiPath<N, DN>(path: string, pathItemObject: OpenAPIV3.PathItemObject<OpenApiExtensions>, schema: OpenAPI.Document, nav: N, decoratedNav: DN, writePromises: Promise<void>[], pagesAcc: Record<string, DecoratedNavigationPage>, options: GenerateOpenApiPagesOptions, findNavGroup: (nav: any, groupName?: string, tagKey?: string) => any): string[];
|
|
51
|
+
export declare function processOpenApiWebhook<N, DN>(webhook: string, webhookObject: OpenAPIV3.PathItemObject<OpenApiExtensions>, schema: OpenAPI.Document, nav: N, decoratedNav: DN, writePromises: Promise<void>[], pagesAcc: Record<string, DecoratedNavigationPage>, options: GenerateOpenApiPagesOptions, findNavGroup: (nav: any, groupName?: string, tagKey?: string) => any): string[];
|
|
50
52
|
export declare const getXMintGroups: ({ pathObject, operationObject, }: {
|
|
51
53
|
pathObject: OpenAPIV3.PathItemObject<OpenApiExtensions>;
|
|
52
54
|
operationObject: OpenAPIV3.OperationObject<OpenApiExtensions> | undefined;
|
package/bin/openapi/common.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { applyOverlays, getOpenApiTitleAndDescription, optionallyAddLeadingSlash, optionallyRemoveLeadingSlash, slugToTitle, isAllowedLocalSchemaUrl, buildOpenApiMetaTag, registerXMintContent, prepareStringToBeValidFilename, generateUniqueFilenameWithoutExtension,
|
|
1
|
+
import { applyOverlays, getOpenApiTitleAndDescription, optionallyAddLeadingSlash, optionallyRemoveLeadingSlash, slugToTitle, isAllowedLocalSchemaUrl, buildOpenApiMetaTag, registerXMintContent, prepareStringToBeValidFilename, generateUniqueFilenameWithoutExtension, createOpenApiBadgeResolver, createOpenApiTagResolver, findOpenApiNavGroup, isOpenApi32, mergePageGroups, } from '@mintlify/common';
|
|
2
|
+
import { getPathItemOperations } from '@mintlify/validation';
|
|
2
3
|
import { outputFile } from 'fs-extra';
|
|
3
4
|
import fse from 'fs-extra';
|
|
4
5
|
import fs from 'fs/promises';
|
|
5
6
|
import yaml from 'js-yaml';
|
|
6
|
-
import { OpenAPIV3 } from 'openapi-types';
|
|
7
7
|
import path, { join, parse, posix } from 'path';
|
|
8
8
|
import { fetchOpenApi } from '../utils/network.js';
|
|
9
9
|
export const getOpenApiDefinition = async (pathOrDocumentOrUrl, localSchema, overlays) => {
|
|
@@ -59,7 +59,8 @@ export const createOpenApiFrontmatterContent = ({ openApiMetaTag, version, depre
|
|
|
59
59
|
Object.entries(metadata)
|
|
60
60
|
.filter(([k]) => !reserved.has(k))
|
|
61
61
|
.forEach(([key, value]) => {
|
|
62
|
-
|
|
62
|
+
const serialized = typeof value === 'string' ? yaml.dump(value, { lineWidth: -1 }).trimEnd() : value;
|
|
63
|
+
frontmatter += `\n${key}: ${serialized}`;
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
66
|
frontmatter += `\n---`;
|
|
@@ -81,94 +82,99 @@ findNavGroup) {
|
|
|
81
82
|
? optionallyAddLeadingSlash(options.openApiFilePath)
|
|
82
83
|
: undefined;
|
|
83
84
|
const generatedSlugs = [];
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
const slugTitle = slugToTitle(filenameWithoutExtension);
|
|
135
|
-
const page = {
|
|
136
|
-
title: titleTag ?? slugTitle,
|
|
137
|
-
description,
|
|
138
|
-
deprecated: operation?.deprecated,
|
|
139
|
-
version: options.version,
|
|
140
|
-
// When a file-path spec has x-mint.href overriding the path, use the slug-derived
|
|
141
|
-
// title for the sidebar so the nav shows the user-chosen name rather than the
|
|
142
|
-
// OpenAPI summary. Only for openApiFilePath specs to avoid changing URL-based behavior.
|
|
143
|
-
...(xMint?.href && titleTag && options.openApiFilePath ? { sidebarTitle: slugTitle } : {}),
|
|
85
|
+
const resolveTags = options.resolveTags ?? createOpenApiTagResolver(schema);
|
|
86
|
+
const resolveBadge = options.resolveBadge ?? createOpenApiBadgeResolver(schema);
|
|
87
|
+
const nestedTags = isOpenApi32(schema);
|
|
88
|
+
getPathItemOperations(pathItemObject).forEach(([method, operation]) => {
|
|
89
|
+
if (isExcludedOperation(operation)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const xMint = operation?.['x-mint'];
|
|
93
|
+
const xMintGroups = getXMintGroups({
|
|
94
|
+
pathObject: pathItemObject,
|
|
95
|
+
operationObject: operation,
|
|
96
|
+
});
|
|
97
|
+
if (xMint?.href) {
|
|
98
|
+
xMint.href = optionallyAddLeadingSlash(xMint.href);
|
|
99
|
+
}
|
|
100
|
+
const tagPath = resolveTags(operation?.tags);
|
|
101
|
+
const tagName = tagPath.at(-1)?.name;
|
|
102
|
+
let title = prepareStringToBeValidFilename(operation?.summary) ??
|
|
103
|
+
`${method.toLowerCase()}-${prepareStringToBeValidFilename(path)}`;
|
|
104
|
+
let folder = prepareStringToBeValidFilename(tagName) ?? '';
|
|
105
|
+
let base = posix.join(options.outDir ?? '', folder, title);
|
|
106
|
+
if (xMint?.href) {
|
|
107
|
+
const slug = optionallyRemoveLeadingSlash(xMint.href);
|
|
108
|
+
title = posix.parse(slug).name;
|
|
109
|
+
folder = posix.parse(slug).dir;
|
|
110
|
+
base = posix.join(options.mountPath ?? '', folder, title);
|
|
111
|
+
}
|
|
112
|
+
const hidden = isHiddenOperation(operation);
|
|
113
|
+
const navGroup = hidden && nestedTags ? [] : findOpenApiNavGroup(nav, tagPath, findNavGroup);
|
|
114
|
+
const decoratedNavGroup = hidden && nestedTags ? [] : findOpenApiNavGroup(decoratedNav, tagPath, findNavGroup);
|
|
115
|
+
const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(nestedTags ? pagesAcc : navGroup, base);
|
|
116
|
+
const openapiMetaTag = buildOpenApiMetaTag({
|
|
117
|
+
filePath: openApiFilePathFromRoot,
|
|
118
|
+
method,
|
|
119
|
+
path,
|
|
120
|
+
});
|
|
121
|
+
const { title: titleTag, description } = getOpenApiTitleAndDescription([
|
|
122
|
+
{
|
|
123
|
+
filename: options.openApiFilePath
|
|
124
|
+
? parse(options.openApiFilePath).name
|
|
125
|
+
: 'filler-filename',
|
|
126
|
+
spec: schema,
|
|
127
|
+
originalFileLocation: openApiFilePathFromRoot,
|
|
128
|
+
},
|
|
129
|
+
], openapiMetaTag);
|
|
130
|
+
let xMintMetadata = xMint?.metadata;
|
|
131
|
+
if (xMintGroups.length > 0) {
|
|
132
|
+
xMintMetadata = {
|
|
144
133
|
...xMintMetadata,
|
|
145
|
-
|
|
146
|
-
href: posix.resolve('/', filenameWithoutExtension),
|
|
134
|
+
groups: mergePageGroups(xMintMetadata?.groups, xMintGroups),
|
|
147
135
|
};
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
136
|
+
}
|
|
137
|
+
const badge = resolveBadge(operation?.tags);
|
|
138
|
+
if (badge !== undefined && xMintMetadata?.tag === undefined) {
|
|
139
|
+
xMintMetadata = { ...xMintMetadata, tag: badge };
|
|
140
|
+
}
|
|
141
|
+
const slugTitle = slugToTitle(filenameWithoutExtension);
|
|
142
|
+
const page = {
|
|
143
|
+
title: titleTag ?? slugTitle,
|
|
144
|
+
description,
|
|
145
|
+
deprecated: operation?.deprecated,
|
|
146
|
+
version: options.version,
|
|
147
|
+
// When a file-path spec has x-mint.href overriding the path, use the slug-derived
|
|
148
|
+
// title for the sidebar so the nav shows the user-chosen name rather than the
|
|
149
|
+
// OpenAPI summary. Only for openApiFilePath specs to avoid changing URL-based behavior.
|
|
150
|
+
...(xMint?.href && titleTag && options.openApiFilePath ? { sidebarTitle: slugTitle } : {}),
|
|
151
|
+
...xMintMetadata,
|
|
152
|
+
openapi: openapiMetaTag,
|
|
153
|
+
href: posix.resolve('/', filenameWithoutExtension),
|
|
154
|
+
};
|
|
155
|
+
if (!isHiddenOperation(operation)) {
|
|
156
|
+
navGroup.push(filenameWithoutExtension);
|
|
157
|
+
decoratedNavGroup.push(page);
|
|
158
|
+
}
|
|
159
|
+
pagesAcc[filenameWithoutExtension] = page;
|
|
160
|
+
generatedSlugs.push(filenameWithoutExtension);
|
|
161
|
+
if (!options.writeFiles) {
|
|
162
|
+
registerXMintContent(filenameWithoutExtension, xMint?.content);
|
|
163
|
+
}
|
|
164
|
+
const targetPath = options.outDirBasePath
|
|
165
|
+
? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`)
|
|
166
|
+
: `${filenameWithoutExtension}.mdx`;
|
|
167
|
+
if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) {
|
|
168
|
+
const input = {
|
|
169
|
+
openApiMetaTag: openapiMetaTag,
|
|
170
|
+
version: options.version,
|
|
171
|
+
deprecated: operation?.deprecated,
|
|
172
|
+
metadata: xMintMetadata,
|
|
173
|
+
extraContent: xMint?.content,
|
|
174
|
+
};
|
|
175
|
+
writePromises.push(options.writeFile
|
|
176
|
+
? Promise.resolve(options.writeFile(targetPath, createOpenApiFrontmatterContent(input)))
|
|
177
|
+
: createOpenApiFrontmatter({ filename: targetPath, ...input }));
|
|
172
178
|
}
|
|
173
179
|
});
|
|
174
180
|
return generatedSlugs;
|
|
@@ -180,69 +186,74 @@ findNavGroup) {
|
|
|
180
186
|
? optionallyAddLeadingSlash(options.openApiFilePath)
|
|
181
187
|
: undefined;
|
|
182
188
|
const generatedSlugs = [];
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
189
|
+
const resolveTags = options.resolveTags ?? createOpenApiTagResolver(schema);
|
|
190
|
+
const resolveBadge = options.resolveBadge ?? createOpenApiBadgeResolver(schema);
|
|
191
|
+
const nestedTags = isOpenApi32(schema);
|
|
192
|
+
getPathItemOperations(webhookObject).forEach(([, operation]) => {
|
|
193
|
+
if (isExcludedOperation(operation)) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const xMint = operation?.['x-mint'];
|
|
197
|
+
if (xMint?.href) {
|
|
198
|
+
xMint.href = optionallyAddLeadingSlash(xMint.href);
|
|
199
|
+
}
|
|
200
|
+
const tagPath = resolveTags(operation?.tags);
|
|
201
|
+
const tagName = tagPath.at(-1)?.name;
|
|
202
|
+
let title = prepareStringToBeValidFilename(operation?.summary) ??
|
|
203
|
+
`${prepareStringToBeValidFilename(webhook)}`;
|
|
204
|
+
let folder = prepareStringToBeValidFilename(tagName) ?? '';
|
|
205
|
+
let base = posix.join(options.outDir ?? '', folder, title);
|
|
206
|
+
if (xMint?.href) {
|
|
207
|
+
const slug = optionallyRemoveLeadingSlash(xMint.href);
|
|
208
|
+
title = posix.parse(slug).name;
|
|
209
|
+
folder = posix.parse(slug).dir;
|
|
210
|
+
base = posix.join(options.mountPath ?? '', folder, title);
|
|
211
|
+
}
|
|
212
|
+
const hidden = isHiddenOperation(operation);
|
|
213
|
+
const navGroup = hidden && nestedTags ? [] : findOpenApiNavGroup(nav, tagPath, findNavGroup);
|
|
214
|
+
const decoratedNavGroup = hidden && nestedTags ? [] : findOpenApiNavGroup(decoratedNav, tagPath, findNavGroup);
|
|
215
|
+
const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(nestedTags ? pagesAcc : navGroup, base);
|
|
216
|
+
const openapiMetaTag = buildOpenApiMetaTag({
|
|
217
|
+
filePath: openApiFilePathFromRoot,
|
|
218
|
+
method: 'webhook',
|
|
219
|
+
path: webhook,
|
|
220
|
+
});
|
|
221
|
+
const badge = resolveBadge(operation?.tags);
|
|
222
|
+
const xMintMetadata = badge !== undefined && xMint?.metadata?.tag === undefined
|
|
223
|
+
? { ...xMint?.metadata, tag: badge }
|
|
224
|
+
: xMint?.metadata;
|
|
225
|
+
const page = {
|
|
226
|
+
title: slugToTitle(filenameWithoutExtension),
|
|
227
|
+
description: operation?.description,
|
|
228
|
+
version: options.version,
|
|
229
|
+
deprecated: operation?.deprecated,
|
|
230
|
+
...xMintMetadata,
|
|
231
|
+
openapi: openapiMetaTag,
|
|
232
|
+
href: posix.resolve('/', filenameWithoutExtension),
|
|
233
|
+
};
|
|
234
|
+
if (!isHiddenOperation(operation)) {
|
|
235
|
+
navGroup.push(filenameWithoutExtension);
|
|
236
|
+
decoratedNavGroup.push(page);
|
|
237
|
+
}
|
|
238
|
+
pagesAcc[filenameWithoutExtension] = page;
|
|
239
|
+
generatedSlugs.push(filenameWithoutExtension);
|
|
240
|
+
if (!options.writeFiles) {
|
|
241
|
+
registerXMintContent(filenameWithoutExtension, xMint?.content);
|
|
242
|
+
}
|
|
243
|
+
const targetPath = options.outDirBasePath
|
|
244
|
+
? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`)
|
|
245
|
+
: `${filenameWithoutExtension}.mdx`;
|
|
246
|
+
if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) {
|
|
247
|
+
const input = {
|
|
248
|
+
openApiMetaTag: openapiMetaTag,
|
|
216
249
|
version: options.version,
|
|
217
250
|
deprecated: operation?.deprecated,
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
href: posix.resolve('/', filenameWithoutExtension),
|
|
251
|
+
metadata: xMintMetadata,
|
|
252
|
+
extraContent: xMint?.content,
|
|
221
253
|
};
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
226
|
-
pagesAcc[filenameWithoutExtension] = page;
|
|
227
|
-
generatedSlugs.push(filenameWithoutExtension);
|
|
228
|
-
if (!options.writeFiles) {
|
|
229
|
-
registerXMintContent(filenameWithoutExtension, xMint?.content);
|
|
230
|
-
}
|
|
231
|
-
const targetPath = options.outDirBasePath
|
|
232
|
-
? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`)
|
|
233
|
-
: `${filenameWithoutExtension}.mdx`;
|
|
234
|
-
if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) {
|
|
235
|
-
const input = {
|
|
236
|
-
openApiMetaTag: openapiMetaTag,
|
|
237
|
-
version: options.version,
|
|
238
|
-
deprecated: operation?.deprecated,
|
|
239
|
-
metadata: xMint?.metadata,
|
|
240
|
-
extraContent: xMint?.content,
|
|
241
|
-
};
|
|
242
|
-
writePromises.push(options.writeFile
|
|
243
|
-
? Promise.resolve(options.writeFile(targetPath, createOpenApiFrontmatterContent(input)))
|
|
244
|
-
: createOpenApiFrontmatter({ filename: targetPath, ...input }));
|
|
245
|
-
}
|
|
254
|
+
writePromises.push(options.writeFile
|
|
255
|
+
? Promise.resolve(options.writeFile(targetPath, createOpenApiFrontmatterContent(input)))
|
|
256
|
+
: createOpenApiFrontmatter({ filename: targetPath, ...input }));
|
|
246
257
|
}
|
|
247
258
|
});
|
|
248
259
|
return generatedSlugs;
|