@redocly/cli 1.2.1 → 1.4.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/CHANGELOG.md +23 -0
- package/lib/__mocks__/@redocly/openapi-core.d.ts +1 -0
- package/lib/__mocks__/@redocly/openapi-core.js +4 -3
- package/lib/__mocks__/utils.d.ts +2 -0
- package/lib/__mocks__/utils.js +3 -1
- package/lib/__tests__/commands/build-docs.test.js +2 -2
- package/lib/__tests__/commands/bundle.test.js +7 -7
- package/lib/__tests__/commands/join.test.js +25 -18
- package/lib/__tests__/commands/lint.test.js +15 -15
- package/lib/__tests__/commands/push-region.test.js +2 -2
- package/lib/__tests__/commands/push.test.js +30 -30
- package/lib/__tests__/fetch-with-timeout.test.js +2 -2
- package/lib/__tests__/utils.test.js +67 -41
- package/lib/__tests__/wrapper.test.js +3 -3
- package/lib/assert-node-version.js +1 -1
- package/lib/commands/build-docs/index.js +9 -9
- package/lib/commands/build-docs/types.d.ts +2 -2
- package/lib/commands/build-docs/utils.js +10 -10
- package/lib/commands/bundle.d.ts +1 -1
- package/lib/commands/bundle.js +25 -25
- package/lib/commands/join.d.ts +3 -3
- package/lib/commands/join.js +49 -48
- package/lib/commands/lint.d.ts +1 -1
- package/lib/commands/lint.js +27 -23
- package/lib/commands/login.d.ts +1 -1
- package/lib/commands/login.js +3 -3
- package/lib/commands/preview-docs/index.d.ts +1 -1
- package/lib/commands/preview-docs/index.js +7 -7
- package/lib/commands/preview-docs/preview-server/hot.js +19 -2
- package/lib/commands/preview-docs/preview-server/preview-server.js +15 -14
- package/lib/commands/preview-docs/preview-server/server.d.ts +3 -1
- package/lib/commands/preview-docs/preview-server/server.js +2 -2
- package/lib/commands/push.d.ts +2 -2
- package/lib/commands/push.js +31 -31
- package/lib/commands/split/__tests__/index.test.js +9 -9
- package/lib/commands/split/index.d.ts +2 -2
- package/lib/commands/split/index.js +41 -40
- package/lib/commands/split/types.d.ts +2 -2
- package/lib/commands/split/types.js +2 -2
- package/lib/commands/stats.d.ts +1 -1
- package/lib/commands/stats.js +9 -9
- package/lib/fetch-with-timeout.js +5 -2
- package/lib/index.js +46 -12
- package/lib/types.d.ts +6 -6
- package/lib/update-version-notifier.js +18 -18
- package/lib/utils.d.ts +6 -3
- package/lib/utils.js +69 -40
- package/lib/wrapper.js +5 -5
- package/package.json +3 -3
- package/src/__mocks__/@redocly/openapi-core.ts +1 -0
- package/src/__mocks__/utils.ts +2 -0
- package/src/__tests__/commands/join.test.ts +37 -7
- package/src/__tests__/utils.test.ts +49 -13
- package/src/commands/join.ts +10 -4
- package/src/commands/lint.ts +6 -1
- package/src/commands/preview-docs/preview-server/hot.js +19 -2
- package/src/commands/preview-docs/preview-server/preview-server.ts +6 -4
- package/src/commands/preview-docs/preview-server/server.ts +2 -2
- package/src/commands/split/__tests__/index.test.ts +14 -5
- package/src/commands/split/index.ts +25 -17
- package/src/fetch-with-timeout.ts +3 -0
- package/src/index.ts +35 -1
- package/src/utils.ts +45 -9
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
(function run() {
|
|
2
2
|
const Socket = window.SimpleWebsocket;
|
|
3
3
|
const port = window.__OPENAPI_CLI_WS_PORT;
|
|
4
|
+
const host = window.__OPENAPI_CLI_WS_HOST;
|
|
4
5
|
|
|
5
6
|
let socket;
|
|
6
7
|
|
|
7
8
|
reconnect();
|
|
8
9
|
|
|
10
|
+
function getFormattedHost() {
|
|
11
|
+
// Use localhost when bound to all interfaces
|
|
12
|
+
if (host === '::' || host === '0.0.0.0') {
|
|
13
|
+
return 'localhost';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Other IPv6 addresses must be wrapped in brackets
|
|
17
|
+
if (host.includes('::')) {
|
|
18
|
+
return `[${host}]`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Otherwise return as-is
|
|
22
|
+
return host;
|
|
23
|
+
}
|
|
24
|
+
|
|
9
25
|
function reconnect() {
|
|
10
|
-
socket = new Socket(`ws
|
|
26
|
+
socket = new Socket(`ws://${getFormattedHost()}:${port}`);
|
|
11
27
|
socket.on('connect', () => {
|
|
12
28
|
socket.send('{"type": "ping"}');
|
|
13
29
|
});
|
|
@@ -29,13 +45,14 @@
|
|
|
29
45
|
|
|
30
46
|
socket.on('close', () => {
|
|
31
47
|
socket.destroy();
|
|
32
|
-
console.log('Connection lost, trying to reconnect in 4s');
|
|
48
|
+
console.log('[hot] Connection lost, trying to reconnect in 4s');
|
|
33
49
|
setTimeout(() => {
|
|
34
50
|
reconnect();
|
|
35
51
|
}, 4000);
|
|
36
52
|
});
|
|
37
53
|
|
|
38
54
|
socket.on('error', () => {
|
|
55
|
+
console.log('[hot] Error connecting to hot reloading server');
|
|
39
56
|
socket.destroy();
|
|
40
57
|
});
|
|
41
58
|
}
|
|
@@ -12,7 +12,8 @@ function getPageHTML(
|
|
|
12
12
|
htmlTemplate: string,
|
|
13
13
|
redocOptions: object = {},
|
|
14
14
|
useRedocPro: boolean,
|
|
15
|
-
wsPort: number
|
|
15
|
+
wsPort: number,
|
|
16
|
+
host: string
|
|
16
17
|
) {
|
|
17
18
|
let templateSrc = readFileSync(htmlTemplate, 'utf-8');
|
|
18
19
|
|
|
@@ -28,6 +29,7 @@ function getPageHTML(
|
|
|
28
29
|
<script>
|
|
29
30
|
window.__REDOC_EXPORT = '${useRedocPro ? 'RedoclyReferenceDocs' : 'Redoc'}';
|
|
30
31
|
window.__OPENAPI_CLI_WS_PORT = ${wsPort};
|
|
32
|
+
window.__OPENAPI_CLI_WS_HOST = "${host}";
|
|
31
33
|
</script>
|
|
32
34
|
<script src="/simplewebsocket.min.js"></script>
|
|
33
35
|
<script src="/hot.js"></script>
|
|
@@ -67,7 +69,7 @@ export default async function startPreviewServer(
|
|
|
67
69
|
|
|
68
70
|
if (request.url?.endsWith('/') || path.extname(request.url!) === '') {
|
|
69
71
|
respondWithGzip(
|
|
70
|
-
getPageHTML(htmlTemplate || defaultTemplate, getOptions(), useRedocPro, wsPort),
|
|
72
|
+
getPageHTML(htmlTemplate || defaultTemplate, getOptions(), useRedocPro, wsPort, host),
|
|
71
73
|
request,
|
|
72
74
|
response,
|
|
73
75
|
{
|
|
@@ -143,7 +145,7 @@ export default async function startPreviewServer(
|
|
|
143
145
|
console.timeEnd(colorette.dim(`GET ${request.url}`));
|
|
144
146
|
};
|
|
145
147
|
|
|
146
|
-
const wsPort = await getPort({ portRange: [32201, 32301] });
|
|
148
|
+
const wsPort = await getPort({ port: 32201, portRange: [32201, 32301], host });
|
|
147
149
|
|
|
148
150
|
const server = startHttpServer(port, host, handler);
|
|
149
151
|
server.on('listening', () => {
|
|
@@ -152,5 +154,5 @@ export default async function startPreviewServer(
|
|
|
152
154
|
);
|
|
153
155
|
});
|
|
154
156
|
|
|
155
|
-
return startWsServer(wsPort);
|
|
157
|
+
return startWsServer(wsPort, host);
|
|
156
158
|
}
|
|
@@ -62,8 +62,8 @@ export function startHttpServer(port: number, host: string, handler: http.Reques
|
|
|
62
62
|
return http.createServer(handler).listen(port, host);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export function startWsServer(port: number) {
|
|
66
|
-
const socketServer = new SocketServer({ port, clientTracking: true });
|
|
65
|
+
export function startWsServer(port: number, host: string) {
|
|
66
|
+
const socketServer = new SocketServer({ port, host, clientTracking: true });
|
|
67
67
|
|
|
68
68
|
socketServer.on('connection', (socket: any) => {
|
|
69
69
|
socket.on('data', (data: string) => {
|
|
@@ -3,12 +3,13 @@ import * as path from 'path';
|
|
|
3
3
|
import * as openapiCore from '@redocly/openapi-core';
|
|
4
4
|
import { ComponentsFiles } from '../types';
|
|
5
5
|
import { blue, green } from 'colorette';
|
|
6
|
+
import { writeToFileByExtension } from '../../../utils';
|
|
6
7
|
|
|
7
8
|
const utils = require('../../../utils');
|
|
8
9
|
|
|
9
10
|
jest.mock('../../../utils', () => ({
|
|
10
11
|
...jest.requireActual('../../../utils'),
|
|
11
|
-
|
|
12
|
+
writeToFileByExtension: jest.fn(),
|
|
12
13
|
}));
|
|
13
14
|
|
|
14
15
|
jest.mock('@redocly/openapi-core', () => ({
|
|
@@ -65,7 +66,9 @@ describe('#split', () => {
|
|
|
65
66
|
openapiDir,
|
|
66
67
|
path.join(openapiDir, 'paths'),
|
|
67
68
|
componentsFiles,
|
|
68
|
-
'_'
|
|
69
|
+
'_',
|
|
70
|
+
undefined,
|
|
71
|
+
'yaml'
|
|
69
72
|
);
|
|
70
73
|
|
|
71
74
|
expect(openapiCore.slash).toHaveBeenCalledWith('paths/test.yaml');
|
|
@@ -82,7 +85,9 @@ describe('#split', () => {
|
|
|
82
85
|
openapiDir,
|
|
83
86
|
path.join(openapiDir, 'webhooks'),
|
|
84
87
|
componentsFiles,
|
|
85
|
-
'webhook_'
|
|
88
|
+
'webhook_',
|
|
89
|
+
undefined,
|
|
90
|
+
'yaml'
|
|
86
91
|
);
|
|
87
92
|
|
|
88
93
|
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
|
|
@@ -99,7 +104,9 @@ describe('#split', () => {
|
|
|
99
104
|
openapiDir,
|
|
100
105
|
path.join(openapiDir, 'webhooks'),
|
|
101
106
|
componentsFiles,
|
|
102
|
-
'webhook_'
|
|
107
|
+
'webhook_',
|
|
108
|
+
undefined,
|
|
109
|
+
'yaml'
|
|
103
110
|
);
|
|
104
111
|
|
|
105
112
|
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
|
|
@@ -118,7 +125,9 @@ describe('#split', () => {
|
|
|
118
125
|
openapiDir,
|
|
119
126
|
path.join(openapiDir, 'paths'),
|
|
120
127
|
componentsFiles,
|
|
121
|
-
'_'
|
|
128
|
+
'_',
|
|
129
|
+
undefined,
|
|
130
|
+
'yaml'
|
|
122
131
|
);
|
|
123
132
|
|
|
124
133
|
expect(utils.escapeLanguageName).nthCalledWith(1, 'C#');
|
|
@@ -10,10 +10,11 @@ import {
|
|
|
10
10
|
printExecutionTime,
|
|
11
11
|
pathToFilename,
|
|
12
12
|
readYaml,
|
|
13
|
-
writeYaml,
|
|
14
13
|
exitWithError,
|
|
15
14
|
escapeLanguageName,
|
|
16
15
|
langToExt,
|
|
16
|
+
writeToFileByExtension,
|
|
17
|
+
getAndValidateFileExtension,
|
|
17
18
|
} from '../../utils';
|
|
18
19
|
import { isString, isObject, isEmptyObject } from '../../js-utils';
|
|
19
20
|
import {
|
|
@@ -46,8 +47,9 @@ export async function handleSplit(argv: SplitOptions) {
|
|
|
46
47
|
const startedAt = performance.now();
|
|
47
48
|
const { api, outDir, separator } = argv;
|
|
48
49
|
validateDefinitionFileName(api!);
|
|
50
|
+
const ext = getAndValidateFileExtension(api);
|
|
49
51
|
const openapi = readYaml(api!) as Oas3Definition | Oas3_1Definition;
|
|
50
|
-
splitDefinition(openapi, outDir, separator);
|
|
52
|
+
splitDefinition(openapi, outDir, separator, ext);
|
|
51
53
|
process.stderr.write(
|
|
52
54
|
`🪓 Document: ${blue(api!)} ${green('is successfully split')}
|
|
53
55
|
and all related files are saved to the directory: ${blue(outDir)} \n`
|
|
@@ -58,18 +60,21 @@ export async function handleSplit(argv: SplitOptions) {
|
|
|
58
60
|
function splitDefinition(
|
|
59
61
|
openapi: Oas3Definition | Oas3_1Definition,
|
|
60
62
|
openapiDir: string,
|
|
61
|
-
pathSeparator: string
|
|
63
|
+
pathSeparator: string,
|
|
64
|
+
ext: string
|
|
62
65
|
) {
|
|
63
66
|
fs.mkdirSync(openapiDir, { recursive: true });
|
|
64
67
|
|
|
65
68
|
const componentsFiles: ComponentsFiles = {};
|
|
66
|
-
iterateComponents(openapi, openapiDir, componentsFiles);
|
|
69
|
+
iterateComponents(openapi, openapiDir, componentsFiles, ext);
|
|
67
70
|
iteratePathItems(
|
|
68
71
|
openapi.paths,
|
|
69
72
|
openapiDir,
|
|
70
73
|
path.join(openapiDir, 'paths'),
|
|
71
74
|
componentsFiles,
|
|
72
|
-
pathSeparator
|
|
75
|
+
pathSeparator,
|
|
76
|
+
undefined,
|
|
77
|
+
ext
|
|
73
78
|
);
|
|
74
79
|
const webhooks =
|
|
75
80
|
(openapi as Oas3_1Definition).webhooks || (openapi as Oas3Definition)['x-webhooks'];
|
|
@@ -80,11 +85,12 @@ function splitDefinition(
|
|
|
80
85
|
path.join(openapiDir, 'webhooks'),
|
|
81
86
|
componentsFiles,
|
|
82
87
|
pathSeparator,
|
|
83
|
-
'webhook_'
|
|
88
|
+
'webhook_',
|
|
89
|
+
ext
|
|
84
90
|
);
|
|
85
91
|
|
|
86
92
|
replace$Refs(openapi, openapiDir, componentsFiles);
|
|
87
|
-
|
|
93
|
+
writeToFileByExtension(openapi, path.join(openapiDir, `openapi.${ext}`));
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
function isStartsWithComponents(node: string) {
|
|
@@ -135,7 +141,7 @@ function traverseDirectoryDeepCallback(
|
|
|
135
141
|
if (isNotYaml(filename)) return;
|
|
136
142
|
const pathData = readYaml(filename);
|
|
137
143
|
replace$Refs(pathData, directory, componentsFiles);
|
|
138
|
-
|
|
144
|
+
writeToFileByExtension(pathData, filename);
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
function crawl(object: any, visitor: any) {
|
|
@@ -251,8 +257,8 @@ function extractFileNameFromPath(filename: string) {
|
|
|
251
257
|
return path.basename(filename, path.extname(filename));
|
|
252
258
|
}
|
|
253
259
|
|
|
254
|
-
function getFileNamePath(componentDirPath: string, componentName: string) {
|
|
255
|
-
return path.join(componentDirPath, componentName) +
|
|
260
|
+
function getFileNamePath(componentDirPath: string, componentName: string, ext: string) {
|
|
261
|
+
return path.join(componentDirPath, componentName) + `.${ext}`;
|
|
256
262
|
}
|
|
257
263
|
|
|
258
264
|
function gatherComponentsFiles(
|
|
@@ -278,13 +284,14 @@ function iteratePathItems(
|
|
|
278
284
|
outDir: string,
|
|
279
285
|
componentsFiles: object,
|
|
280
286
|
pathSeparator: string,
|
|
281
|
-
codeSamplesPathPrefix: string = ''
|
|
287
|
+
codeSamplesPathPrefix: string = '',
|
|
288
|
+
ext: string
|
|
282
289
|
) {
|
|
283
290
|
if (!pathItems) return;
|
|
284
291
|
fs.mkdirSync(outDir, { recursive: true });
|
|
285
292
|
|
|
286
293
|
for (const pathName of Object.keys(pathItems)) {
|
|
287
|
-
const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}
|
|
294
|
+
const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.${ext}`;
|
|
288
295
|
const pathData = pathItems[pathName] as Oas3PathItem;
|
|
289
296
|
|
|
290
297
|
if (isRef(pathData)) continue;
|
|
@@ -314,7 +321,7 @@ function iteratePathItems(
|
|
|
314
321
|
};
|
|
315
322
|
}
|
|
316
323
|
}
|
|
317
|
-
|
|
324
|
+
writeToFileByExtension(pathData, pathFile);
|
|
318
325
|
pathItems[pathName] = {
|
|
319
326
|
$ref: slash(path.relative(openapiDir, pathFile)),
|
|
320
327
|
};
|
|
@@ -326,7 +333,8 @@ function iteratePathItems(
|
|
|
326
333
|
function iterateComponents(
|
|
327
334
|
openapi: Oas3Definition | Oas3_1Definition,
|
|
328
335
|
openapiDir: string,
|
|
329
|
-
componentsFiles: ComponentsFiles
|
|
336
|
+
componentsFiles: ComponentsFiles,
|
|
337
|
+
ext: string
|
|
330
338
|
) {
|
|
331
339
|
const { components } = openapi;
|
|
332
340
|
if (components) {
|
|
@@ -340,7 +348,7 @@ function iterateComponents(
|
|
|
340
348
|
function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
|
|
341
349
|
const componentDirPath = path.join(componentsDir, componentType);
|
|
342
350
|
for (const componentName of Object.keys(components?.[componentType] || {})) {
|
|
343
|
-
const filename = getFileNamePath(componentDirPath, componentName);
|
|
351
|
+
const filename = getFileNamePath(componentDirPath, componentName, ext);
|
|
344
352
|
gatherComponentsFiles(components!, componentsFiles, componentType, componentName, filename);
|
|
345
353
|
}
|
|
346
354
|
}
|
|
@@ -350,7 +358,7 @@ function iterateComponents(
|
|
|
350
358
|
const componentDirPath = path.join(componentsDir, componentType);
|
|
351
359
|
createComponentDir(componentDirPath, componentType);
|
|
352
360
|
for (const componentName of Object.keys(components?.[componentType] || {})) {
|
|
353
|
-
const filename = getFileNamePath(componentDirPath, componentName);
|
|
361
|
+
const filename = getFileNamePath(componentDirPath, componentName, ext);
|
|
354
362
|
const componentData = components?.[componentType]?.[componentName];
|
|
355
363
|
replace$Refs(componentData, path.dirname(filename), componentsFiles);
|
|
356
364
|
implicitlyReferenceDiscriminator(
|
|
@@ -369,7 +377,7 @@ function iterateComponents(
|
|
|
369
377
|
)
|
|
370
378
|
);
|
|
371
379
|
} else {
|
|
372
|
-
|
|
380
|
+
writeToFileByExtension(componentData, filename);
|
|
373
381
|
}
|
|
374
382
|
|
|
375
383
|
if (isNotSecurityComponentType(componentType)) {
|
|
@@ -12,6 +12,9 @@ export default async (url: string, options = {}) => {
|
|
|
12
12
|
controller.abort();
|
|
13
13
|
}, TIMEOUT);
|
|
14
14
|
|
|
15
|
+
// FIXME: fix this (possibly along with this issue: https://github.com/Redocly/redocly-cli/issues/1260)
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
17
|
+
// @ts-ignore
|
|
15
18
|
const res = await nodeFetch(url, { signal: controller.signal, ...options });
|
|
16
19
|
clearTimeout(timeout);
|
|
17
20
|
return res;
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,11 @@ yargs
|
|
|
36
36
|
(yargs) =>
|
|
37
37
|
yargs.positional('api', { type: 'string' }).option({
|
|
38
38
|
config: { description: 'Path to the config file.', type: 'string' },
|
|
39
|
+
'lint-config': {
|
|
40
|
+
description: 'Severity level for config file linting.',
|
|
41
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
42
|
+
default: 'warn' as RuleSeverity,
|
|
43
|
+
},
|
|
39
44
|
format: {
|
|
40
45
|
description: 'Use a specific output format.',
|
|
41
46
|
choices: ['stylish', 'json'] as ReadonlyArray<OutputFormat>,
|
|
@@ -73,6 +78,11 @@ yargs
|
|
|
73
78
|
requiresArg: true,
|
|
74
79
|
type: 'string',
|
|
75
80
|
},
|
|
81
|
+
'lint-config': {
|
|
82
|
+
description: 'Severity level for config file linting.',
|
|
83
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
84
|
+
default: 'warn' as RuleSeverity,
|
|
85
|
+
},
|
|
76
86
|
})
|
|
77
87
|
.demandOption('api'),
|
|
78
88
|
(argv) => {
|
|
@@ -117,13 +127,17 @@ yargs
|
|
|
117
127
|
describe: 'Output file',
|
|
118
128
|
alias: 'o',
|
|
119
129
|
type: 'string',
|
|
120
|
-
default: 'openapi.yaml',
|
|
121
130
|
},
|
|
122
131
|
config: {
|
|
123
132
|
description: 'Path to the config file.',
|
|
124
133
|
requiresArg: true,
|
|
125
134
|
type: 'string',
|
|
126
135
|
},
|
|
136
|
+
'lint-config': {
|
|
137
|
+
description: 'Severity level for config file linting.',
|
|
138
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
139
|
+
default: 'warn' as RuleSeverity,
|
|
140
|
+
},
|
|
127
141
|
}),
|
|
128
142
|
(argv) => {
|
|
129
143
|
process.env.REDOCLY_CLI_COMMAND = 'join';
|
|
@@ -196,6 +210,11 @@ yargs
|
|
|
196
210
|
array: true,
|
|
197
211
|
type: 'string',
|
|
198
212
|
},
|
|
213
|
+
'lint-config': {
|
|
214
|
+
description: 'Severity level for config file linting.',
|
|
215
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
216
|
+
default: 'warn' as RuleSeverity,
|
|
217
|
+
},
|
|
199
218
|
})
|
|
200
219
|
.deprecateOption('batch-id', 'use --job-id')
|
|
201
220
|
.deprecateOption('maybeDestination')
|
|
@@ -342,6 +361,11 @@ yargs
|
|
|
342
361
|
type: 'boolean',
|
|
343
362
|
alias: 'k',
|
|
344
363
|
},
|
|
364
|
+
'lint-config': {
|
|
365
|
+
description: 'Severity level for config file linting.',
|
|
366
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
367
|
+
default: 'warn' as RuleSeverity,
|
|
368
|
+
},
|
|
345
369
|
}),
|
|
346
370
|
(argv) => {
|
|
347
371
|
process.env.REDOCLY_CLI_COMMAND = 'bundle';
|
|
@@ -426,6 +450,11 @@ yargs
|
|
|
426
450
|
description: 'Path to the config file.',
|
|
427
451
|
type: 'string',
|
|
428
452
|
},
|
|
453
|
+
'lint-config': {
|
|
454
|
+
description: 'Severity level for config file linting.',
|
|
455
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
456
|
+
default: 'warn' as RuleSeverity,
|
|
457
|
+
},
|
|
429
458
|
}),
|
|
430
459
|
(argv) => {
|
|
431
460
|
process.env.REDOCLY_CLI_COMMAND = 'preview-docs';
|
|
@@ -471,6 +500,11 @@ yargs
|
|
|
471
500
|
describe: 'Path to the config file.',
|
|
472
501
|
type: 'string',
|
|
473
502
|
},
|
|
503
|
+
'lint-config': {
|
|
504
|
+
description: 'Severity level for config file linting.',
|
|
505
|
+
choices: ['warn', 'error', 'off'] as ReadonlyArray<RuleSeverity>,
|
|
506
|
+
default: 'warn' as RuleSeverity,
|
|
507
|
+
},
|
|
474
508
|
})
|
|
475
509
|
.check((argv: any) => {
|
|
476
510
|
if (argv.theme && !argv.theme?.openapi)
|
package/src/utils.ts
CHANGED
|
@@ -24,7 +24,15 @@ import {
|
|
|
24
24
|
Oas2Definition,
|
|
25
25
|
RedoclyClient,
|
|
26
26
|
} from '@redocly/openapi-core';
|
|
27
|
-
import {
|
|
27
|
+
import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
|
|
28
|
+
import {
|
|
29
|
+
Totals,
|
|
30
|
+
outputExtensions,
|
|
31
|
+
Entrypoint,
|
|
32
|
+
ConfigApis,
|
|
33
|
+
CommandOptions,
|
|
34
|
+
OutputExtensions,
|
|
35
|
+
} from './types';
|
|
28
36
|
import { isEmptyObject } from '@redocly/openapi-core/lib/utils';
|
|
29
37
|
import { Arguments } from 'yargs';
|
|
30
38
|
import { version } from './update-version-notifier';
|
|
@@ -208,6 +216,17 @@ export function readYaml(filename: string) {
|
|
|
208
216
|
return parseYaml(fs.readFileSync(filename, 'utf-8'), { filename });
|
|
209
217
|
}
|
|
210
218
|
|
|
219
|
+
export function writeToFileByExtension(data: unknown, filePath: string, noRefs?: boolean) {
|
|
220
|
+
const ext = getAndValidateFileExtension(filePath);
|
|
221
|
+
|
|
222
|
+
if (ext === 'json') {
|
|
223
|
+
writeJson(data, filePath);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
writeYaml(data, filePath, noRefs);
|
|
228
|
+
}
|
|
229
|
+
|
|
211
230
|
export function writeYaml(data: any, filename: string, noRefs = false) {
|
|
212
231
|
const content = stringifyYaml(data, { noRefs });
|
|
213
232
|
|
|
@@ -219,6 +238,27 @@ export function writeYaml(data: any, filename: string, noRefs = false) {
|
|
|
219
238
|
fs.writeFileSync(filename, content);
|
|
220
239
|
}
|
|
221
240
|
|
|
241
|
+
export function writeJson(data: unknown, filename: string) {
|
|
242
|
+
const content = JSON.stringify(data, null, 2);
|
|
243
|
+
|
|
244
|
+
if (process.env.NODE_ENV === 'test') {
|
|
245
|
+
process.stderr.write(content);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
fs.mkdirSync(dirname(filename), { recursive: true });
|
|
249
|
+
fs.writeFileSync(filename, content);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function getAndValidateFileExtension(fileName: string): NonNullable<OutputExtensions> {
|
|
253
|
+
const ext = fileName.split('.').pop();
|
|
254
|
+
|
|
255
|
+
if (['yaml', 'yml', 'json'].includes(ext!)) {
|
|
256
|
+
return ext as NonNullable<OutputExtensions>;
|
|
257
|
+
}
|
|
258
|
+
process.stderr.write(yellow(`Unsupported file extension: ${ext}. Using yaml.\n`));
|
|
259
|
+
return 'yaml';
|
|
260
|
+
}
|
|
261
|
+
|
|
222
262
|
export function pluralize(label: string, num: number) {
|
|
223
263
|
if (label.endsWith('is')) {
|
|
224
264
|
[label] = label.split(' ');
|
|
@@ -244,6 +284,8 @@ export function handleError(e: Error, ref: string) {
|
|
|
244
284
|
}
|
|
245
285
|
case SyntaxError:
|
|
246
286
|
return exitWithError(`Syntax error: ${e.message} ${e.stack?.split('\n\n')?.[0]}`);
|
|
287
|
+
case ConfigValidationError:
|
|
288
|
+
return exitWithError(e.message);
|
|
247
289
|
default: {
|
|
248
290
|
exitWithError(`Something went wrong when processing ${ref}:\n\n - ${e.message}.`);
|
|
249
291
|
}
|
|
@@ -294,17 +336,11 @@ export function printLintTotals(totals: Totals, definitionsCount: number) {
|
|
|
294
336
|
export function printConfigLintTotals(totals: Totals): void {
|
|
295
337
|
if (totals.errors > 0) {
|
|
296
338
|
process.stderr.write(
|
|
297
|
-
red(
|
|
298
|
-
`❌ Your config has ${totals.errors} ${pluralize('error', totals.errors)}${
|
|
299
|
-
totals.warnings > 0
|
|
300
|
-
? ` and ${totals.warnings} ${pluralize('warning', totals.warnings)}`
|
|
301
|
-
: ''
|
|
302
|
-
}.\n`
|
|
303
|
-
)
|
|
339
|
+
red(`❌ Your config has ${totals.errors} ${pluralize('error', totals.errors)}.`)
|
|
304
340
|
);
|
|
305
341
|
} else if (totals.warnings > 0) {
|
|
306
342
|
process.stderr.write(
|
|
307
|
-
yellow(
|
|
343
|
+
yellow(`⚠️ Your config has ${totals.warnings} ${pluralize('warning', totals.warnings)}.\n`)
|
|
308
344
|
);
|
|
309
345
|
}
|
|
310
346
|
}
|