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

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 (115) hide show
  1. package/__tests__/bundle.test.ts +6 -6
  2. package/__tests__/fixtures/extension.js +1 -1
  3. package/__tests__/login.test.ts +2 -2
  4. package/__tests__/ref-utils.test.ts +1 -1
  5. package/__tests__/utils.ts +30 -18
  6. package/lib/benchmark/benches/recommended-oas3.bench.js +2 -3
  7. package/lib/benchmark/utils.d.ts +2 -1
  8. package/lib/benchmark/utils.js +10 -7
  9. package/lib/bundle.d.ts +2 -2
  10. package/lib/config/all.d.ts +2 -2
  11. package/lib/config/builtIn.d.ts +1 -1
  12. package/lib/config/config-resolvers.d.ts +16 -0
  13. package/lib/config/config-resolvers.js +213 -0
  14. package/lib/config/config.d.ts +14 -129
  15. package/lib/config/config.js +17 -234
  16. package/lib/config/index.d.ts +7 -0
  17. package/lib/config/index.js +19 -0
  18. package/lib/config/load.d.ts +2 -1
  19. package/lib/config/load.js +20 -13
  20. package/lib/config/minimal.d.ts +2 -2
  21. package/lib/config/recommended.d.ts +2 -2
  22. package/lib/config/types.d.ts +113 -0
  23. package/lib/config/types.js +2 -0
  24. package/lib/config/utils.d.ts +13 -0
  25. package/lib/config/utils.js +160 -0
  26. package/lib/format/format.d.ts +1 -1
  27. package/lib/format/format.js +28 -0
  28. package/lib/index.d.ts +1 -2
  29. package/lib/index.js +5 -6
  30. package/lib/lint.d.ts +1 -1
  31. package/lib/lint.js +5 -7
  32. package/lib/redocly/index.d.ts +1 -1
  33. package/lib/redocly/redocly-client-types.d.ts +1 -1
  34. package/lib/redocly/registry-api.d.ts +1 -1
  35. package/lib/resolve.d.ts +1 -1
  36. package/lib/resolve.js +1 -2
  37. package/lib/rules/common/assertions/utils.d.ts +1 -1
  38. package/lib/rules/common/assertions/utils.js +6 -2
  39. package/lib/utils.d.ts +4 -1
  40. package/lib/utils.js +18 -1
  41. package/package.json +5 -2
  42. package/src/__tests__/lint.test.ts +1 -1
  43. package/src/benchmark/benches/recommended-oas3.bench.ts +2 -3
  44. package/src/benchmark/utils.ts +13 -8
  45. package/src/bundle.ts +2 -1
  46. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +140 -0
  47. package/src/config/__tests__/config-resolvers.test.ts +398 -0
  48. package/src/config/__tests__/config.test.ts +17 -29
  49. package/src/config/__tests__/fixtures/plugin.js +1 -1
  50. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +7 -0
  51. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +67 -0
  52. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +8 -0
  53. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +12 -0
  54. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +10 -0
  55. package/src/config/__tests__/fixtures/resolve-config/plugin.js +66 -0
  56. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +4 -0
  57. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +5 -0
  58. package/src/config/__tests__/load.test.ts +8 -1
  59. package/src/config/all.ts +3 -2
  60. package/src/config/builtIn.ts +2 -1
  61. package/src/config/config-resolvers.ts +304 -0
  62. package/src/config/config.ts +40 -457
  63. package/src/config/index.ts +7 -0
  64. package/src/config/load.ts +37 -31
  65. package/src/config/minimal.ts +2 -2
  66. package/src/config/recommended.ts +2 -2
  67. package/src/config/types.ts +168 -0
  68. package/src/config/utils.ts +208 -0
  69. package/src/decorators/__tests__/remove-x-internal.test.ts +5 -5
  70. package/src/format/format.ts +36 -6
  71. package/src/index.ts +6 -2
  72. package/src/lint.ts +4 -5
  73. package/src/redocly/__tests__/redocly-client.test.ts +7 -0
  74. package/src/redocly/index.ts +3 -1
  75. package/src/redocly/redocly-client-types.ts +1 -1
  76. package/src/redocly/registry-api.ts +3 -1
  77. package/src/resolve.ts +2 -4
  78. package/src/rules/__tests__/no-unresolved-refs.test.ts +4 -4
  79. package/src/rules/common/__tests__/info-description.test.ts +3 -3
  80. package/src/rules/common/__tests__/info-license.test.ts +2 -2
  81. package/src/rules/common/__tests__/license-url.test.ts +2 -2
  82. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -1
  83. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +5 -5
  84. package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -1
  85. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +3 -3
  86. package/src/rules/common/__tests__/operation-2xx-response.test.ts +3 -3
  87. package/src/rules/common/__tests__/operation-4xx-response.test.ts +3 -3
  88. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +2 -2
  89. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -1
  90. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +4 -4
  91. package/src/rules/common/__tests__/operation-security-defined.test.ts +2 -2
  92. package/src/rules/common/__tests__/operation-singular-tag.test.ts +2 -2
  93. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +2 -2
  94. package/src/rules/common/__tests__/path-not-include-query.test.ts +2 -2
  95. package/src/rules/common/__tests__/path-params-defined.test.ts +3 -3
  96. package/src/rules/common/__tests__/paths-kebab-case.test.ts +3 -3
  97. package/src/rules/common/__tests__/spec.test.ts +1 -1
  98. package/src/rules/common/__tests__/tag-description.test.ts +2 -2
  99. package/src/rules/common/__tests__/tags-alphabetical.test.ts +2 -2
  100. package/src/rules/common/assertions/utils.ts +5 -2
  101. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  102. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -1
  103. package/src/rules/oas2/__tests__/spec/utils.ts +10 -7
  104. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +3 -3
  105. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +6 -6
  106. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +2 -2
  107. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +8 -8
  108. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +2 -2
  109. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +3 -3
  110. package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -1
  111. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +23 -14
  112. package/src/rules/oas3/__tests__/spec/spec.test.ts +4 -4
  113. package/src/rules/oas3/__tests__/spec/utils.ts +10 -7
  114. package/src/utils.ts +18 -2
  115. package/tsconfig.tsbuildinfo +1 -1
