@redocly/openapi-core 1.0.0-beta.94 → 1.0.0-beta.97

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.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/__tests__/bundle.test.ts +6 -6
  3. package/__tests__/fixtures/extension.js +1 -1
  4. package/__tests__/login.test.ts +2 -2
  5. package/__tests__/ref-utils.test.ts +1 -1
  6. package/__tests__/utils.ts +30 -18
  7. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
  8. package/lib/benchmark/utils.d.ts +2 -1
  9. package/lib/benchmark/utils.js +10 -7
  10. package/lib/bundle.d.ts +2 -2
  11. package/lib/config/all.d.ts +2 -2
  12. package/lib/config/builtIn.d.ts +1 -1
  13. package/lib/config/config-resolvers.d.ts +16 -0
  14. package/lib/config/config-resolvers.js +242 -0
  15. package/lib/config/config.d.ts +18 -130
  16. package/lib/config/config.js +34 -245
  17. package/lib/config/index.d.ts +7 -0
  18. package/lib/config/index.js +19 -0
  19. package/lib/config/load.d.ts +2 -1
  20. package/lib/config/load.js +20 -13
  21. package/lib/config/minimal.d.ts +2 -2
  22. package/lib/config/recommended.d.ts +2 -2
  23. package/lib/config/types.d.ts +113 -0
  24. package/lib/config/types.js +2 -0
  25. package/lib/config/utils.d.ts +13 -0
  26. package/lib/config/utils.js +160 -0
  27. package/lib/format/format.d.ts +1 -1
  28. package/lib/format/format.js +30 -1
  29. package/lib/index.d.ts +1 -2
  30. package/lib/index.js +5 -6
  31. package/lib/lint.d.ts +1 -1
  32. package/lib/lint.js +5 -7
  33. package/lib/redocly/index.d.ts +1 -1
  34. package/lib/redocly/index.js +10 -26
  35. package/lib/redocly/redocly-client-types.d.ts +1 -1
  36. package/lib/redocly/registry-api-types.d.ts +1 -0
  37. package/lib/redocly/registry-api.d.ts +2 -2
  38. package/lib/redocly/registry-api.js +2 -1
  39. package/lib/resolve.d.ts +1 -1
  40. package/lib/resolve.js +1 -2
  41. package/lib/rules/common/assertions/index.js +1 -1
  42. package/lib/rules/common/assertions/utils.d.ts +1 -1
  43. package/lib/rules/common/assertions/utils.js +6 -2
  44. package/lib/utils.d.ts +4 -2
  45. package/lib/utils.js +20 -3
  46. package/package.json +9 -6
  47. package/src/__tests__/lint.test.ts +1 -1
  48. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
  49. package/src/benchmark/benchmark.js +1 -1
  50. package/src/benchmark/utils.ts +13 -8
  51. package/src/bundle.ts +2 -1
  52. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +157 -0
  53. package/src/config/__tests__/config-resolvers.test.ts +429 -0
  54. package/src/config/__tests__/config.test.ts +17 -29
  55. package/src/config/__tests__/fixtures/plugin.js +1 -1
  56. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +12 -0
  57. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
  58. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
  59. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +19 -0
  60. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
  61. package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
  62. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
  63. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
  64. package/src/config/__tests__/load.test.ts +8 -1
  65. package/src/config/all.ts +3 -2
  66. package/src/config/builtIn.ts +2 -1
  67. package/src/config/config-resolvers.ts +359 -0
  68. package/src/config/config.ts +60 -468
  69. package/src/config/index.ts +7 -0
  70. package/src/config/load.ts +37 -31
  71. package/src/config/minimal.ts +2 -2
  72. package/src/config/recommended.ts +2 -2
  73. package/src/config/types.ts +168 -0
  74. package/src/config/utils.ts +208 -0
  75. package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
  76. package/src/format/format.ts +38 -7
  77. package/src/index.ts +6 -2
  78. package/src/lint.ts +4 -5
  79. package/src/redocly/__tests__/redocly-client.test.ts +7 -0
  80. package/src/redocly/index.ts +14 -41
  81. package/src/redocly/redocly-client-types.ts +1 -1
  82. package/src/redocly/registry-api-types.ts +1 -0
  83. package/src/redocly/registry-api.ts +5 -1
  84. package/src/resolve.ts +2 -4
  85. package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
  86. package/src/rules/common/__tests__/info-description.test.ts +3 -3
  87. package/src/rules/common/__tests__/info-license.test.ts +2 -2
  88. package/src/rules/common/__tests__/license-url.test.ts +2 -2
  89. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
  90. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
  91. package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
  92. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
  93. package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
  94. package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
  95. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
  96. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
  97. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
  98. package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
  99. package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
  100. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
  101. package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
  102. package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
  103. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  104. package/src/rules/common/__tests__/spec.test.ts +1 -1
  105. package/src/rules/common/__tests__/tag-description.test.ts +2 -2
  106. package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
  107. package/src/rules/common/assertions/index.ts +1 -1
  108. package/src/rules/common/assertions/utils.ts +5 -2
  109. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  110. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
  111. package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
  112. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  113. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
  114. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
  115. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
  116. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
  117. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  118. package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
  119. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
  120. package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
  121. package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
  122. package/src/utils.ts +21 -4
  123. package/tsconfig.tsbuildinfo +1 -1
