@redocly/cli 1.0.0-beta.98 → 1.0.0-rc.1
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 +96 -0
- package/lib/__mocks__/@redocly/openapi-core.js +79 -0
- package/lib/__mocks__/documents.d.ts +92 -0
- package/lib/__mocks__/documents.js +63 -0
- package/lib/__mocks__/fs.d.ts +9 -0
- package/lib/__mocks__/fs.js +9 -0
- package/lib/__mocks__/perf_hooks.d.ts +4 -0
- package/lib/__mocks__/perf_hooks.js +6 -0
- package/lib/__mocks__/redoc.d.ts +7 -0
- package/lib/__mocks__/redoc.js +5 -0
- package/lib/__mocks__/utils.d.ts +26 -4
- package/lib/__mocks__/utils.js +8 -3
- package/lib/__tests__/commands/build-docs.test.d.ts +1 -0
- package/lib/__tests__/commands/build-docs.test.js +59 -0
- package/lib/__tests__/commands/bundle.test.js +66 -30
- package/lib/__tests__/commands/join.test.d.ts +1 -0
- package/lib/__tests__/commands/join.test.js +85 -0
- package/lib/__tests__/commands/lint.test.d.ts +1 -0
- package/lib/__tests__/commands/lint.test.js +149 -0
- package/lib/__tests__/commands/push-region.test.js +5 -4
- package/lib/__tests__/commands/push.test.js +254 -39
- package/lib/__tests__/fetch-with-timeout.test.d.ts +1 -0
- package/lib/__tests__/fetch-with-timeout.test.js +38 -0
- package/lib/__tests__/fixtures/config.d.ts +22 -0
- package/lib/__tests__/fixtures/config.js +24 -0
- package/lib/__tests__/utils.test.js +429 -1
- package/lib/__tests__/wrapper.test.d.ts +1 -0
- package/lib/__tests__/wrapper.test.js +57 -0
- package/lib/commands/build-docs/index.d.ts +3 -0
- package/lib/commands/build-docs/index.js +50 -0
- package/{src/commands/preview-docs/preview-server/default.hbs → lib/commands/build-docs/template.hbs} +2 -3
- package/lib/commands/build-docs/types.d.ts +23 -0
- package/lib/commands/build-docs/types.js +2 -0
- package/lib/commands/build-docs/utils.d.ts +7 -0
- package/lib/commands/build-docs/utils.js +99 -0
- package/lib/commands/bundle.d.ts +11 -12
- package/lib/commands/bundle.js +26 -24
- package/lib/commands/join.d.ts +12 -3
- package/lib/commands/join.js +295 -109
- package/lib/commands/lint.d.ts +11 -8
- package/lib/commands/lint.js +49 -19
- package/lib/commands/login.d.ts +5 -3
- package/lib/commands/login.js +2 -2
- package/lib/commands/preview-docs/index.d.ts +6 -6
- package/lib/commands/preview-docs/index.js +30 -20
- package/lib/commands/preview-docs/preview-server/oauth2-redirect.html +1 -1
- package/lib/commands/preview-docs/preview-server/preview-server.js +5 -4
- package/lib/commands/preview-docs/preview-server/server.d.ts +1 -1
- package/lib/commands/push.d.ts +21 -10
- package/lib/commands/push.js +110 -63
- package/lib/commands/split/__tests__/index.test.js +23 -8
- package/lib/commands/split/index.d.ts +5 -3
- package/lib/commands/split/index.js +15 -24
- package/lib/commands/split/types.d.ts +11 -11
- package/lib/commands/split/types.js +19 -19
- package/lib/commands/stats.d.ts +7 -4
- package/lib/commands/stats.js +13 -12
- package/lib/fetch-with-timeout.d.ts +2 -0
- package/lib/fetch-with-timeout.js +30 -0
- package/lib/index.js +194 -40
- package/lib/js-utils.d.ts +1 -0
- package/lib/js-utils.js +9 -3
- package/lib/types.d.ts +17 -1
- package/lib/update-version-notifier.d.ts +3 -0
- package/lib/update-version-notifier.js +105 -0
- package/lib/utils.d.ts +39 -5
- package/lib/utils.js +273 -37
- package/lib/wrapper.d.ts +4 -0
- package/lib/wrapper.js +52 -0
- package/package.json +18 -8
- package/README.md +0 -39
- package/src/__mocks__/utils.ts +0 -11
- package/src/__tests__/commands/bundle.test.ts +0 -120
- package/src/__tests__/commands/push-region.test.ts +0 -51
- package/src/__tests__/commands/push.test.ts +0 -158
- package/src/__tests__/utils.test.ts +0 -50
- package/src/assert-node-version.ts +0 -8
- package/src/commands/bundle.ts +0 -178
- package/src/commands/join.ts +0 -488
- package/src/commands/lint.ts +0 -110
- package/src/commands/login.ts +0 -21
- package/src/commands/preview-docs/index.ts +0 -188
- package/src/commands/preview-docs/preview-server/hot.js +0 -42
- package/src/commands/preview-docs/preview-server/oauth2-redirect.html +0 -21
- package/src/commands/preview-docs/preview-server/preview-server.ts +0 -155
- package/src/commands/preview-docs/preview-server/server.ts +0 -91
- package/src/commands/push.ts +0 -357
- package/src/commands/split/__tests__/fixtures/spec.json +0 -70
- package/src/commands/split/__tests__/fixtures/webhooks.json +0 -88
- package/src/commands/split/__tests__/index.test.ts +0 -96
- package/src/commands/split/index.ts +0 -349
- package/src/commands/split/types.ts +0 -73
- package/src/commands/stats.ts +0 -115
- package/src/index.ts +0 -316
- package/src/js-utils.ts +0 -12
- package/src/types.ts +0 -13
- package/src/utils.ts +0 -300
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/lib/commands/join.js
CHANGED
|
@@ -17,23 +17,50 @@ const isEqual = require('lodash.isequal');
|
|
|
17
17
|
const openapi_core_1 = require("@redocly/openapi-core");
|
|
18
18
|
const utils_1 = require("../utils");
|
|
19
19
|
const js_utils_1 = require("../js-utils");
|
|
20
|
+
const types_1 = require("./split/types");
|
|
20
21
|
const COMPONENTS = 'components';
|
|
22
|
+
const Tags = 'tags';
|
|
23
|
+
const xTagGroups = 'x-tagGroups';
|
|
21
24
|
let potentialConflictsTotal = 0;
|
|
22
|
-
function handleJoin(argv, packageVersion) {
|
|
25
|
+
function handleJoin(argv, config, packageVersion) {
|
|
23
26
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
27
|
const startedAt = perf_hooks_1.performance.now();
|
|
25
|
-
if (argv.
|
|
26
|
-
return utils_1.exitWithError(`At least 2
|
|
28
|
+
if (argv.apis.length < 2) {
|
|
29
|
+
return utils_1.exitWithError(`At least 2 apis should be provided. \n\n`);
|
|
27
30
|
}
|
|
28
|
-
const
|
|
29
|
-
const
|
|
31
|
+
const { 'prefix-components-with-info-prop': prefixComponentsWithInfoProp, 'prefix-tags-with-filename': prefixTagsWithFilename, 'prefix-tags-with-info-prop': prefixTagsWithInfoProp, 'without-x-tag-groups': withoutXTagGroups, output: specFilename = 'openapi.yaml', } = argv;
|
|
32
|
+
const usedTagsOptions = [
|
|
33
|
+
prefixTagsWithFilename && 'prefix-tags-with-filename',
|
|
34
|
+
prefixTagsWithInfoProp && 'prefix-tags-with-info-prop',
|
|
35
|
+
withoutXTagGroups && 'without-x-tag-groups',
|
|
36
|
+
].filter(Boolean);
|
|
37
|
+
if (usedTagsOptions.length > 1) {
|
|
38
|
+
return utils_1.exitWithError(`You use ${colorette_1.yellow(usedTagsOptions.join(', '))} together.\nPlease choose only one! \n\n`);
|
|
39
|
+
}
|
|
40
|
+
const apis = yield utils_1.getFallbackApisOrExit(argv.apis, config);
|
|
30
41
|
const externalRefResolver = new openapi_core_1.BaseResolver(config.resolve);
|
|
31
|
-
const documents = yield Promise.all(
|
|
32
|
-
|
|
42
|
+
const documents = yield Promise.all(apis.map(({ path }) => externalRefResolver.resolveDocument(null, path, true)));
|
|
43
|
+
if (!argv.decorate) {
|
|
44
|
+
const decorators = new Set([
|
|
45
|
+
...Object.keys(config.styleguide.decorators.oas3_0),
|
|
46
|
+
...Object.keys(config.styleguide.decorators.oas3_1),
|
|
47
|
+
...Object.keys(config.styleguide.decorators.oas2),
|
|
48
|
+
]);
|
|
49
|
+
config.styleguide.skipDecorators(Array.from(decorators));
|
|
50
|
+
}
|
|
51
|
+
if (!argv.preprocess) {
|
|
52
|
+
const preprocessors = new Set([
|
|
53
|
+
...Object.keys(config.styleguide.preprocessors.oas3_0),
|
|
54
|
+
...Object.keys(config.styleguide.preprocessors.oas3_1),
|
|
55
|
+
...Object.keys(config.styleguide.preprocessors.oas2),
|
|
56
|
+
]);
|
|
57
|
+
config.styleguide.skipPreprocessors(Array.from(preprocessors));
|
|
58
|
+
}
|
|
59
|
+
const bundleResults = yield Promise.all(documents.map((document) => openapi_core_1.bundleDocument({
|
|
33
60
|
document,
|
|
34
|
-
config: config.
|
|
35
|
-
externalRefResolver
|
|
36
|
-
}).catch(e => {
|
|
61
|
+
config: config.styleguide,
|
|
62
|
+
externalRefResolver,
|
|
63
|
+
}).catch((e) => {
|
|
37
64
|
utils_1.exitWithError(`${e.message}: ${colorette_1.blue(document.source.absoluteRef)}`);
|
|
38
65
|
})));
|
|
39
66
|
for (const { problems, bundle: document } of bundleResults) {
|
|
@@ -41,7 +68,7 @@ function handleJoin(argv, packageVersion) {
|
|
|
41
68
|
if (fileTotals.errors) {
|
|
42
69
|
openapi_core_1.formatProblems(problems, {
|
|
43
70
|
totals: fileTotals,
|
|
44
|
-
version: document.parsed.version
|
|
71
|
+
version: document.parsed.version,
|
|
45
72
|
});
|
|
46
73
|
utils_1.exitWithError(`❌ Errors encountered while bundling ${colorette_1.blue(document.source.absoluteRef)}: join will not proceed.\n`);
|
|
47
74
|
}
|
|
@@ -59,34 +86,37 @@ function handleJoin(argv, packageVersion) {
|
|
|
59
86
|
}
|
|
60
87
|
if (argv.lint) {
|
|
61
88
|
for (const document of documents) {
|
|
62
|
-
yield
|
|
89
|
+
yield validateApi(document, config.styleguide, externalRefResolver, packageVersion);
|
|
63
90
|
}
|
|
64
91
|
}
|
|
65
|
-
|
|
66
|
-
|
|
92
|
+
const joinedDef = {};
|
|
93
|
+
const potentialConflicts = {
|
|
67
94
|
tags: {},
|
|
68
95
|
paths: {},
|
|
69
96
|
components: {},
|
|
70
|
-
xWebhooks: {}
|
|
97
|
+
xWebhooks: {},
|
|
71
98
|
};
|
|
72
|
-
const prefixComponentsWithInfoProp = argv['prefix-components-with-info-prop'];
|
|
73
|
-
const prefixTagsWithFilename = argv['prefix-tags-with-filename'];
|
|
74
|
-
const prefixTagsWithInfoProp = argv['prefix-tags-with-info-prop'];
|
|
75
|
-
if (prefixTagsWithFilename && prefixTagsWithInfoProp) {
|
|
76
|
-
return utils_1.exitWithError(`You used ${colorette_1.yellow('prefix-tags-with-filename')} and ${colorette_1.yellow('prefix-tags-with-info-prop')} that do not go together.\nPlease choose only one! \n\n`);
|
|
77
|
-
}
|
|
78
99
|
addInfoSectionAndSpecVersion(documents, prefixComponentsWithInfoProp);
|
|
79
100
|
for (const document of documents) {
|
|
80
101
|
const openapi = document.parsed;
|
|
81
102
|
const { tags, info } = openapi;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
84
|
-
const tagsPrefix = prefixTagsWithFilename
|
|
103
|
+
const api = path.relative(process.cwd(), document.source.absoluteRef);
|
|
104
|
+
const apiFilename = getApiFilename(api);
|
|
105
|
+
const tagsPrefix = prefixTagsWithFilename
|
|
106
|
+
? apiFilename
|
|
107
|
+
: getInfoPrefix(info, prefixTagsWithInfoProp, 'tags');
|
|
85
108
|
const componentsPrefix = getInfoPrefix(info, prefixComponentsWithInfoProp, COMPONENTS);
|
|
86
109
|
if (openapi.hasOwnProperty('x-tagGroups')) {
|
|
87
|
-
process.stderr.write(colorette_1.yellow(`warning: x-tagGroups at ${colorette_1.blue(
|
|
110
|
+
process.stderr.write(colorette_1.yellow(`warning: x-tagGroups at ${colorette_1.blue(api)} will be skipped \n`));
|
|
88
111
|
}
|
|
89
|
-
const context = {
|
|
112
|
+
const context = {
|
|
113
|
+
api,
|
|
114
|
+
apiFilename,
|
|
115
|
+
tags,
|
|
116
|
+
potentialConflicts,
|
|
117
|
+
tagsPrefix,
|
|
118
|
+
componentsPrefix,
|
|
119
|
+
};
|
|
90
120
|
if (tags) {
|
|
91
121
|
populateTags(context);
|
|
92
122
|
}
|
|
@@ -100,52 +130,79 @@ function handleJoin(argv, packageVersion) {
|
|
|
100
130
|
replace$Refs(openapi, componentsPrefix);
|
|
101
131
|
}
|
|
102
132
|
}
|
|
103
|
-
iteratePotentialConflicts(potentialConflicts);
|
|
104
|
-
const specFilename = 'openapi.yaml';
|
|
133
|
+
iteratePotentialConflicts(potentialConflicts, withoutXTagGroups);
|
|
105
134
|
const noRefs = true;
|
|
106
|
-
if (
|
|
107
|
-
utils_1.
|
|
135
|
+
if (potentialConflictsTotal) {
|
|
136
|
+
return utils_1.exitWithError(`Please fix conflicts before running ${colorette_1.yellow('join')}.`);
|
|
108
137
|
}
|
|
138
|
+
utils_1.writeYaml(utils_1.sortTopLevelKeysForOas(joinedDef), specFilename, noRefs);
|
|
109
139
|
utils_1.printExecutionTime('join', startedAt, specFilename);
|
|
110
|
-
function populateTags({
|
|
111
|
-
const xTagGroups = 'x-tagGroups';
|
|
112
|
-
const Tags = 'tags';
|
|
140
|
+
function populateTags({ api, apiFilename, tags, potentialConflicts, tagsPrefix, componentsPrefix, }) {
|
|
113
141
|
if (!joinedDef.hasOwnProperty(Tags)) {
|
|
114
142
|
joinedDef[Tags] = [];
|
|
115
143
|
}
|
|
116
|
-
if (!joinedDef.hasOwnProperty(xTagGroups)) {
|
|
117
|
-
joinedDef[xTagGroups] = [];
|
|
118
|
-
}
|
|
119
144
|
if (!potentialConflicts.tags.hasOwnProperty('all')) {
|
|
120
145
|
potentialConflicts.tags['all'] = {};
|
|
121
146
|
}
|
|
122
|
-
if (!
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
const indexGroup = joinedDef[xTagGroups].findIndex((item) => item.name === entrypointFilename);
|
|
126
|
-
if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
|
|
127
|
-
joinedDef[xTagGroups][indexGroup][Tags] = [];
|
|
147
|
+
if (withoutXTagGroups && !potentialConflicts.tags.hasOwnProperty('description')) {
|
|
148
|
+
potentialConflicts.tags['description'] = {};
|
|
128
149
|
}
|
|
129
150
|
for (const tag of tags) {
|
|
130
151
|
const entrypointTagName = addPrefix(tag.name, tagsPrefix);
|
|
131
152
|
if (tag.description) {
|
|
132
153
|
tag.description = addComponentsPrefix(tag.description, componentsPrefix);
|
|
133
154
|
}
|
|
134
|
-
|
|
155
|
+
const tagDuplicate = joinedDef.tags.find((t) => t.name === entrypointTagName);
|
|
156
|
+
if (tagDuplicate && withoutXTagGroups) {
|
|
157
|
+
// If tag already exist and `without-x-tag-groups` option,
|
|
158
|
+
// check if description are different for potential conflicts warning.
|
|
159
|
+
const isTagDescriptionNotEqual = tag.hasOwnProperty('description') && tagDuplicate.description !== tag.description;
|
|
160
|
+
potentialConflicts.tags.description[entrypointTagName].push(...(isTagDescriptionNotEqual ? [api] : []));
|
|
161
|
+
}
|
|
162
|
+
else if (!tagDuplicate) {
|
|
163
|
+
// Instead add tag to joinedDef if there no duplicate;
|
|
135
164
|
tag['x-displayName'] = tag['x-displayName'] || tag.name;
|
|
136
165
|
tag.name = entrypointTagName;
|
|
137
166
|
joinedDef.tags.push(tag);
|
|
167
|
+
if (withoutXTagGroups) {
|
|
168
|
+
potentialConflicts.tags.description[entrypointTagName] = [api];
|
|
169
|
+
}
|
|
138
170
|
}
|
|
139
|
-
if (!
|
|
140
|
-
|
|
171
|
+
if (!withoutXTagGroups) {
|
|
172
|
+
createXTagGroups(apiFilename);
|
|
173
|
+
if (!tagDuplicate) {
|
|
174
|
+
populateXTagGroups(entrypointTagName, getIndexGroup(apiFilename));
|
|
175
|
+
}
|
|
141
176
|
}
|
|
142
|
-
const doesEntrypointExist = !potentialConflicts.tags.all[entrypointTagName] ||
|
|
143
|
-
|
|
177
|
+
const doesEntrypointExist = !potentialConflicts.tags.all[entrypointTagName] ||
|
|
178
|
+
(potentialConflicts.tags.all[entrypointTagName] &&
|
|
179
|
+
!potentialConflicts.tags.all[entrypointTagName].includes(api));
|
|
144
180
|
potentialConflicts.tags.all[entrypointTagName] = [
|
|
145
|
-
...(potentialConflicts.tags.all[entrypointTagName] || []),
|
|
181
|
+
...(potentialConflicts.tags.all[entrypointTagName] || []),
|
|
182
|
+
...(!withoutXTagGroups && doesEntrypointExist ? [api] : []),
|
|
146
183
|
];
|
|
147
184
|
}
|
|
148
185
|
}
|
|
186
|
+
function getIndexGroup(apiFilename) {
|
|
187
|
+
return joinedDef[xTagGroups].findIndex((item) => item.name === apiFilename);
|
|
188
|
+
}
|
|
189
|
+
function createXTagGroups(apiFilename) {
|
|
190
|
+
if (!joinedDef.hasOwnProperty(xTagGroups)) {
|
|
191
|
+
joinedDef[xTagGroups] = [];
|
|
192
|
+
}
|
|
193
|
+
if (!joinedDef[xTagGroups].some((g) => g.name === apiFilename)) {
|
|
194
|
+
joinedDef[xTagGroups].push({ name: apiFilename, tags: [] });
|
|
195
|
+
}
|
|
196
|
+
const indexGroup = getIndexGroup(apiFilename);
|
|
197
|
+
if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
|
|
198
|
+
joinedDef[xTagGroups][indexGroup][Tags] = [];
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function populateXTagGroups(entrypointTagName, indexGroup) {
|
|
202
|
+
if (!joinedDef[xTagGroups][indexGroup][Tags].find((t) => t.name === entrypointTagName)) {
|
|
203
|
+
joinedDef[xTagGroups][indexGroup][Tags].push(entrypointTagName);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
149
206
|
function collectServers(openapi) {
|
|
150
207
|
const { servers } = openapi;
|
|
151
208
|
if (servers) {
|
|
@@ -159,11 +216,10 @@ function handleJoin(argv, packageVersion) {
|
|
|
159
216
|
}
|
|
160
217
|
}
|
|
161
218
|
}
|
|
162
|
-
function collectInfoDescriptions(openapi, {
|
|
219
|
+
function collectInfoDescriptions(openapi, { apiFilename, componentsPrefix }) {
|
|
163
220
|
const { info } = openapi;
|
|
164
221
|
if (info === null || info === void 0 ? void 0 : info.description) {
|
|
165
|
-
const
|
|
166
|
-
const groupIndex = joinedDef[xTagGroups] ? joinedDef[xTagGroups].findIndex((item) => item.name === entrypointFilename) : -1;
|
|
222
|
+
const groupIndex = joinedDef[xTagGroups] ? getIndexGroup(apiFilename) : -1;
|
|
167
223
|
if (joinedDef.hasOwnProperty(xTagGroups) &&
|
|
168
224
|
groupIndex !== -1 &&
|
|
169
225
|
joinedDef[xTagGroups][groupIndex]['tags'] &&
|
|
@@ -172,86 +228,191 @@ function handleJoin(argv, packageVersion) {
|
|
|
172
228
|
}
|
|
173
229
|
}
|
|
174
230
|
}
|
|
175
|
-
function collectExternalDocs(openapi, {
|
|
231
|
+
function collectExternalDocs(openapi, { api }) {
|
|
176
232
|
const { externalDocs } = openapi;
|
|
177
233
|
if (externalDocs) {
|
|
178
234
|
if (joinedDef.hasOwnProperty('externalDocs')) {
|
|
179
|
-
process.stderr.write(colorette_1.yellow(`warning: skip externalDocs from ${colorette_1.blue(path.basename(
|
|
235
|
+
process.stderr.write(colorette_1.yellow(`warning: skip externalDocs from ${colorette_1.blue(path.basename(api))} \n`));
|
|
180
236
|
return;
|
|
181
237
|
}
|
|
182
238
|
joinedDef['externalDocs'] = externalDocs;
|
|
183
239
|
}
|
|
184
240
|
}
|
|
185
|
-
function collectPaths(openapi, {
|
|
241
|
+
function collectPaths(openapi, { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }) {
|
|
186
242
|
const { paths } = openapi;
|
|
243
|
+
const operationsSet = new Set(js_utils_1.keysOf(types_1.OPENAPI3_METHOD));
|
|
187
244
|
if (paths) {
|
|
188
245
|
if (!joinedDef.hasOwnProperty('paths')) {
|
|
189
246
|
joinedDef['paths'] = {};
|
|
190
247
|
}
|
|
191
|
-
for (const path of
|
|
248
|
+
for (const path of js_utils_1.keysOf(paths)) {
|
|
192
249
|
if (!joinedDef.paths.hasOwnProperty(path)) {
|
|
193
250
|
joinedDef.paths[path] = {};
|
|
194
251
|
}
|
|
195
252
|
if (!potentialConflicts.paths.hasOwnProperty(path)) {
|
|
196
253
|
potentialConflicts.paths[path] = {};
|
|
197
254
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
potentialConflicts.paths[path][operation] = [...(potentialConflicts.paths[path][operation] || []), entrypoint];
|
|
203
|
-
const { operationId } = pathOperation;
|
|
204
|
-
if (operationId) {
|
|
205
|
-
if (!potentialConflicts.paths.hasOwnProperty('operationIds')) {
|
|
206
|
-
potentialConflicts.paths['operationIds'] = {};
|
|
207
|
-
}
|
|
208
|
-
potentialConflicts.paths.operationIds[operationId] = [...(potentialConflicts.paths.operationIds[operationId] || []), entrypoint];
|
|
255
|
+
const pathItem = paths[path];
|
|
256
|
+
for (const field of js_utils_1.keysOf(pathItem)) {
|
|
257
|
+
if (operationsSet.has(field)) {
|
|
258
|
+
collectPathOperation(pathItem, path, field);
|
|
209
259
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
joinedDef.paths[path][operation].tags = tags.map((tag) => addPrefix(tag, tagsPrefix));
|
|
213
|
-
populateTags({ entrypoint, entrypointFilename, tags: formatTags(tags), potentialConflicts, tagsPrefix, componentsPrefix });
|
|
260
|
+
if (field === 'servers') {
|
|
261
|
+
collectPathServers(pathItem, path);
|
|
214
262
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
populateTags({ entrypoint, entrypointFilename, tags: formatTags(['other']), potentialConflicts, tagsPrefix: tagsPrefix || entrypointFilename, componentsPrefix });
|
|
263
|
+
if (field === 'parameters') {
|
|
264
|
+
collectPathParameters(pathItem, path);
|
|
218
265
|
}
|
|
219
|
-
if (
|
|
220
|
-
|
|
266
|
+
if (typeof pathItem[field] === 'string') {
|
|
267
|
+
collectPathStringFields(pathItem, path, field);
|
|
221
268
|
}
|
|
222
|
-
|
|
223
|
-
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function collectPathStringFields(pathItem, path, field) {
|
|
273
|
+
const fieldValue = pathItem[field];
|
|
274
|
+
if (joinedDef.paths[path].hasOwnProperty(field) &&
|
|
275
|
+
joinedDef.paths[path][field] !== fieldValue) {
|
|
276
|
+
process.stderr.write(colorette_1.yellow(`warning: different ${field} values in ${path}\n`));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
joinedDef.paths[path][field] = fieldValue;
|
|
280
|
+
}
|
|
281
|
+
function collectPathServers(pathItem, path) {
|
|
282
|
+
if (!pathItem.servers) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (!joinedDef.paths[path].hasOwnProperty('servers')) {
|
|
286
|
+
joinedDef.paths[path].servers = [];
|
|
287
|
+
}
|
|
288
|
+
for (const server of pathItem.servers) {
|
|
289
|
+
let isFoundServer = false;
|
|
290
|
+
for (const pathServer of joinedDef.paths[path].servers) {
|
|
291
|
+
if (pathServer.url === server.url) {
|
|
292
|
+
if (!isServersEqual(pathServer, server)) {
|
|
293
|
+
utils_1.exitWithError(`Different server values for (${server.url}) in ${path}`);
|
|
294
|
+
}
|
|
295
|
+
isFoundServer = true;
|
|
224
296
|
}
|
|
225
297
|
}
|
|
298
|
+
if (!isFoundServer) {
|
|
299
|
+
joinedDef.paths[path].servers.push(server);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function collectPathParameters(pathItem, path) {
|
|
304
|
+
if (!pathItem.parameters) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
if (!joinedDef.paths[path].hasOwnProperty('parameters')) {
|
|
308
|
+
joinedDef.paths[path].parameters = [];
|
|
309
|
+
}
|
|
310
|
+
for (const parameter of pathItem.parameters) {
|
|
311
|
+
let isFoundParameter = false;
|
|
312
|
+
for (const pathParameter of joinedDef.paths[path]
|
|
313
|
+
.parameters) {
|
|
314
|
+
// Compare $ref only if both are reference objects
|
|
315
|
+
if (openapi_core_1.isRef(pathParameter) && openapi_core_1.isRef(parameter)) {
|
|
316
|
+
if (pathParameter['$ref'] === parameter['$ref']) {
|
|
317
|
+
isFoundParameter = true;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Compare properties only if both are reference objects
|
|
321
|
+
if (!openapi_core_1.isRef(pathParameter) && !openapi_core_1.isRef(parameter)) {
|
|
322
|
+
if (pathParameter.name === parameter.name && pathParameter.in === parameter.in) {
|
|
323
|
+
if (!isEqual(pathParameter.schema, parameter.schema)) {
|
|
324
|
+
utils_1.exitWithError(`Different parameter schemas for (${parameter.name}) in ${path}`);
|
|
325
|
+
}
|
|
326
|
+
isFoundParameter = true;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
if (!isFoundParameter) {
|
|
331
|
+
joinedDef.paths[path].parameters.push(parameter);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function collectPathOperation(pathItem, path, operation) {
|
|
336
|
+
const pathOperation = pathItem[operation];
|
|
337
|
+
if (!pathOperation) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
joinedDef.paths[path][operation] = pathOperation;
|
|
341
|
+
potentialConflicts.paths[path][operation] = [
|
|
342
|
+
...(potentialConflicts.paths[path][operation] || []),
|
|
343
|
+
api,
|
|
344
|
+
];
|
|
345
|
+
const { operationId } = pathOperation;
|
|
346
|
+
if (operationId) {
|
|
347
|
+
if (!potentialConflicts.paths.hasOwnProperty('operationIds')) {
|
|
348
|
+
potentialConflicts.paths['operationIds'] = {};
|
|
349
|
+
}
|
|
350
|
+
potentialConflicts.paths.operationIds[operationId] = [
|
|
351
|
+
...(potentialConflicts.paths.operationIds[operationId] || []),
|
|
352
|
+
api,
|
|
353
|
+
];
|
|
354
|
+
}
|
|
355
|
+
const { tags, security } = joinedDef.paths[path][operation];
|
|
356
|
+
if (tags) {
|
|
357
|
+
joinedDef.paths[path][operation].tags = tags.map((tag) => addPrefix(tag, tagsPrefix));
|
|
358
|
+
populateTags({
|
|
359
|
+
api,
|
|
360
|
+
apiFilename,
|
|
361
|
+
tags: formatTags(tags),
|
|
362
|
+
potentialConflicts,
|
|
363
|
+
tagsPrefix,
|
|
364
|
+
componentsPrefix,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
joinedDef.paths[path][operation]['tags'] = [addPrefix('other', tagsPrefix || apiFilename)];
|
|
369
|
+
populateTags({
|
|
370
|
+
api,
|
|
371
|
+
apiFilename,
|
|
372
|
+
tags: formatTags(['other']),
|
|
373
|
+
potentialConflicts,
|
|
374
|
+
tagsPrefix: tagsPrefix || apiFilename,
|
|
375
|
+
componentsPrefix,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
if (!security && openapi.hasOwnProperty('security')) {
|
|
379
|
+
joinedDef.paths[path][operation]['security'] = addSecurityPrefix(openapi.security, componentsPrefix);
|
|
380
|
+
}
|
|
381
|
+
else if (pathOperation.security) {
|
|
382
|
+
joinedDef.paths[path][operation].security = addSecurityPrefix(pathOperation.security, componentsPrefix);
|
|
226
383
|
}
|
|
227
384
|
}
|
|
228
385
|
}
|
|
229
|
-
function
|
|
386
|
+
function isServersEqual(serverOne, serverTwo) {
|
|
387
|
+
if (serverOne.description === serverTwo.description) {
|
|
388
|
+
return isEqual(serverOne.variables, serverTwo.variables);
|
|
389
|
+
}
|
|
390
|
+
return false;
|
|
391
|
+
}
|
|
392
|
+
function collectComponents(openapi, { api, potentialConflicts, componentsPrefix }) {
|
|
230
393
|
const { components } = openapi;
|
|
231
394
|
if (components) {
|
|
232
395
|
if (!joinedDef.hasOwnProperty(COMPONENTS)) {
|
|
233
396
|
joinedDef[COMPONENTS] = {};
|
|
234
397
|
}
|
|
235
|
-
for (const component of Object.
|
|
398
|
+
for (const [component, componentObj] of Object.entries(components)) {
|
|
236
399
|
if (!potentialConflicts[COMPONENTS].hasOwnProperty(component)) {
|
|
237
400
|
potentialConflicts[COMPONENTS][component] = {};
|
|
238
401
|
joinedDef[COMPONENTS][component] = {};
|
|
239
402
|
}
|
|
240
|
-
// @ts-ignore
|
|
241
|
-
const componentObj = components[component];
|
|
242
403
|
for (const item of Object.keys(componentObj)) {
|
|
243
404
|
const componentPrefix = addPrefix(item, componentsPrefix);
|
|
244
405
|
potentialConflicts.components[component][componentPrefix] = [
|
|
245
|
-
...(potentialConflicts.components[component][item] || []),
|
|
406
|
+
...(potentialConflicts.components[component][item] || []),
|
|
407
|
+
{ [api]: componentObj[item] },
|
|
246
408
|
];
|
|
247
409
|
joinedDef.components[component][componentPrefix] = componentObj[item];
|
|
248
410
|
}
|
|
249
411
|
}
|
|
250
412
|
}
|
|
251
413
|
}
|
|
252
|
-
function collectXWebhooks(openapi, {
|
|
414
|
+
function collectXWebhooks(openapi, { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }) {
|
|
253
415
|
const xWebhooks = 'x-webhooks';
|
|
254
|
-
// @ts-ignore
|
|
255
416
|
const openapiXWebhooks = openapi[xWebhooks];
|
|
256
417
|
if (openapiXWebhooks) {
|
|
257
418
|
if (!joinedDef.hasOwnProperty(xWebhooks)) {
|
|
@@ -263,13 +424,23 @@ function handleJoin(argv, packageVersion) {
|
|
|
263
424
|
potentialConflicts.xWebhooks[webhook] = {};
|
|
264
425
|
}
|
|
265
426
|
for (const operation of Object.keys(openapiXWebhooks[webhook])) {
|
|
266
|
-
potentialConflicts.xWebhooks[webhook][operation] = [
|
|
427
|
+
potentialConflicts.xWebhooks[webhook][operation] = [
|
|
428
|
+
...(potentialConflicts.xWebhooks[webhook][operation] || []),
|
|
429
|
+
api,
|
|
430
|
+
];
|
|
267
431
|
}
|
|
268
432
|
for (const operationKey of Object.keys(joinedDef[xWebhooks][webhook])) {
|
|
269
|
-
|
|
433
|
+
const { tags } = joinedDef[xWebhooks][webhook][operationKey];
|
|
270
434
|
if (tags) {
|
|
271
435
|
joinedDef[xWebhooks][webhook][operationKey].tags = tags.map((tag) => addPrefix(tag, tagsPrefix));
|
|
272
|
-
populateTags({
|
|
436
|
+
populateTags({
|
|
437
|
+
api,
|
|
438
|
+
apiFilename,
|
|
439
|
+
tags: formatTags(tags),
|
|
440
|
+
potentialConflicts,
|
|
441
|
+
tagsPrefix,
|
|
442
|
+
componentsPrefix,
|
|
443
|
+
});
|
|
273
444
|
}
|
|
274
445
|
}
|
|
275
446
|
}
|
|
@@ -277,8 +448,8 @@ function handleJoin(argv, packageVersion) {
|
|
|
277
448
|
}
|
|
278
449
|
function addInfoSectionAndSpecVersion(documents, prefixComponentsWithInfoProp) {
|
|
279
450
|
var _a;
|
|
280
|
-
const
|
|
281
|
-
const openapi =
|
|
451
|
+
const firstApi = documents[0];
|
|
452
|
+
const openapi = firstApi.parsed;
|
|
282
453
|
const componentsPrefix = getInfoPrefix(openapi.info, prefixComponentsWithInfoProp, COMPONENTS);
|
|
283
454
|
if (!openapi.openapi)
|
|
284
455
|
utils_1.exitWithError('Version of specification is not found in. \n');
|
|
@@ -299,14 +470,14 @@ function doesComponentsDiffer(curr, next) {
|
|
|
299
470
|
function validateComponentsDifference(files) {
|
|
300
471
|
let isDiffer = false;
|
|
301
472
|
for (let i = 0, len = files.length; i < len; i++) {
|
|
302
|
-
|
|
473
|
+
const next = files[i + 1];
|
|
303
474
|
if (next && doesComponentsDiffer(files[i], next)) {
|
|
304
475
|
isDiffer = true;
|
|
305
476
|
}
|
|
306
477
|
}
|
|
307
478
|
return isDiffer;
|
|
308
479
|
}
|
|
309
|
-
function iteratePotentialConflicts(potentialConflicts) {
|
|
480
|
+
function iteratePotentialConflicts(potentialConflicts, withoutXTagGroups) {
|
|
310
481
|
for (const group of Object.keys(potentialConflicts)) {
|
|
311
482
|
for (const [key, value] of Object.entries(potentialConflicts[group])) {
|
|
312
483
|
const conflicts = filterConflicts(value);
|
|
@@ -321,20 +492,28 @@ function iteratePotentialConflicts(potentialConflicts) {
|
|
|
321
492
|
}
|
|
322
493
|
}
|
|
323
494
|
else {
|
|
324
|
-
|
|
325
|
-
|
|
495
|
+
if (withoutXTagGroups && group === 'tags') {
|
|
496
|
+
duplicateTagDescriptionWarning(conflicts);
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
potentialConflictsTotal += conflicts.length;
|
|
500
|
+
showConflicts(colorette_1.green(group) + ' => ' + key, conflicts);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
if (group === 'tags' && !withoutXTagGroups) {
|
|
504
|
+
prefixTagSuggestion(conflicts.length);
|
|
326
505
|
}
|
|
327
|
-
prefixTagSuggestion(group, conflicts.length);
|
|
328
506
|
}
|
|
329
507
|
}
|
|
330
508
|
}
|
|
331
509
|
}
|
|
332
|
-
function
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
${
|
|
336
|
-
|
|
337
|
-
|
|
510
|
+
function duplicateTagDescriptionWarning(conflicts) {
|
|
511
|
+
const tagsKeys = conflicts.map(([tagName]) => `\`${tagName}\``);
|
|
512
|
+
const joinString = colorette_1.yellow(', ');
|
|
513
|
+
process.stderr.write(colorette_1.yellow(`\nwarning: ${tagsKeys.length} conflict(s) on the ${colorette_1.red(tagsKeys.join(joinString))} tags description.\n`));
|
|
514
|
+
}
|
|
515
|
+
function prefixTagSuggestion(conflictsLength) {
|
|
516
|
+
process.stderr.write(colorette_1.green(`\n${conflictsLength} conflict(s) on tags.\nSuggestion: please use ${colorette_1.blue('prefix-tags-with-filename')}, ${colorette_1.blue('prefix-tags-with-info-prop')} or ${colorette_1.blue('without-x-tag-groups')} to prevent naming conflicts.\n\n`));
|
|
338
517
|
}
|
|
339
518
|
function showConflicts(key, conflicts) {
|
|
340
519
|
for (const [path, files] of conflicts) {
|
|
@@ -344,7 +523,7 @@ function showConflicts(key, conflicts) {
|
|
|
344
523
|
function filterConflicts(entities) {
|
|
345
524
|
return Object.entries(entities).filter(([_, files]) => files.length > 1);
|
|
346
525
|
}
|
|
347
|
-
function
|
|
526
|
+
function getApiFilename(filePath) {
|
|
348
527
|
return path.basename(filePath, path.extname(filePath));
|
|
349
528
|
}
|
|
350
529
|
function addPrefix(tag, tagsPrefix) {
|
|
@@ -360,10 +539,12 @@ function addComponentsPrefix(description, componentsPrefix) {
|
|
|
360
539
|
});
|
|
361
540
|
}
|
|
362
541
|
function addSecurityPrefix(security, componentsPrefix) {
|
|
363
|
-
return componentsPrefix
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
542
|
+
return componentsPrefix
|
|
543
|
+
? security === null || security === void 0 ? void 0 : security.map((s) => {
|
|
544
|
+
const key = Object.keys(s)[0];
|
|
545
|
+
return { [componentsPrefix + '_' + key]: s[key] };
|
|
546
|
+
})
|
|
547
|
+
: security;
|
|
367
548
|
}
|
|
368
549
|
function getInfoPrefix(info, prefixArg, type) {
|
|
369
550
|
if (!prefixArg)
|
|
@@ -378,7 +559,7 @@ function getInfoPrefix(info, prefixArg, type) {
|
|
|
378
559
|
utils_1.exitWithError(`${colorette_1.yellow(`prefix-${type}-with-info-prop`)} argument value length should not exceed 50 characters. \n\n`);
|
|
379
560
|
return info[prefixArg];
|
|
380
561
|
}
|
|
381
|
-
function
|
|
562
|
+
function validateApi(document, config, externalRefResolver, packageVersion) {
|
|
382
563
|
return __awaiter(this, void 0, void 0, function* () {
|
|
383
564
|
try {
|
|
384
565
|
const results = yield openapi_core_1.lintDocument({ document, config, externalRefResolver });
|
|
@@ -411,9 +592,14 @@ function replace$Refs(obj, componentsPrefix) {
|
|
|
411
592
|
const { mapping } = node.discriminator;
|
|
412
593
|
for (const name of Object.keys(mapping)) {
|
|
413
594
|
if (js_utils_1.isString(mapping[name]) && mapping[name].startsWith(`#/${COMPONENTS}/`)) {
|
|
414
|
-
mapping[name] = mapping[name]
|
|
415
|
-
|
|
416
|
-
|
|
595
|
+
mapping[name] = mapping[name]
|
|
596
|
+
.split('/')
|
|
597
|
+
.map((name, i, arr) => {
|
|
598
|
+
return arr.length - 1 === i && !name.includes(componentsPrefix)
|
|
599
|
+
? componentsPrefix + '_' + name
|
|
600
|
+
: name;
|
|
601
|
+
})
|
|
602
|
+
.join('/');
|
|
417
603
|
}
|
|
418
604
|
}
|
|
419
605
|
}
|
package/lib/commands/lint.d.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
'
|
|
7
|
-
'skip-preprocessor'?: string[];
|
|
1
|
+
import { Config } from '@redocly/openapi-core';
|
|
2
|
+
import type { OutputFormat, RawConfig, RuleSeverity } from '@redocly/openapi-core';
|
|
3
|
+
import type { CommandOptions, Skips } from '../types';
|
|
4
|
+
export declare type LintOptions = {
|
|
5
|
+
apis?: string[];
|
|
6
|
+
'max-problems': number;
|
|
8
7
|
extends?: string[];
|
|
9
8
|
config?: string;
|
|
10
9
|
format: OutputFormat;
|
|
11
|
-
|
|
10
|
+
'generate-ignore-file'?: boolean;
|
|
11
|
+
'lint-config'?: RuleSeverity;
|
|
12
|
+
} & Omit<Skips, 'skip-decorator'>;
|
|
13
|
+
export declare function handleLint(argv: LintOptions, config: Config, version: string): Promise<void>;
|
|
14
|
+
export declare function lintConfigCallback(argv: CommandOptions & Record<string, undefined>, version: string): ((config: RawConfig) => Promise<void>) | undefined;
|