@redocly/cli 1.3.0 → 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 +12 -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 +63 -32
- 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 +1 -1
- package/lib/commands/join.js +49 -48
- package/lib/commands/lint.d.ts +1 -1
- package/lib/commands/lint.js +22 -22
- 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 +11 -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 +66 -38
- 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 +45 -1
- package/src/commands/join.ts +8 -3
- 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 +0 -1
- package/src/utils.ts +40 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { handleJoin } from '../../commands/join';
|
|
2
|
-
import { exitWithError, writeYaml } from '../../utils';
|
|
2
|
+
import { exitWithError, writeToFileByExtension, writeYaml } from '../../utils';
|
|
3
3
|
import { yellow } from 'colorette';
|
|
4
4
|
import { detectSpec } from '@redocly/openapi-core';
|
|
5
5
|
import { loadConfig } from '../../__mocks__/@redocly/openapi-core';
|
|
6
6
|
import { ConfigFixture } from '../fixtures/config';
|
|
7
7
|
|
|
8
8
|
jest.mock('../../utils');
|
|
9
|
+
|
|
9
10
|
jest.mock('colorette');
|
|
10
11
|
|
|
11
12
|
describe('handleJoin fails', () => {
|
|
@@ -80,7 +81,7 @@ describe('handleJoin fails', () => {
|
|
|
80
81
|
);
|
|
81
82
|
});
|
|
82
83
|
|
|
83
|
-
it('should call
|
|
84
|
+
it('should call writeToFileByExtension function', async () => {
|
|
84
85
|
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
|
85
86
|
await handleJoin(
|
|
86
87
|
{
|
|
@@ -90,10 +91,14 @@ describe('handleJoin fails', () => {
|
|
|
90
91
|
'cli-version'
|
|
91
92
|
);
|
|
92
93
|
|
|
93
|
-
expect(
|
|
94
|
+
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
|
95
|
+
expect.any(Object),
|
|
96
|
+
'openapi.yaml',
|
|
97
|
+
expect.any(Boolean)
|
|
98
|
+
);
|
|
94
99
|
});
|
|
95
100
|
|
|
96
|
-
it('should call
|
|
101
|
+
it('should call writeToFileByExtension function for OpenAPI 3.1', async () => {
|
|
97
102
|
(detectSpec as jest.Mock).mockReturnValue('oas3_1');
|
|
98
103
|
await handleJoin(
|
|
99
104
|
{
|
|
@@ -103,10 +108,14 @@ describe('handleJoin fails', () => {
|
|
|
103
108
|
'cli-version'
|
|
104
109
|
);
|
|
105
110
|
|
|
106
|
-
expect(
|
|
111
|
+
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
|
112
|
+
expect.any(Object),
|
|
113
|
+
'openapi.yaml',
|
|
114
|
+
expect.any(Boolean)
|
|
115
|
+
);
|
|
107
116
|
});
|
|
108
117
|
|
|
109
|
-
it('should call
|
|
118
|
+
it('should call writeToFileByExtension function with custom output file', async () => {
|
|
110
119
|
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
|
111
120
|
await handleJoin(
|
|
112
121
|
{
|
|
@@ -117,7 +126,28 @@ describe('handleJoin fails', () => {
|
|
|
117
126
|
'cli-version'
|
|
118
127
|
);
|
|
119
128
|
|
|
120
|
-
expect(
|
|
129
|
+
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
|
130
|
+
expect.any(Object),
|
|
131
|
+
'output.yml',
|
|
132
|
+
expect.any(Boolean)
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should call writeToFileByExtension function with json file extension', async () => {
|
|
137
|
+
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
|
138
|
+
await handleJoin(
|
|
139
|
+
{
|
|
140
|
+
apis: ['first.json', 'second.yaml'],
|
|
141
|
+
},
|
|
142
|
+
ConfigFixture as any,
|
|
143
|
+
'cli-version'
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
|
147
|
+
expect.any(Object),
|
|
148
|
+
'openapi.json',
|
|
149
|
+
expect.any(Boolean)
|
|
150
|
+
);
|
|
121
151
|
});
|
|
122
152
|
|
|
123
153
|
it('should call skipDecorators and skipPreprocessors', async () => {
|
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
HandledError,
|
|
13
13
|
cleanArgs,
|
|
14
14
|
cleanRawInput,
|
|
15
|
+
getAndValidateFileExtension,
|
|
16
|
+
writeYaml,
|
|
17
|
+
writeJson,
|
|
18
|
+
writeToFileByExtension,
|
|
15
19
|
} from '../utils';
|
|
16
20
|
import {
|
|
17
21
|
ResolvedApi,
|
|
@@ -19,11 +23,13 @@ import {
|
|
|
19
23
|
isAbsoluteUrl,
|
|
20
24
|
ResolveError,
|
|
21
25
|
YamlParseError,
|
|
26
|
+
stringifyYaml,
|
|
22
27
|
} from '@redocly/openapi-core';
|
|
23
28
|
import { blue, red, yellow } from 'colorette';
|
|
24
|
-
import { existsSync, statSync } from 'fs';
|
|
29
|
+
import { existsSync, statSync, writeFileSync } from 'fs';
|
|
25
30
|
import * as path from 'path';
|
|
26
31
|
import * as process from 'process';
|
|
32
|
+
import * as utils from '../utils';
|
|
27
33
|
|
|
28
34
|
jest.mock('os');
|
|
29
35
|
jest.mock('colorette');
|
|
@@ -554,4 +560,42 @@ describe('cleanRawInput', () => {
|
|
|
554
560
|
'redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response'
|
|
555
561
|
);
|
|
556
562
|
});
|
|
563
|
+
|
|
564
|
+
describe('validateFileExtension', () => {
|
|
565
|
+
it('should return current file extension', () => {
|
|
566
|
+
expect(getAndValidateFileExtension('test.json')).toEqual('json');
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
it('should return yaml and print warning if file extension does not supported', () => {
|
|
570
|
+
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
571
|
+
(yellow as jest.Mock<any, any>).mockImplementation((text: string) => text);
|
|
572
|
+
|
|
573
|
+
expect(getAndValidateFileExtension('test.xml')).toEqual('yaml');
|
|
574
|
+
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
575
|
+
});
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
describe('writeToFileByExtension', () => {
|
|
579
|
+
beforeEach(() => {
|
|
580
|
+
jest.spyOn(process.stderr, 'write').mockImplementation(jest.fn());
|
|
581
|
+
(yellow as jest.Mock<any, any>).mockImplementation((text: string) => text);
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
afterEach(() => {
|
|
585
|
+
jest.restoreAllMocks();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('should call stringifyYaml function', () => {
|
|
589
|
+
writeToFileByExtension('test data', 'test.yaml');
|
|
590
|
+
expect(stringifyYaml).toHaveBeenCalledWith('test data', { noRefs: false });
|
|
591
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should call JSON.stringify function', () => {
|
|
595
|
+
const stringifySpy = jest.spyOn(JSON, 'stringify').mockImplementation((data) => data);
|
|
596
|
+
writeToFileByExtension('test data', 'test.json');
|
|
597
|
+
expect(stringifySpy).toHaveBeenCalledWith('test data', null, 2);
|
|
598
|
+
expect(process.stderr.write).toHaveBeenCalledWith(`test data`);
|
|
599
|
+
});
|
|
600
|
+
});
|
|
557
601
|
});
|
package/src/commands/join.ts
CHANGED
|
@@ -25,9 +25,10 @@ import {
|
|
|
25
25
|
printExecutionTime,
|
|
26
26
|
handleError,
|
|
27
27
|
printLintTotals,
|
|
28
|
-
writeYaml,
|
|
29
28
|
exitWithError,
|
|
30
29
|
sortTopLevelKeysForOas,
|
|
30
|
+
getAndValidateFileExtension,
|
|
31
|
+
writeToFileByExtension,
|
|
31
32
|
} from '../utils';
|
|
32
33
|
import { isObject, isString, keysOf } from '../js-utils';
|
|
33
34
|
import {
|
|
@@ -70,16 +71,19 @@ export type JoinOptions = {
|
|
|
70
71
|
|
|
71
72
|
export async function handleJoin(argv: JoinOptions, config: Config, packageVersion: string) {
|
|
72
73
|
const startedAt = performance.now();
|
|
74
|
+
|
|
73
75
|
if (argv.apis.length < 2) {
|
|
74
76
|
return exitWithError(`At least 2 apis should be provided. \n\n`);
|
|
75
77
|
}
|
|
76
78
|
|
|
79
|
+
const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]);
|
|
80
|
+
|
|
77
81
|
const {
|
|
78
82
|
'prefix-components-with-info-prop': prefixComponentsWithInfoProp,
|
|
79
83
|
'prefix-tags-with-filename': prefixTagsWithFilename,
|
|
80
84
|
'prefix-tags-with-info-prop': prefixTagsWithInfoProp,
|
|
81
85
|
'without-x-tag-groups': withoutXTagGroups,
|
|
82
|
-
output: specFilename =
|
|
86
|
+
output: specFilename = `openapi.${fileExtension}`,
|
|
83
87
|
} = argv;
|
|
84
88
|
|
|
85
89
|
const usedTagsOptions = [
|
|
@@ -229,7 +233,8 @@ export async function handleJoin(argv: JoinOptions, config: Config, packageVersi
|
|
|
229
233
|
return exitWithError(`Please fix conflicts before running ${yellow('join')}.`);
|
|
230
234
|
}
|
|
231
235
|
|
|
232
|
-
|
|
236
|
+
writeToFileByExtension(sortTopLevelKeysForOas(joinedDef), specFilename, noRefs);
|
|
237
|
+
|
|
233
238
|
printExecutionTime('join', startedAt, specFilename);
|
|
234
239
|
|
|
235
240
|
function populateTags({
|
|
@@ -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
package/src/utils.ts
CHANGED
|
@@ -25,7 +25,14 @@ import {
|
|
|
25
25
|
RedoclyClient,
|
|
26
26
|
} from '@redocly/openapi-core';
|
|
27
27
|
import { ConfigValidationError } from '@redocly/openapi-core/lib/config';
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
Totals,
|
|
30
|
+
outputExtensions,
|
|
31
|
+
Entrypoint,
|
|
32
|
+
ConfigApis,
|
|
33
|
+
CommandOptions,
|
|
34
|
+
OutputExtensions,
|
|
35
|
+
} from './types';
|
|
29
36
|
import { isEmptyObject } from '@redocly/openapi-core/lib/utils';
|
|
30
37
|
import { Arguments } from 'yargs';
|
|
31
38
|
import { version } from './update-version-notifier';
|
|
@@ -209,6 +216,17 @@ export function readYaml(filename: string) {
|
|
|
209
216
|
return parseYaml(fs.readFileSync(filename, 'utf-8'), { filename });
|
|
210
217
|
}
|
|
211
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
|
+
|
|
212
230
|
export function writeYaml(data: any, filename: string, noRefs = false) {
|
|
213
231
|
const content = stringifyYaml(data, { noRefs });
|
|
214
232
|
|
|
@@ -220,6 +238,27 @@ export function writeYaml(data: any, filename: string, noRefs = false) {
|
|
|
220
238
|
fs.writeFileSync(filename, content);
|
|
221
239
|
}
|
|
222
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
|
+
|
|
223
262
|
export function pluralize(label: string, num: number) {
|
|
224
263
|
if (label.endsWith('is')) {
|
|
225
264
|
[label] = label.split(' ');
|