@@ -1,165 +1,53 @@
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 { notUndefined, slash } from '../utils';
7
- import {
8
- OasVersion,
9
- Oas3PreprocessorsSet,
10
- OasMajorVersion,
11
- Oas3DecoratorsSet,
12
- Oas2RuleSet,
13
- Oas2PreprocessorsSet,
14
- Oas2DecoratorsSet,
15
- Oas3RuleSet,
16
- } from '../oas-types';
17
- import { ProblemSeverity, NormalizedProblem } from '../walk';
18
- import recommended from './recommended';
19
- import { NodeType } from '../types';
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';
21
+
22
+ // Alias environment here so this file can work in browser environments too.
23
+ export const env = typeof process !== 'undefined' ? process.env || {} : {};
20
24
 
21
25
  export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
22
26
  const IGNORE_BANNER =
23
27
  `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
24
28
  `# See https://redoc.ly/docs/cli/ for more information.\n`;
25
29
 
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
30
  export const DEFAULT_REGION = 'us';
122
- export type Region = 'us' | 'eu';
123
- export type AccessTokens = { [region in Region]?: string };
124
- const REDOCLY_DOMAIN = process.env.REDOCLY_DOMAIN;
125
- export const DOMAINS: { [region in Region]: string } = {
126
- us: 'redocly.com',
127
- eu: 'eu.redocly.com',
128
- };
129
-
130
- // FIXME: temporary fix for our lab environments
131
- if (REDOCLY_DOMAIN?.endsWith('.redocly.host')) {
132
- DOMAINS[REDOCLY_DOMAIN.split('.')[0] as Region] = REDOCLY_DOMAIN;
133
- }
134
- if (REDOCLY_DOMAIN === 'redoc.online') {
135
- DOMAINS[REDOCLY_DOMAIN as Region] = REDOCLY_DOMAIN;
136
- }
137
- export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
138
31
 
139
- export type DeprecatedRawConfig = {
140
- apiDefinitions?: Record<string, string>;
141
- lint?: LintRawConfig;
142
- resolve?: RawResolveConfig;
143
- region?: Region;
144
- referenceDocs?: Record<string, any>;
145
- };
32
+ function getDomains() {
33
+ const domains: { [region in Region]: string } = {
34
+ us: 'redocly.com',
35
+ eu: 'eu.redocly.com',
36
+ };
146
37
 
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
- };
38
+ // FIXME: temporary fix for our lab environments
39
+ const domain = env.REDOCLY_DOMAIN;
40
+ if (domain?.endsWith('.redocly.host')) {
41
+ domains[domain.split('.')[0] as Region] = domain;
42
+ }
43
+ if (domain === 'redoc.online') {
44
+ domains[domain as Region] = domain;
45
+ }
46
+ return domains;
47
+ }
153
48
 
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
- };
49
+ export const DOMAINS = getDomains();
50
+ export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
163
51
 
