@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.
@@ -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(1);
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 { checkPort, checkNodeVersion, autoUpgradeIfNeeded, getVersions, suppressConsoleWarnings, terminate, readLocalOpenApiFile, } from './helpers.js';
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>', 'check if an openapi spec is valid', (yargs) => yargs
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
- try {
168
- if (isAllowedLocalSchemaUrl(filename, localSchema)) {
169
- yield getOpenApiDocumentFromUrl(filename);
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
+ });