@redocly/openapi-core 1.0.0-beta.106 → 1.0.0-beta.109
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/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +0 -1
- package/lib/benchmark/benches/resolve-with-no-external.bench.js +1 -1
- package/lib/bundle.d.ts +1 -1
- package/lib/bundle.js +9 -6
- package/lib/config/all.js +5 -3
- package/lib/config/config-resolvers.js +32 -14
- package/lib/config/config.d.ts +3 -5
- package/lib/config/config.js +7 -4
- package/lib/config/load.d.ts +7 -0
- package/lib/config/load.js +14 -6
- package/lib/config/minimal.js +7 -4
- package/lib/config/recommended.js +7 -4
- package/lib/config/rules.d.ts +1 -1
- package/lib/config/rules.js +1 -1
- package/lib/config/types.d.ts +7 -0
- package/lib/config/utils.d.ts +2 -2
- package/lib/config/utils.js +49 -11
- package/lib/decorators/common/registry-dependencies.js +2 -2
- package/lib/env.d.ts +3 -0
- package/lib/env.js +8 -0
- package/lib/format/codeframes.js +16 -10
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +49 -26
- package/lib/index.d.ts +5 -5
- package/lib/index.js +3 -1
- package/lib/js-yaml/index.js +1 -0
- package/lib/lint.js +2 -2
- package/lib/logger.d.ts +10 -0
- package/lib/logger.js +31 -0
- package/lib/output.d.ts +3 -0
- package/lib/output.js +9 -0
- package/lib/redocly/index.js +10 -9
- package/lib/redocly/registry-api-types.d.ts +28 -30
- package/lib/redocly/registry-api.d.ts +3 -3
- package/lib/redocly/registry-api.js +7 -1
- package/lib/ref-utils.js +2 -1
- package/lib/resolve.d.ts +1 -1
- package/lib/resolve.js +4 -2
- package/lib/rules/ajv.d.ts +1 -1
- package/lib/rules/ajv.js +7 -7
- package/lib/rules/common/assertions/asserts.js +4 -4
- package/lib/rules/common/assertions/index.js +1 -1
- package/lib/rules/common/no-ambiguous-paths.js +1 -1
- package/lib/rules/common/no-identical-paths.js +1 -1
- package/lib/rules/common/no-invalid-parameter-examples.js +3 -3
- package/lib/rules/common/no-invalid-schema-examples.js +3 -3
- package/lib/rules/common/operation-2xx-response.js +1 -1
- package/lib/rules/common/operation-4xx-response.js +1 -1
- package/lib/rules/common/operation-operationId.js +1 -1
- package/lib/rules/common/operation-tag-defined.js +1 -1
- package/lib/rules/common/path-not-include-query.js +1 -1
- package/lib/rules/common/security-defined.d.ts +2 -0
- package/lib/rules/common/{operation-security-defined.js → security-defined.js} +19 -5
- package/lib/rules/common/spec.js +14 -3
- package/lib/rules/common/tags-alphabetical.js +1 -1
- package/lib/rules/oas2/index.d.ts +1 -1
- package/lib/rules/oas2/index.js +2 -2
- package/lib/rules/oas2/remove-unused-components.js +3 -3
- package/lib/rules/oas2/request-mime-type.js +1 -1
- package/lib/rules/oas2/response-mime-type.js +1 -1
- package/lib/rules/oas3/index.js +8 -4
- package/lib/rules/oas3/no-empty-servers.js +1 -1
- package/lib/rules/oas3/no-invalid-media-type-examples.js +2 -2
- package/lib/rules/oas3/no-server-variables-empty-enum.d.ts +2 -0
- package/lib/rules/oas3/{no-servers-empty-enum.js → no-server-variables-empty-enum.js} +5 -5
- package/lib/rules/oas3/no-unused-components.js +2 -2
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.d.ts +5 -0
- package/lib/rules/oas3/operation-4xx-problem-details-rfc7807.js +36 -0
- package/lib/rules/oas3/remove-unused-components.js +4 -4
- package/lib/rules/oas3/request-mime-type.js +1 -1
- package/lib/rules/oas3/response-mime-type.js +1 -1
- package/lib/rules/oas3/spec-components-invalid-map-name.d.ts +2 -0
- package/lib/rules/oas3/spec-components-invalid-map-name.js +46 -0
- package/lib/rules/other/stats.d.ts +2 -2
- package/lib/rules/other/stats.js +2 -2
- package/lib/rules/utils.d.ts +3 -2
- package/lib/rules/utils.js +16 -4
- package/lib/types/oas2.js +5 -5
- package/lib/types/oas3.js +27 -20
- package/lib/types/oas3_1.js +3 -3
- package/lib/types/redocly-yaml.js +47 -56
- package/lib/utils.d.ts +6 -1
- package/lib/utils.js +24 -7
- package/lib/visitors.d.ts +12 -12
- package/lib/visitors.js +15 -3
- package/lib/walk.d.ts +2 -1
- package/lib/walk.js +6 -3
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/bundle.test.ts.snap +3 -3
- package/src/__tests__/fixtures/extension.js +3 -3
- package/src/__tests__/format.test.ts +76 -0
- package/src/__tests__/lint.test.ts +106 -131
- package/src/__tests__/logger-browser.test.ts +53 -0
- package/src/__tests__/logger.test.ts +47 -0
- package/src/__tests__/output-browser.test.ts +18 -0
- package/src/__tests__/output.test.ts +15 -0
- package/src/__tests__/resolve-http.test.ts +1 -1
- package/src/__tests__/resolve.test.ts +9 -9
- package/src/__tests__/utils-browser.test.ts +11 -0
- package/src/__tests__/utils.test.ts +7 -0
- package/src/__tests__/walk.test.ts +78 -10
- package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +0 -1
- package/src/benchmark/benches/resolve-with-no-external.bench.ts +1 -1
- package/src/bundle.ts +10 -7
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +12 -6
- package/src/config/__tests__/config.test.ts +35 -0
- package/src/config/__tests__/fixtures/plugin-config.yaml +2 -3
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -12
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -8
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -19
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -10
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -4
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -5
- package/src/config/__tests__/load.test.ts +76 -1
- package/src/config/__tests__/utils.test.ts +64 -4
- package/src/config/all.ts +5 -3
- package/src/config/config-resolvers.ts +45 -19
- package/src/config/config.ts +10 -8
- package/src/config/load.ts +31 -7
- package/src/config/minimal.ts +7 -4
- package/src/config/recommended.ts +7 -4
- package/src/config/rules.ts +2 -2
- package/src/config/types.ts +11 -0
- package/src/config/utils.ts +115 -25
- package/src/decorators/common/registry-dependencies.ts +2 -2
- package/src/env.ts +5 -0
- package/src/format/codeframes.ts +15 -9
- package/src/format/format.ts +59 -34
- package/src/index.ts +6 -4
- package/src/js-yaml/index.ts +1 -0
- package/src/lint.ts +2 -2
- package/src/logger.ts +34 -0
- package/src/output.ts +7 -0
- package/src/redocly/index.ts +7 -4
- package/src/redocly/registry-api-types.ts +27 -29
- package/src/redocly/registry-api.ts +18 -7
- package/src/ref-utils.ts +2 -1
- package/src/resolve.ts +7 -5
- package/src/rules/__tests__/utils.test.ts +39 -1
- package/src/rules/ajv.ts +7 -7
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -0
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -1
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +26 -3
- package/src/rules/common/__tests__/security-defined.test.ts +175 -0
- package/src/rules/common/__tests__/spec.test.ts +79 -0
- package/src/rules/common/assertions/__tests__/utils.test.ts +2 -2
- package/src/rules/common/assertions/asserts.ts +4 -4
- package/src/rules/common/assertions/index.ts +1 -1
- package/src/rules/common/no-ambiguous-paths.ts +1 -1
- package/src/rules/common/no-identical-paths.ts +1 -1
- package/src/rules/common/no-invalid-parameter-examples.ts +4 -4
- package/src/rules/common/no-invalid-schema-examples.ts +4 -4
- package/src/rules/common/operation-2xx-response.ts +1 -1
- package/src/rules/common/operation-4xx-response.ts +1 -1
- package/src/rules/common/operation-operationId.ts +1 -1
- package/src/rules/common/operation-tag-defined.ts +1 -1
- package/src/rules/common/path-not-include-query.ts +1 -1
- package/src/rules/common/{operation-security-defined.ts → security-defined.ts} +20 -5
- package/src/rules/common/spec.ts +17 -3
- package/src/rules/common/tags-alphabetical.ts +1 -1
- package/src/rules/oas2/index.ts +2 -2
- package/src/rules/oas2/remove-unused-components.ts +3 -3
- package/src/rules/oas2/request-mime-type.ts +1 -1
- package/src/rules/oas2/response-mime-type.ts +1 -1
- package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +16 -16
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +5 -5
- package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
- package/src/rules/oas3/__tests__/spec/spec.test.ts +10 -0
- package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +217 -0
- package/src/rules/oas3/index.ts +8 -4
- package/src/rules/oas3/no-empty-servers.ts +1 -1
- package/src/rules/oas3/no-invalid-media-type-examples.ts +3 -3
- package/src/rules/oas3/{no-servers-empty-enum.ts → no-server-variables-empty-enum.ts} +3 -3
- package/src/rules/oas3/no-unused-components.ts +2 -2
- package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +36 -0
- package/src/rules/oas3/remove-unused-components.ts +5 -5
- package/src/rules/oas3/request-mime-type.ts +1 -1
- package/src/rules/oas3/response-mime-type.ts +1 -1
- package/src/rules/oas3/spec-components-invalid-map-name.ts +53 -0
- package/src/rules/other/stats.ts +2 -2
- package/src/rules/utils.ts +17 -3
- package/src/types/index.ts +2 -2
- package/src/types/oas2.ts +5 -5
- package/src/types/oas3.ts +27 -20
- package/src/types/oas3_1.ts +3 -3
- package/src/types/redocly-yaml.ts +53 -41
- package/src/utils.ts +31 -4
- package/src/visitors.ts +34 -18
- package/src/walk.ts +15 -11
- package/tsconfig.tsbuildinfo +1 -1
- package/lib/rules/common/operation-security-defined.d.ts +0 -2
- package/lib/rules/oas3/no-servers-empty-enum.d.ts +0 -2
- package/src/rules/common/__tests__/operation-security-defined.test.ts +0 -69
package/src/config/all.ts
CHANGED
|
@@ -26,7 +26,7 @@ export default {
|
|
|
26
26
|
'operation-operationId-url-safe': 'error',
|
|
27
27
|
'operation-parameters-unique': 'error',
|
|
28
28
|
'operation-tag-defined': 'error',
|
|
29
|
-
'
|
|
29
|
+
'security-defined': 'error',
|
|
30
30
|
'operation-singular-tag': 'error',
|
|
31
31
|
'no-unresolved-refs': 'error',
|
|
32
32
|
'no-enum-type-mismatch': 'error',
|
|
@@ -51,7 +51,8 @@ export default {
|
|
|
51
51
|
'no-example-value-and-externalValue': 'error',
|
|
52
52
|
'no-unused-components': 'error',
|
|
53
53
|
'no-undefined-server-variable': 'error',
|
|
54
|
-
'no-
|
|
54
|
+
'no-server-variables-empty-enum': 'error',
|
|
55
|
+
'operation-4xx-problem-details-rfc7807': 'error',
|
|
55
56
|
},
|
|
56
57
|
oas3_1Rules: {
|
|
57
58
|
'no-server-example.com': 'error',
|
|
@@ -60,6 +61,7 @@ export default {
|
|
|
60
61
|
'no-example-value-and-externalValue': 'error',
|
|
61
62
|
'no-unused-components': 'error',
|
|
62
63
|
'no-undefined-server-variable': 'error',
|
|
63
|
-
'no-
|
|
64
|
+
'no-server-variables-empty-enum': 'error',
|
|
65
|
+
'operation-4xx-problem-details-rfc7807': 'error',
|
|
64
66
|
},
|
|
65
67
|
} as PluginStyleguideConfig;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import { blue, red } from 'colorette';
|
|
3
2
|
import { isAbsoluteUrl } from '../ref-utils';
|
|
4
3
|
import { BaseResolver } from '../resolve';
|
|
5
4
|
import { defaultPlugin } from './builtIn';
|
|
@@ -21,8 +20,10 @@ import type {
|
|
|
21
20
|
RuleConfig,
|
|
22
21
|
DeprecatedInRawConfig,
|
|
23
22
|
} from './types';
|
|
24
|
-
import {
|
|
23
|
+
import { isBrowser } from '../env';
|
|
24
|
+
import { isNotString, isString, isDefined, parseYaml } from '../utils';
|
|
25
25
|
import { Config } from './config';
|
|
26
|
+
import { colorize, logger } from '../logger';
|
|
26
27
|
|
|
27
28
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
28
29
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
|
@@ -71,34 +72,57 @@ export function resolvePlugins(
|
|
|
71
72
|
): Plugin[] {
|
|
72
73
|
if (!plugins) return [];
|
|
73
74
|
|
|
74
|
-
//
|
|
75
|
-
const requireFunc =
|
|
75
|
+
// TODO: implement or reuse Resolver approach so it will work in node and browser envs
|
|
76
|
+
const requireFunc = (plugin: string | Plugin): Plugin | undefined => {
|
|
77
|
+
if (isBrowser && isString(plugin)) {
|
|
78
|
+
logger.error(`Cannot load ${plugin}. Plugins aren't supported in browser yet.`);
|
|
79
|
+
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isString(plugin)) {
|
|
84
|
+
const absoltePluginPath = path.resolve(path.dirname(configPath), plugin);
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
return typeof __webpack_require__ === 'function'
|
|
88
|
+
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
__non_webpack_require__(absoltePluginPath)
|
|
91
|
+
: require(absoltePluginPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return plugin;
|
|
95
|
+
};
|
|
76
96
|
|
|
77
97
|
const seenPluginIds = new Map<string, string>();
|
|
78
98
|
|
|
79
99
|
return plugins
|
|
80
100
|
.map((p) => {
|
|
81
101
|
if (isString(p) && isAbsoluteUrl(p)) {
|
|
82
|
-
throw new Error(red(`We don't support remote plugins yet.`));
|
|
102
|
+
throw new Error(colorize.red(`We don't support remote plugins yet.`));
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
// TODO: resolve npm packages similar to eslint
|
|
86
|
-
const pluginModule =
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
const pluginModule = requireFunc(p);
|
|
107
|
+
|
|
108
|
+
if (!pluginModule) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
89
111
|
|
|
90
112
|
const id = pluginModule.id;
|
|
91
113
|
if (typeof id !== 'string') {
|
|
92
|
-
throw new Error(
|
|
114
|
+
throw new Error(
|
|
115
|
+
colorize.red(`Plugin must define \`id\` property in ${colorize.blue(p.toString())}.`)
|
|
116
|
+
);
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
if (seenPluginIds.has(id)) {
|
|
96
120
|
const pluginPath = seenPluginIds.get(id)!;
|
|
97
121
|
throw new Error(
|
|
98
|
-
red(
|
|
99
|
-
`Plugin "id" must be unique. Plugin ${
|
|
100
|
-
|
|
101
|
-
)}" already seen in ${blue(pluginPath)}`
|
|
122
|
+
colorize.red(
|
|
123
|
+
`Plugin "id" must be unique. Plugin ${colorize.blue(
|
|
124
|
+
p.toString()
|
|
125
|
+
)} uses id "${colorize.blue(id)}" already seen in ${colorize.blue(pluginPath)}`
|
|
102
126
|
)
|
|
103
127
|
);
|
|
104
128
|
}
|
|
@@ -153,7 +177,7 @@ export function resolvePlugins(
|
|
|
153
177
|
|
|
154
178
|
return plugin;
|
|
155
179
|
})
|
|
156
|
-
.filter(
|
|
180
|
+
.filter(isDefined);
|
|
157
181
|
}
|
|
158
182
|
|
|
159
183
|
export async function resolveApis({
|
|
@@ -166,7 +190,7 @@ export async function resolveApis({
|
|
|
166
190
|
resolver?: BaseResolver;
|
|
167
191
|
}): Promise<Record<string, ResolvedApi>> {
|
|
168
192
|
const { apis = {}, styleguide: styleguideConfig = {} } = rawConfig;
|
|
169
|
-
|
|
193
|
+
const resolvedApis: Record<string, ResolvedApi> = {};
|
|
170
194
|
for (const [apiName, apiContent] of Object.entries(apis || {})) {
|
|
171
195
|
if (apiContent.styleguide?.extends?.some(isNotString)) {
|
|
172
196
|
throw new Error(
|
|
@@ -284,17 +308,19 @@ export function resolvePreset(presetName: string, plugins: Plugin[]): ResolvedSt
|
|
|
284
308
|
const { pluginId, configName } = parsePresetName(presetName);
|
|
285
309
|
const plugin = plugins.find((p) => p.id === pluginId);
|
|
286
310
|
if (!plugin) {
|
|
287
|
-
throw new Error(
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Invalid config ${colorize.red(presetName)}: plugin ${pluginId} is not included.`
|
|
313
|
+
);
|
|
288
314
|
}
|
|
289
315
|
|
|
290
|
-
const preset = plugin.configs?.[configName]
|
|
316
|
+
const preset = plugin.configs?.[configName];
|
|
291
317
|
if (!preset) {
|
|
292
318
|
throw new Error(
|
|
293
319
|
pluginId
|
|
294
|
-
? `Invalid config ${red(
|
|
320
|
+
? `Invalid config ${colorize.red(
|
|
295
321
|
presetName
|
|
296
322
|
)}: plugin ${pluginId} doesn't export config with name ${configName}.`
|
|
297
|
-
: `Invalid config ${red(presetName)}: there is no such built-in config.`
|
|
323
|
+
: `Invalid config ${colorize.red(presetName)}: there is no such built-in config.`
|
|
298
324
|
);
|
|
299
325
|
}
|
|
300
326
|
return preset;
|
package/src/config/config.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { parseYaml, stringifyYaml } from '../js-yaml';
|
|
|
4
4
|
import { slash, doesYamlFileExist } from '../utils';
|
|
5
5
|
import { NormalizedProblem } from '../walk';
|
|
6
6
|
import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
|
|
7
|
+
import { env } from '../env';
|
|
7
8
|
|
|
8
9
|
import type { NodeType } from '../types';
|
|
9
10
|
import type {
|
|
@@ -19,9 +20,6 @@ import type {
|
|
|
19
20
|
} from './types';
|
|
20
21
|
import { getResolveConfig } from './utils';
|
|
21
22
|
|
|
22
|
-
// Alias environment here so this file can work in browser environments too.
|
|
23
|
-
export const env = typeof process !== 'undefined' ? process.env || {} : {};
|
|
24
|
-
|
|
25
23
|
export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
|
|
26
24
|
const IGNORE_BANNER =
|
|
27
25
|
`# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
|
|
@@ -172,9 +170,11 @@ export class StyleguideConfig {
|
|
|
172
170
|
case OasVersion.Version3_1:
|
|
173
171
|
if (!plugin.typeExtension.oas3) continue;
|
|
174
172
|
extendedTypes = plugin.typeExtension.oas3(extendedTypes, version);
|
|
173
|
+
break;
|
|
175
174
|
case OasVersion.Version2:
|
|
176
175
|
if (!plugin.typeExtension.oas2) continue;
|
|
177
176
|
extendedTypes = plugin.typeExtension.oas2(extendedTypes, version);
|
|
177
|
+
break;
|
|
178
178
|
default:
|
|
179
179
|
throw new Error('Not implemented');
|
|
180
180
|
}
|
|
@@ -192,7 +192,7 @@ export class StyleguideConfig {
|
|
|
192
192
|
severity: settings,
|
|
193
193
|
};
|
|
194
194
|
} else {
|
|
195
|
-
return { severity: 'error'
|
|
195
|
+
return { severity: 'error', ...settings };
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -203,10 +203,10 @@ export class StyleguideConfig {
|
|
|
203
203
|
const settings = this.preprocessors[oasVersion][ruleId] || 'off';
|
|
204
204
|
if (typeof settings === 'string') {
|
|
205
205
|
return {
|
|
206
|
-
severity: settings === 'on' ?
|
|
206
|
+
severity: settings === 'on' ? 'error' : settings,
|
|
207
207
|
};
|
|
208
208
|
} else {
|
|
209
|
-
return { severity: 'error'
|
|
209
|
+
return { severity: 'error', ...settings };
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -216,10 +216,10 @@ export class StyleguideConfig {
|
|
|
216
216
|
const settings = this.decorators[oasVersion][ruleId] || 'off';
|
|
217
217
|
if (typeof settings === 'string') {
|
|
218
218
|
return {
|
|
219
|
-
severity: settings === 'on' ?
|
|
219
|
+
severity: settings === 'on' ? 'error' : settings,
|
|
220
220
|
};
|
|
221
221
|
} else {
|
|
222
|
-
return { severity: 'error'
|
|
222
|
+
return { severity: 'error', ...settings };
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -250,12 +250,14 @@ export class StyleguideConfig {
|
|
|
250
250
|
getRulesForOasVersion(version: OasMajorVersion) {
|
|
251
251
|
switch (version) {
|
|
252
252
|
case OasMajorVersion.Version3:
|
|
253
|
+
// eslint-disable-next-line no-case-declarations
|
|
253
254
|
const oas3Rules: Oas3RuleSet[] = []; // default ruleset
|
|
254
255
|
this.plugins.forEach((p) => p.preprocessors?.oas3 && oas3Rules.push(p.preprocessors.oas3));
|
|
255
256
|
this.plugins.forEach((p) => p.rules?.oas3 && oas3Rules.push(p.rules.oas3));
|
|
256
257
|
this.plugins.forEach((p) => p.decorators?.oas3 && oas3Rules.push(p.decorators.oas3));
|
|
257
258
|
return oas3Rules;
|
|
258
259
|
case OasMajorVersion.Version2:
|
|
260
|
+
// eslint-disable-next-line no-case-declarations
|
|
259
261
|
const oas2Rules: Oas2RuleSet[] = []; // default ruleset
|
|
260
262
|
this.plugins.forEach((p) => p.preprocessors?.oas2 && oas2Rules.push(p.preprocessors.oas2));
|
|
261
263
|
this.plugins.forEach((p) => p.rules?.oas2 && oas2Rules.push(p.rules.oas2));
|
package/src/config/load.ts
CHANGED
|
@@ -2,20 +2,24 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { RedoclyClient } from '../redocly';
|
|
4
4
|
import { isEmptyObject, loadYaml, doesYamlFileExist } from '../utils';
|
|
5
|
+
import { parseYaml } from '../js-yaml';
|
|
5
6
|
import { Config, DOMAINS } from './config';
|
|
6
7
|
import { transformConfig } from './utils';
|
|
7
8
|
import { resolveConfig } from './config-resolvers';
|
|
8
9
|
|
|
9
|
-
import type { DeprecatedInRawConfig, RawConfig, Region } from './types';
|
|
10
|
+
import type { DeprecatedInRawConfig, FlatRawConfig, RawConfig, Region } from './types';
|
|
11
|
+
import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
|
10
12
|
|
|
11
13
|
async function addConfigMetadata({
|
|
12
14
|
rawConfig,
|
|
13
15
|
customExtends,
|
|
14
16
|
configPath,
|
|
17
|
+
tokens,
|
|
15
18
|
}: {
|
|
16
19
|
rawConfig: RawConfig;
|
|
17
20
|
customExtends?: string[];
|
|
18
21
|
configPath?: string;
|
|
22
|
+
tokens?: RegionalTokenWithValidity[];
|
|
19
23
|
}): Promise<Config> {
|
|
20
24
|
if (customExtends !== undefined) {
|
|
21
25
|
rawConfig.styleguide = rawConfig.styleguide || {};
|
|
@@ -25,10 +29,7 @@ async function addConfigMetadata({
|
|
|
25
29
|
// rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
const tokens = await redoclyClient.getTokens();
|
|
30
|
-
|
|
31
|
-
if (tokens.length) {
|
|
32
|
+
if (tokens?.length) {
|
|
32
33
|
if (!rawConfig.resolve) rawConfig.resolve = {};
|
|
33
34
|
if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
|
|
34
35
|
rawConfig.resolve.http.headers = [...(rawConfig.resolve.http.headers ?? [])];
|
|
@@ -69,10 +70,15 @@ export async function loadConfig(
|
|
|
69
70
|
if (typeof processRawConfig === 'function') {
|
|
70
71
|
await processRawConfig(rawConfig);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
const redoclyClient = new RedoclyClient();
|
|
75
|
+
const tokens = await redoclyClient.getTokens();
|
|
76
|
+
|
|
77
|
+
return addConfigMetadata({
|
|
73
78
|
rawConfig,
|
|
74
79
|
customExtends,
|
|
75
80
|
configPath,
|
|
81
|
+
tokens,
|
|
76
82
|
});
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -96,9 +102,27 @@ export function findConfig(dir?: string): string | undefined {
|
|
|
96
102
|
export async function getConfig(configPath: string | undefined = findConfig()): Promise<RawConfig> {
|
|
97
103
|
if (!configPath || !doesYamlFileExist(configPath)) return {};
|
|
98
104
|
try {
|
|
99
|
-
const rawConfig =
|
|
105
|
+
const rawConfig =
|
|
106
|
+
(await loadYaml<RawConfig & DeprecatedInRawConfig & FlatRawConfig>(configPath)) || {};
|
|
100
107
|
return transformConfig(rawConfig);
|
|
101
108
|
} catch (e) {
|
|
102
109
|
throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
|
|
103
110
|
}
|
|
104
111
|
}
|
|
112
|
+
|
|
113
|
+
type CreateConfigOptions = {
|
|
114
|
+
extends?: string[];
|
|
115
|
+
tokens?: RegionalTokenWithValidity[];
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export async function createConfig(
|
|
119
|
+
config: string | RawConfig,
|
|
120
|
+
options?: CreateConfigOptions
|
|
121
|
+
): Promise<Config> {
|
|
122
|
+
return addConfigMetadata({
|
|
123
|
+
rawConfig: transformConfig(
|
|
124
|
+
typeof config === 'string' ? (parseYaml(config) as RawConfig) : config
|
|
125
|
+
),
|
|
126
|
+
...options,
|
|
127
|
+
});
|
|
128
|
+
}
|
package/src/config/minimal.ts
CHANGED
|
@@ -24,7 +24,7 @@ export default {
|
|
|
24
24
|
'operation-operationId-unique': 'warn',
|
|
25
25
|
'operation-parameters-unique': 'warn',
|
|
26
26
|
'operation-tag-defined': 'off',
|
|
27
|
-
'
|
|
27
|
+
'security-defined': 'warn',
|
|
28
28
|
'operation-operationId-url-safe': 'warn',
|
|
29
29
|
'operation-singular-tag': 'off',
|
|
30
30
|
'no-unresolved-refs': 'error',
|
|
@@ -36,7 +36,7 @@ export default {
|
|
|
36
36
|
oas3_0Rules: {
|
|
37
37
|
'no-invalid-media-type-examples': {
|
|
38
38
|
severity: 'warn',
|
|
39
|
-
|
|
39
|
+
allowAdditionalProperties: false,
|
|
40
40
|
},
|
|
41
41
|
'no-server-example.com': 'warn',
|
|
42
42
|
'no-server-trailing-slash': 'error',
|
|
@@ -44,15 +44,18 @@ export default {
|
|
|
44
44
|
'no-example-value-and-externalValue': 'warn',
|
|
45
45
|
'no-unused-components': 'warn',
|
|
46
46
|
'no-undefined-server-variable': 'warn',
|
|
47
|
-
'no-
|
|
47
|
+
'no-server-variables-empty-enum': 'error',
|
|
48
|
+
'spec-components-invalid-map-name': 'warn',
|
|
48
49
|
},
|
|
49
50
|
oas3_1Rules: {
|
|
51
|
+
'no-invalid-media-type-examples': 'warn',
|
|
50
52
|
'no-server-example.com': 'warn',
|
|
51
53
|
'no-server-trailing-slash': 'error',
|
|
52
54
|
'no-empty-servers': 'warn',
|
|
53
55
|
'no-example-value-and-externalValue': 'warn',
|
|
54
56
|
'no-unused-components': 'warn',
|
|
55
57
|
'no-undefined-server-variable': 'warn',
|
|
56
|
-
'no-
|
|
58
|
+
'no-server-variables-empty-enum': 'error',
|
|
59
|
+
'spec-components-invalid-map-name': 'warn',
|
|
57
60
|
},
|
|
58
61
|
} as PluginStyleguideConfig;
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
'operation-operationId-url-safe': 'error',
|
|
26
26
|
'operation-parameters-unique': 'error',
|
|
27
27
|
'operation-tag-defined': 'off',
|
|
28
|
-
'
|
|
28
|
+
'security-defined': 'error',
|
|
29
29
|
'operation-singular-tag': 'off',
|
|
30
30
|
'no-unresolved-refs': 'error',
|
|
31
31
|
'no-enum-type-mismatch': 'error',
|
|
@@ -36,7 +36,7 @@ export default {
|
|
|
36
36
|
oas3_0Rules: {
|
|
37
37
|
'no-invalid-media-type-examples': {
|
|
38
38
|
severity: 'warn',
|
|
39
|
-
|
|
39
|
+
allowAdditionalProperties: false,
|
|
40
40
|
},
|
|
41
41
|
'no-server-example.com': 'warn',
|
|
42
42
|
'no-server-trailing-slash': 'error',
|
|
@@ -44,15 +44,18 @@ export default {
|
|
|
44
44
|
'no-example-value-and-externalValue': 'error',
|
|
45
45
|
'no-unused-components': 'warn',
|
|
46
46
|
'no-undefined-server-variable': 'error',
|
|
47
|
-
'no-
|
|
47
|
+
'no-server-variables-empty-enum': 'error',
|
|
48
|
+
'spec-components-invalid-map-name': 'error',
|
|
48
49
|
},
|
|
49
50
|
oas3_1Rules: {
|
|
51
|
+
'no-invalid-media-type-examples': 'warn',
|
|
50
52
|
'no-server-example.com': 'warn',
|
|
51
53
|
'no-server-trailing-slash': 'error',
|
|
52
54
|
'no-empty-servers': 'error',
|
|
53
55
|
'no-example-value-and-externalValue': 'error',
|
|
54
56
|
'no-unused-components': 'warn',
|
|
55
57
|
'no-undefined-server-variable': 'error',
|
|
56
|
-
'no-
|
|
58
|
+
'no-server-variables-empty-enum': 'error',
|
|
59
|
+
'spec-components-invalid-map-name': 'error',
|
|
57
60
|
},
|
|
58
61
|
} as PluginStyleguideConfig;
|
package/src/config/rules.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RuleSet, OasVersion } from '../oas-types';
|
|
2
2
|
import { StyleguideConfig } from './config';
|
|
3
|
-
import {
|
|
3
|
+
import { isDefined } from '../utils';
|
|
4
4
|
|
|
5
5
|
export function initRules<T extends Function, P extends RuleSet<T>>(
|
|
6
6
|
rules: P[],
|
|
@@ -42,5 +42,5 @@ export function initRules<T extends Function, P extends RuleSet<T>>(
|
|
|
42
42
|
})
|
|
43
43
|
)
|
|
44
44
|
.flatMap((visitor) => visitor)
|
|
45
|
-
.filter(
|
|
45
|
+
.filter(isDefined);
|
|
46
46
|
}
|
package/src/config/types.ts
CHANGED
|
@@ -110,6 +110,7 @@ export type ResolveHeader =
|
|
|
110
110
|
|
|
111
111
|
export type RawResolveConfig = {
|
|
112
112
|
http?: Partial<HttpResolveConfig>;
|
|
113
|
+
doNotResolveExamples?: boolean;
|
|
113
114
|
};
|
|
114
115
|
|
|
115
116
|
export type HttpResolveConfig = {
|
|
@@ -128,6 +129,7 @@ export type AccessTokens = { [region in Region]?: string };
|
|
|
128
129
|
export type DeprecatedInRawConfig = {
|
|
129
130
|
apiDefinitions?: Record<string, string>;
|
|
130
131
|
lint?: StyleguideRawConfig;
|
|
132
|
+
styleguide?: StyleguideRawConfig;
|
|
131
133
|
referenceDocs?: Record<string, any>;
|
|
132
134
|
apis?: Record<string, Api & DeprecatedInApi>;
|
|
133
135
|
};
|
|
@@ -153,6 +155,15 @@ export type RawConfig = {
|
|
|
153
155
|
organization?: string;
|
|
154
156
|
} & FeaturesConfig;
|
|
155
157
|
|
|
158
|
+
export type FlatApi = Omit<Api, 'styleguide'> &
|
|
159
|
+
Omit<ApiStyleguideRawConfig, 'doNotResolveExamples'>;
|
|
160
|
+
|
|
161
|
+
export type FlatRawConfig = Omit<RawConfig, 'styleguide' | 'resolve' | 'apis'> &
|
|
162
|
+
Omit<StyleguideRawConfig, 'doNotResolveExamples'> & {
|
|
163
|
+
resolve?: RawResolveConfig;
|
|
164
|
+
apis?: Record<string, FlatApi>;
|
|
165
|
+
};
|
|
166
|
+
|
|
156
167
|
export type ResolvedConfig = Omit<RawConfig, 'apis' | 'styleguide'> & {
|
|
157
168
|
apis: Record<string, ResolvedApi>;
|
|
158
169
|
styleguide: ResolvedStyleguideConfig;
|
package/src/config/utils.ts
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
assignExisting,
|
|
3
|
+
isDefined,
|
|
4
|
+
isTruthy,
|
|
5
|
+
showErrorForDeprecatedField,
|
|
6
|
+
showWarningForDeprecatedField,
|
|
7
|
+
} from '../utils';
|
|
3
8
|
import { Config } from './config';
|
|
4
|
-
|
|
5
9
|
import type {
|
|
6
10
|
Api,
|
|
7
11
|
DeprecatedInApi,
|
|
8
12
|
DeprecatedInRawConfig,
|
|
13
|
+
FlatApi,
|
|
14
|
+
FlatRawConfig,
|
|
9
15
|
Plugin,
|
|
10
16
|
RawConfig,
|
|
11
17
|
RawResolveConfig,
|
|
12
18
|
ResolveConfig,
|
|
13
19
|
ResolvedStyleguideConfig,
|
|
14
20
|
RulesFields,
|
|
21
|
+
StyleguideRawConfig,
|
|
15
22
|
} from './types';
|
|
23
|
+
import { logger, colorize } from '../logger';
|
|
16
24
|
|
|
17
25
|
export function parsePresetName(presetName: string): { pluginId: string; configName: string } {
|
|
18
26
|
if (presetName.indexOf('/') > -1) {
|
|
@@ -34,13 +42,86 @@ export function transformApiDefinitionsToApis(
|
|
|
34
42
|
return apis;
|
|
35
43
|
}
|
|
36
44
|
|
|
45
|
+
function extractFlatConfig<
|
|
46
|
+
T extends Partial<
|
|
47
|
+
(Api & DeprecatedInApi & FlatApi) & (DeprecatedInRawConfig & RawConfig & FlatRawConfig)
|
|
48
|
+
>
|
|
49
|
+
>({
|
|
50
|
+
plugins,
|
|
51
|
+
extends: _extends,
|
|
52
|
+
|
|
53
|
+
rules,
|
|
54
|
+
oas2Rules,
|
|
55
|
+
oas3_0Rules,
|
|
56
|
+
oas3_1Rules,
|
|
57
|
+
|
|
58
|
+
preprocessors,
|
|
59
|
+
oas2Preprocessors,
|
|
60
|
+
oas3_0Preprocessors,
|
|
61
|
+
oas3_1Preprocessors,
|
|
62
|
+
|
|
63
|
+
decorators,
|
|
64
|
+
oas2Decorators,
|
|
65
|
+
oas3_0Decorators,
|
|
66
|
+
oas3_1Decorators,
|
|
67
|
+
|
|
68
|
+
...rawConfigRest
|
|
69
|
+
}: T): {
|
|
70
|
+
styleguideConfig?: StyleguideRawConfig;
|
|
71
|
+
rawConfigRest: Omit<T, 'plugins' | 'extends' | RulesFields>;
|
|
72
|
+
} {
|
|
73
|
+
const styleguideConfig = {
|
|
74
|
+
plugins,
|
|
75
|
+
extends: _extends,
|
|
76
|
+
|
|
77
|
+
rules,
|
|
78
|
+
oas2Rules,
|
|
79
|
+
oas3_0Rules,
|
|
80
|
+
oas3_1Rules,
|
|
81
|
+
|
|
82
|
+
preprocessors,
|
|
83
|
+
oas2Preprocessors,
|
|
84
|
+
oas3_0Preprocessors,
|
|
85
|
+
oas3_1Preprocessors,
|
|
86
|
+
|
|
87
|
+
decorators,
|
|
88
|
+
oas2Decorators,
|
|
89
|
+
oas3_0Decorators,
|
|
90
|
+
oas3_1Decorators,
|
|
91
|
+
|
|
92
|
+
doNotResolveExamples: (rawConfigRest as FlatRawConfig).resolve?.doNotResolveExamples,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
(rawConfigRest.lint && rawConfigRest.styleguide) ||
|
|
97
|
+
(Object.values(styleguideConfig).some(isDefined) &&
|
|
98
|
+
(rawConfigRest.lint || rawConfigRest.styleguide))
|
|
99
|
+
) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Do not use 'lint', 'styleguide' and flat syntax together. \nSee more about the configuration in the docs: https://redocly.com/docs/cli/configuration/ \n`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
styleguideConfig: Object.values(styleguideConfig).some(isDefined)
|
|
107
|
+
? styleguideConfig
|
|
108
|
+
: undefined,
|
|
109
|
+
|
|
110
|
+
rawConfigRest,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
37
114
|
function transformApis(
|
|
38
|
-
legacyApis?: Record<string, Api & DeprecatedInApi>
|
|
115
|
+
legacyApis?: Record<string, Api & DeprecatedInApi & FlatApi>
|
|
39
116
|
): Record<string, Api> | undefined {
|
|
40
117
|
if (!legacyApis) return undefined;
|
|
41
118
|
const apis: Record<string, Api> = {};
|
|
42
119
|
for (const [apiName, { lint, ...apiContent }] of Object.entries(legacyApis)) {
|
|
43
|
-
|
|
120
|
+
const { styleguideConfig, rawConfigRest } = extractFlatConfig(apiContent);
|
|
121
|
+
apis[apiName] = {
|
|
122
|
+
styleguide: styleguideConfig || lint,
|
|
123
|
+
...rawConfigRest,
|
|
124
|
+
};
|
|
44
125
|
}
|
|
45
126
|
return apis;
|
|
46
127
|
}
|
|
@@ -79,7 +160,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
|
|
|
79
160
|
extendPaths: [],
|
|
80
161
|
};
|
|
81
162
|
|
|
82
|
-
for (
|
|
163
|
+
for (const rulesConf of rulesConfList) {
|
|
83
164
|
if (rulesConf.extends) {
|
|
84
165
|
throw new Error(
|
|
85
166
|
`'extends' is not supported in shared configs yet: ${JSON.stringify(rulesConf, null, 2)}.`
|
|
@@ -124,14 +205,14 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
|
|
|
124
205
|
config.rawConfig?.styleguide?.extendPaths,
|
|
125
206
|
]
|
|
126
207
|
.flat()
|
|
127
|
-
.filter(
|
|
208
|
+
.filter(isTruthy);
|
|
128
209
|
|
|
129
210
|
const pluginPaths = [
|
|
130
211
|
...Object.values(config.apis).map((api) => api?.styleguide?.pluginPaths),
|
|
131
212
|
config.rawConfig?.styleguide?.pluginPaths,
|
|
132
213
|
]
|
|
133
214
|
.flat()
|
|
134
|
-
.filter(
|
|
215
|
+
.filter(isTruthy);
|
|
135
216
|
|
|
136
217
|
return apiName
|
|
137
218
|
? new Config(
|
|
@@ -160,34 +241,41 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
|
|
|
160
241
|
}
|
|
161
242
|
|
|
162
243
|
function checkForDeprecatedFields(
|
|
163
|
-
deprecatedField: keyof DeprecatedInRawConfig,
|
|
164
|
-
updatedField: keyof
|
|
165
|
-
rawConfig: DeprecatedInRawConfig & RawConfig
|
|
244
|
+
deprecatedField: keyof (DeprecatedInRawConfig & RawConfig),
|
|
245
|
+
updatedField: keyof FlatRawConfig | undefined,
|
|
246
|
+
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig
|
|
166
247
|
): void {
|
|
167
248
|
const isDeprecatedFieldInApis =
|
|
168
249
|
rawConfig.apis &&
|
|
169
250
|
Object.values(rawConfig.apis).some(
|
|
170
|
-
(api: Api & DeprecatedInApi & DeprecatedInRawConfig) =>
|
|
251
|
+
(api: Api & FlatApi & DeprecatedInApi & DeprecatedInRawConfig & RawConfig & FlatRawConfig) =>
|
|
252
|
+
api[deprecatedField]
|
|
171
253
|
);
|
|
172
254
|
|
|
173
|
-
if (rawConfig[deprecatedField] &&
|
|
174
|
-
|
|
255
|
+
if (rawConfig[deprecatedField] && updatedField === null) {
|
|
256
|
+
showWarningForDeprecatedField(deprecatedField);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (rawConfig[deprecatedField] && updatedField && rawConfig[updatedField]) {
|
|
260
|
+
showErrorForDeprecatedField(deprecatedField, updatedField);
|
|
175
261
|
}
|
|
176
262
|
|
|
177
263
|
if (rawConfig[deprecatedField] || isDeprecatedFieldInApis) {
|
|
178
|
-
|
|
179
|
-
`The ${yellow(deprecatedField)} field is deprecated. Use ${green(
|
|
180
|
-
updatedField
|
|
181
|
-
)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`
|
|
182
|
-
);
|
|
264
|
+
showWarningForDeprecatedField(deprecatedField, updatedField);
|
|
183
265
|
}
|
|
184
266
|
}
|
|
185
267
|
|
|
186
|
-
export function transformConfig(
|
|
187
|
-
|
|
268
|
+
export function transformConfig(
|
|
269
|
+
rawConfig: DeprecatedInRawConfig & RawConfig & FlatRawConfig
|
|
270
|
+
): RawConfig {
|
|
271
|
+
const migratedFields: [
|
|
272
|
+
keyof (DeprecatedInRawConfig & RawConfig),
|
|
273
|
+
keyof FlatRawConfig | undefined
|
|
274
|
+
][] = [
|
|
188
275
|
['apiDefinitions', 'apis'],
|
|
189
276
|
['referenceDocs', 'features.openapi'],
|
|
190
|
-
['lint',
|
|
277
|
+
['lint', undefined],
|
|
278
|
+
['styleguide', undefined],
|
|
191
279
|
];
|
|
192
280
|
|
|
193
281
|
for (const [deprecatedField, updatedField] of migratedFields) {
|
|
@@ -196,11 +284,13 @@ export function transformConfig(rawConfig: DeprecatedInRawConfig & RawConfig): R
|
|
|
196
284
|
|
|
197
285
|
const { apis, apiDefinitions, referenceDocs, lint, ...rest } = rawConfig;
|
|
198
286
|
|
|
287
|
+
const { styleguideConfig, rawConfigRest } = extractFlatConfig(rest);
|
|
288
|
+
|
|
199
289
|
return {
|
|
200
290
|
'features.openapi': referenceDocs,
|
|
201
291
|
apis: transformApis(apis) || transformApiDefinitionsToApis(apiDefinitions),
|
|
202
|
-
styleguide: lint,
|
|
203
|
-
...
|
|
292
|
+
styleguide: styleguideConfig || lint,
|
|
293
|
+
...rawConfigRest,
|
|
204
294
|
};
|
|
205
295
|
}
|
|
206
296
|
|
|
@@ -221,7 +311,7 @@ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
|
|
|
221
311
|
results.push(p);
|
|
222
312
|
seen.add(p.id);
|
|
223
313
|
} else if (p.id) {
|
|
224
|
-
|
|
314
|
+
logger.warn(`Duplicate plugin id "${colorize.red(p.id)}".\n`);
|
|
225
315
|
}
|
|
226
316
|
}
|
|
227
317
|
return results;
|
|
@@ -4,10 +4,10 @@ import { isRedoclyRegistryURL } from '../../redocly';
|
|
|
4
4
|
import { Oas3Decorator, Oas2Decorator } from '../../visitors';
|
|
5
5
|
|
|
6
6
|
export const RegistryDependencies: Oas3Decorator | Oas2Decorator = () => {
|
|
7
|
-
|
|
7
|
+
const registryDependencies = new Set<string>();
|
|
8
8
|
|
|
9
9
|
return {
|
|
10
|
-
|
|
10
|
+
Root: {
|
|
11
11
|
leave(_: any, ctx: UserContext) {
|
|
12
12
|
const data = ctx.getVisitorData();
|
|
13
13
|
data.links = Array.from(registryDependencies);
|