@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
@@ -0,0 +1,168 @@
1
+ import type { ProblemSeverity } from '../walk';
2
+ import type {
3
+ Oas3PreprocessorsSet,
4
+ OasMajorVersion,
5
+ Oas3DecoratorsSet,
6
+ Oas2RuleSet,
7
+ Oas2PreprocessorsSet,
8
+ Oas2DecoratorsSet,
9
+ Oas3RuleSet,
10
+ OasVersion,
11
+ } from '../oas-types';
12
+ import type { NodeType } from '../types';
13
+
14
+ export type RuleConfig =
15
+ | ProblemSeverity
16
+ | 'off'
17
+ | ({
18
+ severity?: ProblemSeverity;
19
+ } & Record<string, any>);
20
+
21
+ export type PreprocessorConfig =
22
+ | ProblemSeverity
23
+ | 'off'
24
+ | 'on'
25
+ | ({
26
+ severity?: ProblemSeverity;
27
+ } & Record<string, any>);
28
+
29
+ export type DecoratorConfig = PreprocessorConfig;
30
+
31
+ export type LintRawConfig = {
32
+ plugins?: (string | Plugin)[];
33
+ extends?: string[];
34
+ doNotResolveExamples?: boolean;
35
+ recommendedFallback?: boolean;
36
+
37
+ rules?: Record<string, RuleConfig>;
38
+ oas2Rules?: Record<string, RuleConfig>;
39
+ oas3_0Rules?: Record<string, RuleConfig>;
40
+ oas3_1Rules?: Record<string, RuleConfig>;
41
+
42
+ preprocessors?: Record<string, PreprocessorConfig>;
43
+ oas2Preprocessors?: Record<string, PreprocessorConfig>;
44
+ oas3_0Preprocessors?: Record<string, PreprocessorConfig>;
45
+ oas3_1Preprocessors?: Record<string, PreprocessorConfig>;
46
+
47
+ decorators?: Record<string, DecoratorConfig>;
48
+ oas2Decorators?: Record<string, DecoratorConfig>;
49
+ oas3_0Decorators?: Record<string, DecoratorConfig>;
50
+ oas3_1Decorators?: Record<string, DecoratorConfig>;
51
+ };
52
+
53
+ export type ResolvedLintConfig = PluginLintConfig & {
54
+ plugins?: Plugin[];
55
+ recommendedFallback?: boolean;
56
+ extends?: void | never;
57
+ extendPaths?: string[];
58
+ pluginPaths?: string[];
59
+ };
60
+
61
+ export type PreprocessorsConfig = {
62
+ oas3?: Oas3PreprocessorsSet;
63
+ oas2?: Oas2PreprocessorsSet;
64
+ };
65
+
66
+ export type DecoratorsConfig = {
67
+ oas3?: Oas3DecoratorsSet;
68
+ oas2?: Oas2DecoratorsSet;
69
+ };
70
+
71
+ export type TypesExtensionFn = (
72
+ types: Record<string, NodeType>,
73
+ oasVersion: OasVersion,
74
+ ) => Record<string, NodeType>;
75
+
76
+ export type TypeExtensionsConfig = Partial<Record<OasMajorVersion, TypesExtensionFn>>;
77
+
78
+ export type CustomRulesConfig = {
79
+ oas3?: Oas3RuleSet;
80
+ oas2?: Oas2RuleSet;
81
+ };
82
+
83
+ export type Plugin = {
84
+ id: string;
85
+ configs?: Record<string, PluginLintConfig>;
86
+ rules?: CustomRulesConfig;
87
+ preprocessors?: PreprocessorsConfig;
88
+ decorators?: DecoratorsConfig;
89
+ typeExtension?: TypeExtensionsConfig;
90
+ };
91
+
92
+ export type PluginLintConfig = Omit<LintRawConfig, 'plugins' | 'extends'>;
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
+ export type Region = 'us' | 'eu';
122
+
123
+ export type AccessTokens = { [region in Region]?: string };
124
+
125
+ export type DeprecatedRawConfig = {
126
+ apiDefinitions?: Record<string, string>;
127
+ lint?: LintRawConfig;
128
+ resolve?: RawResolveConfig;
129
+ region?: Region;
130
+ referenceDocs?: Record<string, any>;
131
+ };
132
+
133
+ export type Api = {
134
+ root: string;
135
+ lint?: Omit<LintRawConfig, 'plugins'>;
136
+ 'features.openapi'?: Record<string, any>;
137
+ 'features.mockServer'?: Record<string, any>;
138
+ };
139
+ export type ResolvedApi = Omit<Api, 'lint'> & { lint: Omit<ResolvedLintConfig, 'plugins'>};
140
+
141
+ export type RawConfig = {
142
+ apis?: Record<string, Api>;
143
+ lint?: LintRawConfig;
144
+ resolve?: RawResolveConfig;
145
+ region?: Region;
146
+ 'features.openapi'?: Record<string, any>;
147
+ 'features.mockServer'?: Record<string, any>;
148
+ organization?: string;
149
+ };
150
+
151
+ export type ResolvedConfig = Omit<RawConfig, 'lint' | 'apis'> & {
152
+ lint: ResolvedLintConfig;
153
+ apis: Record<string,ResolvedApi>
154
+ };
155
+
156
+ export type RulesFields =
157
+ | 'rules'
158
+ | 'oas2Rules'
159
+ | 'oas3_0Rules'
160
+ | 'oas3_1Rules'
161
+ | 'preprocessors'
162
+ | 'oas2Preprocessors'
163
+ | 'oas3_0Preprocessors'
164
+ | 'oas3_1Preprocessors'
165
+ | 'decorators'
166
+ | 'oas2Decorators'
167
+ | 'oas3_0Decorators'
168
+ | 'oas3_1Decorators';
@@ -0,0 +1,208 @@
1
+ import { green, yellow } from 'colorette';
2
+ import { assignExisting } from '../utils';
3
+ import { Config } from './config';
4
+
5
+ import type {
6
+ Api,
7
+ DeprecatedRawConfig,
8
+ Plugin,
9
+ RawConfig,
10
+ RawResolveConfig,
11
+ ResolveConfig,
12
+ ResolvedLintConfig,
13
+ RulesFields,
14
+ } from './types';
15
+
16
+ export function parsePresetName(presetName: string): { pluginId: string; configName: string } {
17
+ if (presetName.indexOf('/') > -1) {
18
+ const [pluginId, configName] = presetName.split('/');
19
+ return { pluginId, configName };
20
+ } else {
21
+ return { pluginId: '', configName: presetName };
22
+ }
23
+ }
24
+
25
+ export function transformApiDefinitionsToApis(
26
+ apiDefinitions: Record<string, string> = {},
27
+ ): Record<string, Api> {
28
+ let apis: Record<string, Api> = {};
29
+ for (const [apiName, apiPath] of Object.entries(apiDefinitions)) {
30
+ apis[apiName] = { root: apiPath };
31
+ }
32
+ return apis;
33
+ }
34
+
35
+ export function prefixRules<T extends Record<string, any>>(rules: T, prefix: string) {
36
+ if (!prefix) return rules;
37
+
38
+ const res: any = {};
39
+ for (const name of Object.keys(rules)) {
40
+ res[`${prefix}/${name}`] = rules[name];
41
+ }
42
+
43
+ return res;
44
+ }
45
+
46
+ export function mergeExtends(rulesConfList: ResolvedLintConfig[]) {
47
+ const result: Omit<ResolvedLintConfig, RulesFields> &
48
+ Required<Pick<ResolvedLintConfig, RulesFields>> = {
49
+ rules: {},
50
+ oas2Rules: {},
51
+ oas3_0Rules: {},
52
+ oas3_1Rules: {},
53
+
54
+ preprocessors: {},
55
+ oas2Preprocessors: {},
56
+ oas3_0Preprocessors: {},
57
+ oas3_1Preprocessors: {},
58
+
59
+ decorators: {},
60
+ oas2Decorators: {},
61
+ oas3_0Decorators: {},
62
+ oas3_1Decorators: {},
63
+
64
+ plugins: [],
65
+ pluginPaths: [],
66
+ extendPaths: [],
67
+ };
68
+
69
+ for (let rulesConf of rulesConfList) {
70
+ if (rulesConf.extends) {
71
+ throw new Error(
72
+ `\`extends\` is not supported in shared configs yet: ${JSON.stringify(
73
+ rulesConf,
74
+ null,
75
+ 2,
76
+ )}.`,
77
+ );
78
+ }
79
+
80
+ Object.assign(result.rules, rulesConf.rules);
81
+ Object.assign(result.oas2Rules, rulesConf.oas2Rules);
82
+ assignExisting(result.oas2Rules, rulesConf.rules || {});
83
+ Object.assign(result.oas3_0Rules, rulesConf.oas3_0Rules);
84
+ assignExisting(result.oas3_0Rules, rulesConf.rules || {});
85
+ Object.assign(result.oas3_1Rules, rulesConf.oas3_1Rules);
86
+ assignExisting(result.oas3_1Rules, rulesConf.rules || {});
87
+
88
+ Object.assign(result.preprocessors, rulesConf.preprocessors);
89
+ Object.assign(result.oas2Preprocessors, rulesConf.oas2Preprocessors);
90
+ assignExisting(result.oas2Preprocessors, rulesConf.preprocessors || {});
91
+ Object.assign(result.oas3_0Preprocessors, rulesConf.oas3_0Preprocessors);
92
+ assignExisting(result.oas3_0Preprocessors, rulesConf.preprocessors || {});
93
+ Object.assign(result.oas3_1Preprocessors, rulesConf.oas3_1Preprocessors);
94
+ assignExisting(result.oas3_1Preprocessors, rulesConf.preprocessors || {});
95
+
96
+ Object.assign(result.decorators, rulesConf.decorators);
97
+ Object.assign(result.oas2Decorators, rulesConf.oas2Decorators);
98
+ assignExisting(result.oas2Decorators, rulesConf.decorators || {});
99
+ Object.assign(result.oas3_0Decorators, rulesConf.oas3_0Decorators);
100
+ assignExisting(result.oas3_0Decorators, rulesConf.decorators || {});
101
+ Object.assign(result.oas3_1Decorators, rulesConf.oas3_1Decorators);
102
+ assignExisting(result.oas3_1Decorators, rulesConf.decorators || {});
103
+
104
+ result.plugins!.push(...(rulesConf.plugins || []));
105
+ result.pluginPaths!.push(...(rulesConf.pluginPaths || []));
106
+ result.extendPaths!.push(...new Set(rulesConf.extendPaths));
107
+ }
108
+
109
+ return result;
110
+ }
111
+
112
+ export function getMergedConfig(config: Config, entrypointAlias?: string): Config {
113
+ const extendPaths = [
114
+ ...Object.values(config.apis).map((api) => api?.lint?.extendPaths),
115
+ config.rawConfig?.lint?.extendPaths,
116
+ ]
117
+ .flat()
118
+ .filter(Boolean) as string[];
119
+
120
+ const pluginPaths = [
121
+ ...Object.values(config.apis).map((api) => api?.lint?.pluginPaths),
122
+ config.rawConfig?.lint?.pluginPaths,
123
+ ]
124
+ .flat()
125
+ .filter(Boolean) as string[];
126
+
127
+ return entrypointAlias
128
+ ? new Config(
129
+ {
130
+ ...config.rawConfig,
131
+ lint: {
132
+ ...(config.apis[entrypointAlias]
133
+ ? config.apis[entrypointAlias].lint
134
+ : config.rawConfig.lint),
135
+ extendPaths,
136
+ pluginPaths,
137
+ },
138
+ 'features.openapi': {
139
+ ...config['features.openapi'],
140
+ ...config.apis[entrypointAlias]?.['features.openapi'],
141
+ },
142
+ 'features.mockServer': {
143
+ ...config['features.mockServer'],
144
+ ...config.apis[entrypointAlias]?.['features.mockServer'],
145
+ },
146
+ // TODO: merge everything else here
147
+ },
148
+ config.configFile,
149
+ )
150
+ : config;
151
+ }
152
+
153
+ export function transformConfig(rawConfig: DeprecatedRawConfig | RawConfig): RawConfig {
154
+ if ((rawConfig as RawConfig).apis && (rawConfig as DeprecatedRawConfig).apiDefinitions) {
155
+ throw new Error("Do not use 'apiDefinitions' field. Use 'apis' instead.\n");
156
+ }
157
+ if (
158
+ (rawConfig as RawConfig)['features.openapi'] &&
159
+ (rawConfig as DeprecatedRawConfig).referenceDocs
160
+ ) {
161
+ throw new Error("Do not use 'referenceDocs' field. Use 'features.openapi' instead.\n");
162
+ }
163
+ const { apiDefinitions, referenceDocs, ...rest } = rawConfig as DeprecatedRawConfig & RawConfig;
164
+ if (apiDefinitions) {
165
+ process.stderr.write(
166
+ `The ${yellow('apiDefinitions')} field is deprecated. Use ${green(
167
+ 'apis',
168
+ )} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
169
+ );
170
+ }
171
+ if (referenceDocs) {
172
+ process.stderr.write(
173
+ `The ${yellow('referenceDocs')} field is deprecated. Use ${green(
174
+ 'features.openapi',
175
+ )} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`,
176
+ );
177
+ }
178
+ return {
179
+ 'features.openapi': referenceDocs,
180
+ apis: transformApiDefinitionsToApis(apiDefinitions),
181
+ ...rest,
182
+ };
183
+ }
184
+
185
+ export function getResolveConfig(resolve?: RawResolveConfig): ResolveConfig {
186
+ return {
187
+ http: {
188
+ headers: resolve?.http?.headers ?? [],
189
+ customFetch: undefined,
190
+ },
191
+ };
192
+ }
193
+
194
+ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
195
+ const seen = new Set();
196
+ const results = [];
197
+ for (const p of plugins) {
198
+ if (!seen.has(p.id)) {
199
+ results.push(p);
200
+ seen.add(p.id);
201
+ } else if (p.id) {
202
+ process.stderr.write(
203
+ `Duplicate plugin id "${yellow(p.id)}".\n`,
204
+ );
205
+ }
206
+ }
207
+ return results;
208
+ }
@@ -25,7 +25,7 @@ describe('oas3 remove-x-internal', () => {
25
25
  const { bundle: res } = await bundleDocument({
26
26
  document: testDocument,
27
27
  externalRefResolver: new BaseResolver(),
28
- config: makeConfig({}, { 'remove-x-internal': { internalFlagProperty: 'removeit' } }),
28
+ config: await makeConfig({}, { 'remove-x-internal': { internalFlagProperty: 'removeit' } }),
29
29
  });
30
30
  expect(res.parsed).toMatchInlineSnapshot(`
31
31
  openapi: 3.0.0
@@ -92,7 +92,7 @@ describe('oas3 remove-x-internal', () => {
92
92
  const { bundle: res } = await bundleDocument({
93
93
  document: testDoc,
94
94
  externalRefResolver: new BaseResolver(),
95
- config: makeConfig({}, { 'remove-x-internal': 'on' }),
95
+ config: await makeConfig({}, { 'remove-x-internal': 'on' }),
96
96
  });
97
97
  expect(res.parsed).toMatchInlineSnapshot(`
98
98
  openapi: 3.1.0
@@ -165,7 +165,7 @@ describe('oas3 remove-x-internal', () => {
165
165
  const { bundle: res } = await bundleDocument({
166
166
  document: testDoc,
167
167
  externalRefResolver: new BaseResolver(),
168
- config: makeConfig({}, { 'remove-x-internal': 'on' }),
168
+ config: await makeConfig({}, { 'remove-x-internal': 'on' }),
169
169
  });
170
170
 
171
171
  expect(res.parsed).toMatchInlineSnapshot(`
@@ -239,7 +239,7 @@ describe('oas3 remove-x-internal', () => {
239
239
  const { bundle: res } = await bundleDocument({
240
240
  document: testDoc,
241
241
  externalRefResolver: new BaseResolver(),
242
- config: makeConfig({}, { 'remove-x-internal': 'on' }),
242
+ config: await makeConfig({}, { 'remove-x-internal': 'on' }),
243
243
  });
244
244
  expect(res.parsed).toMatchInlineSnapshot(`
245
245
  openapi: 3.0.1
@@ -302,7 +302,7 @@ describe('oas2 remove-x-internal', () => {
302
302
  const { bundle: res } = await bundleDocument({
303
303
  document: testDoc,
304
304
  externalRefResolver: new BaseResolver(),
305
- config: makeConfig({}, { 'remove-x-internal': 'on' }),
305
+ config: await makeConfig({}, { 'remove-x-internal': 'on' }),
306
306
  });
307
307
  expect(res.parsed).toMatchInlineSnapshot(`
308
308
  swagger: '2.0'
@@ -16,10 +16,10 @@ import { NormalizedProblem, ProblemSeverity, LineColLocationObject, LocationObje
16
16
  import { getCodeframe, getLineColLocation } from './codeframes';
17
17
 
18
18
  export type Totals = {
19
- errors: number;
20
- warnings: number;
21
- ignored: number;
22
- }
19
+ errors: number;
20
+ warnings: number;
21
+ ignored: number;
22
+ };
23
23
 
24
24
  const ERROR_MESSAGE = {
25
25
  INVALID_SEVERITY_LEVEL: 'Invalid severity level; accepted values: error or warn',
@@ -40,13 +40,18 @@ const SEVERITY_NAMES = {
40
40
  error: 'Error',
41
41
  };
42
42
 
43
+ const CODECLIMATE_SEVERITY_MAPPING = {
44
+ error: 'critical',
45
+ warn: 'minor',
46
+ };
47
+
43
48
  const MAX_SUGGEST = 5;
44
49
 
45
50
  function severityToNumber(severity: ProblemSeverity) {
46
51
  return severity === 'error' ? 1 : 2;
47
52
  }
48
53
 
49
- export type OutputFormat = 'codeframe' | 'stylish' | 'json' | 'checkstyle';
54
+ export type OutputFormat = 'codeframe' | 'stylish' | 'json' | 'checkstyle' | 'codeclimate';
50
55
 
51
56
  export function getTotals(problems: (NormalizedProblem & { ignored?: boolean })[]): Totals {
52
57
  let errors = 0;
@@ -76,7 +81,7 @@ export function formatProblems(
76
81
  cwd?: string;
77
82
  format?: OutputFormat;
78
83
  color?: boolean;
79
- totals: Totals
84
+ totals: Totals;
80
85
  version: string;
81
86
  },
82
87
  ) {
@@ -142,6 +147,9 @@ export function formatProblems(
142
147
  process.stdout.write(`</checkstyle>\n`);
143
148
  break;
144
149
  }
150
+ case 'codeclimate':
151
+ outputForCodeClimate();
152
+ break;
145
153
  }
146
154
 
147
155
  if (totalProblems - ignoredProblems > maxProblems) {
@@ -152,6 +160,25 @@ export function formatProblems(
152
160
  );
153
161
  }
154
162
 
163
+ function outputForCodeClimate() {
164
+ const issues = problems.map((p) => {
165
+ const location = p.location[0]; // TODO: support multiple location
166
+ const lineCol = getLineColLocation(location);
167
+ return {
168
+ description: p.message,
169
+ location: {
170
+ path: path.relative(cwd, location.source.absoluteRef),
171
+ lines: {
172
+ begin: lineCol.start.line,
173
+ },
174
+ },
175
+ severity: CODECLIMATE_SEVERITY_MAPPING[p.severity],
176
+ fingerprint: `${p.ruleId}${p.location.length > 0 ? '-' + p.location[0].pointer : ''}`,
177
+ };
178
+ });
179
+ process.stdout.write(JSON.stringify(issues, null, 2));
180
+ }
181
+
155
182
  function outputJSON() {
156
183
  const resultObject = {
157
184
  totals,
@@ -214,6 +241,9 @@ export function formatProblems(
214
241
 
215
242
  function formatStylish(problem: OnlyLineColProblem, locationPad: number, ruleIdPad: number) {
216
243
  const color = COLORS[problem.severity];
244
+ if (!SEVERITY_NAMES[problem.severity]) {
245
+ return 'Error not found severity. Please check your config file. Allowed values: \`warn,error,off\`'
246
+ }
217
247
  const severityName = color(SEVERITY_NAMES[problem.severity].toLowerCase().padEnd(7));
218
248
  const { start } = problem.location[0];
219
249
  return ` ${`${start.line}:${start.col}`.padEnd(
package/src/index.ts CHANGED
@@ -29,9 +29,13 @@ export {
29
29
  Region,
30
30
  getMergedConfig,
31
31
  transformConfig,
32
- } from './config/config';
32
+ loadConfig,
33
+ getConfig,
34
+ findConfig,
35
+ CONFIG_FILE_NAMES,
36
+ } from './config';
37
+
33
38
 
34
- export { loadConfig, getConfig, findConfig, CONFIG_FILE_NAMES } from './config/load';
35
39
  export { RedoclyClient, isRedoclyRegistryURL } from './redocly';
36
40
 
37
41
  export {
package/src/lint.ts CHANGED
@@ -7,14 +7,13 @@ import { Oas3Types } from './types/oas3';
7
7
  import { Oas2Types } from './types/oas2';
8
8
  import { NodeType } from './types';
9
9
  import { ProblemSeverity, WalkContext, walkDocument } from './walk';
10
- import { LintConfig, Config } from './config/config';
10
+ import { LintConfig, Config, initRules, defaultPlugin, resolvePlugins } from './config';
11
11
  import { normalizeTypes } from './types';
12
- import { initRules } from './config/rules';
13
12
  import { releaseAjvInstance } from './rules/ajv';
14
13
  import { detectOpenAPI, Oas3RuleSet, OasMajorVersion, OasVersion, openAPIMajor } from './oas-types';
15
14
  import { ConfigTypes } from './types/redocly-yaml';
16
15
  import { OasSpec } from './rules/common/spec';
17
- import { defaultPlugin } from './config/builtIn';
16
+
18
17
 
19
18
  export async function lint(opts: {
20
19
  ref: string;
@@ -104,9 +103,9 @@ export async function lintConfig(opts: {
104
103
  oasVersion: OasVersion.Version3_0,
105
104
  visitorsData: {},
106
105
  };
106
+ const plugins = resolvePlugins([defaultPlugin]);
107
107
  const config = new LintConfig({
108
- plugins: [defaultPlugin],
109
- extends: [],
108
+ plugins,
110
109
  rules: { spec: 'error' },
111
110
  });
112
111
 
@@ -1,5 +1,12 @@
1
1
  import { RedoclyClient } from '../index';
2
2
 
3
+ jest.mock('node-fetch', () => ({
4
+ default: jest.fn(() => ({
5
+ ok: true,
6
+ json: jest.fn().mockResolvedValue({}),
7
+ })),
8
+ }));
9
+
3
10
  describe('RedoclyClient', () => {
4
11
  const REDOCLY_DOMAIN_US = 'redocly.com';
5
12
  const REDOCLY_DOMAIN_EU = 'eu.redocly.com';
@@ -3,10 +3,12 @@ import { resolve } from 'path';
3
3
  import { homedir } from 'os';
4
4
  import { red, green, gray, yellow } from 'colorette';
5
5
  import { RegistryApi } from './registry-api';
6
- import { AccessTokens, DEFAULT_REGION, DOMAINS, Region, AVAILABLE_REGIONS } from '../config/config';
6
+ import { DEFAULT_REGION, DOMAINS, AVAILABLE_REGIONS } from '../config/config';
7
7
  import { RegionalToken, RegionalTokenWithValidity } from './redocly-client-types';
8
8
  import { isNotEmptyObject } from '../utils';
9
9
 
10
+ import type { AccessTokens, Region } from '../config/types';
11
+
10
12
  const TOKEN_FILENAME = '.redocly-config.json';
11
13
 
12
14
  let REDOCLY_DOMAIN: string; // workaround for the isRedoclyRegistryURL, see more below
@@ -1,4 +1,4 @@
1
- import { Region } from '../config/config';
1
+ import type { Region } from '../config/types';
2
2
 
3
3
  export interface RegionalToken {
4
4
  region: Region;
@@ -1,9 +1,11 @@
1
1
  import fetch, { RequestInit, HeadersInit } from 'node-fetch';
2
2
  import { RegistryApiTypes } from './registry-api-types';
3
- import { AccessTokens, Region, DEFAULT_REGION, DOMAINS } from '../config/config';
3
+ import { DEFAULT_REGION, DOMAINS } from '../config/config';
4
4
  import { isNotEmptyObject } from '../utils';
5
5
  const version = require('../../package.json').version;
6
6
 
7
+ import type { AccessTokens, Region } from '../config/types';
8
+
7
9
  export class RegistryApi {
8
10
  constructor(private accessTokens: AccessTokens, private region: Region) {}
9
11
 
package/src/resolve.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import * as url from 'url';
4
-
5
3
  import { OasRef } from './typings/openapi';
6
4
  import { isRef, joinPointer, escapePointer, parseRef, isAbsoluteUrl } from './ref-utils';
7
5
  import type { YAMLNode, LoadOptions } from 'yaml-ast-parser';
8
6
  import { NormalizedNodeType, isNamedType } from './types';
9
7
  import { readFileFromUrl, parseYaml } from './utils';
10
- import { ResolveConfig } from './config/config';
8
+ import { ResolveConfig } from './config/types';
11
9
 
12
10
  export type CollectedRefs = Map<string /* absoluteFilePath */, Document>;
13
11
 
@@ -105,7 +103,7 @@ export class BaseResolver {
105
103
  }
106
104
 
107
105
  if (base && isAbsoluteUrl(base)) {
108
- return url.resolve(base, ref);
106
+ return new URL(ref, base).href;
109
107
  }
110
108
 
111
109
  return path.resolve(base ? path.dirname(base) : process.cwd(), ref);
@@ -21,7 +21,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
21
21
  const results = await lintDocument({
22
22
  externalRefResolver: new BaseResolver(),
23
23
  document,
24
- config: makeConfig({
24
+ config: await makeConfig({
25
25
  'no-unresolved-refs': 'error',
26
26
  }),
27
27
  });
@@ -61,7 +61,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
61
61
  const results = await lintDocument({
62
62
  externalRefResolver: new BaseResolver(),
63
63
  document,
64
- config: makeConfig({
64
+ config: await makeConfig({
65
65
  'no-unresolved-refs': 'error',
66
66
  }),
67
67
  });
@@ -118,7 +118,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
118
118
  const results = await lintDocument({
119
119
  externalRefResolver: new BaseResolver(),
120
120
  document,
121
- config: makeConfig({
121
+ config: await makeConfig({
122
122
  'no-unresolved-refs': 'error',
123
123
  }),
124
124
  });
@@ -142,7 +142,7 @@ describe('oas3 boolean-parameter-prefixes', () => {
142
142
  const results = await lintDocument({
143
143
  externalRefResolver: new BaseResolver(),
144
144
  document,
145
- config: makeConfig({
145
+ config: await makeConfig({
146
146
  'no-unresolved-refs': 'error',
147
147
  }),
148
148
  });
@@ -17,7 +17,7 @@ describe('Oas3 info-description', () => {
17
17
  const results = await lintDocument({
18
18
  externalRefResolver: new BaseResolver(),
19
19
  document,
20
- config: makeConfig({
20
+ config: await makeConfig({
21
21
  'info-description': 'error',
22
22
  }),
23
23
  });
@@ -55,7 +55,7 @@ describe('Oas3 info-description', () => {
55
55
  const results = await lintDocument({
56
56
  externalRefResolver: new BaseResolver(),
57
57
  document,
58
- config: makeConfig({
58
+ config: await makeConfig({
59
59
  'info-description': 'error',
60
60
  }),
61
61
  });
@@ -92,7 +92,7 @@ describe('Oas3 info-description', () => {
92
92
  const results = await lintDocument({
93
93
  externalRefResolver: new BaseResolver(),
94
94
  document,
95
- config: makeConfig({
95
+ config: await makeConfig({
96
96
  'info-description': 'error',
97
97
  }),
98
98
  });