@redocly/cli 1.0.0-beta.104 → 1.0.0-beta.107
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/bin/cli.js +1 -1
- package/lib/__mocks__/@redocly/openapi-core.d.ts +2 -1
- package/lib/__mocks__/@redocly/openapi-core.js +2 -1
- package/lib/__mocks__/perf_hooks.js +1 -1
- package/lib/__mocks__/utils.d.ts +1 -1
- package/lib/__mocks__/utils.js +2 -2
- package/lib/__tests__/commands/bundle.test.js +52 -17
- package/lib/__tests__/commands/join.test.js +4 -4
- package/lib/__tests__/commands/lint.test.js +14 -8
- package/lib/__tests__/commands/push-region.test.js +2 -2
- package/lib/__tests__/commands/push.test.js +18 -18
- package/lib/__tests__/fixtures/config.d.ts +1 -1
- package/lib/__tests__/fixtures/config.js +1 -1
- package/lib/commands/bundle.d.ts +4 -12
- package/lib/commands/bundle.js +12 -11
- package/lib/commands/join.d.ts +1 -1
- package/lib/commands/join.js +105 -56
- package/lib/commands/lint.d.ts +3 -9
- package/lib/commands/lint.js +14 -11
- package/lib/commands/preview-docs/index.d.ts +3 -5
- package/lib/commands/preview-docs/index.js +14 -14
- package/lib/commands/push.d.ts +6 -6
- package/lib/commands/push.js +26 -26
- package/lib/commands/split/__tests__/index.test.js +8 -8
- package/lib/commands/split/index.d.ts +1 -1
- package/lib/commands/split/index.js +12 -11
- 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 -7
- package/lib/index.js +12 -16
- package/lib/js-utils.js +2 -2
- package/lib/types.d.ts +13 -1
- package/lib/utils.d.ts +4 -4
- package/lib/utils.js +15 -17
- package/package.json +2 -2
- package/src/__mocks__/@redocly/openapi-core.ts +1 -0
- package/src/__mocks__/perf_hooks.ts +2 -2
- package/src/__mocks__/utils.ts +3 -1
- package/src/__tests__/commands/bundle.test.ts +71 -22
- package/src/__tests__/commands/join.test.ts +8 -8
- package/src/__tests__/commands/lint.test.ts +24 -11
- package/src/__tests__/commands/push-region.test.ts +2 -2
- package/src/__tests__/commands/push.test.ts +19 -24
- package/src/__tests__/fixtures/config.ts +1 -1
- package/src/__tests__/utils.test.ts +5 -8
- package/src/commands/bundle.ts +28 -40
- package/src/commands/join.ts +209 -119
- package/src/commands/lint.ts +30 -30
- package/src/commands/login.ts +2 -2
- package/src/commands/preview-docs/index.ts +33 -40
- package/src/commands/preview-docs/preview-server/preview-server.ts +6 -6
- package/src/commands/preview-docs/preview-server/server.ts +1 -1
- package/src/commands/push.ts +44 -53
- package/src/commands/split/__tests__/index.test.ts +47 -30
- package/src/commands/split/index.ts +84 -46
- package/src/commands/split/types.ts +19 -7
- package/src/commands/stats.ts +27 -24
- package/src/index.ts +16 -20
- package/src/js-utils.ts +2 -2
- package/src/types.ts +14 -1
- package/src/utils.ts +53 -53
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { iteratePathItems, handleSplit } from '../index';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as openapiCore from '@redocly/openapi-core';
|
|
4
|
-
import {
|
|
5
|
-
ComponentsFiles,
|
|
6
|
-
} from '../types';
|
|
4
|
+
import { ComponentsFiles } from '../types';
|
|
7
5
|
import { blue, green } from 'colorette';
|
|
8
6
|
|
|
9
7
|
const utils = require('../../../utils');
|
|
@@ -23,16 +21,14 @@ describe('#split', () => {
|
|
|
23
21
|
const componentsFiles: ComponentsFiles = {};
|
|
24
22
|
|
|
25
23
|
it('should split the file and show the success message', async () => {
|
|
26
|
-
const filePath =
|
|
24
|
+
const filePath = 'packages/cli/src/commands/split/__tests__/fixtures/spec.json';
|
|
27
25
|
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
28
26
|
|
|
29
|
-
await handleSplit
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
);
|
|
27
|
+
await handleSplit({
|
|
28
|
+
api: filePath,
|
|
29
|
+
outDir: openapiDir,
|
|
30
|
+
separator: '_',
|
|
31
|
+
});
|
|
36
32
|
|
|
37
33
|
expect(process.stderr.write).toBeCalledTimes(2);
|
|
38
34
|
expect((process.stderr.write as jest.Mock).mock.calls[0][0]).toBe(
|
|
@@ -44,65 +40,86 @@ describe('#split', () => {
|
|
|
44
40
|
);
|
|
45
41
|
});
|
|
46
42
|
|
|
47
|
-
|
|
48
43
|
it('should use the correct separator', async () => {
|
|
49
|
-
const filePath =
|
|
44
|
+
const filePath = 'packages/cli/src/commands/split/__tests__/fixtures/spec.json';
|
|
50
45
|
|
|
51
46
|
jest.spyOn(utils, 'pathToFilename').mockImplementation(() => 'newFilePath');
|
|
52
47
|
|
|
53
|
-
await handleSplit
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
);
|
|
48
|
+
await handleSplit({
|
|
49
|
+
api: filePath,
|
|
50
|
+
outDir: openapiDir,
|
|
51
|
+
separator: '_',
|
|
52
|
+
});
|
|
60
53
|
|
|
61
54
|
expect(utils.pathToFilename).toBeCalledWith(expect.anything(), '_');
|
|
62
55
|
utils.pathToFilename.mockRestore();
|
|
63
56
|
});
|
|
64
57
|
|
|
65
58
|
it('should have correct path with paths', () => {
|
|
66
|
-
const openapi = require(
|
|
67
|
-
|
|
59
|
+
const openapi = require('./fixtures/spec.json');
|
|
60
|
+
|
|
68
61
|
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'paths/test.yaml');
|
|
69
62
|
jest.spyOn(path, 'relative').mockImplementation(() => 'paths/test.yaml');
|
|
70
|
-
iteratePathItems(
|
|
63
|
+
iteratePathItems(
|
|
64
|
+
openapi.paths,
|
|
65
|
+
openapiDir,
|
|
66
|
+
path.join(openapiDir, 'paths'),
|
|
67
|
+
componentsFiles,
|
|
68
|
+
'_'
|
|
69
|
+
);
|
|
71
70
|
|
|
72
71
|
expect(openapiCore.slash).toHaveBeenCalledWith('paths/test.yaml');
|
|
73
72
|
expect(path.relative).toHaveBeenCalledWith('test', 'test/paths/test.yaml');
|
|
74
73
|
});
|
|
75
74
|
|
|
76
75
|
it('should have correct path with webhooks', () => {
|
|
77
|
-
const openapi = require(
|
|
76
|
+
const openapi = require('./fixtures/webhooks.json');
|
|
78
77
|
|
|
79
78
|
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'webhooks/test.yaml');
|
|
80
79
|
jest.spyOn(path, 'relative').mockImplementation(() => 'webhooks/test.yaml');
|
|
81
|
-
iteratePathItems(
|
|
80
|
+
iteratePathItems(
|
|
81
|
+
openapi.webhooks,
|
|
82
|
+
openapiDir,
|
|
83
|
+
path.join(openapiDir, 'webhooks'),
|
|
84
|
+
componentsFiles,
|
|
85
|
+
'webhook_'
|
|
86
|
+
);
|
|
82
87
|
|
|
83
88
|
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
|
|
84
89
|
expect(path.relative).toHaveBeenCalledWith('test', 'test/webhooks/test.yaml');
|
|
85
90
|
});
|
|
86
91
|
|
|
87
92
|
it('should have correct path with x-webhooks', () => {
|
|
88
|
-
const openapi = require(
|
|
93
|
+
const openapi = require('./fixtures/spec.json');
|
|
89
94
|
|
|
90
95
|
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'webhooks/test.yaml');
|
|
91
96
|
jest.spyOn(path, 'relative').mockImplementation(() => 'webhooks/test.yaml');
|
|
92
|
-
iteratePathItems(
|
|
97
|
+
iteratePathItems(
|
|
98
|
+
openapi['x-webhooks'],
|
|
99
|
+
openapiDir,
|
|
100
|
+
path.join(openapiDir, 'webhooks'),
|
|
101
|
+
componentsFiles,
|
|
102
|
+
'webhook_'
|
|
103
|
+
);
|
|
93
104
|
|
|
94
105
|
expect(openapiCore.slash).toHaveBeenCalledWith('webhooks/test.yaml');
|
|
95
106
|
expect(path.relative).toHaveBeenCalledWith('test', 'test/webhooks/test.yaml');
|
|
96
107
|
});
|
|
97
108
|
|
|
98
109
|
it('should create correct folder name for code samples', async () => {
|
|
99
|
-
const openapi = require(
|
|
110
|
+
const openapi = require('./fixtures/samples.json');
|
|
100
111
|
|
|
101
|
-
const fs = require('fs')
|
|
112
|
+
const fs = require('fs');
|
|
102
113
|
jest.spyOn(fs, 'writeFileSync').mockImplementation(() => {});
|
|
103
114
|
|
|
104
115
|
jest.spyOn(utils, 'escapeLanguageName');
|
|
105
|
-
iteratePathItems(
|
|
116
|
+
iteratePathItems(
|
|
117
|
+
openapi.paths,
|
|
118
|
+
openapiDir,
|
|
119
|
+
path.join(openapiDir, 'paths'),
|
|
120
|
+
componentsFiles,
|
|
121
|
+
'_'
|
|
122
|
+
);
|
|
106
123
|
|
|
107
124
|
expect(utils.escapeLanguageName).nthCalledWith(1, 'C#');
|
|
108
125
|
expect(utils.escapeLanguageName).nthReturnedWith(1, 'C_sharp');
|
|
@@ -5,7 +5,14 @@ import * as path from 'path';
|
|
|
5
5
|
import { performance } from 'perf_hooks';
|
|
6
6
|
const isEqual = require('lodash.isequal');
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
printExecutionTime,
|
|
10
|
+
pathToFilename,
|
|
11
|
+
readYaml,
|
|
12
|
+
writeYaml,
|
|
13
|
+
exitWithError,
|
|
14
|
+
escapeLanguageName,
|
|
15
|
+
} from '../../utils';
|
|
9
16
|
import { isString, isObject, isEmptyObject } from '../../js-utils';
|
|
10
17
|
import {
|
|
11
18
|
Definition,
|
|
@@ -23,42 +30,56 @@ import {
|
|
|
23
30
|
componentsPath,
|
|
24
31
|
OPENAPI3_METHOD_NAMES,
|
|
25
32
|
OPENAPI3_COMPONENT_NAMES,
|
|
26
|
-
Referenced
|
|
33
|
+
Referenced,
|
|
27
34
|
} from './types';
|
|
28
35
|
|
|
29
|
-
export async function handleSplit
|
|
30
|
-
entrypoint: string;
|
|
31
|
-
outDir: string
|
|
32
|
-
separator: string
|
|
33
|
-
}) {
|
|
36
|
+
export async function handleSplit(argv: { api: string; outDir: string; separator: string }) {
|
|
34
37
|
const startedAt = performance.now();
|
|
35
|
-
const {
|
|
36
|
-
validateDefinitionFileName(
|
|
37
|
-
const openapi = readYaml(
|
|
38
|
+
const { api, outDir, separator } = argv;
|
|
39
|
+
validateDefinitionFileName(api!);
|
|
40
|
+
const openapi = readYaml(api!) as Oas3Definition | Oas3_1Definition;
|
|
38
41
|
splitDefinition(openapi, outDir, separator);
|
|
39
42
|
process.stderr.write(
|
|
40
|
-
`🪓 Document: ${blue(
|
|
41
|
-
and all related files are saved to the directory: ${blue(outDir)} \n
|
|
43
|
+
`🪓 Document: ${blue(api!)} ${green('is successfully split')}
|
|
44
|
+
and all related files are saved to the directory: ${blue(outDir)} \n`
|
|
42
45
|
);
|
|
43
|
-
printExecutionTime('split', startedAt,
|
|
46
|
+
printExecutionTime('split', startedAt, api!);
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
function splitDefinition(
|
|
49
|
+
function splitDefinition(
|
|
50
|
+
openapi: Oas3Definition | Oas3_1Definition,
|
|
51
|
+
openapiDir: string,
|
|
52
|
+
pathSeparator: string
|
|
53
|
+
) {
|
|
47
54
|
fs.mkdirSync(openapiDir, { recursive: true });
|
|
48
55
|
|
|
49
56
|
const componentsFiles: ComponentsFiles = {};
|
|
50
57
|
iterateComponents(openapi, openapiDir, componentsFiles);
|
|
51
|
-
iteratePathItems(
|
|
52
|
-
|
|
58
|
+
iteratePathItems(
|
|
59
|
+
openapi.paths,
|
|
60
|
+
openapiDir,
|
|
61
|
+
path.join(openapiDir, 'paths'),
|
|
62
|
+
componentsFiles,
|
|
63
|
+
pathSeparator
|
|
64
|
+
);
|
|
65
|
+
const webhooks =
|
|
66
|
+
(openapi as Oas3_1Definition).webhooks || (openapi as Oas3Definition)['x-webhooks'];
|
|
53
67
|
// use webhook_ prefix for code samples to prevent potential name-clashes with paths samples
|
|
54
|
-
iteratePathItems(
|
|
68
|
+
iteratePathItems(
|
|
69
|
+
webhooks,
|
|
70
|
+
openapiDir,
|
|
71
|
+
path.join(openapiDir, 'webhooks'),
|
|
72
|
+
componentsFiles,
|
|
73
|
+
pathSeparator,
|
|
74
|
+
'webhook_'
|
|
75
|
+
);
|
|
55
76
|
|
|
56
77
|
replace$Refs(openapi, openapiDir, componentsFiles);
|
|
57
78
|
writeYaml(openapi, path.join(openapiDir, 'openapi.yaml'));
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
function isStartsWithComponents(node: string) {
|
|
61
|
-
return node.startsWith(componentsPath)
|
|
82
|
+
return node.startsWith(componentsPath);
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
function isNotYaml(filename: string) {
|
|
@@ -77,7 +98,10 @@ function validateDefinitionFileName(fileName: string) {
|
|
|
77
98
|
if (!fs.existsSync(fileName)) exitWithError(`File ${blue(fileName)} does not exist \n`);
|
|
78
99
|
const file = loadFile(fileName);
|
|
79
100
|
if ((file as Oas2Definition).swagger) exitWithError('OpenAPI 2 is not supported by this command');
|
|
80
|
-
if (!(file as Oas3Definition | Oas3_1Definition).openapi)
|
|
101
|
+
if (!(file as Oas3Definition | Oas3_1Definition).openapi)
|
|
102
|
+
exitWithError(
|
|
103
|
+
'File does not conform to the OpenAPI Specification. OpenAPI version is not specified'
|
|
104
|
+
);
|
|
81
105
|
return true;
|
|
82
106
|
}
|
|
83
107
|
|
|
@@ -90,8 +114,8 @@ function langToExt(lang: string) {
|
|
|
90
114
|
bash: '.sh',
|
|
91
115
|
javascript: '.js',
|
|
92
116
|
js: '.js',
|
|
93
|
-
python: '.py'
|
|
94
|
-
}
|
|
117
|
+
python: '.py',
|
|
118
|
+
};
|
|
95
119
|
return langObj[lang];
|
|
96
120
|
}
|
|
97
121
|
|
|
@@ -111,7 +135,7 @@ function traverseDirectoryDeep(directory: string, callback: any, componentsFiles
|
|
|
111
135
|
function traverseDirectoryDeepCallback(
|
|
112
136
|
filename: string,
|
|
113
137
|
directory: string,
|
|
114
|
-
componentsFiles: object
|
|
138
|
+
componentsFiles: object
|
|
115
139
|
) {
|
|
116
140
|
if (isNotYaml(filename)) return;
|
|
117
141
|
const pathData = readYaml(filename);
|
|
@@ -127,11 +151,7 @@ function crawl(object: any, visitor: any) {
|
|
|
127
151
|
}
|
|
128
152
|
}
|
|
129
153
|
|
|
130
|
-
function replace$Refs(
|
|
131
|
-
obj: any,
|
|
132
|
-
relativeFrom: string,
|
|
133
|
-
componentFiles = {} as ComponentsFiles
|
|
134
|
-
) {
|
|
154
|
+
function replace$Refs(obj: any, relativeFrom: string, componentFiles = {} as ComponentsFiles) {
|
|
135
155
|
crawl(obj, (node: any) => {
|
|
136
156
|
if (node.$ref && isString(node.$ref) && isStartsWithComponents(node.$ref)) {
|
|
137
157
|
replace(node, '$ref');
|
|
@@ -156,7 +176,9 @@ function replace$Refs(
|
|
|
156
176
|
const filesGroupName = componentFiles[groupName];
|
|
157
177
|
if (!filesGroupName || !filesGroupName[name!]) return;
|
|
158
178
|
let filename = path.relative(relativeFrom, filesGroupName[name!].filename);
|
|
159
|
-
if (!filename.startsWith('.')) {
|
|
179
|
+
if (!filename.startsWith('.')) {
|
|
180
|
+
filename = '.' + path.sep + filename;
|
|
181
|
+
}
|
|
160
182
|
node[key] = filename;
|
|
161
183
|
}
|
|
162
184
|
}
|
|
@@ -182,30 +204,40 @@ function implicitlyReferenceDiscriminator(
|
|
|
182
204
|
const discriminatorEnum = discriminatorPropSchema && discriminatorPropSchema.enum;
|
|
183
205
|
const mapping = (obj.discriminator.mapping = obj.discriminator.mapping || {});
|
|
184
206
|
for (const name of Object.keys(implicitMapping)) {
|
|
185
|
-
if (discriminatorEnum && !discriminatorEnum.includes(name)) {
|
|
207
|
+
if (discriminatorEnum && !discriminatorEnum.includes(name)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
186
210
|
if (mapping[name] && mapping[name] !== implicitMapping[name]) {
|
|
187
|
-
process.stderr.write(
|
|
188
|
-
|
|
189
|
-
|
|
211
|
+
process.stderr.write(
|
|
212
|
+
yellow(
|
|
213
|
+
`warning: explicit mapping overlaps with local mapping entry ${red(name)} at ${blue(
|
|
214
|
+
filename
|
|
215
|
+
)}. Please check it.`
|
|
216
|
+
)
|
|
217
|
+
);
|
|
190
218
|
}
|
|
191
219
|
mapping[name] = implicitMapping[name];
|
|
192
220
|
}
|
|
193
221
|
}
|
|
194
222
|
|
|
195
223
|
function isNotSecurityComponentType(componentType: string) {
|
|
196
|
-
return componentType !== OPENAPI3_COMPONENT.SecuritySchemes
|
|
224
|
+
return componentType !== OPENAPI3_COMPONENT.SecuritySchemes;
|
|
197
225
|
}
|
|
198
226
|
|
|
199
227
|
function findComponentTypes(components: any) {
|
|
200
|
-
return OPENAPI3_COMPONENT_NAMES
|
|
201
|
-
|
|
228
|
+
return OPENAPI3_COMPONENT_NAMES.filter(
|
|
229
|
+
(item) => isNotSecurityComponentType(item) && Object.keys(components).includes(item)
|
|
230
|
+
);
|
|
202
231
|
}
|
|
203
232
|
|
|
204
233
|
function doesFileDiffer(filename: string, componentData: any) {
|
|
205
234
|
return fs.existsSync(filename) && !isEqual(readYaml(filename), componentData);
|
|
206
235
|
}
|
|
207
236
|
|
|
208
|
-
function removeEmptyComponents(
|
|
237
|
+
function removeEmptyComponents(
|
|
238
|
+
openapi: Oas3Definition | Oas3_1Definition,
|
|
239
|
+
componentType: Oas3ComponentName
|
|
240
|
+
) {
|
|
209
241
|
if (openapi.components && isEmptyObject(openapi.components[componentType])) {
|
|
210
242
|
delete openapi.components[componentType];
|
|
211
243
|
}
|
|
@@ -237,19 +269,21 @@ function gatherComponentsFiles(
|
|
|
237
269
|
) {
|
|
238
270
|
let inherits = [];
|
|
239
271
|
if (componentType === OPENAPI3_COMPONENT.Schemas) {
|
|
240
|
-
inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
|
|
272
|
+
inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
|
|
273
|
+
.map((s: any) => s.$ref)
|
|
274
|
+
.filter(Boolean);
|
|
241
275
|
}
|
|
242
276
|
componentsFiles[componentType] = componentsFiles[componentType] || {};
|
|
243
277
|
componentsFiles[componentType][componentName] = { inherits, filename };
|
|
244
278
|
}
|
|
245
279
|
|
|
246
280
|
function iteratePathItems(
|
|
247
|
-
pathItems: Record<string,
|
|
281
|
+
pathItems: Record<string, Referenced<Oas3PathItem>> | undefined,
|
|
248
282
|
openapiDir: string,
|
|
249
283
|
outDir: string,
|
|
250
284
|
componentsFiles: object,
|
|
251
285
|
pathSeparator: string,
|
|
252
|
-
codeSamplesPathPrefix: string = ''
|
|
286
|
+
codeSamplesPathPrefix: string = ''
|
|
253
287
|
) {
|
|
254
288
|
if (!pathItems) return;
|
|
255
289
|
fs.mkdirSync(outDir, { recursive: true });
|
|
@@ -259,7 +293,7 @@ function iteratePathItems(
|
|
|
259
293
|
const pathData = pathItems[pathName] as Oas3PathItem;
|
|
260
294
|
|
|
261
295
|
if (isRef(pathData)) continue;
|
|
262
|
-
|
|
296
|
+
|
|
263
297
|
for (const method of OPENAPI3_METHOD_NAMES) {
|
|
264
298
|
const methodData = pathData[method];
|
|
265
299
|
const methodDataXCode = methodData?.['x-code-samples'] || methodData?.['x-codeSamples'];
|
|
@@ -273,20 +307,20 @@ function iteratePathItems(
|
|
|
273
307
|
'code_samples',
|
|
274
308
|
escapeLanguageName(sample.lang),
|
|
275
309
|
codeSamplesPathPrefix + pathToFilename(pathName, pathSeparator),
|
|
276
|
-
method + langToExt(sample.lang)
|
|
310
|
+
method + langToExt(sample.lang)
|
|
277
311
|
);
|
|
278
312
|
|
|
279
313
|
fs.mkdirSync(path.dirname(sampleFileName), { recursive: true });
|
|
280
314
|
fs.writeFileSync(sampleFileName, sample.source);
|
|
281
315
|
// @ts-ignore
|
|
282
316
|
sample.source = {
|
|
283
|
-
$ref: slash(path.relative(outDir, sampleFileName))
|
|
317
|
+
$ref: slash(path.relative(outDir, sampleFileName)),
|
|
284
318
|
};
|
|
285
319
|
}
|
|
286
320
|
}
|
|
287
321
|
writeYaml(pathData, pathFile);
|
|
288
322
|
pathItems[pathName] = {
|
|
289
|
-
$ref: slash(path.relative(openapiDir, pathFile))
|
|
323
|
+
$ref: slash(path.relative(openapiDir, pathFile)),
|
|
290
324
|
};
|
|
291
325
|
|
|
292
326
|
traverseDirectoryDeep(outDir, traverseDirectoryDeepCallback, componentsFiles);
|
|
@@ -329,9 +363,13 @@ function iterateComponents(
|
|
|
329
363
|
);
|
|
330
364
|
|
|
331
365
|
if (doesFileDiffer(filename, componentData)) {
|
|
332
|
-
process.stderr.write(
|
|
333
|
-
|
|
334
|
-
|
|
366
|
+
process.stderr.write(
|
|
367
|
+
yellow(
|
|
368
|
+
`warning: conflict for ${componentName} - file already exists with different content: ${blue(
|
|
369
|
+
filename
|
|
370
|
+
)} ... Skip.\n`
|
|
371
|
+
)
|
|
372
|
+
);
|
|
335
373
|
} else {
|
|
336
374
|
writeYaml(componentData, filename);
|
|
337
375
|
}
|
|
@@ -9,9 +9,21 @@ import {
|
|
|
9
9
|
Oas3ComponentName,
|
|
10
10
|
Oas3_1Webhooks,
|
|
11
11
|
Oas2Definition,
|
|
12
|
-
Referenced
|
|
13
|
-
} from
|
|
14
|
-
export {
|
|
12
|
+
Referenced,
|
|
13
|
+
} from '@redocly/openapi-core';
|
|
14
|
+
export {
|
|
15
|
+
Oas3_1Definition,
|
|
16
|
+
Oas3Definition,
|
|
17
|
+
Oas2Definition,
|
|
18
|
+
Oas3Components,
|
|
19
|
+
Oas3Paths,
|
|
20
|
+
Oas3PathItem,
|
|
21
|
+
Oas3ComponentName,
|
|
22
|
+
Oas3_1Schema,
|
|
23
|
+
Oas3Schema,
|
|
24
|
+
Oas3_1Webhooks,
|
|
25
|
+
Referenced,
|
|
26
|
+
};
|
|
15
27
|
export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
|
|
16
28
|
export interface ComponentsFiles {
|
|
17
29
|
[schemas: string]: any;
|
|
@@ -34,7 +46,7 @@ enum OPENAPI3_METHOD {
|
|
|
34
46
|
Options = 'options',
|
|
35
47
|
Head = 'head',
|
|
36
48
|
Patch = 'patch',
|
|
37
|
-
Trace = 'trace'
|
|
49
|
+
Trace = 'trace',
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
export const OPENAPI3_METHOD_NAMES: OPENAPI3_METHOD[] = [
|
|
@@ -45,7 +57,7 @@ export const OPENAPI3_METHOD_NAMES: OPENAPI3_METHOD[] = [
|
|
|
45
57
|
OPENAPI3_METHOD.Options,
|
|
46
58
|
OPENAPI3_METHOD.Head,
|
|
47
59
|
OPENAPI3_METHOD.Patch,
|
|
48
|
-
OPENAPI3_METHOD.Trace
|
|
60
|
+
OPENAPI3_METHOD.Trace,
|
|
49
61
|
];
|
|
50
62
|
|
|
51
63
|
export enum OPENAPI3_COMPONENT {
|
|
@@ -57,7 +69,7 @@ export enum OPENAPI3_COMPONENT {
|
|
|
57
69
|
RequestBodies = 'requestBodies',
|
|
58
70
|
Links = 'links',
|
|
59
71
|
Callbacks = 'callbacks',
|
|
60
|
-
SecuritySchemes = 'securitySchemes'
|
|
72
|
+
SecuritySchemes = 'securitySchemes',
|
|
61
73
|
}
|
|
62
74
|
|
|
63
75
|
export const OPENAPI3_COMPONENT_NAMES: OPENAPI3_COMPONENT[] = [
|
|
@@ -69,5 +81,5 @@ export const OPENAPI3_COMPONENT_NAMES: OPENAPI3_COMPONENT[] = [
|
|
|
69
81
|
OPENAPI3_COMPONENT.Headers,
|
|
70
82
|
OPENAPI3_COMPONENT.Links,
|
|
71
83
|
OPENAPI3_COMPONENT.Callbacks,
|
|
72
|
-
OPENAPI3_COMPONENT.SecuritySchemes
|
|
84
|
+
OPENAPI3_COMPONENT.SecuritySchemes,
|
|
73
85
|
];
|
package/src/commands/stats.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { performance } from 'perf_hooks';
|
|
|
2
2
|
import * as colors from 'colorette';
|
|
3
3
|
import {
|
|
4
4
|
Config,
|
|
5
|
-
|
|
5
|
+
StyleguideConfig,
|
|
6
6
|
loadConfig,
|
|
7
7
|
normalizeTypes,
|
|
8
8
|
Oas3Types,
|
|
@@ -18,22 +18,22 @@ import {
|
|
|
18
18
|
WalkContext,
|
|
19
19
|
walkDocument,
|
|
20
20
|
Stats,
|
|
21
|
-
bundle
|
|
21
|
+
bundle,
|
|
22
22
|
} from '@redocly/openapi-core';
|
|
23
23
|
|
|
24
|
-
import {
|
|
24
|
+
import { getFallbackApisOrExit } from '../utils';
|
|
25
25
|
import { printExecutionTime } from '../utils';
|
|
26
26
|
|
|
27
27
|
const statsAccumulator: StatsAccumulator = {
|
|
28
28
|
refs: { metric: '🚗 References', total: 0, color: 'red', items: new Set() },
|
|
29
29
|
externalDocs: { metric: '📦 External Documents', total: 0, color: 'magenta' },
|
|
30
|
-
schemas: { metric: '📈 Schemas', total: 0, color: 'white'},
|
|
30
|
+
schemas: { metric: '📈 Schemas', total: 0, color: 'white' },
|
|
31
31
|
parameters: { metric: '👉 Parameters', total: 0, color: 'yellow', items: new Set() },
|
|
32
32
|
links: { metric: '🔗 Links', total: 0, color: 'cyan', items: new Set() },
|
|
33
33
|
pathItems: { metric: '➡️ Path Items', total: 0, color: 'green' },
|
|
34
34
|
operations: { metric: '👷 Operations', total: 0, color: 'yellow' },
|
|
35
35
|
tags: { metric: '🔖 Tags', total: 0, color: 'white', items: new Set() },
|
|
36
|
-
}
|
|
36
|
+
};
|
|
37
37
|
|
|
38
38
|
function printStatsStylish(statsAccumulator: StatsAccumulator) {
|
|
39
39
|
for (const node in statsAccumulator) {
|
|
@@ -48,35 +48,35 @@ function printStatsJson(statsAccumulator: StatsAccumulator) {
|
|
|
48
48
|
json[key] = {
|
|
49
49
|
metric: statsAccumulator[key as StatsName].metric,
|
|
50
50
|
total: statsAccumulator[key as StatsName].total,
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
52
|
}
|
|
53
53
|
process.stdout.write(JSON.stringify(json, null, 2));
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
function printStats(statsAccumulator: StatsAccumulator,
|
|
57
|
-
process.stderr.write(`Document: ${colors.magenta(
|
|
56
|
+
function printStats(statsAccumulator: StatsAccumulator, api: string, format: string) {
|
|
57
|
+
process.stderr.write(`Document: ${colors.magenta(api)} stats:\n\n`);
|
|
58
58
|
switch (format) {
|
|
59
|
-
case 'stylish':
|
|
60
|
-
|
|
59
|
+
case 'stylish':
|
|
60
|
+
printStatsStylish(statsAccumulator);
|
|
61
|
+
break;
|
|
62
|
+
case 'json':
|
|
63
|
+
printStatsJson(statsAccumulator);
|
|
64
|
+
break;
|
|
61
65
|
}
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
export async function handleStats
|
|
65
|
-
config?: string;
|
|
66
|
-
entrypoint?: string;
|
|
67
|
-
format: string;
|
|
68
|
-
}) {
|
|
68
|
+
export async function handleStats(argv: { config?: string; api?: string; format: string }) {
|
|
69
69
|
const config: Config = await loadConfig(argv.config);
|
|
70
|
-
const [{ path }] = await
|
|
70
|
+
const [{ path }] = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
|
71
71
|
const externalRefResolver = new BaseResolver(config.resolve);
|
|
72
72
|
const { bundle: document } = await bundle({ config, ref: path });
|
|
73
|
-
const lintConfig:
|
|
73
|
+
const lintConfig: StyleguideConfig = config.styleguide;
|
|
74
74
|
const oasVersion = detectOpenAPI(document.parsed);
|
|
75
75
|
const oasMajorVersion = openAPIMajor(oasVersion);
|
|
76
76
|
const types = normalizeTypes(
|
|
77
77
|
lintConfig.extendTypes(
|
|
78
78
|
oasMajorVersion === OasMajorVersion.Version3 ? Oas3Types : Oas2Types,
|
|
79
|
-
oasVersion
|
|
79
|
+
oasVersion
|
|
80
80
|
),
|
|
81
81
|
lintConfig
|
|
82
82
|
);
|
|
@@ -86,7 +86,7 @@ export async function handleStats (argv: {
|
|
|
86
86
|
problems: [],
|
|
87
87
|
oasVersion: oasVersion,
|
|
88
88
|
visitorsData: {},
|
|
89
|
-
}
|
|
89
|
+
};
|
|
90
90
|
|
|
91
91
|
const resolvedRefMap = await resolveDocument({
|
|
92
92
|
rootDocument: document,
|
|
@@ -94,11 +94,14 @@ export async function handleStats (argv: {
|
|
|
94
94
|
externalRefResolver,
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
-
const statsVisitor = normalizeVisitors(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
const statsVisitor = normalizeVisitors(
|
|
98
|
+
[
|
|
99
|
+
{
|
|
100
|
+
severity: 'warn',
|
|
101
|
+
ruleId: 'stats',
|
|
102
|
+
visitor: Stats(statsAccumulator),
|
|
103
|
+
},
|
|
104
|
+
],
|
|
102
105
|
types
|
|
103
106
|
);
|
|
104
107
|
|