@redocly/cli 1.0.0-beta.96
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/README.md +39 -0
- package/bin/cli.js +3 -0
- package/lib/__mocks__/utils.d.ts +17 -0
- package/lib/__mocks__/utils.js +14 -0
- package/lib/__tests__/commands/bundle.test.d.ts +1 -0
- package/lib/__tests__/commands/bundle.test.js +92 -0
- package/lib/__tests__/commands/push-region.test.d.ts +1 -0
- package/lib/__tests__/commands/push-region.test.js +55 -0
- package/lib/__tests__/commands/push.test.d.ts +1 -0
- package/lib/__tests__/commands/push.test.js +153 -0
- package/lib/__tests__/utils.test.d.ts +1 -0
- package/lib/__tests__/utils.test.js +41 -0
- package/lib/assert-node-version.d.ts +1 -0
- package/lib/assert-node-version.js +10 -0
- package/lib/commands/bundle.d.ts +19 -0
- package/lib/commands/bundle.js +128 -0
- package/lib/commands/join.d.ts +7 -0
- package/lib/commands/join.js +421 -0
- package/lib/commands/lint.d.ts +11 -0
- package/lib/commands/lint.js +80 -0
- package/lib/commands/login.d.ts +6 -0
- package/lib/commands/login.js +28 -0
- package/lib/commands/preview-docs/index.d.ts +12 -0
- package/lib/commands/preview-docs/index.js +141 -0
- package/lib/commands/preview-docs/preview-server/default.hbs +24 -0
- package/lib/commands/preview-docs/preview-server/hot.js +42 -0
- package/lib/commands/preview-docs/preview-server/oauth2-redirect.html +21 -0
- package/lib/commands/preview-docs/preview-server/preview-server.d.ts +5 -0
- package/lib/commands/preview-docs/preview-server/preview-server.js +120 -0
- package/lib/commands/preview-docs/preview-server/server.d.ts +23 -0
- package/lib/commands/preview-docs/preview-server/server.js +85 -0
- package/lib/commands/push.d.ts +25 -0
- package/lib/commands/push.js +247 -0
- package/lib/commands/split/__tests__/index.test.d.ts +1 -0
- package/lib/commands/split/__tests__/index.test.js +70 -0
- package/lib/commands/split/index.d.ts +8 -0
- package/lib/commands/split/index.js +279 -0
- package/lib/commands/split/types.d.ts +37 -0
- package/lib/commands/split/types.js +52 -0
- package/lib/commands/stats.d.ts +5 -0
- package/lib/commands/stats.js +92 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +269 -0
- package/lib/js-utils.d.ts +3 -0
- package/lib/js-utils.js +16 -0
- package/lib/types.d.ts +13 -0
- package/lib/types.js +5 -0
- package/lib/utils.d.ts +28 -0
- package/lib/utils.js +260 -0
- package/package.json +54 -0
- package/src/__mocks__/utils.ts +11 -0
- package/src/__tests__/commands/bundle.test.ts +120 -0
- package/src/__tests__/commands/push-region.test.ts +51 -0
- package/src/__tests__/commands/push.test.ts +156 -0
- package/src/__tests__/utils.test.ts +50 -0
- package/src/assert-node-version.ts +8 -0
- package/src/commands/bundle.ts +178 -0
- package/src/commands/join.ts +488 -0
- package/src/commands/lint.ts +110 -0
- package/src/commands/login.ts +19 -0
- package/src/commands/preview-docs/index.ts +188 -0
- package/src/commands/preview-docs/preview-server/default.hbs +24 -0
- package/src/commands/preview-docs/preview-server/hot.js +42 -0
- package/src/commands/preview-docs/preview-server/oauth2-redirect.html +21 -0
- package/src/commands/preview-docs/preview-server/preview-server.ts +150 -0
- package/src/commands/preview-docs/preview-server/server.ts +91 -0
- package/src/commands/push.ts +355 -0
- package/src/commands/split/__tests__/fixtures/spec.json +70 -0
- package/src/commands/split/__tests__/fixtures/webhooks.json +88 -0
- package/src/commands/split/__tests__/index.test.ts +96 -0
- package/src/commands/split/index.ts +349 -0
- package/src/commands/split/types.ts +73 -0
- package/src/commands/stats.ts +115 -0
- package/src/index.ts +311 -0
- package/src/js-utils.ts +12 -0
- package/src/types.ts +13 -0
- package/src/utils.ts +300 -0
- package/tsconfig.json +9 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handleJoin = void 0;
|
|
13
|
+
const path = require("path");
|
|
14
|
+
const colorette_1 = require("colorette");
|
|
15
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
16
|
+
const isEqual = require('lodash.isequal');
|
|
17
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
18
|
+
const utils_1 = require("../utils");
|
|
19
|
+
const js_utils_1 = require("../js-utils");
|
|
20
|
+
const COMPONENTS = 'components';
|
|
21
|
+
let potentialConflictsTotal = 0;
|
|
22
|
+
function handleJoin(argv, packageVersion) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const startedAt = perf_hooks_1.performance.now();
|
|
25
|
+
if (argv.entrypoints.length < 2) {
|
|
26
|
+
return utils_1.exitWithError(`At least 2 entrypoints should be provided. \n\n`);
|
|
27
|
+
}
|
|
28
|
+
const config = yield openapi_core_1.loadConfig();
|
|
29
|
+
const entrypoints = yield utils_1.getFallbackEntryPointsOrExit(argv.entrypoints, config);
|
|
30
|
+
const externalRefResolver = new openapi_core_1.BaseResolver(config.resolve);
|
|
31
|
+
const documents = yield Promise.all(entrypoints.map(({ path }) => externalRefResolver.resolveDocument(null, path, true)));
|
|
32
|
+
const bundleResults = yield Promise.all(documents.map(document => openapi_core_1.bundleDocument({
|
|
33
|
+
document,
|
|
34
|
+
config: config.lint,
|
|
35
|
+
externalRefResolver
|
|
36
|
+
}).catch(e => {
|
|
37
|
+
utils_1.exitWithError(`${e.message}: ${colorette_1.blue(document.source.absoluteRef)}`);
|
|
38
|
+
})));
|
|
39
|
+
for (const { problems, bundle: document } of bundleResults) {
|
|
40
|
+
const fileTotals = openapi_core_1.getTotals(problems);
|
|
41
|
+
if (fileTotals.errors) {
|
|
42
|
+
openapi_core_1.formatProblems(problems, {
|
|
43
|
+
totals: fileTotals,
|
|
44
|
+
version: document.parsed.version
|
|
45
|
+
});
|
|
46
|
+
utils_1.exitWithError(`❌ Errors encountered while bundling ${colorette_1.blue(document.source.absoluteRef)}: join will not proceed.\n`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const document of documents) {
|
|
50
|
+
try {
|
|
51
|
+
const version = openapi_core_1.detectOpenAPI(document.parsed);
|
|
52
|
+
if (version !== openapi_core_1.OasVersion.Version3_0) {
|
|
53
|
+
return utils_1.exitWithError(`Only OpenAPI 3 is supported: ${colorette_1.blue(document.source.absoluteRef)} \n\n`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
return utils_1.exitWithError(`${e.message}: ${colorette_1.blue(document.source.absoluteRef)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (argv.lint) {
|
|
61
|
+
for (const document of documents) {
|
|
62
|
+
yield validateEntrypoint(document, config.lint, externalRefResolver, packageVersion);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
let joinedDef = {};
|
|
66
|
+
let potentialConflicts = {
|
|
67
|
+
tags: {},
|
|
68
|
+
paths: {},
|
|
69
|
+
components: {},
|
|
70
|
+
xWebhooks: {}
|
|
71
|
+
};
|
|
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
|
+
addInfoSectionAndSpecVersion(documents, prefixComponentsWithInfoProp);
|
|
79
|
+
for (const document of documents) {
|
|
80
|
+
const openapi = document.parsed;
|
|
81
|
+
const { tags, info } = openapi;
|
|
82
|
+
const entrypoint = path.relative(process.cwd(), document.source.absoluteRef);
|
|
83
|
+
const entrypointFilename = getEntrypointFilename(entrypoint);
|
|
84
|
+
const tagsPrefix = prefixTagsWithFilename ? entrypointFilename : getInfoPrefix(info, prefixTagsWithInfoProp, 'tags');
|
|
85
|
+
const componentsPrefix = getInfoPrefix(info, prefixComponentsWithInfoProp, COMPONENTS);
|
|
86
|
+
if (openapi.hasOwnProperty('x-tagGroups')) {
|
|
87
|
+
process.stderr.write(colorette_1.yellow(`warning: x-tagGroups at ${colorette_1.blue(entrypoint)} will be skipped \n`));
|
|
88
|
+
}
|
|
89
|
+
const context = { entrypoint, entrypointFilename, tags, potentialConflicts, tagsPrefix, componentsPrefix };
|
|
90
|
+
if (tags) {
|
|
91
|
+
populateTags(context);
|
|
92
|
+
}
|
|
93
|
+
collectServers(openapi);
|
|
94
|
+
collectInfoDescriptions(openapi, context);
|
|
95
|
+
collectExternalDocs(openapi, context);
|
|
96
|
+
collectPaths(openapi, context);
|
|
97
|
+
collectComponents(openapi, context);
|
|
98
|
+
collectXWebhooks(openapi, context);
|
|
99
|
+
if (componentsPrefix) {
|
|
100
|
+
replace$Refs(openapi, componentsPrefix);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
iteratePotentialConflicts(potentialConflicts);
|
|
104
|
+
const specFilename = 'openapi.yaml';
|
|
105
|
+
const noRefs = true;
|
|
106
|
+
if (!potentialConflictsTotal) {
|
|
107
|
+
utils_1.writeYaml(joinedDef, specFilename, noRefs);
|
|
108
|
+
}
|
|
109
|
+
utils_1.printExecutionTime('join', startedAt, specFilename);
|
|
110
|
+
function populateTags({ entrypoint, entrypointFilename, tags, potentialConflicts, tagsPrefix, componentsPrefix }) {
|
|
111
|
+
const xTagGroups = 'x-tagGroups';
|
|
112
|
+
const Tags = 'tags';
|
|
113
|
+
if (!joinedDef.hasOwnProperty(Tags)) {
|
|
114
|
+
joinedDef[Tags] = [];
|
|
115
|
+
}
|
|
116
|
+
if (!joinedDef.hasOwnProperty(xTagGroups)) {
|
|
117
|
+
joinedDef[xTagGroups] = [];
|
|
118
|
+
}
|
|
119
|
+
if (!potentialConflicts.tags.hasOwnProperty('all')) {
|
|
120
|
+
potentialConflicts.tags['all'] = {};
|
|
121
|
+
}
|
|
122
|
+
if (!joinedDef[xTagGroups].some((g) => g.name === entrypointFilename)) {
|
|
123
|
+
joinedDef[xTagGroups].push({ name: entrypointFilename, tags: [] });
|
|
124
|
+
}
|
|
125
|
+
const indexGroup = joinedDef[xTagGroups].findIndex((item) => item.name === entrypointFilename);
|
|
126
|
+
if (!joinedDef[xTagGroups][indexGroup].hasOwnProperty(Tags)) {
|
|
127
|
+
joinedDef[xTagGroups][indexGroup][Tags] = [];
|
|
128
|
+
}
|
|
129
|
+
for (const tag of tags) {
|
|
130
|
+
const entrypointTagName = addPrefix(tag.name, tagsPrefix);
|
|
131
|
+
if (tag.description) {
|
|
132
|
+
tag.description = addComponentsPrefix(tag.description, componentsPrefix);
|
|
133
|
+
}
|
|
134
|
+
if (!joinedDef.tags.find((t) => t.name === entrypointTagName)) {
|
|
135
|
+
tag['x-displayName'] = tag['x-displayName'] || tag.name;
|
|
136
|
+
tag.name = entrypointTagName;
|
|
137
|
+
joinedDef.tags.push(tag);
|
|
138
|
+
}
|
|
139
|
+
if (!joinedDef[xTagGroups][indexGroup][Tags].find((t) => t === entrypointTagName)) {
|
|
140
|
+
joinedDef[xTagGroups][indexGroup][Tags].push(entrypointTagName);
|
|
141
|
+
}
|
|
142
|
+
const doesEntrypointExist = !potentialConflicts.tags.all[entrypointTagName] || (potentialConflicts.tags.all[entrypointTagName] &&
|
|
143
|
+
!potentialConflicts.tags.all[entrypointTagName].includes(entrypoint));
|
|
144
|
+
potentialConflicts.tags.all[entrypointTagName] = [
|
|
145
|
+
...(potentialConflicts.tags.all[entrypointTagName] || []), ...(doesEntrypointExist ? [entrypoint] : [])
|
|
146
|
+
];
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function collectServers(openapi) {
|
|
150
|
+
const { servers } = openapi;
|
|
151
|
+
if (servers) {
|
|
152
|
+
if (!joinedDef.hasOwnProperty('servers')) {
|
|
153
|
+
joinedDef['servers'] = [];
|
|
154
|
+
}
|
|
155
|
+
for (const server of servers) {
|
|
156
|
+
if (!joinedDef.servers.some((s) => s.url === server.url)) {
|
|
157
|
+
joinedDef.servers.push(server);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function collectInfoDescriptions(openapi, { entrypointFilename, componentsPrefix }) {
|
|
163
|
+
const { info } = openapi;
|
|
164
|
+
if (info === null || info === void 0 ? void 0 : info.description) {
|
|
165
|
+
const xTagGroups = 'x-tagGroups';
|
|
166
|
+
const groupIndex = joinedDef[xTagGroups] ? joinedDef[xTagGroups].findIndex((item) => item.name === entrypointFilename) : -1;
|
|
167
|
+
if (joinedDef.hasOwnProperty(xTagGroups) &&
|
|
168
|
+
groupIndex !== -1 &&
|
|
169
|
+
joinedDef[xTagGroups][groupIndex]['tags'] &&
|
|
170
|
+
joinedDef[xTagGroups][groupIndex]['tags'].length) {
|
|
171
|
+
joinedDef[xTagGroups][groupIndex]['description'] = addComponentsPrefix(info.description, componentsPrefix);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function collectExternalDocs(openapi, { entrypoint }) {
|
|
176
|
+
const { externalDocs } = openapi;
|
|
177
|
+
if (externalDocs) {
|
|
178
|
+
if (joinedDef.hasOwnProperty('externalDocs')) {
|
|
179
|
+
process.stderr.write(colorette_1.yellow(`warning: skip externalDocs from ${colorette_1.blue(path.basename(entrypoint))} \n`));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
joinedDef['externalDocs'] = externalDocs;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function collectPaths(openapi, { entrypointFilename, entrypoint, potentialConflicts, tagsPrefix, componentsPrefix }) {
|
|
186
|
+
const { paths } = openapi;
|
|
187
|
+
if (paths) {
|
|
188
|
+
if (!joinedDef.hasOwnProperty('paths')) {
|
|
189
|
+
joinedDef['paths'] = {};
|
|
190
|
+
}
|
|
191
|
+
for (const path of Object.keys(paths)) {
|
|
192
|
+
if (!joinedDef.paths.hasOwnProperty(path)) {
|
|
193
|
+
joinedDef.paths[path] = {};
|
|
194
|
+
}
|
|
195
|
+
if (!potentialConflicts.paths.hasOwnProperty(path)) {
|
|
196
|
+
potentialConflicts.paths[path] = {};
|
|
197
|
+
}
|
|
198
|
+
for (const operation of Object.keys(paths[path])) {
|
|
199
|
+
// @ts-ignore
|
|
200
|
+
const pathOperation = paths[path][operation];
|
|
201
|
+
joinedDef.paths[path][operation] = pathOperation;
|
|
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];
|
|
209
|
+
}
|
|
210
|
+
let { tags, security } = joinedDef.paths[path][operation];
|
|
211
|
+
if (tags) {
|
|
212
|
+
joinedDef.paths[path][operation].tags = tags.map((tag) => addPrefix(tag, tagsPrefix));
|
|
213
|
+
populateTags({ entrypoint, entrypointFilename, tags: formatTags(tags), potentialConflicts, tagsPrefix, componentsPrefix });
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
joinedDef.paths[path][operation]['tags'] = [addPrefix('other', tagsPrefix || entrypointFilename)];
|
|
217
|
+
populateTags({ entrypoint, entrypointFilename, tags: formatTags(['other']), potentialConflicts, tagsPrefix: tagsPrefix || entrypointFilename, componentsPrefix });
|
|
218
|
+
}
|
|
219
|
+
if (!security && openapi.hasOwnProperty('security')) {
|
|
220
|
+
joinedDef.paths[path][operation]['security'] = addSecurityPrefix(openapi.security, componentsPrefix);
|
|
221
|
+
}
|
|
222
|
+
else if (pathOperation.security) {
|
|
223
|
+
joinedDef.paths[path][operation].security = addSecurityPrefix(pathOperation.security, componentsPrefix);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function collectComponents(openapi, { entrypoint, potentialConflicts, componentsPrefix }) {
|
|
230
|
+
const { components } = openapi;
|
|
231
|
+
if (components) {
|
|
232
|
+
if (!joinedDef.hasOwnProperty(COMPONENTS)) {
|
|
233
|
+
joinedDef[COMPONENTS] = {};
|
|
234
|
+
}
|
|
235
|
+
for (const component of Object.keys(components)) {
|
|
236
|
+
if (!potentialConflicts[COMPONENTS].hasOwnProperty(component)) {
|
|
237
|
+
potentialConflicts[COMPONENTS][component] = {};
|
|
238
|
+
joinedDef[COMPONENTS][component] = {};
|
|
239
|
+
}
|
|
240
|
+
// @ts-ignore
|
|
241
|
+
const componentObj = components[component];
|
|
242
|
+
for (const item of Object.keys(componentObj)) {
|
|
243
|
+
const componentPrefix = addPrefix(item, componentsPrefix);
|
|
244
|
+
potentialConflicts.components[component][componentPrefix] = [
|
|
245
|
+
...(potentialConflicts.components[component][item] || []), { [entrypoint]: componentObj[item] }
|
|
246
|
+
];
|
|
247
|
+
joinedDef.components[component][componentPrefix] = componentObj[item];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
function collectXWebhooks(openapi, { entrypointFilename, entrypoint, potentialConflicts, tagsPrefix, componentsPrefix }) {
|
|
253
|
+
const xWebhooks = 'x-webhooks';
|
|
254
|
+
// @ts-ignore
|
|
255
|
+
const openapiXWebhooks = openapi[xWebhooks];
|
|
256
|
+
if (openapiXWebhooks) {
|
|
257
|
+
if (!joinedDef.hasOwnProperty(xWebhooks)) {
|
|
258
|
+
joinedDef[xWebhooks] = {};
|
|
259
|
+
}
|
|
260
|
+
for (const webhook of Object.keys(openapiXWebhooks)) {
|
|
261
|
+
joinedDef[xWebhooks][webhook] = openapiXWebhooks[webhook];
|
|
262
|
+
if (!potentialConflicts.xWebhooks.hasOwnProperty(webhook)) {
|
|
263
|
+
potentialConflicts.xWebhooks[webhook] = {};
|
|
264
|
+
}
|
|
265
|
+
for (const operation of Object.keys(openapiXWebhooks[webhook])) {
|
|
266
|
+
potentialConflicts.xWebhooks[webhook][operation] = [...(potentialConflicts.xWebhooks[webhook][operation] || []), entrypoint];
|
|
267
|
+
}
|
|
268
|
+
for (const operationKey of Object.keys(joinedDef[xWebhooks][webhook])) {
|
|
269
|
+
let { tags } = joinedDef[xWebhooks][webhook][operationKey];
|
|
270
|
+
if (tags) {
|
|
271
|
+
joinedDef[xWebhooks][webhook][operationKey].tags = tags.map((tag) => addPrefix(tag, tagsPrefix));
|
|
272
|
+
populateTags({ entrypoint, entrypointFilename, tags: formatTags(tags), potentialConflicts, tagsPrefix, componentsPrefix });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function addInfoSectionAndSpecVersion(documents, prefixComponentsWithInfoProp) {
|
|
279
|
+
var _a;
|
|
280
|
+
const firstEntrypoint = documents[0];
|
|
281
|
+
const openapi = firstEntrypoint.parsed;
|
|
282
|
+
const componentsPrefix = getInfoPrefix(openapi.info, prefixComponentsWithInfoProp, COMPONENTS);
|
|
283
|
+
if (!openapi.openapi)
|
|
284
|
+
utils_1.exitWithError('Version of specification is not found in. \n');
|
|
285
|
+
if (!openapi.info)
|
|
286
|
+
utils_1.exitWithError('Info section is not found in specification. \n');
|
|
287
|
+
if ((_a = openapi.info) === null || _a === void 0 ? void 0 : _a.description) {
|
|
288
|
+
openapi.info.description = addComponentsPrefix(openapi.info.description, componentsPrefix);
|
|
289
|
+
}
|
|
290
|
+
joinedDef.openapi = openapi.openapi;
|
|
291
|
+
joinedDef.info = openapi.info;
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
exports.handleJoin = handleJoin;
|
|
296
|
+
function doesComponentsDiffer(curr, next) {
|
|
297
|
+
return !isEqual(Object.values(curr)[0], Object.values(next)[0]);
|
|
298
|
+
}
|
|
299
|
+
function validateComponentsDifference(files) {
|
|
300
|
+
let isDiffer = false;
|
|
301
|
+
for (let i = 0, len = files.length; i < len; i++) {
|
|
302
|
+
let next = files[i + 1];
|
|
303
|
+
if (next && doesComponentsDiffer(files[i], next)) {
|
|
304
|
+
isDiffer = true;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return isDiffer;
|
|
308
|
+
}
|
|
309
|
+
function iteratePotentialConflicts(potentialConflicts) {
|
|
310
|
+
for (const group of Object.keys(potentialConflicts)) {
|
|
311
|
+
for (const [key, value] of Object.entries(potentialConflicts[group])) {
|
|
312
|
+
const conflicts = filterConflicts(value);
|
|
313
|
+
if (conflicts.length) {
|
|
314
|
+
if (group === COMPONENTS) {
|
|
315
|
+
for (const [_, conflict] of Object.entries(conflicts)) {
|
|
316
|
+
if (validateComponentsDifference(conflict[1])) {
|
|
317
|
+
conflict[1] = conflict[1].map((c) => Object.keys(c)[0]);
|
|
318
|
+
showConflicts(colorette_1.green(group) + ' => ' + key, [conflict]);
|
|
319
|
+
potentialConflictsTotal += 1;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
showConflicts(colorette_1.green(group) + ' => ' + key, conflicts);
|
|
325
|
+
potentialConflictsTotal += conflicts.length;
|
|
326
|
+
}
|
|
327
|
+
prefixTagSuggestion(group, conflicts.length);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function prefixTagSuggestion(group, conflictsLength) {
|
|
333
|
+
if (group === 'tags') {
|
|
334
|
+
process.stderr.write(colorette_1.green(`
|
|
335
|
+
${conflictsLength} conflict(s) on tags.
|
|
336
|
+
Suggestion: please use ${colorette_1.blue('prefix-tags-with-filename')} or ${colorette_1.blue('prefix-tags-with-info-prop')} to prevent naming conflicts. \n\n`));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function showConflicts(key, conflicts) {
|
|
340
|
+
for (const [path, files] of conflicts) {
|
|
341
|
+
process.stderr.write(colorette_1.yellow(`Conflict on ${key} : ${colorette_1.red(path)} in files: ${colorette_1.blue(files)} \n`));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function filterConflicts(entities) {
|
|
345
|
+
return Object.entries(entities).filter(([_, files]) => files.length > 1);
|
|
346
|
+
}
|
|
347
|
+
function getEntrypointFilename(filePath) {
|
|
348
|
+
return path.basename(filePath, path.extname(filePath));
|
|
349
|
+
}
|
|
350
|
+
function addPrefix(tag, tagsPrefix) {
|
|
351
|
+
return tagsPrefix ? tagsPrefix + '_' + tag : tag;
|
|
352
|
+
}
|
|
353
|
+
function formatTags(tags) {
|
|
354
|
+
return tags.map((tag) => ({ name: tag }));
|
|
355
|
+
}
|
|
356
|
+
function addComponentsPrefix(description, componentsPrefix) {
|
|
357
|
+
return description.replace(/"(#\/components\/.*?)"/g, (match) => {
|
|
358
|
+
const componentName = path.basename(match);
|
|
359
|
+
return match.replace(componentName, addPrefix(componentName, componentsPrefix));
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function addSecurityPrefix(security, componentsPrefix) {
|
|
363
|
+
return componentsPrefix ? security === null || security === void 0 ? void 0 : security.map((s) => {
|
|
364
|
+
const key = Object.keys(s)[0];
|
|
365
|
+
return { [componentsPrefix + '_' + key]: s[key] };
|
|
366
|
+
}) : security;
|
|
367
|
+
}
|
|
368
|
+
function getInfoPrefix(info, prefixArg, type) {
|
|
369
|
+
if (!prefixArg)
|
|
370
|
+
return '';
|
|
371
|
+
if (!info)
|
|
372
|
+
utils_1.exitWithError('Info section is not found in specification. \n');
|
|
373
|
+
if (!info[prefixArg])
|
|
374
|
+
utils_1.exitWithError(`${colorette_1.yellow(`prefix-${type}-with-info-prop`)} argument value is not found in info section. \n`);
|
|
375
|
+
if (!js_utils_1.isString(info[prefixArg]))
|
|
376
|
+
utils_1.exitWithError(`${colorette_1.yellow(`prefix-${type}-with-info-prop`)} argument value should be string. \n\n`);
|
|
377
|
+
if (info[prefixArg].length > 50)
|
|
378
|
+
utils_1.exitWithError(`${colorette_1.yellow(`prefix-${type}-with-info-prop`)} argument value length should not exceed 50 characters. \n\n`);
|
|
379
|
+
return info[prefixArg];
|
|
380
|
+
}
|
|
381
|
+
function validateEntrypoint(document, config, externalRefResolver, packageVersion) {
|
|
382
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
383
|
+
try {
|
|
384
|
+
const results = yield openapi_core_1.lintDocument({ document, config, externalRefResolver });
|
|
385
|
+
const fileTotals = openapi_core_1.getTotals(results);
|
|
386
|
+
openapi_core_1.formatProblems(results, { format: 'stylish', totals: fileTotals, version: packageVersion });
|
|
387
|
+
utils_1.printLintTotals(fileTotals, 2);
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
utils_1.handleError(err, document.parsed);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function crawl(object, visitor) {
|
|
395
|
+
if (!js_utils_1.isObject(object))
|
|
396
|
+
return;
|
|
397
|
+
for (const key of Object.keys(object)) {
|
|
398
|
+
visitor(object, key);
|
|
399
|
+
crawl(object[key], visitor);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
function replace$Refs(obj, componentsPrefix) {
|
|
403
|
+
crawl(obj, (node) => {
|
|
404
|
+
if (node.$ref && js_utils_1.isString(node.$ref) && node.$ref.startsWith(`#/${COMPONENTS}/`)) {
|
|
405
|
+
const name = path.basename(node.$ref);
|
|
406
|
+
node.$ref = node.$ref.replace(name, componentsPrefix + '_' + name);
|
|
407
|
+
}
|
|
408
|
+
else if (node.discriminator &&
|
|
409
|
+
node.discriminator.mapping &&
|
|
410
|
+
js_utils_1.isObject(node.discriminator.mapping)) {
|
|
411
|
+
const { mapping } = node.discriminator;
|
|
412
|
+
for (const name of Object.keys(mapping)) {
|
|
413
|
+
if (js_utils_1.isString(mapping[name]) && mapping[name].startsWith(`#/${COMPONENTS}/`)) {
|
|
414
|
+
mapping[name] = mapping[name].split('/').map((name, i, arr) => {
|
|
415
|
+
return (arr.length - 1 === i && !name.includes(componentsPrefix)) ? componentsPrefix + '_' + name : name;
|
|
416
|
+
}).join('/');
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OutputFormat } from '@redocly/openapi-core';
|
|
2
|
+
export declare function handleLint(argv: {
|
|
3
|
+
entrypoints: string[];
|
|
4
|
+
'max-problems'?: number;
|
|
5
|
+
'generate-ignore-file'?: boolean;
|
|
6
|
+
'skip-rule'?: string[];
|
|
7
|
+
'skip-preprocessor'?: string[];
|
|
8
|
+
extends?: string[];
|
|
9
|
+
config?: string;
|
|
10
|
+
format: OutputFormat;
|
|
11
|
+
}, version: string): Promise<void>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handleLint = void 0;
|
|
13
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
14
|
+
const utils_1 = require("../utils");
|
|
15
|
+
const colorette_1 = require("colorette");
|
|
16
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
17
|
+
function handleLint(argv, version) {
|
|
18
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
19
|
+
const config = yield openapi_core_1.loadConfig(argv.config, argv.extends);
|
|
20
|
+
const entrypoints = yield utils_1.getFallbackEntryPointsOrExit(argv.entrypoints, config);
|
|
21
|
+
if (argv['generate-ignore-file']) {
|
|
22
|
+
config.lint.ignore = {}; // clear ignore
|
|
23
|
+
}
|
|
24
|
+
const totals = { errors: 0, warnings: 0, ignored: 0 };
|
|
25
|
+
let totalIgnored = 0;
|
|
26
|
+
// TODO: use shared externalRef resolver, blocked by preprocessors now as they can mutate documents
|
|
27
|
+
for (const { path, alias } of entrypoints) {
|
|
28
|
+
try {
|
|
29
|
+
const startedAt = perf_hooks_1.performance.now();
|
|
30
|
+
const resolvedConfig = openapi_core_1.getMergedConfig(config, alias);
|
|
31
|
+
resolvedConfig.lint.skipRules(argv['skip-rule']);
|
|
32
|
+
resolvedConfig.lint.skipPreprocessors(argv['skip-preprocessor']);
|
|
33
|
+
if (resolvedConfig.lint.recommendedFallback) {
|
|
34
|
+
process.stderr.write(`No configurations were defined in extends -- using built in ${colorette_1.blue('recommended')} configuration by default.\n${colorette_1.red('Warning! This default behavior is going to be deprecated soon.')}\n\n`);
|
|
35
|
+
}
|
|
36
|
+
process.stderr.write(colorette_1.gray(`validating ${path.replace(process.cwd(), '')}...\n`));
|
|
37
|
+
const results = yield openapi_core_1.lint({
|
|
38
|
+
ref: path,
|
|
39
|
+
config: resolvedConfig,
|
|
40
|
+
});
|
|
41
|
+
const fileTotals = openapi_core_1.getTotals(results);
|
|
42
|
+
totals.errors += fileTotals.errors;
|
|
43
|
+
totals.warnings += fileTotals.warnings;
|
|
44
|
+
totals.ignored += fileTotals.ignored;
|
|
45
|
+
if (argv['generate-ignore-file']) {
|
|
46
|
+
for (let m of results) {
|
|
47
|
+
config.lint.addIgnore(m);
|
|
48
|
+
totalIgnored++;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
openapi_core_1.formatProblems(results, {
|
|
53
|
+
format: argv.format,
|
|
54
|
+
maxProblems: argv['max-problems'],
|
|
55
|
+
totals: fileTotals,
|
|
56
|
+
version,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const elapsed = utils_1.getExecutionTime(startedAt);
|
|
60
|
+
process.stderr.write(colorette_1.gray(`${path.replace(process.cwd(), '')}: validated in ${elapsed}\n\n`));
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
totals.errors++;
|
|
64
|
+
utils_1.handleError(e, path);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (argv['generate-ignore-file']) {
|
|
68
|
+
config.lint.saveIgnore();
|
|
69
|
+
process.stderr.write(`Generated ignore file with ${totalIgnored} ${utils_1.pluralize('problem', totalIgnored)}.\n\n`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
utils_1.printLintTotals(totals, entrypoints.length);
|
|
73
|
+
}
|
|
74
|
+
utils_1.printUnusedWarnings(config.lint);
|
|
75
|
+
// defer process exit to allow STDOUT pipe to flush
|
|
76
|
+
// see https://github.com/nodejs/node-v0.x-archive/issues/3737#issuecomment-19156072
|
|
77
|
+
process.once('exit', () => process.exit(totals.errors === 0 || argv['generate-ignore-file'] ? 0 : 1));
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
exports.handleLint = handleLint;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.handleLogin = exports.promptClientToken = void 0;
|
|
13
|
+
const openapi_core_1 = require("@redocly/openapi-core");
|
|
14
|
+
const colorette_1 = require("colorette");
|
|
15
|
+
const utils_1 = require("../utils");
|
|
16
|
+
function promptClientToken(domain) {
|
|
17
|
+
return utils_1.promptUser(colorette_1.green(`\n 🔑 Copy your API key from ${colorette_1.blue(`https://app.${domain}/profile`)} and paste it below`), true);
|
|
18
|
+
}
|
|
19
|
+
exports.promptClientToken = promptClientToken;
|
|
20
|
+
function handleLogin(argv) {
|
|
21
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
22
|
+
const region = argv.region || (yield openapi_core_1.loadConfig()).region;
|
|
23
|
+
const client = new openapi_core_1.RedoclyClient(region);
|
|
24
|
+
const clientToken = yield promptClientToken(client.domain);
|
|
25
|
+
client.login(clientToken, argv.verbose);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
exports.handleLogin = handleLogin;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare function previewDocs(argv: {
|
|
2
|
+
port: number;
|
|
3
|
+
host: string;
|
|
4
|
+
'use-community-edition'?: boolean;
|
|
5
|
+
config?: string;
|
|
6
|
+
entrypoint?: string;
|
|
7
|
+
'skip-rule'?: string[];
|
|
8
|
+
'skip-decorator'?: string[];
|
|
9
|
+
'skip-preprocessor'?: string[];
|
|
10
|
+
force?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function debounce(func: Function, wait: number, immediate?: boolean): (...args: any[]) => void;
|