@mintlify/scraping 4.0.70 → 4.0.71
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__/generateOpenApiPagesForDocsConfig.test.ts +149 -0
- package/__test__/getOpenApiDefinition.test.ts +1 -1
- package/__test__/prepareStringToBeValidFilename.test.ts +1 -1
- package/bin/index.d.ts +1 -0
- package/bin/index.js +1 -0
- package/bin/index.js.map +1 -1
- package/bin/openapi/common.d.ts +26 -0
- package/bin/openapi/common.js +112 -0
- package/bin/openapi/common.js.map +1 -0
- package/bin/openapi/generateOpenApiPages.d.ts +3 -20
- package/bin/openapi/generateOpenApiPages.js +6 -113
- package/bin/openapi/generateOpenApiPages.js.map +1 -1
- package/bin/openapi/generateOpenApiPagesForDocsConfig.d.ts +4 -0
- package/bin/openapi/generateOpenApiPagesForDocsConfig.js +42 -0
- package/bin/openapi/generateOpenApiPagesForDocsConfig.js.map +1 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/index.ts +1 -0
- package/src/openapi/common.ts +168 -0
- package/src/openapi/generateOpenApiPages.ts +27 -158
- package/src/openapi/generateOpenApiPagesForDocsConfig.ts +81 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mintlify/scraping",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.71",
|
|
4
4
|
"description": "Scrape documentation frameworks to Mintlify docs",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=18.0.0"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"format:check": "prettier . --check"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@mintlify/common": "1.0.
|
|
41
|
+
"@mintlify/common": "1.0.226",
|
|
42
42
|
"@mintlify/openapi-parser": "^0.0.7",
|
|
43
43
|
"fs-extra": "^11.1.1",
|
|
44
44
|
"hast-util-to-mdast": "^10.1.0",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"@mintlify/models": "0.0.160",
|
|
62
62
|
"@mintlify/prettier-config": "1.0.4",
|
|
63
63
|
"@mintlify/ts-config": "2.0.2",
|
|
64
|
-
"@mintlify/validation": "0.1.
|
|
64
|
+
"@mintlify/validation": "0.1.252",
|
|
65
65
|
"@trivago/prettier-plugin-sort-imports": "^4.2.1",
|
|
66
66
|
"@tsconfig/recommended": "1.x",
|
|
67
67
|
"@types/hast": "^3.0.4",
|
|
@@ -78,5 +78,5 @@
|
|
|
78
78
|
"typescript": "^5.5.3",
|
|
79
79
|
"vitest": "^2.0.4"
|
|
80
80
|
},
|
|
81
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "69fa039abf3b3c3d29de08eb967fea8f78fe8119"
|
|
82
82
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getOpenApiTitleAndDescription,
|
|
3
|
+
optionallyAddLeadingSlash,
|
|
4
|
+
slugToTitle,
|
|
5
|
+
} from '@mintlify/common';
|
|
6
|
+
import type { DecoratedNavigationPage, NavigationEntry } from '@mintlify/models';
|
|
7
|
+
import { outputFile } from 'fs-extra';
|
|
8
|
+
import fse from 'fs-extra';
|
|
9
|
+
import fs from 'fs/promises';
|
|
10
|
+
import yaml from 'js-yaml';
|
|
11
|
+
import { OpenAPI, OpenAPIV3 } from 'openapi-types';
|
|
12
|
+
import path, { join, parse, resolve } from 'path';
|
|
13
|
+
|
|
14
|
+
import { fetchOpenApi } from '../utils/network.js';
|
|
15
|
+
|
|
16
|
+
export const getOpenApiDefinition = async (
|
|
17
|
+
pathOrDocumentOrUrl: string | OpenAPI.Document | URL
|
|
18
|
+
): Promise<{ document: OpenAPI.Document; isUrl: boolean }> => {
|
|
19
|
+
if (typeof pathOrDocumentOrUrl === 'string') {
|
|
20
|
+
if (pathOrDocumentOrUrl.startsWith('http://')) {
|
|
21
|
+
// This is an invalid location either for a file or a URL
|
|
22
|
+
throw new Error('Only HTTPS URLs are supported. Please provide an HTTPS URL');
|
|
23
|
+
} else {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(pathOrDocumentOrUrl);
|
|
26
|
+
pathOrDocumentOrUrl = url;
|
|
27
|
+
} catch {
|
|
28
|
+
const pathname = path.join(process.cwd(), pathOrDocumentOrUrl.toString());
|
|
29
|
+
const file = await fs.readFile(pathname, 'utf-8');
|
|
30
|
+
pathOrDocumentOrUrl = yaml.load(file) as OpenAPI.Document;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const isUrl = pathOrDocumentOrUrl instanceof URL;
|
|
35
|
+
if (pathOrDocumentOrUrl instanceof URL) {
|
|
36
|
+
if (pathOrDocumentOrUrl.protocol !== 'https:') {
|
|
37
|
+
throw new Error('Only HTTPS URLs are supported. Please provide an HTTPS URL');
|
|
38
|
+
}
|
|
39
|
+
pathOrDocumentOrUrl = await fetchOpenApi(pathOrDocumentOrUrl);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { document: pathOrDocumentOrUrl, isUrl };
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// returns a filename that is unique within the given array of pages
|
|
46
|
+
export const generateUniqueFilenameWithoutExtension = (
|
|
47
|
+
pages: NavigationEntry[],
|
|
48
|
+
base: string
|
|
49
|
+
): string => {
|
|
50
|
+
let filename = base;
|
|
51
|
+
if (pages.includes(filename)) {
|
|
52
|
+
let extension = 1;
|
|
53
|
+
filename = `${base}-${extension}`;
|
|
54
|
+
while (pages.includes(filename)) {
|
|
55
|
+
extension += 1;
|
|
56
|
+
filename = `${base}-${extension}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return filename;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const createOpenApiFrontmatter = async (
|
|
63
|
+
filename: string,
|
|
64
|
+
openApiMetaTag: string,
|
|
65
|
+
version?: string
|
|
66
|
+
) => {
|
|
67
|
+
const data = `---
|
|
68
|
+
openapi: ${openApiMetaTag}${version ? `\nversion: ${version}` : ''}
|
|
69
|
+
---`;
|
|
70
|
+
|
|
71
|
+
await outputFile(filename, data);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const prepareStringToBeValidFilename = (str?: string) =>
|
|
75
|
+
str
|
|
76
|
+
? str
|
|
77
|
+
.replaceAll(' ', '-')
|
|
78
|
+
.replace(/\{.*?\}/g, '-') // remove path parameters
|
|
79
|
+
.replace(/^-/, '')
|
|
80
|
+
.replace(/-$/, '')
|
|
81
|
+
.replace(/[{}(),.'\n\/]/g, '') // remove special characters
|
|
82
|
+
.replaceAll(/--/g, '-') // replace double hyphens
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
: undefined;
|
|
85
|
+
|
|
86
|
+
export type GenerateOpenApiPagesOptions = {
|
|
87
|
+
openApiFilePath?: string;
|
|
88
|
+
version?: string;
|
|
89
|
+
writeFiles?: boolean;
|
|
90
|
+
outDir?: string;
|
|
91
|
+
outDirBasePath?: string;
|
|
92
|
+
overwrite?: boolean;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export type OpenApiPageGenerationResult<N, DN> = {
|
|
96
|
+
nav: N;
|
|
97
|
+
decoratedNav: DN;
|
|
98
|
+
spec: OpenAPI.Document;
|
|
99
|
+
pagesAcc: Record<string, DecoratedNavigationPage>;
|
|
100
|
+
isUrl: boolean;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function processOpenApiPath<N, DN>(
|
|
104
|
+
path: string,
|
|
105
|
+
pathItemObject: OpenAPIV3.PathItemObject,
|
|
106
|
+
schema: OpenAPI.Document,
|
|
107
|
+
nav: N,
|
|
108
|
+
decoratedNav: DN,
|
|
109
|
+
writePromises: Promise<void>[],
|
|
110
|
+
pagesAcc: Record<string, DecoratedNavigationPage>,
|
|
111
|
+
options: GenerateOpenApiPagesOptions,
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
findNavGroup: (nav: any, groupName?: string) => any
|
|
114
|
+
) {
|
|
115
|
+
const openApiFilePathFromRoot = options.openApiFilePath
|
|
116
|
+
? optionallyAddLeadingSlash(options.openApiFilePath)
|
|
117
|
+
: undefined;
|
|
118
|
+
|
|
119
|
+
Object.values(OpenAPIV3.HttpMethods).forEach((method) => {
|
|
120
|
+
if (method in pathItemObject) {
|
|
121
|
+
const operation = pathItemObject[method];
|
|
122
|
+
const groupName = operation?.tags?.[0];
|
|
123
|
+
const title =
|
|
124
|
+
prepareStringToBeValidFilename(operation?.summary) ??
|
|
125
|
+
`${method}-${prepareStringToBeValidFilename(path)}`;
|
|
126
|
+
const folder = prepareStringToBeValidFilename(groupName) ?? '';
|
|
127
|
+
const base = join(options.outDir ?? '', folder, title);
|
|
128
|
+
|
|
129
|
+
const navGroup = findNavGroup(nav, groupName);
|
|
130
|
+
const decoratedNavGroup = findNavGroup(decoratedNav, groupName);
|
|
131
|
+
|
|
132
|
+
const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(navGroup, base);
|
|
133
|
+
const openapiMetaTag = `${
|
|
134
|
+
openApiFilePathFromRoot ? `${openApiFilePathFromRoot} ` : ''
|
|
135
|
+
}${method} ${path}`;
|
|
136
|
+
const { title: titleTag, description } = getOpenApiTitleAndDescription(
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
filename: options.openApiFilePath
|
|
140
|
+
? parse(options.openApiFilePath).name
|
|
141
|
+
: 'filler-filename',
|
|
142
|
+
spec: schema,
|
|
143
|
+
originalFileLocation: options.openApiFilePath,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
openapiMetaTag
|
|
147
|
+
);
|
|
148
|
+
navGroup.push(filenameWithoutExtension);
|
|
149
|
+
const page: DecoratedNavigationPage = {
|
|
150
|
+
openapi: openapiMetaTag,
|
|
151
|
+
href: resolve('/', filenameWithoutExtension),
|
|
152
|
+
title: titleTag ?? slugToTitle(filenameWithoutExtension),
|
|
153
|
+
description,
|
|
154
|
+
version: options.version,
|
|
155
|
+
};
|
|
156
|
+
decoratedNavGroup.push(page);
|
|
157
|
+
pagesAcc[filenameWithoutExtension] = page;
|
|
158
|
+
const targetPath = options.outDirBasePath
|
|
159
|
+
? join(options.outDirBasePath, `${filenameWithoutExtension}.mdx`)
|
|
160
|
+
: `${filenameWithoutExtension}.mdx`;
|
|
161
|
+
if (options.writeFiles && (!fse.pathExistsSync(targetPath) || options.overwrite)) {
|
|
162
|
+
writePromises.push(createOpenApiFrontmatter(targetPath, openapiMetaTag, options.version));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const DEFAULT_API_GROUP_NAME = 'API Reference';
|
|
@@ -1,80 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getOpenApiTitleAndDescription,
|
|
3
|
-
optionallyAddLeadingSlash,
|
|
4
|
-
slugToTitle,
|
|
5
|
-
validate,
|
|
6
|
-
} from '@mintlify/common';
|
|
1
|
+
import { validate } from '@mintlify/common';
|
|
7
2
|
import type {
|
|
8
3
|
DecoratedNavigation,
|
|
9
4
|
DecoratedNavigationGroup,
|
|
10
5
|
Navigation,
|
|
11
|
-
NavigationEntry,
|
|
12
6
|
NavigationGroup,
|
|
13
7
|
DecoratedNavigationPage,
|
|
14
8
|
} from '@mintlify/models';
|
|
15
|
-
import
|
|
16
|
-
import fs from 'fs/promises';
|
|
17
|
-
import yaml from 'js-yaml';
|
|
18
|
-
import { OpenAPI, OpenAPIV3 } from 'openapi-types';
|
|
19
|
-
import { join, resolve, parse } from 'path';
|
|
20
|
-
import path from 'path';
|
|
21
|
-
|
|
22
|
-
import { fetchOpenApi } from '../utils/network.js';
|
|
23
|
-
|
|
24
|
-
export const getOpenApiDefinition = async (
|
|
25
|
-
pathOrDocumentOrUrl: string | OpenAPI.Document | URL
|
|
26
|
-
): Promise<{ document: OpenAPI.Document; isUrl: boolean }> => {
|
|
27
|
-
if (typeof pathOrDocumentOrUrl === 'string') {
|
|
28
|
-
if (pathOrDocumentOrUrl.startsWith('http://')) {
|
|
29
|
-
// This is an invalid location either for a file or a URL
|
|
30
|
-
throw new Error('Only HTTPS URLs are supported. Please provide an HTTPS URL');
|
|
31
|
-
} else {
|
|
32
|
-
try {
|
|
33
|
-
const url = new URL(pathOrDocumentOrUrl);
|
|
34
|
-
pathOrDocumentOrUrl = url;
|
|
35
|
-
} catch {
|
|
36
|
-
const pathname = path.join(process.cwd(), pathOrDocumentOrUrl.toString());
|
|
37
|
-
const file = await fs.readFile(pathname, 'utf-8');
|
|
38
|
-
pathOrDocumentOrUrl = yaml.load(file) as OpenAPI.Document;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const isUrl = pathOrDocumentOrUrl instanceof URL;
|
|
44
|
-
if (pathOrDocumentOrUrl instanceof URL) {
|
|
45
|
-
if (pathOrDocumentOrUrl.protocol !== 'https:') {
|
|
46
|
-
throw new Error('Only HTTPS URLs are supported. Please provide an HTTPS URL');
|
|
47
|
-
}
|
|
48
|
-
pathOrDocumentOrUrl = await fetchOpenApi(pathOrDocumentOrUrl);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
return { document: pathOrDocumentOrUrl as OpenAPI.Document, isUrl };
|
|
52
|
-
};
|
|
9
|
+
import { OpenAPI } from 'openapi-types';
|
|
53
10
|
|
|
54
|
-
|
|
11
|
+
import {
|
|
12
|
+
getOpenApiDefinition,
|
|
13
|
+
GenerateOpenApiPagesOptions,
|
|
14
|
+
OpenApiPageGenerationResult,
|
|
15
|
+
processOpenApiPath,
|
|
16
|
+
DEFAULT_API_GROUP_NAME,
|
|
17
|
+
} from './common.js';
|
|
18
|
+
|
|
19
|
+
export async function generateOpenApiPages(
|
|
55
20
|
pathOrDocumentOrUrl: string | OpenAPI.Document | URL,
|
|
56
|
-
opts?:
|
|
57
|
-
|
|
58
|
-
version?: string | undefined;
|
|
59
|
-
writeFiles?: boolean;
|
|
60
|
-
outDir?: string;
|
|
61
|
-
outDirBasePath?: string;
|
|
62
|
-
overwrite?: boolean;
|
|
63
|
-
}
|
|
64
|
-
): Promise<{
|
|
65
|
-
nav: Navigation;
|
|
66
|
-
decoratedNav: DecoratedNavigation;
|
|
67
|
-
spec: OpenAPI.Document;
|
|
68
|
-
pagesAcc: Record<string, DecoratedNavigationPage>;
|
|
69
|
-
isUrl: boolean;
|
|
70
|
-
}> => {
|
|
71
|
-
const { openApiFilePath, version, writeFiles, outDir, outDirBasePath } = opts ?? {};
|
|
72
|
-
const openApiFilePathFromRoot = openApiFilePath
|
|
73
|
-
? optionallyAddLeadingSlash(openApiFilePath)
|
|
74
|
-
: undefined;
|
|
75
|
-
|
|
21
|
+
opts?: GenerateOpenApiPagesOptions
|
|
22
|
+
): Promise<OpenApiPageGenerationResult<Navigation, DecoratedNavigation>> {
|
|
76
23
|
const { document, isUrl } = await getOpenApiDefinition(pathOrDocumentOrUrl);
|
|
77
|
-
|
|
78
24
|
const { schema } = await validate(document);
|
|
79
25
|
|
|
80
26
|
if (schema?.paths === undefined || Object.keys(schema.paths).length === 0) {
|
|
@@ -85,56 +31,22 @@ export const generateOpenApiPages = async (
|
|
|
85
31
|
const decoratedNav: DecoratedNavigation = [];
|
|
86
32
|
const writePromises: Promise<void>[] = [];
|
|
87
33
|
const pagesAcc: Record<string, DecoratedNavigationPage> = {};
|
|
34
|
+
|
|
88
35
|
Object.entries(schema.paths).forEach(([path, pathItemObject]) => {
|
|
89
36
|
if (!pathItemObject || typeof pathItemObject !== 'object') {
|
|
90
37
|
return;
|
|
91
38
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const navGroup = findNavGroup<NavigationGroup>(nav, groupName);
|
|
104
|
-
const decoratedNavGroup = findNavGroup<DecoratedNavigationGroup>(decoratedNav, groupName);
|
|
105
|
-
|
|
106
|
-
const filenameWithoutExtension = generateUniqueFilenameWithoutExtension(navGroup, base);
|
|
107
|
-
const openapiMetaTag = `${
|
|
108
|
-
openApiFilePathFromRoot ? `${openApiFilePathFromRoot} ` : ''
|
|
109
|
-
}${method} ${path}`;
|
|
110
|
-
const { title: titleTag, description } = getOpenApiTitleAndDescription(
|
|
111
|
-
[
|
|
112
|
-
{
|
|
113
|
-
filename: openApiFilePath ? parse(openApiFilePath).name : 'filler-filename',
|
|
114
|
-
spec: schema as OpenAPI.Document,
|
|
115
|
-
originalFileLocation: openApiFilePath,
|
|
116
|
-
},
|
|
117
|
-
],
|
|
118
|
-
openapiMetaTag
|
|
119
|
-
);
|
|
120
|
-
navGroup.push(filenameWithoutExtension);
|
|
121
|
-
const page: DecoratedNavigationPage = {
|
|
122
|
-
openapi: openapiMetaTag,
|
|
123
|
-
href: resolve('/', filenameWithoutExtension),
|
|
124
|
-
title: titleTag ?? slugToTitle(filenameWithoutExtension),
|
|
125
|
-
description,
|
|
126
|
-
version,
|
|
127
|
-
};
|
|
128
|
-
decoratedNavGroup.push(page);
|
|
129
|
-
pagesAcc[filenameWithoutExtension] = page;
|
|
130
|
-
const targetPath = outDirBasePath
|
|
131
|
-
? join(outDirBasePath, `${filenameWithoutExtension}.mdx`)
|
|
132
|
-
: `${filenameWithoutExtension}.mdx`;
|
|
133
|
-
if (writeFiles && (!fse.pathExistsSync(targetPath) || opts?.overwrite)) {
|
|
134
|
-
writePromises.push(createOpenApiFrontmatter(targetPath, openapiMetaTag, version));
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
});
|
|
39
|
+
processOpenApiPath<Navigation, DecoratedNavigation>(
|
|
40
|
+
path,
|
|
41
|
+
pathItemObject,
|
|
42
|
+
schema as OpenAPI.Document,
|
|
43
|
+
nav,
|
|
44
|
+
decoratedNav,
|
|
45
|
+
writePromises,
|
|
46
|
+
pagesAcc,
|
|
47
|
+
opts ?? {},
|
|
48
|
+
findNavGroup
|
|
49
|
+
);
|
|
138
50
|
});
|
|
139
51
|
|
|
140
52
|
await Promise.all(writePromises);
|
|
@@ -146,16 +58,11 @@ export const generateOpenApiPages = async (
|
|
|
146
58
|
pagesAcc,
|
|
147
59
|
isUrl,
|
|
148
60
|
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// returns the group with the given group name, or the top-level group if no group name is provided
|
|
61
|
+
}
|
|
152
62
|
const findNavGroup = <T extends NavigationGroup | DecoratedNavigationGroup>(
|
|
153
63
|
nav: T['pages'][number][],
|
|
154
|
-
groupName
|
|
64
|
+
groupName: string = DEFAULT_API_GROUP_NAME
|
|
155
65
|
): T['pages'][number][] => {
|
|
156
|
-
if (groupName === undefined) {
|
|
157
|
-
groupName = 'API Reference';
|
|
158
|
-
}
|
|
159
66
|
const group = nav.find(
|
|
160
67
|
(fileOrGroup) =>
|
|
161
68
|
typeof fileOrGroup === 'object' && 'group' in fileOrGroup && fileOrGroup.group === groupName
|
|
@@ -171,41 +78,3 @@ const findNavGroup = <T extends NavigationGroup | DecoratedNavigationGroup>(
|
|
|
171
78
|
return group.pages;
|
|
172
79
|
}
|
|
173
80
|
};
|
|
174
|
-
|
|
175
|
-
// returns a filename that is unique within the given array of pages
|
|
176
|
-
const generateUniqueFilenameWithoutExtension = (pages: NavigationEntry[], base: string): string => {
|
|
177
|
-
let filename = base;
|
|
178
|
-
if (pages.includes(filename)) {
|
|
179
|
-
let extension = 1;
|
|
180
|
-
filename = `${base}-${extension}`;
|
|
181
|
-
while (pages.includes(filename)) {
|
|
182
|
-
extension += 1;
|
|
183
|
-
filename = `${base}-${extension}`;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return filename;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
const createOpenApiFrontmatter = async (
|
|
190
|
-
filename: string,
|
|
191
|
-
openApiMetaTag: string,
|
|
192
|
-
version?: string
|
|
193
|
-
) => {
|
|
194
|
-
const data = `---
|
|
195
|
-
openapi: ${openApiMetaTag}${version ? `\nversion: ${version}` : ''}
|
|
196
|
-
---`;
|
|
197
|
-
|
|
198
|
-
await outputFile(filename, data);
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
export const prepareStringToBeValidFilename = (str?: string) =>
|
|
202
|
-
str
|
|
203
|
-
? str
|
|
204
|
-
.replaceAll(' ', '-')
|
|
205
|
-
.replace(/\{.*?\}/g, '-') // remove path parameters
|
|
206
|
-
.replace(/^-/, '')
|
|
207
|
-
.replace(/-$/, '')
|
|
208
|
-
.replace(/[{}(),.'\n\/]/g, '') // remove special characters
|
|
209
|
-
.replaceAll(/--/g, '-') // replace double hyphens
|
|
210
|
-
.toLowerCase()
|
|
211
|
-
: undefined;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { validate } from '@mintlify/common';
|
|
2
|
+
import { DecoratedNavigationPage } from '@mintlify/models';
|
|
3
|
+
import {
|
|
4
|
+
DecoratedGroupsConfig,
|
|
5
|
+
GroupsConfig,
|
|
6
|
+
PagesConfig,
|
|
7
|
+
DecoratedPagesConfig,
|
|
8
|
+
} from '@mintlify/validation';
|
|
9
|
+
import { OpenAPI } from 'openapi-types';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
getOpenApiDefinition,
|
|
13
|
+
GenerateOpenApiPagesOptions,
|
|
14
|
+
OpenApiPageGenerationResult,
|
|
15
|
+
processOpenApiPath,
|
|
16
|
+
DEFAULT_API_GROUP_NAME,
|
|
17
|
+
} from './common.js';
|
|
18
|
+
|
|
19
|
+
export async function generateOpenApiPagesForDocsConfig(
|
|
20
|
+
pathOrDocumentOrUrl: string | OpenAPI.Document | URL,
|
|
21
|
+
opts?: GenerateOpenApiPagesOptions
|
|
22
|
+
): Promise<OpenApiPageGenerationResult<GroupsConfig, DecoratedGroupsConfig>> {
|
|
23
|
+
const { document, isUrl } = await getOpenApiDefinition(pathOrDocumentOrUrl);
|
|
24
|
+
const { schema } = await validate(document);
|
|
25
|
+
|
|
26
|
+
if (schema?.paths === undefined || Object.keys(schema.paths).length === 0) {
|
|
27
|
+
throw new Error('No paths defined.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const nav: GroupsConfig = [];
|
|
31
|
+
const decoratedNav: DecoratedGroupsConfig = [];
|
|
32
|
+
const writePromises: Promise<void>[] = [];
|
|
33
|
+
const pagesAcc: Record<string, DecoratedNavigationPage> = {};
|
|
34
|
+
|
|
35
|
+
Object.entries(schema.paths).forEach(([path, pathItemObject]) => {
|
|
36
|
+
if (!pathItemObject || typeof pathItemObject !== 'object') {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
processOpenApiPath<GroupsConfig, DecoratedGroupsConfig>(
|
|
40
|
+
path,
|
|
41
|
+
pathItemObject,
|
|
42
|
+
schema as OpenAPI.Document,
|
|
43
|
+
nav,
|
|
44
|
+
decoratedNav,
|
|
45
|
+
writePromises,
|
|
46
|
+
pagesAcc,
|
|
47
|
+
opts ?? {},
|
|
48
|
+
findNavGroup
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await Promise.all(writePromises);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
nav,
|
|
56
|
+
decoratedNav,
|
|
57
|
+
spec: schema as OpenAPI.Document,
|
|
58
|
+
pagesAcc,
|
|
59
|
+
isUrl,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const findNavGroup = (
|
|
64
|
+
nav: GroupsConfig | DecoratedGroupsConfig,
|
|
65
|
+
groupName: string = DEFAULT_API_GROUP_NAME
|
|
66
|
+
): PagesConfig | DecoratedPagesConfig => {
|
|
67
|
+
const group = nav.find(
|
|
68
|
+
(fileOrGroup) =>
|
|
69
|
+
typeof fileOrGroup === 'object' && 'group' in fileOrGroup && fileOrGroup.group === groupName
|
|
70
|
+
);
|
|
71
|
+
if (group === undefined || !('pages' in group)) {
|
|
72
|
+
const newGroup = {
|
|
73
|
+
group: groupName,
|
|
74
|
+
pages: [],
|
|
75
|
+
};
|
|
76
|
+
nav.push(newGroup);
|
|
77
|
+
return newGroup.pages;
|
|
78
|
+
} else {
|
|
79
|
+
return group.pages;
|
|
80
|
+
}
|
|
81
|
+
};
|