164
52
  export class LintConfig {
165
53
  plugins: Plugin[];
@@ -172,48 +60,38 @@ export class LintConfig {
172
60
  private _usedRules: Set<string> = new Set();
173
61
  private _usedVersions: Set<OasVersion> = new Set();
174
62
 
175
- recommendedFallback: boolean = false;
176
-
177
- constructor(public rawConfig: LintRawConfig, public configFile?: string) {
178
- this.plugins = rawConfig.plugins ? resolvePlugins(rawConfig.plugins, configFile) : [];
179
- this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
180
-
181
- if (!rawConfig.extends) {
182
- this.recommendedFallback = true;
183
- }
63
+ recommendedFallback: boolean;
184
64
 
185
- const extendConfigs: LintRawConfig[] = rawConfig.extends
186
- ? resolvePresets(rawConfig.extends, this.plugins)
187
- : [recommended];
65
+ extendPaths: string[];
66
+ pluginPaths: string[];
188
67
 
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
- }
68
+ constructor(public rawConfig: ResolvedLintConfig, public configFile?: string) {
69
+ this.plugins = rawConfig.plugins || [];
70
+ this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
196
71
 
197
- const merged = mergeExtends(extendConfigs);
72
+ this.recommendedFallback = rawConfig.recommendedFallback || false
198
73
 
199
74
  this.rules = {
200
- [OasVersion.Version2]: { ...merged.rules, ...merged.oas2Rules },
201
- [OasVersion.Version3_0]: { ...merged.rules, ...merged.oas3_0Rules },
202
- [OasVersion.Version3_1]: { ...merged.rules, ...merged.oas3_1Rules },
75
+ [OasVersion.Version2]: { ...rawConfig.rules, ...rawConfig.oas2Rules },
76
+ [OasVersion.Version3_0]: { ...rawConfig.rules, ...rawConfig.oas3_0Rules },
77
+ [OasVersion.Version3_1]: { ...rawConfig.rules, ...rawConfig.oas3_1Rules },
203
78
  };
204
79
 
205
80
  this.preprocessors = {
206
- [OasVersion.Version2]: { ...merged.preprocessors, ...merged.oas2Preprocessors },
207
- [OasVersion.Version3_0]: { ...merged.preprocessors, ...merged.oas3_0Preprocessors },
208
- [OasVersion.Version3_1]: { ...merged.preprocessors, ...merged.oas3_1Preprocessors },
81
+ [OasVersion.Version2]: { ...rawConfig.preprocessors, ...rawConfig.oas2Preprocessors },
82
+ [OasVersion.Version3_0]: { ...rawConfig.preprocessors, ...rawConfig.oas3_0Preprocessors },
83
+ [OasVersion.Version3_1]: { ...rawConfig.preprocessors, ...rawConfig.oas3_1Preprocessors },
209
84
  };
210
85
 
211
86
  this.decorators = {
212
- [OasVersion.Version2]: { ...merged.decorators, ...merged.oas2Decorators },
213
- [OasVersion.Version3_0]: { ...merged.decorators, ...merged.oas3_0Decorators },
214
- [OasVersion.Version3_1]: { ...merged.decorators, ...merged.oas3_1Decorators },
87
+ [OasVersion.Version2]: { ...rawConfig.decorators, ...rawConfig.oas2Decorators },
88
+ [OasVersion.Version3_0]: { ...rawConfig.decorators, ...rawConfig.oas3_0Decorators },
89
+ [OasVersion.Version3_1]: { ...rawConfig.decorators, ...rawConfig.oas3_1Decorators },
215
90
  };
216
91
 
92
+ this.extendPaths = rawConfig.extendPaths || [];
93
+ this.pluginPaths = rawConfig.pluginPaths || [];
94
+
217
95
  const dir = this.configFile
218
96
  ? path.dirname(this.configFile)
219
97
  : (typeof process !== 'undefined' && process.cwd()) || '';
@@ -230,7 +108,7 @@ export class LintConfig {
230
108
 
231
109
  // resolve ignore paths
232
110
  for (const fileName of Object.keys(this.ignore)) {
233
- this.ignore[path.resolve(dirname(ignoreFile), fileName)] = this.ignore[fileName];
111
+ this.ignore[path.resolve(path.dirname(ignoreFile), fileName)] = this.ignore[fileName];
234
112
  for (const ruleId of Object.keys(this.ignore[fileName])) {
235
113
  this.ignore[fileName][ruleId] = new Set(this.ignore[fileName][ruleId]);
236
114
  }
@@ -414,7 +292,7 @@ export class LintConfig {
414
292
  }
415
293
 
416
294
  export class Config {
417
- apis: Record<string, Api>;
295
+ apis: Record<string, ResolvedApi>;
418
296
  lint: LintConfig;
419
297
  resolve: ResolveConfig;
420
298
  licenseKey?: string;
@@ -422,299 +300,13 @@ export class Config {
422
300
  'features.openapi': Record<string, any>;
423
301
  'features.mockServer'?: Record<string, any>;
424
302
  organization?: string;
425
- constructor(public rawConfig: RawConfig, public configFile?: string) {
303
+ constructor(public rawConfig: ResolvedConfig, public configFile?: string) {
426
304
  this.apis = rawConfig.apis || {};
427
305
  this.lint = new LintConfig(rawConfig.lint || {}, configFile);
428
306
  this['features.openapi'] = rawConfig['features.openapi'] || {};
429
307
  this['features.mockServer'] = rawConfig['features.mockServer'] || {};
430
- this.resolve = {
431
- http: {
432
- headers: rawConfig?.resolve?.http?.headers ?? [],
433
- customFetch: undefined,
434
- },
435
- };
308
+ this.resolve = getResolveConfig(rawConfig?.resolve);
436
309
  this.region = rawConfig.region;
437
310
  this.organization = rawConfig.organization;
438
311
  }
439
312
  }
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
- }
@@ -0,0 +1,7 @@
1
+ export * from './config';
2
+ export * from './types';
3
+ export * from './rules';
4
+ export * from './builtIn';
5
+ export * from './load';
6
+ export * from './utils';
7
+ export * from './config-resolvers';
@@ -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, RawConfig, Region, transformConfig } from './config';
6
- import { defaultPlugin } from './builtIn';
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
- export async function loadConfig(configPath: string | undefined = findConfig(), customExtends?: string[]): Promise<Config> {
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
- matches: `https://api.${domain}/registry/**`,
28
- name: 'Authorization',
29
- envVariable: undefined,
30
- value: item.token,
31
- },
32
- //support redocly.com domain for future compatibility
33
- ...(item.region === 'us' ? [{
34
- matches: `https://api.redoc.ly/registry/**`,
35
- name: 'Authorization',
36
- envVariable: undefined,
37
- value: item.token,
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
- return new Config(
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
- .map(name => dir ? path.resolve(dir, name) : name)
59
- .filter(fs.existsSync);
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.