@mintlify/cli 4.0.1077 → 4.0.1079
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__/openApiCheck.test.ts +48 -8
- package/bin/cli.js +21 -31
- package/bin/openApiCheck.js +54 -0
- package/bin/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/cli.tsx +27 -39
- package/src/openApiCheck.tsx +51 -0
|
@@ -3,6 +3,7 @@ import * as previewing from '@mintlify/previewing';
|
|
|
3
3
|
import { mockProcessExit } from 'vitest-mock-process';
|
|
4
4
|
|
|
5
5
|
import { readLocalOpenApiFile } from '../src/helpers.js';
|
|
6
|
+
import { getOpenApiFilenamesFromDocsConfig } from '../src/openApiCheck.js';
|
|
6
7
|
import { mockValidOpenApiDocument, runCommand } from './utils.js';
|
|
7
8
|
|
|
8
9
|
vi.mock('@mintlify/common', () => ({
|
|
@@ -11,8 +12,8 @@ vi.mock('@mintlify/common', () => ({
|
|
|
11
12
|
validate: vi.fn(),
|
|
12
13
|
}));
|
|
13
14
|
|
|
14
|
-
vi.mock('../src/helpers.js', async () => {
|
|
15
|
-
const originalModule = await import('../src/helpers.js');
|
|
15
|
+
vi.mock('../src/helpers.js', async (importOriginal) => {
|
|
16
|
+
const originalModule = await importOriginal<typeof import('../src/helpers.js')>();
|
|
16
17
|
return {
|
|
17
18
|
...originalModule,
|
|
18
19
|
readLocalOpenApiFile: vi.fn(),
|
|
@@ -22,6 +23,34 @@ vi.mock('../src/helpers.js', async () => {
|
|
|
22
23
|
const addLogSpy = vi.spyOn(previewing, 'addLog');
|
|
23
24
|
const processExitMock = mockProcessExit();
|
|
24
25
|
|
|
26
|
+
describe('getOpenApiFilenamesFromDocsConfig', () => {
|
|
27
|
+
it('reads api.openapi string', () => {
|
|
28
|
+
expect(
|
|
29
|
+
getOpenApiFilenamesFromDocsConfig({
|
|
30
|
+
api: { openapi: 'https://example.com/openapi.yaml' },
|
|
31
|
+
})
|
|
32
|
+
).toEqual(['https://example.com/openapi.yaml']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('reads api.openapi array and object source form', () => {
|
|
36
|
+
expect(
|
|
37
|
+
getOpenApiFilenamesFromDocsConfig({
|
|
38
|
+
api: { openapi: ['https://a.com/o.yaml', 'https://b.com/o.yaml'] },
|
|
39
|
+
})
|
|
40
|
+
).toEqual(['https://a.com/o.yaml', 'https://b.com/o.yaml']);
|
|
41
|
+
expect(
|
|
42
|
+
getOpenApiFilenamesFromDocsConfig({
|
|
43
|
+
api: { openapi: { source: 'https://c.com/o.yaml' } },
|
|
44
|
+
})
|
|
45
|
+
).toEqual(['https://c.com/o.yaml']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns empty when api or openapi is missing', () => {
|
|
49
|
+
expect(getOpenApiFilenamesFromDocsConfig({})).toEqual([]);
|
|
50
|
+
expect(getOpenApiFilenamesFromDocsConfig({ api: {} })).toEqual([]);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
25
54
|
describe('openApiCheck', () => {
|
|
26
55
|
beforeEach(() => {
|
|
27
56
|
vi.clearAllMocks();
|
|
@@ -31,12 +60,22 @@ describe('openApiCheck', () => {
|
|
|
31
60
|
vi.clearAllMocks();
|
|
32
61
|
});
|
|
33
62
|
|
|
63
|
+
const expectDeprecationWarning = () => {
|
|
64
|
+
expect(addLogSpy).toHaveBeenNthCalledWith(
|
|
65
|
+
1,
|
|
66
|
+
expect.objectContaining({
|
|
67
|
+
props: { message: 'openapi-check is deprecated, use `mintlify validate` instead' },
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
34
72
|
it('valid openApi file from url', async () => {
|
|
35
73
|
vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(true);
|
|
36
74
|
vi.mocked(getOpenApiDocumentFromUrl).mockResolvedValueOnce(mockValidOpenApiDocument);
|
|
37
75
|
|
|
38
76
|
await runCommand('openapi-check', 'https://petstore3.swagger.io/api/v3/openapi.json');
|
|
39
77
|
|
|
78
|
+
expectDeprecationWarning();
|
|
40
79
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
41
80
|
expect.objectContaining({
|
|
42
81
|
props: { message: 'OpenAPI definition is valid.' },
|
|
@@ -53,6 +92,7 @@ describe('openApiCheck', () => {
|
|
|
53
92
|
|
|
54
93
|
await runCommand('openapi-check', 'https://petstore3.swagger.io/api/v3/openapi.json');
|
|
55
94
|
|
|
95
|
+
expectDeprecationWarning();
|
|
56
96
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
57
97
|
expect.objectContaining({
|
|
58
98
|
props: { message: 'Could not parse OpenAPI document.' },
|
|
@@ -67,6 +107,7 @@ describe('openApiCheck', () => {
|
|
|
67
107
|
|
|
68
108
|
await runCommand('openapi-check', 'http://localhost:3000/openapi.json');
|
|
69
109
|
|
|
110
|
+
expectDeprecationWarning();
|
|
70
111
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
71
112
|
expect.objectContaining({
|
|
72
113
|
props: { message: 'OpenAPI definition is valid.' },
|
|
@@ -77,19 +118,16 @@ describe('openApiCheck', () => {
|
|
|
77
118
|
|
|
78
119
|
it('invalid openApi file from localhost', async () => {
|
|
79
120
|
vi.mocked(isAllowedLocalSchemaUrl).mockReturnValueOnce(false);
|
|
80
|
-
vi.mocked(readLocalOpenApiFile).mockResolvedValueOnce(undefined);
|
|
81
121
|
|
|
82
122
|
await runCommand('openapi-check', 'http://localhost:3000/openapi.json');
|
|
83
123
|
|
|
124
|
+
expectDeprecationWarning();
|
|
84
125
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
85
126
|
expect.objectContaining({
|
|
86
|
-
props: {
|
|
87
|
-
message:
|
|
88
|
-
'failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.',
|
|
89
|
-
},
|
|
127
|
+
props: { message: 'include the --local-schema flag to check locally hosted OpenAPI files' },
|
|
90
128
|
})
|
|
91
129
|
);
|
|
92
|
-
expect(processExitMock).toHaveBeenCalledWith(
|
|
130
|
+
expect(processExitMock).toHaveBeenCalledWith(0);
|
|
93
131
|
});
|
|
94
132
|
|
|
95
133
|
it('valid openApi file from local file', async () => {
|
|
@@ -102,6 +140,7 @@ describe('openApiCheck', () => {
|
|
|
102
140
|
|
|
103
141
|
await runCommand('openapi-check', 'test/openapi.yaml');
|
|
104
142
|
|
|
143
|
+
expectDeprecationWarning();
|
|
105
144
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
106
145
|
expect.objectContaining({
|
|
107
146
|
props: { message: 'OpenAPI definition is valid.' },
|
|
@@ -117,6 +156,7 @@ describe('openApiCheck', () => {
|
|
|
117
156
|
|
|
118
157
|
await runCommand('openapi-check', 'test/openapi.yaml');
|
|
119
158
|
|
|
159
|
+
expectDeprecationWarning();
|
|
120
160
|
expect(addLogSpy).toHaveBeenCalledWith(
|
|
121
161
|
expect.objectContaining({
|
|
122
162
|
props: { message: 'some schema parsing error' },
|
package/bin/cli.js
CHANGED
|
@@ -8,22 +8,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
-
import { validate, getOpenApiDocumentFromUrl, isAllowedLocalSchemaUrl } from '@mintlify/common';
|
|
12
11
|
import { buildGraph, getBrokenExternalLinks, renameFilesAndUpdateLinksInContent, } from '@mintlify/link-rot';
|
|
13
12
|
import { addLog, dev, exportSite, validateBuild, ErrorLog, SpinnerLog, SuccessLog, Logs, clearLogs, BrokenLinksLog, WarningLog, } from '@mintlify/previewing';
|
|
14
13
|
import { checkUrl } from '@mintlify/scraping';
|
|
15
14
|
import { render, Text } from 'ink';
|
|
15
|
+
import fs from 'node:fs/promises';
|
|
16
16
|
import path from 'path';
|
|
17
17
|
import yargs from 'yargs';
|
|
18
18
|
import { hideBin } from 'yargs/helpers';
|
|
19
19
|
import { accessibilityCheck } from './accessibilityCheck.js';
|
|
20
20
|
import { setTelemetryEnabled } from './config.js';
|
|
21
|
-
import {
|
|
21
|
+
import { CMD_EXEC_PATH } from './constants.js';
|
|
22
|
+
import { checkPort, checkNodeVersion, autoUpgradeIfNeeded, getVersions, suppressConsoleWarnings, terminate, } from './helpers.js';
|
|
22
23
|
import { init } from './init.js';
|
|
23
24
|
import { login } from './login.js';
|
|
24
25
|
import { logout } from './logout.js';
|
|
25
26
|
import { mdxLinter } from './mdxLinter.js';
|
|
26
27
|
import { migrateMdx } from './migrateMdx.js';
|
|
28
|
+
import { checkOpenApiFile, getOpenApiFilenamesFromDocsConfig } from './openApiCheck.js';
|
|
27
29
|
import { scrapeSite, scrapePage, scrapeOpenApi } from './scrape.js';
|
|
28
30
|
import { status } from './status.js';
|
|
29
31
|
import { createTelemetryMiddleware } from './telemetry/middleware.js';
|
|
@@ -122,6 +124,19 @@ export const cli = ({ packageName = 'mint' }) => {
|
|
|
122
124
|
})
|
|
123
125
|
.usage('usage: mintlify validate [options]')
|
|
124
126
|
.example('mintlify validate', 'validate the build'), (argv) => __awaiter(void 0, void 0, void 0, function* () {
|
|
127
|
+
if (!argv['disable-openapi']) {
|
|
128
|
+
let openApiFilenames = [];
|
|
129
|
+
try {
|
|
130
|
+
const docsJson = yield fs.readFile(path.join(CMD_EXEC_PATH, 'docs.json'), 'utf-8');
|
|
131
|
+
openApiFilenames = getOpenApiFilenamesFromDocsConfig(JSON.parse(docsJson));
|
|
132
|
+
}
|
|
133
|
+
catch (_a) { }
|
|
134
|
+
const results = yield Promise.all(openApiFilenames.map((file) => checkOpenApiFile(file, argv['local-schema'])));
|
|
135
|
+
if (results.some((valid) => !valid)) {
|
|
136
|
+
yield terminate(1);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
125
140
|
const { cli: cliVersion } = getVersions();
|
|
126
141
|
yield validateBuild(Object.assign(Object.assign({}, argv), { packageName,
|
|
127
142
|
cliVersion }));
|
|
@@ -153,7 +168,7 @@ export const cli = ({ packageName = 'mint' }) => {
|
|
|
153
168
|
cliVersion }));
|
|
154
169
|
yield terminate(0);
|
|
155
170
|
}))
|
|
156
|
-
.command('openapi-check <filename>',
|
|
171
|
+
.command('openapi-check <filename>', false, (yargs) => yargs
|
|
157
172
|
.positional('filename', {
|
|
158
173
|
describe: 'the filename of the OpenAPI spec (e.g. ./openapi.yaml) or the URL to the OpenAPI spec (e.g. https://petstore3.swagger.io/api/v3/openapi.json)',
|
|
159
174
|
type: 'string',
|
|
@@ -164,34 +179,9 @@ export const cli = ({ packageName = 'mint' }) => {
|
|
|
164
179
|
default: false,
|
|
165
180
|
description: 'use a locally hosted schema file (note: only https protocol is supported in production)',
|
|
166
181
|
}), (_a) => __awaiter(void 0, [_a], void 0, function* ({ filename, 'local-schema': localSchema }) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
|
|
171
|
-
yield terminate(0);
|
|
172
|
-
}
|
|
173
|
-
if (filename.startsWith('http://') && !localSchema) {
|
|
174
|
-
addLog(_jsx(WarningLog, { message: "include the --local-schema flag to check locally hosted OpenAPI files" }));
|
|
175
|
-
addLog(_jsx(WarningLog, { message: "only https protocol is supported in production" }));
|
|
176
|
-
yield terminate(0);
|
|
177
|
-
}
|
|
178
|
-
const document = yield readLocalOpenApiFile(filename);
|
|
179
|
-
if (!document) {
|
|
180
|
-
throw new Error('failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.');
|
|
181
|
-
}
|
|
182
|
-
yield validate(document);
|
|
183
|
-
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
|
|
184
|
-
}
|
|
185
|
-
catch (err) {
|
|
186
|
-
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
|
|
187
|
-
addLog(_jsx(ErrorLog, { message: `file not found, please check the path provided: ${filename}` }));
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
|
|
191
|
-
}
|
|
192
|
-
yield terminate(1);
|
|
193
|
-
}
|
|
194
|
-
yield terminate(0);
|
|
182
|
+
addLog(_jsx(WarningLog, { message: "openapi-check is deprecated, use `mintlify validate` instead" }));
|
|
183
|
+
const valid = yield checkOpenApiFile(filename, localSchema);
|
|
184
|
+
yield terminate(valid ? 0 : 1);
|
|
195
185
|
}))
|
|
196
186
|
.command('broken-links', 'check for broken links', (yargs) => yargs
|
|
197
187
|
.option('check-anchors', {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { validate, getOpenApiDocumentFromUrl, isAllowedLocalSchemaUrl } from '@mintlify/common';
|
|
12
|
+
import { addLog, ErrorLog, SuccessLog, WarningLog } from '@mintlify/previewing';
|
|
13
|
+
import { readLocalOpenApiFile } from './helpers.js';
|
|
14
|
+
export const getOpenApiFilenamesFromDocsConfig = (config) => {
|
|
15
|
+
var _a;
|
|
16
|
+
const openapi = (_a = config.api) === null || _a === void 0 ? void 0 : _a.openapi;
|
|
17
|
+
if (openapi === undefined)
|
|
18
|
+
return [];
|
|
19
|
+
if (typeof openapi === 'string')
|
|
20
|
+
return [openapi];
|
|
21
|
+
if (Array.isArray(openapi))
|
|
22
|
+
return openapi;
|
|
23
|
+
return [openapi.source];
|
|
24
|
+
};
|
|
25
|
+
export const checkOpenApiFile = (filename, localSchema) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
try {
|
|
27
|
+
if (isAllowedLocalSchemaUrl(filename, localSchema)) {
|
|
28
|
+
yield getOpenApiDocumentFromUrl(filename);
|
|
29
|
+
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
if (filename.startsWith('http://') && !localSchema) {
|
|
33
|
+
addLog(_jsx(WarningLog, { message: "include the --local-schema flag to check locally hosted OpenAPI files" }));
|
|
34
|
+
addLog(_jsx(WarningLog, { message: "only https protocol is supported in production" }));
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
const document = yield readLocalOpenApiFile(filename);
|
|
38
|
+
if (!document) {
|
|
39
|
+
throw new Error('failed to parse OpenAPI spec: could not parse file correctly, please check for any syntax errors.');
|
|
40
|
+
}
|
|
41
|
+
yield validate(document);
|
|
42
|
+
addLog(_jsx(SuccessLog, { message: "OpenAPI definition is valid." }));
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
if (err && typeof err === 'object' && 'code' in err && err.code === 'ENOENT') {
|
|
47
|
+
addLog(_jsx(ErrorLog, { message: `file not found, please check the path provided: ${filename}` }));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
addLog(_jsx(ErrorLog, { message: err instanceof Error ? err.message : 'unknown error' }));
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
});
|