@redocly/openapi-core 1.0.0 → 1.0.2

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 (275) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/__tests__/utils.ts +88 -0
  3. package/lib/config/all.js +0 -1
  4. package/lib/config/minimal.js +0 -1
  5. package/lib/config/recommended.js +0 -1
  6. package/package.json +1 -1
  7. package/src/__tests__/__snapshots__/bundle.test.ts.snap +437 -0
  8. package/src/__tests__/bundle.test.ts +236 -0
  9. package/src/__tests__/codeframes.test.ts +530 -0
  10. package/src/__tests__/fixtures/.redocly.lint-ignore.yaml +5 -0
  11. package/src/__tests__/fixtures/extension.js +24 -0
  12. package/src/__tests__/fixtures/refs/definitions.yaml +3 -0
  13. package/src/__tests__/fixtures/refs/examples.yaml +8 -0
  14. package/src/__tests__/fixtures/refs/external-request-body.yaml +13 -0
  15. package/src/__tests__/fixtures/refs/externalref.yaml +35 -0
  16. package/src/__tests__/fixtures/refs/hosted.yaml +35 -0
  17. package/src/__tests__/fixtures/refs/openapi-with-external-refs-conflicting-names.yaml +21 -0
  18. package/src/__tests__/fixtures/refs/openapi-with-external-refs.yaml +33 -0
  19. package/src/__tests__/fixtures/refs/openapi-with-url-refs.yaml +18 -0
  20. package/src/__tests__/fixtures/refs/param-b.yaml +1 -0
  21. package/src/__tests__/fixtures/refs/param-c.yaml +1 -0
  22. package/src/__tests__/fixtures/refs/rename.yaml +1 -0
  23. package/src/__tests__/fixtures/refs/requestBody.yaml +9 -0
  24. package/src/__tests__/fixtures/refs/schema-a.yaml +1 -0
  25. package/src/__tests__/fixtures/refs/simple.yaml +1 -0
  26. package/src/__tests__/fixtures/refs/vendor.schema.yaml +20 -0
  27. package/src/__tests__/fixtures/resolve/External.yaml +10 -0
  28. package/src/__tests__/fixtures/resolve/External2.yaml +4 -0
  29. package/src/__tests__/fixtures/resolve/description.md +3 -0
  30. package/src/__tests__/fixtures/resolve/externalInfo.yaml +4 -0
  31. package/src/__tests__/fixtures/resolve/externalLicense.yaml +1 -0
  32. package/src/__tests__/fixtures/resolve/openapi-with-back.yaml +13 -0
  33. package/src/__tests__/fixtures/resolve/openapi-with-md-description.yaml +5 -0
  34. package/src/__tests__/fixtures/resolve/openapi.yaml +28 -0
  35. package/src/__tests__/fixtures/resolve/schemas/type-a.yaml +10 -0
  36. package/src/__tests__/fixtures/resolve/schemas/type-b.yaml +6 -0
  37. package/src/__tests__/fixtures/resolve/transitive/a.yaml +1 -0
  38. package/src/__tests__/fixtures/resolve/transitive/components.yaml +5 -0
  39. package/src/__tests__/fixtures/resolve/transitive/schemas.yaml +3 -0
  40. package/src/__tests__/format.test.ts +76 -0
  41. package/src/__tests__/js-yaml.test.ts +73 -0
  42. package/src/__tests__/lint.test.ts +392 -0
  43. package/src/__tests__/logger-browser.test.ts +53 -0
  44. package/src/__tests__/logger.test.ts +47 -0
  45. package/src/__tests__/login.test.ts +17 -0
  46. package/src/__tests__/normalizeVisitors.test.ts +151 -0
  47. package/src/__tests__/output-browser.test.ts +18 -0
  48. package/src/__tests__/output.test.ts +15 -0
  49. package/src/__tests__/ref-utils.test.ts +120 -0
  50. package/src/__tests__/resolve-http.test.ts +77 -0
  51. package/src/__tests__/resolve.test.ts +431 -0
  52. package/src/__tests__/utils-browser.test.ts +11 -0
  53. package/src/__tests__/utils.test.ts +144 -0
  54. package/src/__tests__/walk.test.ts +1545 -0
  55. package/src/benchmark/benches/lint-with-many-rules.bench.ts +35 -0
  56. package/src/benchmark/benches/lint-with-nested-rule.bench.ts +39 -0
  57. package/src/benchmark/benches/lint-with-no-rules.bench.ts +20 -0
  58. package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +35 -0
  59. package/src/benchmark/benches/lint-with-top-level-rule.bench.ts +32 -0
  60. package/src/benchmark/benches/rebilly.yaml +32275 -0
  61. package/src/benchmark/benches/recommended-oas3.bench.ts +22 -0
  62. package/src/benchmark/benches/resolve-with-no-external.bench.ts +23 -0
  63. package/src/benchmark/benchmark.js +311 -0
  64. package/src/benchmark/colors.js +29 -0
  65. package/src/benchmark/fork.js +83 -0
  66. package/src/benchmark/utils.ts +36 -0
  67. package/src/bundle.ts +417 -0
  68. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +164 -0
  69. package/src/config/__tests__/__snapshots__/config.test.ts.snap +144 -0
  70. package/src/config/__tests__/config-resolvers.test.ts +491 -0
  71. package/src/config/__tests__/config.test.ts +312 -0
  72. package/src/config/__tests__/fixtures/ingore-file.ts +8 -0
  73. package/src/config/__tests__/fixtures/load-redocly.yaml +2 -0
  74. package/src/config/__tests__/fixtures/plugin-config.yaml +2 -0
  75. package/src/config/__tests__/fixtures/plugin.js +56 -0
  76. package/src/config/__tests__/fixtures/resolve-config/api/nested-config.yaml +11 -0
  77. package/src/config/__tests__/fixtures/resolve-config/api/plugin.js +69 -0
  78. package/src/config/__tests__/fixtures/resolve-config/local-config-with-circular.yaml +7 -0
  79. package/src/config/__tests__/fixtures/resolve-config/local-config-with-custom-function.yaml +17 -0
  80. package/src/config/__tests__/fixtures/resolve-config/local-config-with-file.yaml +18 -0
  81. package/src/config/__tests__/fixtures/resolve-config/local-config-with-wrong-custom-function.yaml +15 -0
  82. package/src/config/__tests__/fixtures/resolve-config/local-config.yaml +9 -0
  83. package/src/config/__tests__/fixtures/resolve-config/plugin.js +80 -0
  84. package/src/config/__tests__/fixtures/resolve-remote-configs/nested-remote-config.yaml +3 -0
  85. package/src/config/__tests__/fixtures/resolve-remote-configs/remote-config.yaml +4 -0
  86. package/src/config/__tests__/load.test.ts +167 -0
  87. package/src/config/__tests__/resolve-plugins.test.ts +27 -0
  88. package/src/config/__tests__/utils.test.ts +204 -0
  89. package/src/config/all.ts +74 -0
  90. package/src/config/builtIn.ts +37 -0
  91. package/src/config/config-resolvers.ts +474 -0
  92. package/src/config/config.ts +332 -0
  93. package/src/config/index.ts +7 -0
  94. package/src/config/load.ts +144 -0
  95. package/src/config/minimal.ts +61 -0
  96. package/src/config/recommended.ts +61 -0
  97. package/src/config/rules.ts +54 -0
  98. package/src/config/types.ts +231 -0
  99. package/src/config/utils.ts +349 -0
  100. package/src/decorators/__tests__/filter-in.test.ts +310 -0
  101. package/src/decorators/__tests__/filter-out.test.ts +335 -0
  102. package/src/decorators/__tests__/media-type-examples-override.test.ts +665 -0
  103. package/src/decorators/__tests__/remove-x-internal.test.ts +316 -0
  104. package/src/decorators/__tests__/resources/request.yaml +3 -0
  105. package/src/decorators/__tests__/resources/response.yaml +3 -0
  106. package/src/decorators/common/filters/filter-helper.ts +72 -0
  107. package/src/decorators/common/filters/filter-in.ts +18 -0
  108. package/src/decorators/common/filters/filter-out.ts +18 -0
  109. package/src/decorators/common/info-description-override.ts +24 -0
  110. package/src/decorators/common/info-override.ts +15 -0
  111. package/src/decorators/common/media-type-examples-override.ts +79 -0
  112. package/src/decorators/common/operation-description-override.ts +30 -0
  113. package/src/decorators/common/registry-dependencies.ts +25 -0
  114. package/src/decorators/common/remove-x-internal.ts +59 -0
  115. package/src/decorators/common/tag-description-override.ts +25 -0
  116. package/src/decorators/oas2/index.ts +20 -0
  117. package/src/decorators/oas3/index.ts +22 -0
  118. package/src/env.ts +5 -0
  119. package/src/format/codeframes.ts +216 -0
  120. package/src/format/format.ts +375 -0
  121. package/src/index.ts +71 -0
  122. package/src/js-yaml/index.ts +14 -0
  123. package/src/lint.ts +148 -0
  124. package/src/logger.ts +34 -0
  125. package/src/oas-types.ts +57 -0
  126. package/src/output.ts +7 -0
  127. package/src/redocly/__tests__/redocly-client.test.ts +146 -0
  128. package/src/redocly/index.ts +187 -0
  129. package/src/redocly/redocly-client-types.ts +10 -0
  130. package/src/redocly/registry-api-types.ts +32 -0
  131. package/src/redocly/registry-api.ts +150 -0
  132. package/src/ref-utils.ts +85 -0
  133. package/src/resolve.ts +417 -0
  134. package/src/rules/__tests__/fixtures/code-sample.php +9 -0
  135. package/src/rules/__tests__/fixtures/invalid-yaml.yaml +1 -0
  136. package/src/rules/__tests__/fixtures/ref.yaml +1 -0
  137. package/src/rules/__tests__/no-unresolved-refs.test.ts +257 -0
  138. package/src/rules/__tests__/utils.test.ts +160 -0
  139. package/src/rules/ajv.ts +102 -0
  140. package/src/rules/common/__tests__/info-license.test.ts +62 -0
  141. package/src/rules/common/__tests__/license-url.test.ts +63 -0
  142. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +96 -0
  143. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +210 -0
  144. package/src/rules/common/__tests__/no-identical-paths.test.ts +58 -0
  145. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +85 -0
  146. package/src/rules/common/__tests__/operation-2xx-response.test.ts +192 -0
  147. package/src/rules/common/__tests__/operation-4xx-response.test.ts +231 -0
  148. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +76 -0
  149. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +45 -0
  150. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +167 -0
  151. package/src/rules/common/__tests__/operation-singular-tag.test.ts +72 -0
  152. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +95 -0
  153. package/src/rules/common/__tests__/path-not-include-query.test.ts +64 -0
  154. package/src/rules/common/__tests__/path-params-defined.test.ts +202 -0
  155. package/src/rules/common/__tests__/paths-kebab-case.test.ts +108 -0
  156. package/src/rules/common/__tests__/scalar-property-missing-example.test.ts +264 -0
  157. package/src/rules/common/__tests__/security-defined.test.ts +175 -0
  158. package/src/rules/common/__tests__/spec-strict-refs.test.ts +69 -0
  159. package/src/rules/common/__tests__/spec.test.ts +610 -0
  160. package/src/rules/common/__tests__/tag-description.test.ts +65 -0
  161. package/src/rules/common/__tests__/tags-alphabetical.test.ts +64 -0
  162. package/src/rules/common/assertions/__tests__/asserts.test.ts +869 -0
  163. package/src/rules/common/assertions/__tests__/index.test.ts +100 -0
  164. package/src/rules/common/assertions/__tests__/utils.test.ts +236 -0
  165. package/src/rules/common/assertions/asserts.ts +357 -0
  166. package/src/rules/common/assertions/index.ts +53 -0
  167. package/src/rules/common/assertions/utils.ts +331 -0
  168. package/src/rules/common/info-contact.ts +15 -0
  169. package/src/rules/common/info-license-url.ts +10 -0
  170. package/src/rules/common/info-license.ts +15 -0
  171. package/src/rules/common/no-ambiguous-paths.ts +50 -0
  172. package/src/rules/common/no-enum-type-mismatch.ts +52 -0
  173. package/src/rules/common/no-http-verbs-in-paths.ts +36 -0
  174. package/src/rules/common/no-identical-paths.ts +24 -0
  175. package/src/rules/common/no-invalid-parameter-examples.ts +36 -0
  176. package/src/rules/common/no-invalid-schema-examples.ts +27 -0
  177. package/src/rules/common/no-path-trailing-slash.ts +15 -0
  178. package/src/rules/common/operation-2xx-response.ts +24 -0
  179. package/src/rules/common/operation-4xx-response.ts +24 -0
  180. package/src/rules/common/operation-description.ts +13 -0
  181. package/src/rules/common/operation-operationId-unique.ts +21 -0
  182. package/src/rules/common/operation-operationId-url-safe.ts +19 -0
  183. package/src/rules/common/operation-operationId.ts +17 -0
  184. package/src/rules/common/operation-parameters-unique.ts +48 -0
  185. package/src/rules/common/operation-singular-tag.ts +17 -0
  186. package/src/rules/common/operation-summary.ts +13 -0
  187. package/src/rules/common/operation-tag-defined.ts +26 -0
  188. package/src/rules/common/parameter-description.ts +22 -0
  189. package/src/rules/common/path-declaration-must-exist.ts +15 -0
  190. package/src/rules/common/path-excludes-patterns.ts +23 -0
  191. package/src/rules/common/path-http-verbs-order.ts +30 -0
  192. package/src/rules/common/path-not-include-query.ts +17 -0
  193. package/src/rules/common/path-params-defined.ts +65 -0
  194. package/src/rules/common/path-segment-plural.ts +31 -0
  195. package/src/rules/common/paths-kebab-case.ts +19 -0
  196. package/src/rules/common/required-string-property-missing-min-length.ts +44 -0
  197. package/src/rules/common/response-contains-header.ts +35 -0
  198. package/src/rules/common/scalar-property-missing-example.ts +58 -0
  199. package/src/rules/common/security-defined.ts +65 -0
  200. package/src/rules/common/spec-strict-refs.ts +30 -0
  201. package/src/rules/common/spec.ts +175 -0
  202. package/src/rules/common/tag-description.ts +10 -0
  203. package/src/rules/common/tags-alphabetical.ts +20 -0
  204. package/src/rules/no-unresolved-refs.ts +51 -0
  205. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +110 -0
  206. package/src/rules/oas2/__tests__/response-contains-header.test.ts +174 -0
  207. package/src/rules/oas2/__tests__/response-contains-property.test.ts +155 -0
  208. package/src/rules/oas2/__tests__/spec/fixtures/description.md +1 -0
  209. package/src/rules/oas2/__tests__/spec/info.test.ts +355 -0
  210. package/src/rules/oas2/__tests__/spec/operation.test.ts +123 -0
  211. package/src/rules/oas2/__tests__/spec/paths.test.ts +245 -0
  212. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +35 -0
  213. package/src/rules/oas2/__tests__/spec/utils.ts +32 -0
  214. package/src/rules/oas2/boolean-parameter-prefixes.ts +26 -0
  215. package/src/rules/oas2/index.ts +91 -0
  216. package/src/rules/oas2/remove-unused-components.ts +81 -0
  217. package/src/rules/oas2/request-mime-type.ts +16 -0
  218. package/src/rules/oas2/response-contains-property.ts +36 -0
  219. package/src/rules/oas2/response-mime-type.ts +16 -0
  220. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +111 -0
  221. package/src/rules/oas3/__tests__/component-name-unique.test.ts +823 -0
  222. package/src/rules/oas3/__tests__/fixtures/common.yaml +11 -0
  223. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +205 -0
  224. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +65 -0
  225. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +473 -0
  226. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +60 -0
  227. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +79 -0
  228. package/src/rules/oas3/__tests__/no-unused-components.test.ts +131 -0
  229. package/src/rules/oas3/__tests__/operation-4xx-problem-details-rfc7807.test.ts +145 -0
  230. package/src/rules/oas3/__tests__/response-contains-header.test.ts +389 -0
  231. package/src/rules/oas3/__tests__/response-contains-property.test.ts +403 -0
  232. package/src/rules/oas3/__tests__/spec/callbacks.test.ts +41 -0
  233. package/src/rules/oas3/__tests__/spec/fixtures/description.md +1 -0
  234. package/src/rules/oas3/__tests__/spec/info.test.ts +391 -0
  235. package/src/rules/oas3/__tests__/spec/operation.test.ts +253 -0
  236. package/src/rules/oas3/__tests__/spec/paths.test.ts +284 -0
  237. package/src/rules/oas3/__tests__/spec/referenceableScalars.test.ts +77 -0
  238. package/src/rules/oas3/__tests__/spec/servers.test.ts +505 -0
  239. package/src/rules/oas3/__tests__/spec/spec.test.ts +298 -0
  240. package/src/rules/oas3/__tests__/spec/utils.ts +32 -0
  241. package/src/rules/oas3/__tests__/spec-components-invalid-map-name.test.ts +276 -0
  242. package/src/rules/oas3/__tests__/utils/lint-document-for-test.ts +23 -0
  243. package/src/rules/oas3/boolean-parameter-prefixes.ts +28 -0
  244. package/src/rules/oas3/component-name-unique.ts +158 -0
  245. package/src/rules/oas3/index.ts +113 -0
  246. package/src/rules/oas3/no-empty-servers.ts +22 -0
  247. package/src/rules/oas3/no-example-value-and-externalValue.ts +14 -0
  248. package/src/rules/oas3/no-invalid-media-type-examples.ts +49 -0
  249. package/src/rules/oas3/no-server-example.com.ts +14 -0
  250. package/src/rules/oas3/no-server-trailing-slash.ts +15 -0
  251. package/src/rules/oas3/no-server-variables-empty-enum.ts +66 -0
  252. package/src/rules/oas3/no-undefined-server-variable.ts +30 -0
  253. package/src/rules/oas3/no-unused-components.ts +75 -0
  254. package/src/rules/oas3/operation-4xx-problem-details-rfc7807.ts +35 -0
  255. package/src/rules/oas3/remove-unused-components.ts +95 -0
  256. package/src/rules/oas3/request-mime-type.ts +30 -0
  257. package/src/rules/oas3/response-contains-property.ts +38 -0
  258. package/src/rules/oas3/response-mime-type.ts +30 -0
  259. package/src/rules/oas3/spec-components-invalid-map-name.ts +69 -0
  260. package/src/rules/other/stats.ts +73 -0
  261. package/src/rules/utils.ts +193 -0
  262. package/src/types/config-external-schemas.ts +917 -0
  263. package/src/types/index.ts +149 -0
  264. package/src/types/oas2.ts +478 -0
  265. package/src/types/oas3.ts +597 -0
  266. package/src/types/oas3_1.ts +258 -0
  267. package/src/types/redocly-yaml.ts +1040 -0
  268. package/src/typings/common.ts +17 -0
  269. package/src/typings/openapi.ts +298 -0
  270. package/src/typings/swagger.ts +236 -0
  271. package/src/utils.ts +276 -0
  272. package/src/visitors.ts +491 -0
  273. package/src/walk.ts +439 -0
  274. package/tsconfig.json +8 -0
  275. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,332 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { parseYaml, stringifyYaml } from '../js-yaml';
