@redocly/openapi-core 1.0.0-beta.93 → 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 +1 -1
- package/__tests__/bundle.test.ts +6 -6
- package/__tests__/fixtures/extension.js +1 -1
- package/__tests__/login.test.ts +2 -2
- package/__tests__/normalizeVisitors.test.ts +1 -1
- package/__tests__/ref-utils.test.ts +1 -1
- package/__tests__/utils.ts +30 -18
- package/__tests__/walk.test.ts +6 -6
- package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
- package/lib/benchmark/utils.d.ts +2 -1
- package/lib/benchmark/utils.js +10 -7
- package/lib/bundle.d.ts +2 -2
- package/lib/config/all.d.ts +2 -2
- package/lib/config/builtIn.d.ts +1 -1
- package/lib/config/config-resolvers.d.ts +16 -0
- package/lib/config/config-resolvers.js +213 -0
- package/lib/config/config.d.ts +14 -129
- package/lib/config/config.js +17 -234
- package/lib/config/index.d.ts +7 -0
- package/lib/config/index.js +19 -0
- package/lib/config/load.d.ts +2 -1
- package/lib/config/load.js +20 -13
- package/lib/config/minimal.d.ts +2 -2
- package/lib/config/recommended.d.ts +2 -2
- package/lib/config/types.d.ts +113 -0
- package/lib/config/types.js +2 -0
- package/lib/config/utils.d.ts +13 -0
- package/lib/config/utils.js +160 -0
- package/lib/format/format.d.ts +1 -1
- package/lib/format/format.js +28 -0
- package/lib/index.d.ts +1 -2
- package/lib/index.js +5 -6
- package/lib/lint.d.ts +1 -1
- package/lib/lint.js +5 -7
- package/lib/redocly/index.d.ts +1 -1
- package/lib/redocly/index.js +1 -1
- package/lib/redocly/redocly-client-types.d.ts +1 -1
- package/lib/redocly/registry-api.d.ts +1 -1
- package/lib/resolve.d.ts +1 -1
- package/lib/resolve.js +1 -2
- package/lib/rules/common/assertions/index.js +1 -1
- package/lib/rules/common/assertions/utils.d.ts +1 -1
- package/lib/rules/common/assertions/utils.js +6 -2
- package/lib/types/index.js +2 -2
- package/lib/types/oas3_1.js +31 -5
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +18 -1
- package/package.json +7 -4
- package/src/__tests__/lint.test.ts +1 -1
- package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
- package/src/benchmark/benchmark.js +1 -1
- package/src/benchmark/utils.ts +13 -8
- package/src/bundle.ts +2 -1
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +140 -0
- package/src/config/__tests__/config-resolvers.test.ts +398 -0
- package/src/config/__tests__/config.test.ts +244 -0
- package/src/config/__tests__/fixtures/plugin.js +1 -1
- package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +7 -0
- package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +12 -0
- package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
- package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
- package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
- package/src/config/__tests__/load.test.ts +8 -1
- package/src/config/all.ts +3 -2
- package/src/config/builtIn.ts +2 -1
- package/src/config/config-resolvers.ts +304 -0
- package/src/config/config.ts +40 -457
- package/src/config/index.ts +7 -0
- package/src/config/load.ts +37 -31
- package/src/config/minimal.ts +2 -2
- package/src/config/recommended.ts +2 -2
- package/src/config/types.ts +168 -0
- package/src/config/utils.ts +208 -0
- package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
- package/src/format/format.ts +36 -6
- package/src/index.ts +6 -2
- package/src/lint.ts +4 -5
- package/src/redocly/__tests__/redocly-client.test.ts +7 -0
- package/src/redocly/index.ts +4 -2
- package/src/redocly/redocly-client-types.ts +1 -1
- package/src/redocly/registry-api.ts +3 -1
- package/src/resolve.ts +2 -4
- package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
- package/src/rules/common/__tests__/info-description.test.ts +3 -3
- package/src/rules/common/__tests__/info-license.test.ts +2 -2
- package/src/rules/common/__tests__/license-url.test.ts +2 -2
- package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
- package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
- package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
- package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
- package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
- package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
- package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
- package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
- package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
- package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
- package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
- package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
- package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
- package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
- package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
- package/src/rules/common/__tests__/spec.test.ts +1 -1
- package/src/rules/common/__tests__/tag-description.test.ts +2 -2
- package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
- package/src/rules/common/assertions/index.ts +1 -1
- package/src/rules/common/assertions/utils.ts +5 -2
- package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
- package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
- package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
- package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
- package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
- package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
- package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
- package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
- package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
- package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
- package/src/rules/oas3/__tests__/spec/servers.test.ts +1 -1
- package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
- package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
- package/src/types/index.ts +2 -2
- package/src/types/oas3_1.ts +32 -7
- package/src/utils.ts +18 -2
- package/tsconfig.tsbuildinfo +1 -1
package/src/config/config.ts
CHANGED
|
@@ -1,126 +1,30 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
|
-
import { dirname } from 'path';
|
|
4
|
-
import { red, blue, yellow, green } from 'colorette';
|
|
5
3
|
import { parseYaml, stringifyYaml } from '../js-yaml';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
4
|
+
import { slash } from '../utils';
|
|
5
|
+
import { NormalizedProblem } from '../walk';
|
|
6
|
+
import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
|
|
7
|
+
|
|
8
|
+
import type { NodeType } from '../types';
|
|
9
|
+
import type {
|
|
10
|
+
DecoratorConfig,
|
|
11
|
+
Plugin,
|
|
12
|
+
PreprocessorConfig,
|
|
13
|
+
Region,
|
|
14
|
+
ResolveConfig,
|
|
15
|
+
ResolvedApi,
|
|
16
|
+
ResolvedConfig,
|
|
17
|
+
ResolvedLintConfig,
|
|
18
|
+
RuleConfig,
|
|
19
|
+
} from './types';
|
|
20
|
+
import { getResolveConfig } from './utils';
|
|
20
21
|
|
|
21
22
|
export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
|
|
22
23
|
const IGNORE_BANNER =
|
|
23
24
|
`# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
|
|
24
25
|
`# See https://redoc.ly/docs/cli/ for more information.\n`;
|
|
25
26
|
|
|
26
|
-
export type RuleConfig =
|
|
27
|
-
| ProblemSeverity
|
|
28
|
-
| 'off'
|
|
29
|
-
| ({
|
|
30
|
-
severity?: ProblemSeverity;
|
|
31
|
-
} & Record<string, any>);
|
|
32
|
-
|
|
33
|
-
export type PreprocessorConfig =
|
|
34
|
-
| ProblemSeverity
|
|
35
|
-
| 'off'
|
|
36
|
-
| 'on'
|
|
37
|
-
| ({
|
|
38
|
-
severity?: ProblemSeverity;
|
|
39
|
-
} & Record<string, any>);
|
|
40
|
-
|
|
41
|
-
export type DecoratorConfig = PreprocessorConfig;
|
|
42
|
-
|
|
43
|
-
export type LintRawConfig = {
|
|
44
|
-
plugins?: (string | Plugin)[];
|
|
45
|
-
extends?: string[];
|
|
46
|
-
doNotResolveExamples?: boolean;
|
|
47
|
-
|
|
48
|
-
rules?: Record<string, RuleConfig>;
|
|
49
|
-
oas2Rules?: Record<string, RuleConfig>;
|
|
50
|
-
oas3_0Rules?: Record<string, RuleConfig>;
|
|
51
|
-
oas3_1Rules?: Record<string, RuleConfig>;
|
|
52
|
-
|
|
53
|
-
preprocessors?: Record<string, PreprocessorConfig>;
|
|
54
|
-
oas2Preprocessors?: Record<string, PreprocessorConfig>;
|
|
55
|
-
oas3_0Preprocessors?: Record<string, PreprocessorConfig>;
|
|
56
|
-
oas3_1Preprocessors?: Record<string, PreprocessorConfig>;
|
|
57
|
-
|
|
58
|
-
decorators?: Record<string, DecoratorConfig>;
|
|
59
|
-
oas2Decorators?: Record<string, DecoratorConfig>;
|
|
60
|
-
oas3_0Decorators?: Record<string, DecoratorConfig>;
|
|
61
|
-
oas3_1Decorators?: Record<string, DecoratorConfig>;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
export type PreprocessorsConfig = {
|
|
65
|
-
oas3?: Oas3PreprocessorsSet;
|
|
66
|
-
oas2?: Oas2PreprocessorsSet;
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export type DecoratorsConfig = {
|
|
70
|
-
oas3?: Oas3DecoratorsSet;
|
|
71
|
-
oas2?: Oas2DecoratorsSet;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
export type TypesExtensionFn = (
|
|
75
|
-
types: Record<string, NodeType>,
|
|
76
|
-
oasVersion: OasVersion,
|
|
77
|
-
) => Record<string, NodeType>;
|
|
78
|
-
|
|
79
|
-
export type TypeExtensionsConfig = Partial<Record<OasMajorVersion, TypesExtensionFn>>;
|
|
80
|
-
export type CustomRulesConfig = {
|
|
81
|
-
oas3?: Oas3RuleSet;
|
|
82
|
-
oas2?: Oas2RuleSet;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
export type Plugin = {
|
|
86
|
-
id: string;
|
|
87
|
-
configs?: Record<string, LintRawConfig>;
|
|
88
|
-
rules?: CustomRulesConfig;
|
|
89
|
-
preprocessors?: PreprocessorsConfig;
|
|
90
|
-
decorators?: DecoratorsConfig;
|
|
91
|
-
typeExtension?: TypeExtensionsConfig;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export type ResolveHeader =
|
|
95
|
-
| {
|
|
96
|
-
name: string;
|
|
97
|
-
envVariable?: undefined;
|
|
98
|
-
value: string;
|
|
99
|
-
matches: string;
|
|
100
|
-
}
|
|
101
|
-
| {
|
|
102
|
-
name: string;
|
|
103
|
-
value?: undefined;
|
|
104
|
-
envVariable: string;
|
|
105
|
-
matches: string;
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
export type RawResolveConfig = {
|
|
109
|
-
http?: Partial<HttpResolveConfig>;
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export type HttpResolveConfig = {
|
|
113
|
-
headers: ResolveHeader[];
|
|
114
|
-
customFetch?: Function;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
export type ResolveConfig = {
|
|
118
|
-
http: HttpResolveConfig;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
27
|
export const DEFAULT_REGION = 'us';
|
|
122
|
-
export type Region = 'us' | 'eu';
|
|
123
|
-
export type AccessTokens = { [region in Region]?: string };
|
|
124
28
|
const REDOCLY_DOMAIN = process.env.REDOCLY_DOMAIN;
|
|
125
29
|
export const DOMAINS: { [region in Region]: string } = {
|
|
126
30
|
us: 'redocly.com',
|
|
@@ -136,31 +40,6 @@ if (REDOCLY_DOMAIN === 'redoc.online') {
|
|
|
136
40
|
}
|
|
137
41
|
export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
|
|
138
42
|
|
|
139
|
-
export type DeprecatedRawConfig = {
|
|
140
|
-
apiDefinitions?: Record<string, string>;
|
|
141
|
-
lint?: LintRawConfig;
|
|
142
|
-
resolve?: RawResolveConfig;
|
|
143
|
-
region?: Region;
|
|
144
|
-
referenceDocs?: Record<string, any>;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export type Api = {
|
|
148
|
-
root: string;
|
|
149
|
-
lint?: Omit<LintRawConfig, 'plugins'>;
|
|
150
|
-
'features.openapi'?: Record<string, any>;
|
|
151
|
-
'features.mockServer'?: Record<string, any>;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
export type RawConfig = {
|
|
155
|
-
apis?: Record<string, Api>;
|
|
156
|
-
lint?: LintRawConfig;
|
|
157
|
-
resolve?: RawResolveConfig;
|
|
158
|
-
region?: Region;
|
|
159
|
-
'features.openapi'?: Record<string, any>;
|
|
160
|
-
'features.mockServer'?: Record<string, any>;
|
|
161
|
-
organization?: string;
|
|
162
|
-
};
|
|
163
|
-
|
|
164
43
|
export class LintConfig {
|
|
165
44
|
plugins: Plugin[];
|
|
166
45
|
ignore: Record<string, Record<string, Set<string>>> = {};
|
|
@@ -172,48 +51,38 @@ export class LintConfig {
|
|
|
172
51
|
private _usedRules: Set<string> = new Set();
|
|
173
52
|
private _usedVersions: Set<OasVersion> = new Set();
|
|
174
53
|
|
|
175
|
-
recommendedFallback: boolean
|
|
54
|
+
recommendedFallback: boolean;
|
|
176
55
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
|
|
56
|
+
extendPaths: string[];
|
|
57
|
+
pluginPaths: string[];
|
|
180
58
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const extendConfigs: LintRawConfig[] = rawConfig.extends
|
|
186
|
-
? resolvePresets(rawConfig.extends, this.plugins)
|
|
187
|
-
: [recommended];
|
|
188
|
-
|
|
189
|
-
if (rawConfig.rules || rawConfig.preprocessors || rawConfig.decorators) {
|
|
190
|
-
extendConfigs.push({
|
|
191
|
-
rules: rawConfig.rules,
|
|
192
|
-
preprocessors: rawConfig.preprocessors,
|
|
193
|
-
decorators: rawConfig.decorators,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
59
|
+
constructor(public rawConfig: ResolvedLintConfig, public configFile?: string) {
|
|
60
|
+
this.plugins = rawConfig.plugins || [];
|
|
61
|
+
this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
|
|
196
62
|
|
|
197
|
-
|
|
63
|
+
this.recommendedFallback = rawConfig.recommendedFallback || false
|
|
198
64
|
|
|
199
65
|
this.rules = {
|
|
200
|
-
[OasVersion.Version2]: { ...
|
|
201
|
-
[OasVersion.Version3_0]: { ...
|
|
202
|
-
[OasVersion.Version3_1]: { ...
|
|
66
|
+
[OasVersion.Version2]: { ...rawConfig.rules, ...rawConfig.oas2Rules },
|
|
67
|
+
[OasVersion.Version3_0]: { ...rawConfig.rules, ...rawConfig.oas3_0Rules },
|
|
68
|
+
[OasVersion.Version3_1]: { ...rawConfig.rules, ...rawConfig.oas3_1Rules },
|
|
203
69
|
};
|
|
204
70
|
|
|
205
71
|
this.preprocessors = {
|
|
206
|
-
[OasVersion.Version2]: { ...
|
|
207
|
-
[OasVersion.Version3_0]: { ...
|
|
208
|
-
[OasVersion.Version3_1]: { ...
|
|
72
|
+
[OasVersion.Version2]: { ...rawConfig.preprocessors, ...rawConfig.oas2Preprocessors },
|
|
73
|
+
[OasVersion.Version3_0]: { ...rawConfig.preprocessors, ...rawConfig.oas3_0Preprocessors },
|
|
74
|
+
[OasVersion.Version3_1]: { ...rawConfig.preprocessors, ...rawConfig.oas3_1Preprocessors },
|
|
209
75
|
};
|
|
210
76
|
|
|
211
77
|
this.decorators = {
|
|
212
|
-
[OasVersion.Version2]: { ...
|
|
213
|
-
[OasVersion.Version3_0]: { ...
|
|
214
|
-
[OasVersion.Version3_1]: { ...
|
|
78
|
+
[OasVersion.Version2]: { ...rawConfig.decorators, ...rawConfig.oas2Decorators },
|
|
79
|
+
[OasVersion.Version3_0]: { ...rawConfig.decorators, ...rawConfig.oas3_0Decorators },
|
|
80
|
+
[OasVersion.Version3_1]: { ...rawConfig.decorators, ...rawConfig.oas3_1Decorators },
|
|
215
81
|
};
|
|
216
82
|
|
|
83
|
+
this.extendPaths = rawConfig.extendPaths || [];
|
|
84
|
+
this.pluginPaths = rawConfig.pluginPaths || [];
|
|
85
|
+
|
|
217
86
|
const dir = this.configFile
|
|
218
87
|
? path.dirname(this.configFile)
|
|
219
88
|
: (typeof process !== 'undefined' && process.cwd()) || '';
|
|
@@ -230,7 +99,7 @@ export class LintConfig {
|
|
|
230
99
|
|
|
231
100
|
// resolve ignore paths
|
|
232
101
|
for (const fileName of Object.keys(this.ignore)) {
|
|
233
|
-
this.ignore[path.resolve(dirname(ignoreFile), fileName)] = this.ignore[fileName];
|
|
102
|
+
this.ignore[path.resolve(path.dirname(ignoreFile), fileName)] = this.ignore[fileName];
|
|
234
103
|
for (const ruleId of Object.keys(this.ignore[fileName])) {
|
|
235
104
|
this.ignore[fileName][ruleId] = new Set(this.ignore[fileName][ruleId]);
|
|
236
105
|
}
|
|
@@ -414,7 +283,7 @@ export class LintConfig {
|
|
|
414
283
|
}
|
|
415
284
|
|
|
416
285
|
export class Config {
|
|
417
|
-
apis: Record<string,
|
|
286
|
+
apis: Record<string, ResolvedApi>;
|
|
418
287
|
lint: LintConfig;
|
|
419
288
|
resolve: ResolveConfig;
|
|
420
289
|
licenseKey?: string;
|
|
@@ -422,299 +291,13 @@ export class Config {
|
|
|
422
291
|
'features.openapi': Record<string, any>;
|
|
423
292
|
'features.mockServer'?: Record<string, any>;
|
|
424
293
|
organization?: string;
|
|
425
|
-
constructor(public rawConfig:
|
|
294
|
+
constructor(public rawConfig: ResolvedConfig, public configFile?: string) {
|
|
426
295
|
this.apis = rawConfig.apis || {};
|
|
427
296
|
this.lint = new LintConfig(rawConfig.lint || {}, configFile);
|
|
428
297
|
this['features.openapi'] = rawConfig['features.openapi'] || {};
|
|
429
298
|
this['features.mockServer'] = rawConfig['features.mockServer'] || {};
|
|
430
|
-
this.resolve =
|
|
431
|
-
http: {
|
|
432
|
-
headers: rawConfig?.resolve?.http?.headers ?? [],
|
|
433
|
-
customFetch: undefined,
|
|
434
|
-
},
|
|
435
|
-
};
|
|
299
|
+
this.resolve = getResolveConfig(rawConfig?.resolve);
|
|
436
300
|
this.region = rawConfig.region;
|
|
437
301
|
this.organization = rawConfig.organization;
|
|
438
302
|
}
|
|
439
303
|
}
|
|
440
|
-
|
|
441
|
-
function resolvePresets(presets: string[], plugins: Plugin[]) {
|
|
442
|
-
return presets.map((presetName) => {
|
|
443
|
-
const { pluginId, configName } = parsePresetName(presetName);
|
|
444
|
-
const plugin = plugins.find((p) => p.id === pluginId);
|
|
445
|
-
if (!plugin) {
|
|
446
|
-
throw new Error(`Invalid config ${red(presetName)}: plugin ${pluginId} is not included.`);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const preset = plugin.configs?.[configName]!;
|
|
450
|
-
if (!preset) {
|
|
451
|
-
throw new Error(
|
|
452
|
-
pluginId
|
|
453
|
-
? `Invalid config ${red(
|
|
454
|
-
presetName,
|
|
455
|
-
)}: plugin ${pluginId} doesn't export config with name ${configName}.`
|
|
456
|
-
: `Invalid config ${red(presetName)}: there is no such built-in config.`,
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
|
-
return preset;
|
|
460
|
-
});
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
function parsePresetName(presetName: string): { pluginId: string; configName: string } {
|
|
464
|
-
if (presetName.indexOf('/') > -1) {
|
|
465
|
-
const [pluginId, configName] = presetName.split('/');
|
|
466
|
-
return { pluginId, configName };
|
|
467
|
-
} else {
|
|
468
|
-
return { pluginId: '', configName: presetName };
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
function resolvePlugins(plugins: (string | Plugin)[] | null, configPath: string = ''): Plugin[] {
|
|
473
|
-
if (!plugins) return [];
|
|
474
|
-
|
|
475
|
-
// @ts-ignore
|
|
476
|
-
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
|
|
477
|
-
|
|
478
|
-
const seenPluginIds = new Map<string, string>();
|
|
479
|
-
|
|
480
|
-
return plugins
|
|
481
|
-
.map((p) => {
|
|
482
|
-
// TODO: resolve npm packages similar to eslint
|
|
483
|
-
const pluginModule =
|
|
484
|
-
typeof p === 'string'
|
|
485
|
-
? (requireFunc(path.resolve(path.dirname(configPath), p)) as Plugin)
|
|
486
|
-
: p;
|
|
487
|
-
|
|
488
|
-
const id = pluginModule.id;
|
|
489
|
-
if (typeof id !== 'string') {
|
|
490
|
-
throw new Error(red(`Plugin must define \`id\` property in ${blue(p.toString())}.`));
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
if (seenPluginIds.has(id)) {
|
|
494
|
-
const pluginPath = seenPluginIds.get(id)!;
|
|
495
|
-
throw new Error(
|
|
496
|
-
red(
|
|
497
|
-
`Plugin "id" must be unique. Plugin ${blue(p.toString())} uses id "${blue(
|
|
498
|
-
id,
|
|
499
|
-
)}" already seen in ${blue(pluginPath)}`,
|
|
500
|
-
),
|
|
501
|
-
);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
seenPluginIds.set(id, p.toString());
|
|
505
|
-
|
|
506
|
-
const plugin: Plugin = {
|
|
507
|
-
id,
|
|
508
|
-
...(pluginModule.configs ? { configs: pluginModule.configs } : {}),
|
|
509
|
-
...(pluginModule.typeExtension ? { typeExtension: pluginModule.typeExtension } : {}),
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
if (pluginModule.rules) {
|
|
513
|
-
if (!pluginModule.rules.oas3 && !pluginModule.rules.oas2) {
|
|
514
|
-
throw new Error(`Plugin rules must have \`oas3\` or \`oas2\` rules "${p}.`);
|
|
515
|
-
}
|
|
516
|
-
plugin.rules = {};
|
|
517
|
-
if (pluginModule.rules.oas3) {
|
|
518
|
-
plugin.rules.oas3 = prefixRules(pluginModule.rules.oas3, id);
|
|
519
|
-
}
|
|
520
|
-
if (pluginModule.rules.oas2) {
|
|
521
|
-
plugin.rules.oas2 = prefixRules(pluginModule.rules.oas2, id);
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
if (pluginModule.preprocessors) {
|
|
525
|
-
if (!pluginModule.preprocessors.oas3 && !pluginModule.preprocessors.oas2) {
|
|
526
|
-
throw new Error(
|
|
527
|
-
`Plugin \`preprocessors\` must have \`oas3\` or \`oas2\` preprocessors "${p}.`,
|
|
528
|
-
);
|
|
529
|
-
}
|
|
530
|
-
plugin.preprocessors = {};
|
|
531
|
-
if (pluginModule.preprocessors.oas3) {
|
|
532
|
-
plugin.preprocessors.oas3 = prefixRules(pluginModule.preprocessors.oas3, id);
|
|
533
|
-
}
|
|
534
|
-
if (pluginModule.preprocessors.oas2) {
|
|
535
|
-
plugin.preprocessors.oas2 = prefixRules(pluginModule.preprocessors.oas2, id);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
if (pluginModule.decorators) {
|
|
540
|
-
if (!pluginModule.decorators.oas3 && !pluginModule.decorators.oas2) {
|
|
541
|
-
throw new Error(`Plugin \`decorators\` must have \`oas3\` or \`oas2\` decorators "${p}.`);
|
|
542
|
-
}
|
|
543
|
-
plugin.decorators = {};
|
|
544
|
-
if (pluginModule.decorators.oas3) {
|
|
545
|
-
plugin.decorators.oas3 = prefixRules(pluginModule.decorators.oas3, id);
|
|
546
|
-
}
|
|
547
|
-
if (pluginModule.decorators.oas2) {
|
|
548
|
-
plugin.decorators.oas2 = prefixRules(pluginModule.decorators.oas2, id);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return plugin;
|
|
553
|
-
})
|
|
554
|
-
.filter(notUndefined);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
function prefixRules<T extends Record<string, any>>(rules: T, prefix: string) {
|
|
558
|
-
if (!prefix) return rules;
|
|
559
|
-
|
|
560
|
-
const res: any = {};
|
|
561
|
-
for (const name of Object.keys(rules)) {
|
|
562
|
-
res[`${prefix}/${name}`] = rules[name];
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
return res;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
type RulesFields =
|
|
569
|
-
| 'rules'
|
|
570
|
-
| 'oas2Rules'
|
|
571
|
-
| 'oas3_0Rules'
|
|
572
|
-
| 'oas3_1Rules'
|
|
573
|
-
| 'preprocessors'
|
|
574
|
-
| 'oas2Preprocessors'
|
|
575
|
-
| 'oas3_0Preprocessors'
|
|
576
|
-
| 'oas3_1Preprocessors'
|
|
577
|
-
| 'decorators'
|
|
578
|
-
| 'oas2Decorators'
|
|
579
|
-
| 'oas3_0Decorators'
|
|
580
|
-
| 'oas3_1Decorators';
|
|
581
|
-
|
|
582
|
-
function mergeExtends(rulesConfList: LintRawConfig[]) {
|
|
583
|
-
const result: Omit<LintRawConfig, RulesFields> & Required<Pick<LintRawConfig, RulesFields>> = {
|
|
584
|
-
rules: {},
|
|
585
|
-
oas2Rules: {},
|
|
586
|
-
oas3_0Rules: {},
|
|
587
|
-
oas3_1Rules: {},
|
|
588
|
-
|
|
589
|
-
preprocessors: {},
|
|
590
|
-
oas2Preprocessors: {},
|
|
591
|
-
oas3_0Preprocessors: {},
|
|
592
|
-
oas3_1Preprocessors: {},
|
|
593
|
-
|
|
594
|
-
decorators: {},
|
|
595
|
-
oas2Decorators: {},
|
|
596
|
-
oas3_0Decorators: {},
|
|
597
|
-
oas3_1Decorators: {},
|
|
598
|
-
};
|
|
599
|
-
|
|
600
|
-
for (let rulesConf of rulesConfList) {
|
|
601
|
-
if (rulesConf.extends) {
|
|
602
|
-
throw new Error(
|
|
603
|
-
`\`extends\` is not supported in shared configs yet: ${JSON.stringify(
|
|
604
|
-
rulesConf,
|
|
605
|
-
null,
|
|
606
|
-
2,
|
|
607
|
-
)}.`,
|
|
608
|
-
);
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
Object.assign(result.rules, rulesConf.rules);
|
|
612
|
-
Object.assign(result.oas2Rules, rulesConf.oas2Rules);
|
|
613
|
-
assignExisting(result.oas2Rules, rulesConf.rules || {});
|
|
614
|
-
Object.assign(result.oas3_0Rules, rulesConf.oas3_0Rules);
|
|
615
|
-
assignExisting(result.oas3_0Rules, rulesConf.rules || {});
|
|
616
|
-
Object.assign(result.oas3_1Rules, rulesConf.oas3_1Rules);
|
|
617
|
-
assignExisting(result.oas3_1Rules, rulesConf.rules || {});
|
|
618
|
-
|
|
619
|
-
Object.assign(result.preprocessors, rulesConf.preprocessors);
|
|
620
|
-
Object.assign(result.oas2Preprocessors, rulesConf.oas2Preprocessors);
|
|
621
|
-
assignExisting(result.oas2Preprocessors, rulesConf.preprocessors || {});
|
|
622
|
-
Object.assign(result.oas3_0Preprocessors, rulesConf.oas3_0Preprocessors);
|
|
623
|
-
assignExisting(result.oas3_0Preprocessors, rulesConf.preprocessors || {});
|
|
624
|
-
Object.assign(result.oas3_1Preprocessors, rulesConf.oas3_1Preprocessors);
|
|
625
|
-
assignExisting(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
|
|
626
|
-
|
|
627
|
-
Object.assign(result.decorators, rulesConf.decorators);
|
|
628
|
-
Object.assign(result.oas2Decorators, rulesConf.oas2Decorators);
|
|
629
|
-
assignExisting(result.oas2Decorators, rulesConf.decorators || {});
|
|
630
|
-
Object.assign(result.oas3_0Decorators, rulesConf.oas3_0Decorators);
|
|
631
|
-
assignExisting(result.oas3_0Decorators, rulesConf.decorators || {});
|
|
632
|
-
Object.assign(result.oas3_1Decorators, rulesConf.oas3_1Decorators);
|
|
633
|
-
assignExisting(result.oas3_1Decorators, rulesConf.decorators || {});
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return result;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
function assignExisting<T>(target: Record<string, T>, obj: Record<string, T>) {
|
|
640
|
-
for (let k of Object.keys(obj)) {
|
|
641
|
-
if (target.hasOwnProperty(k)) {
|
|
642
|
-
target[k] = obj[k];
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
export function getMergedConfig(config: Config, entrypointAlias?: string): Config {
|
|
648
|
-
return entrypointAlias
|
|
649
|
-
? new Config(
|
|
650
|
-
{
|
|
651
|
-
...config.rawConfig,
|
|
652
|
-
lint: getMergedLintConfig(config, entrypointAlias),
|
|
653
|
-
'features.openapi': {
|
|
654
|
-
...config['features.openapi'],
|
|
655
|
-
...config.apis[entrypointAlias]?.['features.openapi'],
|
|
656
|
-
},
|
|
657
|
-
'features.mockServer': {
|
|
658
|
-
...config['features.mockServer'],
|
|
659
|
-
...config.apis[entrypointAlias]?.['features.mockServer'],
|
|
660
|
-
},
|
|
661
|
-
// TODO: merge everything else here
|
|
662
|
-
},
|
|
663
|
-
config.configFile,
|
|
664
|
-
)
|
|
665
|
-
: config;
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
export function getMergedLintConfig(config: Config, entrypointAlias?: string) {
|
|
669
|
-
const apiLint = entrypointAlias ? config.apis[entrypointAlias]?.lint : {};
|
|
670
|
-
const mergedLint = {
|
|
671
|
-
...config.rawConfig.lint,
|
|
672
|
-
...apiLint,
|
|
673
|
-
rules: { ...config.rawConfig.lint?.rules, ...apiLint?.rules },
|
|
674
|
-
preprocessors: { ...config.rawConfig.lint?.preprocessors, ...apiLint?.preprocessors },
|
|
675
|
-
decorators: { ...config.rawConfig.lint?.decorators, ...apiLint?.decorators },
|
|
676
|
-
};
|
|
677
|
-
return mergedLint;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function transformApiDefinitionsToApis(
|
|
681
|
-
apiDefinitions: Record<string, string> = {},
|
|
682
|
-
): Record<string, Api> {
|
|
683
|
-
let apis: Record<string, Api> = {};
|
|
684
|
-
for (const [apiName, apiPath] of Object.entries(apiDefinitions)) {
|
|
685
|
-
apis[apiName] = { root: apiPath };
|
|
686
|
-
}
|
|
687
|
-
return apis;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
export function transformConfig(rawConfig: DeprecatedRawConfig | RawConfig): RawConfig {
|
|
691
|
-
if ((rawConfig as RawConfig).apis && (rawConfig as DeprecatedRawConfig).apiDefinitions) {
|
|
692
|
-
throw new Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");
|
|
693
|
-
}
|
|
694
|
-
if (
|
|
695
|
-
(rawConfig as RawConfig)['features.openapi'] &&
|
|
696
|
-
(rawConfig as DeprecatedRawConfig).referenceDocs
|
|
697
|
-
) {
|
|
698
|
-
throw new Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");
|
|
699
|
-
}
|
|
700
|
-
const { apiDefinitions, referenceDocs, ...rest } = rawConfig as DeprecatedRawConfig & RawConfig;
|
|
701
|
-
if (apiDefinitions) {
|
|
702
|
-
process.stderr.write(
|
|
703
|
-
`The ${yellow('apiDefinitions')} field is deprecated. Use ${green(
|
|
704
|
-
'apis',
|
|
705
|
-
)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
|
|
706
|
-
);
|
|
707
|
-
}
|
|
708
|
-
if (referenceDocs) {
|
|
709
|
-
process.stderr.write(
|
|
710
|
-
`The ${yellow('referenceDocs')} field is deprecated. Use ${green(
|
|
711
|
-
'features.openapi',
|
|
712
|
-
)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
|
|
713
|
-
);
|
|
714
|
-
}
|
|
715
|
-
return {
|
|
716
|
-
'features.openapi': referenceDocs,
|
|
717
|
-
apis: transformApiDefinitionsToApis(apiDefinitions),
|
|
718
|
-
...rest,
|
|
719
|
-
};
|
|
720
|
-
}
|
package/src/config/load.ts
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { RedoclyClient } from '../redocly';
|
|
4
|
-
import { loadYaml } from '../utils';
|
|
5
|
-
import { Config, DOMAINS
|
|
6
|
-
import {
|
|
4
|
+
import { isEmptyObject, loadYaml } from '../utils';
|
|
5
|
+
import { Config, DOMAINS } from './config';
|
|
6
|
+
import { transformConfig } from './utils';
|
|
7
|
+
import { resolveConfig } from './config-resolvers';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
const rawConfig = await getConfig(configPath);
|
|
9
|
+
import type { RawConfig, Region } from './types';
|
|
10
10
|
|
|
11
|
+
export async function loadConfig(
|
|
12
|
+
configPath: string | undefined = findConfig(),
|
|
13
|
+
customExtends?: string[],
|
|
14
|
+
): Promise<Config> {
|
|
15
|
+
const rawConfig = await getConfig(configPath);
|
|
11
16
|
if (customExtends !== undefined) {
|
|
12
17
|
rawConfig.lint = rawConfig.lint || {};
|
|
13
18
|
rawConfig.lint.extends = customExtends;
|
|
19
|
+
} else if (isEmptyObject(rawConfig)) {
|
|
20
|
+
// TODO: check if we can add recommended here. add message here?
|
|
21
|
+
// rawConfig.lint = { extends: ['recommended'], recommendedFallback: true };
|
|
14
22
|
}
|
|
15
23
|
|
|
16
24
|
const redoclyClient = new RedoclyClient();
|
|
@@ -23,40 +31,38 @@ export async function loadConfig(configPath: string | undefined = findConfig(),
|
|
|
23
31
|
|
|
24
32
|
for (const item of tokens) {
|
|
25
33
|
const domain = DOMAINS[item.region as Region];
|
|
26
|
-
rawConfig.resolve.http.headers.push(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
rawConfig.resolve.http.headers.push(
|
|
35
|
+
{
|
|
36
|
+
matches: `https://api.${domain}/registry/**`,
|
|
37
|
+
name: 'Authorization',
|
|
38
|
+
envVariable: undefined,
|
|
39
|
+
value: item.token,
|
|
40
|
+
},
|
|
41
|
+
//support redocly.com domain for future compatibility
|
|
42
|
+
...(item.region === 'us'
|
|
43
|
+
? [
|
|
44
|
+
{
|
|
45
|
+
matches: `https://api.redoc.ly/registry/**`,
|
|
46
|
+
name: 'Authorization',
|
|
47
|
+
envVariable: undefined,
|
|
48
|
+
value: item.token,
|
|
49
|
+
},
|
|
50
|
+
]
|
|
51
|
+
: []),
|
|
52
|
+
);
|
|
39
53
|
}
|
|
40
54
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...rawConfig,
|
|
44
|
-
lint: {
|
|
45
|
-
...rawConfig?.lint,
|
|
46
|
-
plugins: [...(rawConfig?.lint?.plugins || []), defaultPlugin], // inject default plugin
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
configPath,
|
|
50
|
-
);
|
|
55
|
+
|
|
56
|
+
return resolveConfig(rawConfig, configPath);
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
export const CONFIG_FILE_NAMES = ['redocly.yaml', 'redocly.yml', '.redocly.yaml', '.redocly.yml'];
|
|
54
60
|
|
|
55
61
|
export function findConfig(dir?: string): string | undefined {
|
|
56
62
|
if (!fs.hasOwnProperty('existsSync')) return;
|
|
57
|
-
const existingConfigFiles = CONFIG_FILE_NAMES
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
const existingConfigFiles = CONFIG_FILE_NAMES.map((name) =>
|
|
64
|
+
dir ? path.resolve(dir, name) : name,
|
|
65
|
+
).filter(fs.existsSync);
|
|
60
66
|
if (existingConfigFiles.length > 1) {
|
|
61
67
|
throw new Error(`
|
|
62
68
|
Multiple configuration files are not allowed.
|
package/src/config/minimal.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PluginLintConfig } from './types';
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
rules: {
|
|
@@ -55,4 +55,4 @@ export default {
|
|
|
55
55
|
'no-undefined-server-variable': 'warn',
|
|
56
56
|
'no-servers-empty-enum': 'error',
|
|
57
57
|
},
|
|
58
|
-
} as
|
|
58
|
+
} as PluginLintConfig;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PluginLintConfig } from './types';
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
4
|
rules: {
|
|
@@ -55,4 +55,4 @@ export default {
|
|
|
55
55
|
'no-undefined-server-variable': 'error',
|
|
56
56
|
'no-servers-empty-enum': 'error',
|
|
57
57
|
},
|
|
58
|
-
} as
|
|
58
|
+
} as PluginLintConfig;
|