@@ -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 { 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';
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 = false;
54
+ recommendedFallback: boolean;
176
55
 
177
- constructor(public rawConfig: LintRawConfig, public configFile?: string) {
178
- this.plugins = rawConfig.plugins ? resolvePlugins(rawConfig.plugins, configFile) : [];
179
- this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
56
+ extendPaths: string[];
57
+ pluginPaths: string[];
180
58
 
181
- if (!rawConfig.extends) {
182
- this.recommendedFallback = true;
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
- const merged = mergeExtends(extendConfigs);
63
+ this.recommendedFallback = rawConfig.recommendedFallback || false
198
64
 
199
65
  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 },
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]: { ...merged.preprocessors, ...merged.oas2Preprocessors },
207
- [OasVersion.Version3_0]: { ...merged.preprocessors, ...merged.oas3_0Preprocessors },
208
- [OasVersion.Version3_1]: { ...merged.preprocessors, ...merged.oas3_1Preprocessors },
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]: { ...merged.decorators, ...merged.oas2Decorators },
213
- [OasVersion.Version3_0]: { ...merged.decorators, ...merged.oas3_0Decorators },
214
- [OasVersion.Version3_1]: { ...merged.decorators, ...merged.oas3_1Decorators },
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, Api>;
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: RawConfig, public configFile?: string) {
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
- }
@@ -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.
@@ -1,4 +1,4 @@
1
- import { LintRawConfig } from './config';
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 LintRawConfig;
58
+ } as PluginLintConfig;
@@ -1,4 +1,4 @@
1
- import { LintRawConfig } from './config';
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 LintRawConfig;
58
+ } as PluginLintConfig;