4
+ import { slash, doesYamlFileExist } from '../utils';
5
+ import { NormalizedProblem } from '../walk';
6
+ import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
7
+ import { isBrowser, env } from '../env';
8
+
9
+ import type { NodeType } from '../types';
10
+ import type {
11
+ DecoratorConfig,
12
+ Plugin,
13
+ PreprocessorConfig,
14
+ Region,
15
+ ResolveConfig,
16
+ ResolvedApi,
17
+ ResolvedConfig,
18
+ ResolvedStyleguideConfig,
19
+ RuleConfig,
20
+ RuleSettings,
21
+ Telemetry,
22
+ ThemeRawConfig,
23
+ } from './types';
24
+ import { getResolveConfig } from './utils';
25
+ import { isAbsoluteUrl } from '../ref-utils';
26
+
27
+ export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
28
+ const IGNORE_BANNER =
29
+ `# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
30
+ `# See https://redoc.ly/docs/cli/ for more information.\n`;
31
+
32
+ export const DEFAULT_REGION = 'us';
33
+
34
+ function getDomains() {
35
+ const domains: { [region in Region]: string } = {
36
+ us: 'redocly.com',
37
+ eu: 'eu.redocly.com',
38
+ };
39
+
40
+ // FIXME: temporary fix for our lab environments
41
+ const domain = env.REDOCLY_DOMAIN;
42
+ if (domain?.endsWith('.redocly.host')) {
43
+ domains[domain.split('.')[0] as Region] = domain;
44
+ }
45
+ if (domain === 'redoc.online') {
46
+ domains[domain as Region] = domain;
47
+ }
48
+ return domains;
49
+ }
50
+
51
+ function getIgnoreFilePath(configFile?: string): string | undefined {
52
+ if (configFile) {
53
+ return doesYamlFileExist(configFile)
54
+ ? path.join(path.dirname(configFile), IGNORE_FILE)
55
+ : path.join(configFile, IGNORE_FILE);
56
+ } else {
57
+ return isBrowser ? undefined : path.join(process.cwd(), IGNORE_FILE);
58
+ }
59
+ }
60
+
61
+ export const DOMAINS = getDomains();
62
+ export const AVAILABLE_REGIONS = Object.keys(DOMAINS) as Region[];
63
+
64
+ export class StyleguideConfig {
65
+ plugins: Plugin[];
66
+ ignore: Record<string, Record<string, Set<string>>> = {};
67
+ doNotResolveExamples: boolean;
68
+ rules: Record<OasVersion, Record<string, RuleConfig>>;
69
+ preprocessors: Record<OasVersion, Record<string, PreprocessorConfig>>;
70
+ decorators: Record<OasVersion, Record<string, DecoratorConfig>>;
71
+
72
+ private _usedRules: Set<string> = new Set();
73
+ private _usedVersions: Set<OasVersion> = new Set();
74
+
75
+ recommendedFallback: boolean;
76
+
77
+ extendPaths: string[];
78
+ pluginPaths: string[];
79
+
80
+ constructor(public rawConfig: ResolvedStyleguideConfig, public configFile?: string) {
81
+ this.plugins = rawConfig.plugins || [];
82
+ this.doNotResolveExamples = !!rawConfig.doNotResolveExamples;
83
+ this.recommendedFallback = rawConfig.recommendedFallback || false;
84
+
85
+ this.rules = {
86
+ [OasVersion.Version2]: { ...rawConfig.rules, ...rawConfig.oas2Rules },
87
+ [OasVersion.Version3_0]: { ...rawConfig.rules, ...rawConfig.oas3_0Rules },
88
+ [OasVersion.Version3_1]: { ...rawConfig.rules, ...rawConfig.oas3_1Rules },
89
+ };
90
+
91
+ this.preprocessors = {
92
+ [OasVersion.Version2]: { ...rawConfig.preprocessors, ...rawConfig.oas2Preprocessors },
93
+ [OasVersion.Version3_0]: { ...rawConfig.preprocessors, ...rawConfig.oas3_0Preprocessors },
94
+ [OasVersion.Version3_1]: { ...rawConfig.preprocessors, ...rawConfig.oas3_1Preprocessors },
95
+ };
96
+
97
+ this.decorators = {
98
+ [OasVersion.Version2]: { ...rawConfig.decorators, ...rawConfig.oas2Decorators },
99
+ [OasVersion.Version3_0]: { ...rawConfig.decorators, ...rawConfig.oas3_0Decorators },
100
+ [OasVersion.Version3_1]: { ...rawConfig.decorators, ...rawConfig.oas3_1Decorators },
101
+ };
102
+
103
+ this.extendPaths = rawConfig.extendPaths || [];
104
+ this.pluginPaths = rawConfig.pluginPaths || [];
105
+ this.resolveIgnore(getIgnoreFilePath(configFile));
106
+ }
107
+
108
+ resolveIgnore(ignoreFile?: string) {
109
+ if (!ignoreFile || !doesYamlFileExist(ignoreFile)) return;
110
+
111
+ this.ignore =
112
+ (parseYaml(fs.readFileSync(ignoreFile, 'utf-8')) as Record<
113
+ string,
114
+ Record<string, Set<string>>
115
+ >) || {};
116
+
117
+ // resolve ignore paths
118
+ for (const fileName of Object.keys(this.ignore)) {
119
+ this.ignore[
120
+ isAbsoluteUrl(fileName) ? fileName : path.resolve(path.dirname(ignoreFile), fileName)
121
+ ] = this.ignore[fileName];
122
+
123
+ for (const ruleId of Object.keys(this.ignore[fileName])) {
124
+ this.ignore[fileName][ruleId] = new Set(this.ignore[fileName][ruleId]);
125
+ }
126
+
127
+ if (!isAbsoluteUrl(fileName)) {
128
+ delete this.ignore[fileName];
129
+ }
130
+ }
131
+ }
132
+
133
+ saveIgnore() {
134
+ const dir = this.configFile ? path.dirname(this.configFile) : process.cwd();
135
+ const ignoreFile = path.join(dir, IGNORE_FILE);
136
+ const mapped: Record<string, any> = {};
137
+ for (const absFileName of Object.keys(this.ignore)) {
138
+ const mappedDefinitionName = isAbsoluteUrl(absFileName)
139
+ ? absFileName
140
+ : slash(path.relative(dir, absFileName));
141
+ const ignoredRules = (mapped[mappedDefinitionName] = this.ignore[absFileName]);
142
+
143
+ for (const ruleId of Object.keys(ignoredRules)) {
144
+ ignoredRules[ruleId] = Array.from(ignoredRules[ruleId]) as any;
145
+ }
146
+ }
147
+ fs.writeFileSync(ignoreFile, IGNORE_BANNER + stringifyYaml(mapped));
148
+ }
149
+
150
+ addIgnore(problem: NormalizedProblem) {
151
+ const ignore = this.ignore;
152
+ const loc = problem.location[0];
153
+ if (loc.pointer === undefined) return;
154
+
155
+ const fileIgnore = (ignore[loc.source.absoluteRef] = ignore[loc.source.absoluteRef] || {});
156
+ const ruleIgnore = (fileIgnore[problem.ruleId] = fileIgnore[problem.ruleId] || new Set());
157
+
158
+ ruleIgnore.add(loc.pointer);
159
+ }
160
+
161
+ addProblemToIgnore(problem: NormalizedProblem) {
162
+ const loc = problem.location[0];
163
+ if (loc.pointer === undefined) return problem;
164
+
165
+ const fileIgnore = this.ignore[loc.source.absoluteRef] || {};
166
+ const ruleIgnore = fileIgnore[problem.ruleId];
167
+ const ignored = ruleIgnore && ruleIgnore.has(loc.pointer);
168
+ return ignored
169
+ ? {
170
+ ...problem,
171
+ ignored,
172
+ }
173
+ : problem;
174
+ }
175
+
176
+ extendTypes(types: Record<string, NodeType>, version: OasVersion) {
177
+ let extendedTypes = types;
178
+ for (const plugin of this.plugins) {
179
+ if (plugin.typeExtension !== undefined) {
180
+ switch (version) {
181
+ case OasVersion.Version3_0:
182
+ case OasVersion.Version3_1:
183
+ if (!plugin.typeExtension.oas3) continue;
184
+ extendedTypes = plugin.typeExtension.oas3(extendedTypes, version);
185
+ break;
186
+ case OasVersion.Version2:
187
+ if (!plugin.typeExtension.oas2) continue;
188
+ extendedTypes = plugin.typeExtension.oas2(extendedTypes, version);
189
+ break;
190
+ default:
191
+ throw new Error('Not implemented');
192
+ }
193
+ }
194
+ }
195
+ return extendedTypes;
196
+ }
197
+
198
+ getRuleSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
199
+ this._usedRules.add(ruleId);
200
+ this._usedVersions.add(oasVersion);
201
+ const settings = this.rules[oasVersion][ruleId] || 'off';
202
+ if (typeof settings === 'string') {
203
+ return {
204
+ severity: settings,
205
+ };
206
+ } else {
207
+ return { severity: 'error', ...settings };
208
+ }
209
+ }
210
+
211
+ getPreprocessorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
212
+ this._usedRules.add(ruleId);
213
+ this._usedVersions.add(oasVersion);
214
+
215
+ const settings = this.preprocessors[oasVersion][ruleId] || 'off';
216
+ if (typeof settings === 'string') {
217
+ return {
218
+ severity: settings === 'on' ? 'error' : settings,
219
+ };
220
+ } else {
221
+ return { severity: 'error', ...settings };
222
+ }
223
+ }
224
+
225
+ getDecoratorSettings(ruleId: string, oasVersion: OasVersion): RuleSettings {
226
+ this._usedRules.add(ruleId);
227
+ this._usedVersions.add(oasVersion);
228
+ const settings = this.decorators[oasVersion][ruleId] || 'off';
229
+ if (typeof settings === 'string') {
230
+ return {
231
+ severity: settings === 'on' ? 'error' : settings,
232
+ };
233
+ } else {
234
+ return { severity: 'error', ...settings };
235
+ }
236
+ }
237
+
238
+ getUnusedRules() {
239
+ const rules = [];
240
+ const decorators = [];
241
+ const preprocessors = [];
242
+
243
+ for (const usedVersion of Array.from(this._usedVersions)) {
244
+ rules.push(
245
+ ...Object.keys(this.rules[usedVersion]).filter((name) => !this._usedRules.has(name))
246
+ );
247
+ decorators.push(
248
+ ...Object.keys(this.decorators[usedVersion]).filter((name) => !this._usedRules.has(name))
249
+ );
250
+ preprocessors.push(
251
+ ...Object.keys(this.preprocessors[usedVersion]).filter((name) => !this._usedRules.has(name))
252
+ );
253
+ }
254
+
255
+ return {
256
+ rules,
257
+ preprocessors,
258
+ decorators,
259
+ };
260
+ }
261
+
262
+ getRulesForOasVersion(version: OasMajorVersion) {
263
+ switch (version) {
264
+ case OasMajorVersion.Version3:
265
+ // eslint-disable-next-line no-case-declarations
266
+ const oas3Rules: Oas3RuleSet[] = []; // default ruleset
267
+ this.plugins.forEach((p) => p.preprocessors?.oas3 && oas3Rules.push(p.preprocessors.oas3));
268
+ this.plugins.forEach((p) => p.rules?.oas3 && oas3Rules.push(p.rules.oas3));
269
+ this.plugins.forEach((p) => p.decorators?.oas3 && oas3Rules.push(p.decorators.oas3));
270
+ return oas3Rules;
271
+ case OasMajorVersion.Version2:
272
+ // eslint-disable-next-line no-case-declarations
273
+ const oas2Rules: Oas2RuleSet[] = []; // default ruleset
274
+ this.plugins.forEach((p) => p.preprocessors?.oas2 && oas2Rules.push(p.preprocessors.oas2));
275
+ this.plugins.forEach((p) => p.rules?.oas2 && oas2Rules.push(p.rules.oas2));
276
+ this.plugins.forEach((p) => p.decorators?.oas2 && oas2Rules.push(p.decorators.oas2));
277
+ return oas2Rules;
278
+ }
279
+ }
280
+
281
+ skipRules(rules?: string[]) {
282
+ for (const ruleId of rules || []) {
283
+ for (const version of Object.values(OasVersion)) {
284
+ if (this.rules[version][ruleId]) {
285
+ this.rules[version][ruleId] = 'off';
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ skipPreprocessors(preprocessors?: string[]) {
292
+ for (const preprocessorId of preprocessors || []) {
293
+ for (const version of Object.values(OasVersion)) {
294
+ if (this.preprocessors[version][preprocessorId]) {
295
+ this.preprocessors[version][preprocessorId] = 'off';
296
+ }
297
+ }
298
+ }
299
+ }
300
+
301
+ skipDecorators(decorators?: string[]) {
302
+ for (const decoratorId of decorators || []) {
303
+ for (const version of Object.values(OasVersion)) {
304
+ if (this.decorators[version][decoratorId]) {
305
+ this.decorators[version][decoratorId] = 'off';
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+
312
+ export class Config {
313
+ apis: Record<string, ResolvedApi>;
314
+ styleguide: StyleguideConfig;
315
+ resolve: ResolveConfig;
316
+ licenseKey?: string;
317
+ region?: Region;
318
+ theme: ThemeRawConfig;
319
+ organization?: string;
320
+ files: string[];
321
+ telemetry?: Telemetry;
322
+ constructor(public rawConfig: ResolvedConfig, public configFile?: string) {
323
+ this.apis = rawConfig.apis || {};
324
+ this.styleguide = new StyleguideConfig(rawConfig.styleguide || {}, configFile);
325
+ this.theme = rawConfig.theme || {};
326
+ this.resolve = getResolveConfig(rawConfig?.resolve);
327
+ this.region = rawConfig.region;
328
+ this.organization = rawConfig.organization;
329
+ this.files = rawConfig.files || [];
330
+ this.telemetry = rawConfig.telemetry;
331
+ }
332
+ }
@@ -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';
@@ -0,0 +1,144 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { RedoclyClient } from '../redocly';
4
+ import { isEmptyObject, loadYaml, doesYamlFileExist } from '../utils';
5
+ import { parseYaml } from '../js-yaml';
6
+ import { Config, DOMAINS } from './config';
7
+ import { transformConfig } from './utils';
8
+ import { resolveConfig } from './config-resolvers';
9
+
10
+ import type { DeprecatedInRawConfig, FlatRawConfig, RawConfig, Region } from './types';
11
+ import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
12
+
13
+ async function addConfigMetadata({
14
+ rawConfig,
15
+ customExtends,
16
+ configPath,
17
+ tokens,
18
+ files,
19
+ region,
20
+ }: {
21
+ rawConfig: RawConfig;
22
+ customExtends?: string[];
23
+ configPath?: string;
24
+ tokens?: RegionalTokenWithValidity[];
25
+ files?: string[];
26
+ region?: Region;
27
+ }): Promise<Config> {
28
+ if (customExtends !== undefined) {
29
+ rawConfig.styleguide = rawConfig.styleguide || {};
30
+ rawConfig.styleguide.extends = customExtends;
31
+ } else if (isEmptyObject(rawConfig)) {
32
+ rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
33
+ }
34
+
35
+ if (tokens?.length) {
36
+ if (!rawConfig.resolve) rawConfig.resolve = {};
37
+ if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
38
+ rawConfig.resolve.http.headers = [...(rawConfig.resolve.http.headers ?? [])];
39
+
40
+ for (const item of tokens) {
41
+ const domain = DOMAINS[item.region as Region];
42
+ rawConfig.resolve.http.headers.push(
43
+ {
44
+ matches: `https://api.${domain}/registry/**`,
45
+ name: 'Authorization',
46
+ envVariable: undefined,
47
+ value: item.token,
48
+ },
49
+ //support redocly.com domain for future compatibility
50
+ ...(item.region === 'us'
51
+ ? [
52
+ {
53
+ matches: `https://api.redoc.ly/registry/**`,
54
+ name: 'Authorization',
55
+ envVariable: undefined,
56
+ value: item.token,
57
+ },
58
+ ]
59
+ : [])
60
+ );
61
+ }
62
+ }
63
+
64
+ return resolveConfig(
65
+ { ...rawConfig, files: files ?? rawConfig.files, region: region ?? rawConfig.region },
66
+ configPath
67
+ );
68
+ }
69
+
70
+ export async function loadConfig(
71
+ options: {
72
+ configPath?: string;
73
+ customExtends?: string[];
74
+ processRawConfig?: (rawConfig: RawConfig) => void | Promise<void>;
75
+ files?: string[];
76
+ region?: Region;
77
+ } = {}
78
+ ): Promise<Config> {
79
+ const { configPath = findConfig(), customExtends, processRawConfig, files, region } = options;
80
+ const rawConfig = await getConfig(configPath, processRawConfig);
81
+
82
+ const redoclyClient = new RedoclyClient();
83
+ const tokens = await redoclyClient.getTokens();
84
+
85
+ return addConfigMetadata({
86
+ rawConfig,
87
+ customExtends,
88
+ configPath,
89
+ tokens,
90
+ files,
91
+ region,
92
+ });
93
+ }
94
+
95
+ export const CONFIG_FILE_NAMES = ['redocly.yaml', 'redocly.yml', '.redocly.yaml', '.redocly.yml'];
96
+
97
+ export function findConfig(dir?: string): string | undefined {
98
+ if (!fs.hasOwnProperty('existsSync')) return;
99
+ const existingConfigFiles = CONFIG_FILE_NAMES.map((name) =>
100
+ dir ? path.resolve(dir, name) : name
101
+ ).filter(fs.existsSync);
102
+ if (existingConfigFiles.length > 1) {
103
+ throw new Error(`
104
+ Multiple configuration files are not allowed.
105
+ Found the following files: ${existingConfigFiles.join(', ')}.
106
+ Please use 'redocly.yaml' instead.
107
+ `);
108
+ }
109
+ return existingConfigFiles[0];
110
+ }
111
+
112
+ export async function getConfig(
113
+ configPath: string | undefined = findConfig(),
114
+ processRawConfig?: (rawConfig: RawConfig) => void | Promise<void>
115
+ ): Promise<RawConfig> {
116
+ if (!configPath || !doesYamlFileExist(configPath)) return {};
117
+ try {
118
+ const rawConfig =
119
+ (await loadYaml<RawConfig & DeprecatedInRawConfig & FlatRawConfig>(configPath)) || {};
120
+ if (typeof processRawConfig === 'function') {
121
+ await processRawConfig(rawConfig);
122
+ }
123
+ return transformConfig(rawConfig);
124
+ } catch (e) {
125
+ throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
126
+ }
127
+ }
128
+
129
+ type CreateConfigOptions = {
130
+ extends?: string[];
131
+ tokens?: RegionalTokenWithValidity[];
132
+ };
133
+
134
+ export async function createConfig(
135
+ config: string | RawConfig,
136
+ options?: CreateConfigOptions
137
+ ): Promise<Config> {
138
+ return addConfigMetadata({
139
+ rawConfig: transformConfig(
140
+ typeof config === 'string' ? (parseYaml(config) as RawConfig) : config
141
+ ),
142
+ ...options,
143
+ });
144
+ }
@@ -0,0 +1,61 @@
1
+ import type { PluginStyleguideConfig } from './types';
2
+
3
+ export default {
4
+ rules: {
5
+ 'info-contact': 'off',
6
+ 'info-license': 'off',
7
+ 'info-license-url': 'off',
8
+ 'tag-description': 'warn',
9
+ 'tags-alphabetical': 'off',
10
+ 'parameter-description': 'off',
11
+ 'no-path-trailing-slash': 'warn',
12
+ 'no-identical-paths': 'warn',
13
+ 'no-ambiguous-paths': 'warn',
14
+ 'path-declaration-must-exist': 'warn',
15
+ 'path-not-include-query': 'warn',
16
+ 'path-parameters-defined': 'warn',
17
+ 'operation-description': 'off',
18
+ 'operation-2xx-response': 'warn',
19
+ 'operation-4xx-response': 'off',
20
+ 'operation-operationId': 'warn',
21
+ 'operation-summary': 'warn',
22
+ 'operation-operationId-unique': 'warn',
23
+ 'operation-parameters-unique': 'warn',
24
+ 'operation-tag-defined': 'off',
25
+ 'security-defined': 'warn',
26
+ 'operation-operationId-url-safe': 'warn',
27
+ 'operation-singular-tag': 'off',
28
+ 'no-unresolved-refs': 'error',
29
+ 'no-enum-type-mismatch': 'warn',
30
+ 'boolean-parameter-prefixes': 'off',
31
+ 'paths-kebab-case': 'off',
32
+ spec: 'error',
33
+ 'spec-strict-refs': 'off',
34
+ 'component-name-unique': 'off',
35
+ },
36
+ oas3_0Rules: {
37
+ 'no-invalid-media-type-examples': {
38
+ severity: 'warn',
39
+ allowAdditionalProperties: false,
40
+ },
41
+ 'no-server-example.com': 'warn',
42
+ 'no-server-trailing-slash': 'error',
43
+ 'no-empty-servers': 'warn',
44
+ 'no-example-value-and-externalValue': 'warn',
45
+ 'no-unused-components': 'warn',
46
+ 'no-undefined-server-variable': 'warn',
47
+ 'no-server-variables-empty-enum': 'error',
48
+ 'spec-components-invalid-map-name': 'warn',
49
+ },
50
+ oas3_1Rules: {
51
+ 'no-invalid-media-type-examples': 'warn',
52
+ 'no-server-example.com': 'warn',
53
+ 'no-server-trailing-slash': 'error',
54
+ 'no-empty-servers': 'warn',
55
+ 'no-example-value-and-externalValue': 'warn',
56
+ 'no-unused-components': 'warn',
57
+ 'no-undefined-server-variable': 'warn',
58
+ 'no-server-variables-empty-enum': 'error',
59
+ 'spec-components-invalid-map-name': 'warn',
60
+ },
61
+ } as PluginStyleguideConfig;
@@ -0,0 +1,61 @@
1
+ import type { PluginStyleguideConfig } from './types';
2
+
3
+ export default {
4
+ rules: {
5
+ 'info-contact': 'off',
6
+ 'info-license': 'warn',
7
+ 'info-license-url': 'warn',
8
+ 'tag-description': 'warn',
9
+ 'tags-alphabetical': 'off',
10
+ 'parameter-description': 'off',
11
+ 'no-path-trailing-slash': 'error',
12
+ 'no-identical-paths': 'error',
13
+ 'no-ambiguous-paths': 'warn',
14
+ 'path-declaration-must-exist': 'error',
15
+ 'path-not-include-query': 'error',
16
+ 'path-parameters-defined': 'error',
17
+ 'operation-description': 'off',
18
+ 'operation-2xx-response': 'warn',
19
+ 'operation-4xx-response': 'warn',
20
+ 'operation-operationId': 'warn',
21
+ 'operation-summary': 'error',
22
+ 'operation-operationId-unique': 'error',
23
+ 'operation-operationId-url-safe': 'error',
24
+ 'operation-parameters-unique': 'error',
25
+ 'operation-tag-defined': 'off',
26
+ 'security-defined': 'error',
27
+ 'operation-singular-tag': 'off',
28
+ 'no-unresolved-refs': 'error',
29
+ 'no-enum-type-mismatch': 'error',
30
+ 'boolean-parameter-prefixes': 'off',
31
+ 'paths-kebab-case': 'off',
32
+ spec: 'error',
33
+ 'spec-strict-refs': 'off',
34
+ 'component-name-unique': 'off',
35
+ },
36
+ oas3_0Rules: {
37
+ 'no-invalid-media-type-examples': {
38
+ severity: 'warn',
39
+ allowAdditionalProperties: false,
40
+ },
41
+ 'no-server-example.com': 'warn',
42
+ 'no-server-trailing-slash': 'error',
43
+ 'no-empty-servers': 'error',
44
+ 'no-example-value-and-externalValue': 'error',
45
+ 'no-unused-components': 'warn',
46
+ 'no-undefined-server-variable': 'error',
47
+ 'no-server-variables-empty-enum': 'error',
48
+ 'spec-components-invalid-map-name': 'error',
49
+ },
50
+ oas3_1Rules: {
51
+ 'no-invalid-media-type-examples': 'warn',
52
+ 'no-server-example.com': 'warn',
53
+ 'no-server-trailing-slash': 'error',
54
+ 'no-empty-servers': 'error',
55
+ 'no-example-value-and-externalValue': 'error',
56
+ 'no-unused-components': 'warn',
57
+ 'no-undefined-server-variable': 'error',
58
+ 'no-server-variables-empty-enum': 'error',
59
+ 'spec-components-invalid-map-name': 'error',
60
+ },
61
+ } as PluginStyleguideConfig;
@@ -0,0 +1,54 @@
1
+ import { RuleSet, OasVersion } from '../oas-types';
2
+ import { StyleguideConfig } from './config';
3
+ import { isDefined } from '../utils';
4
+ import type { ProblemSeverity } from '../walk';
5
+
6
+ type InitializedRule = {
7
+ severity: ProblemSeverity;
8
+ ruleId: string;
9
+ visitor: any;
10
+ };
11
+
12
+ export function initRules<T extends Function, P extends RuleSet<T>>(
13
+ rules: P[],
14
+ config: StyleguideConfig,
15
+ type: 'rules' | 'preprocessors' | 'decorators',
16
+ oasVersion: OasVersion
17
+ ): InitializedRule[] {
18
+ return rules
19
+ .flatMap((ruleset) =>
20
+ Object.keys(ruleset).map((ruleId) => {
21
+ const rule = ruleset[ruleId];
22
+
23
+ const ruleSettings =
24
+ type === 'rules'
25
+ ? config.getRuleSettings(ruleId, oasVersion)
26
+ : type === 'preprocessors'
27
+ ? config.getPreprocessorSettings(ruleId, oasVersion)
28
+ : config.getDecoratorSettings(ruleId, oasVersion);
29
+
30
+ if (ruleSettings.severity === 'off') {
31
+ return undefined;
32
+ }
33
+ const severity: ProblemSeverity = ruleSettings.severity;
34
+
35
+ const visitors = rule(ruleSettings);
36
+
37
+ if (Array.isArray(visitors)) {
38
+ return visitors.map((visitor: any) => ({
39
+ severity,
40
+ ruleId,
41
+ visitor: visitor,
42
+ }));
43
+ }
44
+
45
+ return {
46
+ severity,
47
+ ruleId,
48
+ visitor: visitors, // note: actually it is only one visitor object
49
+ };
50
+ })
51
+ )
52
+ .flatMap((visitor) => visitor)
53
+ .filter(isDefined);
54
+ }