@redocly/openapi-core 1.0.0-beta.107 → 1.0.0-beta.108

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 (98) hide show
  1. package/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +0 -1
  2. package/lib/bundle.js +5 -2
  3. package/lib/config/all.js +2 -2
  4. package/lib/config/config-resolvers.js +31 -13
  5. package/lib/config/config.d.ts +3 -5
  6. package/lib/config/config.js +7 -4
  7. package/lib/config/load.d.ts +7 -0
  8. package/lib/config/load.js +14 -6
  9. package/lib/config/minimal.js +2 -2
  10. package/lib/config/recommended.js +2 -2
  11. package/lib/config/rules.d.ts +1 -1
  12. package/lib/config/utils.js +5 -5
  13. package/lib/decorators/common/registry-dependencies.js +1 -1
  14. package/lib/env.d.ts +3 -0
  15. package/lib/env.js +8 -0
  16. package/lib/format/codeframes.js +16 -10
  17. package/lib/format/format.js +28 -26
  18. package/lib/index.d.ts +5 -5
  19. package/lib/index.js +3 -1
  20. package/lib/js-yaml/index.js +1 -0
  21. package/lib/logger.d.ts +10 -0
  22. package/lib/logger.js +31 -0
  23. package/lib/output.d.ts +3 -0
  24. package/lib/output.js +9 -0
  25. package/lib/redocly/index.js +10 -9
  26. package/lib/redocly/registry-api-types.d.ts +28 -30
  27. package/lib/redocly/registry-api.d.ts +4 -3
  28. package/lib/redocly/registry-api.js +7 -2
  29. package/lib/ref-utils.js +2 -1
  30. package/lib/resolve.d.ts +1 -1
  31. package/lib/resolve.js +1 -1
  32. package/lib/rules/ajv.js +1 -1
  33. package/lib/rules/common/assertions/asserts.js +4 -4
  34. package/lib/rules/common/assertions/index.js +1 -1
  35. package/lib/rules/common/operation-security-defined.js +1 -1
  36. package/lib/rules/common/spec.js +2 -2
  37. package/lib/rules/oas2/remove-unused-components.js +2 -2
  38. package/lib/rules/oas3/index.js +2 -2
  39. package/lib/rules/oas3/no-server-variables-empty-enum.d.ts +2 -0
  40. package/lib/rules/oas3/{no-servers-empty-enum.js → no-server-variables-empty-enum.js} +4 -4
  41. package/lib/rules/oas3/no-unused-components.js +1 -1
  42. package/lib/rules/oas3/remove-unused-components.js +3 -3
  43. package/lib/rules/utils.d.ts +1 -1
  44. package/lib/rules/utils.js +1 -1
  45. package/lib/types/redocly-yaml.js +1 -1
  46. package/lib/utils.d.ts +3 -0
  47. package/lib/utils.js +15 -7
  48. package/lib/visitors.d.ts +1 -1
  49. package/lib/visitors.js +2 -2
  50. package/package.json +1 -1
  51. package/src/__tests__/logger-browser.test.ts +53 -0
  52. package/src/__tests__/logger.test.ts +47 -0
  53. package/src/__tests__/output-browser.test.ts +18 -0
  54. package/src/__tests__/output.test.ts +15 -0
  55. package/src/__tests__/utils-browser.test.ts +11 -0
  56. package/src/__tests__/utils.test.ts +7 -0
  57. package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +0 -1
  58. package/src/bundle.ts +6 -3
  59. package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +4 -4
  60. package/src/config/__tests__/config.test.ts +35 -0
  61. package/src/config/__tests__/load.test.ts +79 -1
  62. package/src/config/all.ts +2 -2
  63. package/src/config/config-resolvers.ts +43 -17
  64. package/src/config/config.ts +10 -8
  65. package/src/config/load.ts +28 -5
  66. package/src/config/minimal.ts +2 -2
  67. package/src/config/recommended.ts +2 -2
  68. package/src/config/utils.ts +6 -5
  69. package/src/decorators/common/registry-dependencies.ts +1 -1
  70. package/src/env.ts +5 -0
  71. package/src/format/codeframes.ts +15 -9
  72. package/src/format/format.ts +28 -33
  73. package/src/index.ts +6 -4
  74. package/src/js-yaml/index.ts +1 -0
  75. package/src/logger.ts +34 -0
  76. package/src/output.ts +7 -0
  77. package/src/redocly/index.ts +7 -4
  78. package/src/redocly/registry-api-types.ts +27 -29
  79. package/src/redocly/registry-api.ts +16 -6
  80. package/src/ref-utils.ts +2 -1
  81. package/src/resolve.ts +4 -4
  82. package/src/rules/ajv.ts +1 -1
  83. package/src/rules/common/assertions/asserts.ts +4 -4
  84. package/src/rules/common/assertions/index.ts +1 -1
  85. package/src/rules/common/operation-security-defined.ts +1 -1
  86. package/src/rules/common/spec.ts +2 -2
  87. package/src/rules/oas2/remove-unused-components.ts +2 -2
  88. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +16 -16
  89. package/src/rules/oas3/index.ts +2 -2
  90. package/src/rules/oas3/{no-servers-empty-enum.ts → no-server-variables-empty-enum.ts} +2 -2
  91. package/src/rules/oas3/no-unused-components.ts +1 -1
  92. package/src/rules/oas3/remove-unused-components.ts +4 -4
  93. package/src/rules/utils.ts +1 -1
  94. package/src/types/redocly-yaml.ts +1 -1
  95. package/src/utils.ts +16 -6
  96. package/src/visitors.ts +5 -5
  97. package/tsconfig.tsbuildinfo +1 -1
  98. package/lib/rules/oas3/no-servers-empty-enum.d.ts +0 -2
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NoUnusedComponents = void 0;
4
4
  const NoUnusedComponents = () => {
5
- let components = new Map();
5
+ const components = new Map();
6
6
  function registerComponent(location, name) {
7
7
  var _a;
8
8
  components.set(location.absolutePointer, {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RemoveUnusedComponents = void 0;
4
4
  const utils_1 = require("../../utils");
5
5
  const RemoveUnusedComponents = () => {
6
- let components = new Map();
6
+ const components = new Map();
7
7
  function registerComponent(location, componentType, name) {
8
8
  var _a;
9
9
  components.set(location.absolutePointer, {
@@ -32,8 +32,8 @@ const RemoveUnusedComponents = () => {
32
32
  data.removedCount = 0;
33
33
  components.forEach((usageInfo) => {
34
34
  const { used, componentType, name } = usageInfo;
35
- if (!used && componentType) {
36
- let componentChild = root.components[componentType];
35
+ if (!used && componentType && root.components) {
36
+ const componentChild = root.components[componentType];
37
37
  delete componentChild[name];
38
38
  data.removedCount++;
39
39
  if (utils_1.isEmptyObject(componentChild)) {
@@ -1,7 +1,7 @@
1
1
  import { UserContext } from '../walk';
2
2
  import { Location } from '../ref-utils';
3
3
  import { Oas3Schema, Referenced } from '../typings/openapi';
4
- export declare function oasTypeOf(value: unknown): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "integer" | "array" | "null";
4
+ export declare function oasTypeOf(value: unknown): "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | "null" | "integer" | "array";
5
5
  /**
6
6
  * Checks if value matches specified JSON schema type
7
7
  *
@@ -90,7 +90,7 @@ function validateExample(example, schema, dataLoc, { resolve, location, report }
90
90
  try {
91
91
  const { valid, errors } = ajv_1.validateJsonSchema(example, schema, location.child('schema'), dataLoc.pointer, resolve, allowAdditionalProperties);
92
92
  if (!valid) {
93
- for (let error of errors) {
93
+ for (const error of errors) {
94
94
  report({
95
95
  message: `Example value must conform to the schema: ${error.message}.`,
96
96
  location: Object.assign(Object.assign({}, new ref_utils_1.Location(dataLoc.source, error.instancePath)), { reportOnKey: error.keyword === 'additionalProperties' }),
@@ -43,7 +43,7 @@ const builtInRulesList = [
43
43
  'no-identical-paths',
44
44
  'no-ambiguous-paths',
45
45
  'no-undefined-server-variable',
46
- 'no-servers-empty-enum',
46
+ 'no-server-variables-empty-enum',
47
47
  'no-http-verbs-in-paths',
48
48
  'path-excludes-patterns',
49
49
  'request-mime-type',
package/lib/utils.d.ts CHANGED
@@ -43,3 +43,6 @@ export declare function isCustomRuleId(id: string): boolean;
43
43
  export declare function doesYamlFileExist(filePath: string): boolean;
44
44
  export declare function showWarningForDeprecatedField(deprecatedField: string, updatedField: string): void;
45
45
  export declare function showErrorForDeprecatedField(deprecatedField: string, updatedField: string): void;
46
+ export declare type Falsy = undefined | null | false | '' | 0;
47
+ export declare function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy;
48
+ export declare function identity<T>(value: T): T;
package/lib/utils.js CHANGED
@@ -9,15 +9,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
12
+ exports.identity = exports.isTruthy = exports.showErrorForDeprecatedField = exports.showWarningForDeprecatedField = exports.doesYamlFileExist = exports.isCustomRuleId = exports.getMatchingStatusCodeRange = exports.assignExisting = exports.isNotString = exports.isString = exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.readFileFromUrl = exports.isEmptyArray = exports.isEmptyObject = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
13
13
  const fs = require("fs");
14
14
  const path_1 = require("path");
15
15
  const minimatch = require("minimatch");
16
16
  const node_fetch_1 = require("node-fetch");
17
17
  const pluralize = require("pluralize");
18
18
  const js_yaml_1 = require("./js-yaml");
19
- const config_1 = require("./config");
20
- const colorette_1 = require("colorette");
19
+ const env_1 = require("./env");
20
+ const logger_1 = require("./logger");
21
21
  var js_yaml_2 = require("./js-yaml");
22
22
  Object.defineProperty(exports, "parseYaml", { enumerable: true, get: function () { return js_yaml_2.parseYaml; } });
23
23
  Object.defineProperty(exports, "stringifyYaml", { enumerable: true, get: function () { return js_yaml_2.stringifyYaml; } });
@@ -59,7 +59,7 @@ function readFileFromUrl(url, config) {
59
59
  for (const header of config.headers) {
60
60
  if (match(url, header.matches)) {
61
61
  headers[header.name] =
62
- header.envVariable !== undefined ? config_1.env[header.envVariable] || '' : header.value;
62
+ header.envVariable !== undefined ? env_1.env[header.envVariable] || '' : header.value;
63
63
  }
64
64
  }
65
65
  const req = yield (config.customFetch || node_fetch_1.default)(url, {
@@ -90,7 +90,7 @@ exports.omitObjectProps = omitObjectProps;
90
90
  function splitCamelCaseIntoWords(str) {
91
91
  const camel = str
92
92
  .split(/(?:[-._])|([A-Z][a-z]+)/)
93
- .filter(Boolean)
93
+ .filter(isTruthy)
94
94
  .map((item) => item.toLocaleLowerCase());
95
95
  const caps = str
96
96
  .split(/([A-Z]{2,})/)
@@ -168,7 +168,7 @@ function isNotString(value) {
168
168
  }
169
169
  exports.isNotString = isNotString;
170
170
  function assignExisting(target, obj) {
171
- for (let k of Object.keys(obj)) {
171
+ for (const k of Object.keys(obj)) {
172
172
  if (target.hasOwnProperty(k)) {
173
173
  target[k] = obj[k];
174
174
  }
@@ -190,10 +190,18 @@ function doesYamlFileExist(filePath) {
190
190
  }
191
191
  exports.doesYamlFileExist = doesYamlFileExist;
192
192
  function showWarningForDeprecatedField(deprecatedField, updatedField) {
193
- process.stderr.write(`The ${colorette_1.yellow(deprecatedField)} field is deprecated. Use ${colorette_1.green(updatedField)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`);
193
+ logger_1.logger.warn(`The ${logger_1.colorize.red(deprecatedField)} field is deprecated. Use ${logger_1.colorize.green(updatedField)} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`);
194
194
  }
195
195
  exports.showWarningForDeprecatedField = showWarningForDeprecatedField;
196
196
  function showErrorForDeprecatedField(deprecatedField, updatedField) {
197
197
  throw new Error(`Do not use '${deprecatedField}' field. Use '${updatedField}' instead.\n`);
198
198
  }
199
199
  exports.showErrorForDeprecatedField = showErrorForDeprecatedField;
200
+ function isTruthy(value) {
201
+ return !!value;
202
+ }
203
+ exports.isTruthy = isTruthy;
204
+ function identity(value) {
205
+ return value;
206
+ }
207
+ exports.identity = identity;
package/lib/visitors.d.ts CHANGED
@@ -16,7 +16,7 @@ declare type VisitObject<T> = {
16
16
  };
17
17
  declare type NestedVisitObject<T, P> = VisitObject<T> & NestedVisitor<P>;
18
18
  declare type VisitFunctionOrObject<T> = VisitFunction<T> | VisitObject<T>;
19
- declare type VisitorNode<T extends any> = {
19
+ declare type VisitorNode<T> = {
20
20
  ruleId: string;
21
21
  severity: ProblemSeverity;
22
22
  context: VisitorLevelContext | VisitorSkippedLevelContext;
package/lib/visitors.js CHANGED
@@ -30,7 +30,7 @@ function normalizeVisitors(visitorsConfig, types) {
30
30
  return;
31
31
  stack = [...stack, from];
32
32
  const possibleChildren = new Set();
33
- for (let type of Object.values(from.properties)) {
33
+ for (const type of Object.values(from.properties)) {
34
34
  if (type === to) {
35
35
  addWeakFromStack(ruleConf, stack);
36
36
  continue;
@@ -55,7 +55,7 @@ function normalizeVisitors(visitorsConfig, types) {
55
55
  possibleChildren.add(from.items);
56
56
  }
57
57
  }
58
- for (let fromType of Array.from(possibleChildren.values())) {
58
+ for (const fromType of Array.from(possibleChildren.values())) {
59
59
  addWeakNodes(ruleConf, fromType, to, parentContext, stack);
60
60
  }
61
61
  function addWeakFromStack(ruleConf, stack) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.107",
3
+ "version": "1.0.0-beta.108",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import * as colorette from 'colorette';
6
+ import { logger, colorize } from '../logger';
7
+
8
+ describe('Logger in Browser', () => {
9
+ it('should call "console.error"', () => {
10
+ const error = jest.spyOn(console, 'error').mockImplementation();
11
+
12
+ logger.error('error');
13
+
14
+ expect(error).toBeCalledTimes(1);
15
+ expect(error).toBeCalledWith('error');
16
+
17
+ error.mockRestore();
18
+ });
19
+
20
+ it('should call "console.log"', () => {
21
+ const log = jest.spyOn(console, 'log').mockImplementation();
22
+
23
+ logger.info('info');
24
+
25
+ expect(log).toBeCalledTimes(1);
26
+ expect(log).toBeCalledWith('info');
27
+
28
+ log.mockRestore();
29
+ });
30
+
31
+ it('should call "console.warn"', () => {
32
+ const warn = jest.spyOn(console, 'warn').mockImplementation();
33
+
34
+ logger.warn('warn');
35
+
36
+ expect(warn).toBeCalledTimes(1);
37
+ expect(warn).toBeCalledWith('warn');
38
+
39
+ warn.mockRestore();
40
+ });
41
+ });
42
+
43
+ describe('colorize in Browser', () => {
44
+ it('should not call original colorette lib', () => {
45
+ const color = 'cyan';
46
+ const spyingCyan = jest.spyOn(colorette, color);
47
+
48
+ const colorized = colorize.cyan(color);
49
+
50
+ expect(spyingCyan).not.toBeCalled();
51
+ expect(colorized).toEqual(color);
52
+ });
53
+ });
@@ -0,0 +1,47 @@
1
+ import * as colorette from 'colorette';
2
+ import { logger, colorize } from '../logger';
3
+
4
+ describe('Logger in nodejs', () => {
5
+ let spyingStderr: jest.SpyInstance;
6
+
7
+ beforeEach(() => {
8
+ spyingStderr = jest.spyOn(process.stderr, 'write').mockImplementation();
9
+ });
10
+
11
+ afterEach(() => {
12
+ spyingStderr.mockRestore();
13
+ });
14
+
15
+ it('should call "process.stderr.write" for error severity', () => {
16
+ logger.error('error');
17
+
18
+ expect(spyingStderr).toBeCalledTimes(1);
19
+ expect(spyingStderr).toBeCalledWith(colorette.red('error'));
20
+ });
21
+
22
+ it('should call "process.stderr.write" for warn severity', () => {
23
+ logger.warn('warn');
24
+
25
+ expect(spyingStderr).toBeCalledTimes(1);
26
+ expect(spyingStderr).toBeCalledWith(colorette.yellow('warn'));
27
+ });
28
+
29
+ it('should call "process.stderr.write" for info severity', () => {
30
+ logger.info('info');
31
+
32
+ expect(spyingStderr).toBeCalledTimes(1);
33
+ expect(spyingStderr).toBeCalledWith('info');
34
+ });
35
+ });
36
+
37
+ describe('colorize in nodejs', () => {
38
+ it('should call original colorette lib', () => {
39
+ const color = 'cyan';
40
+ const spyingCyan = jest.spyOn(colorette, color);
41
+
42
+ const colorized = colorize.cyan(color);
43
+
44
+ expect(spyingCyan).toBeCalledWith(color);
45
+ expect(colorized).toEqual(colorette[color](color));
46
+ });
47
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { output } from '../output';
6
+
7
+ describe('output', () => {
8
+ it('should ignore all parsable data in browser', () => {
9
+ const spyingStdout = jest.spyOn(process.stdout, 'write').mockImplementation();
10
+ const data = '{ "errors" : [] }';
11
+
12
+ output.write(data);
13
+
14
+ expect(spyingStdout).not.toBeCalled();
15
+
16
+ spyingStdout.mockRestore();
17
+ });
18
+ });
@@ -0,0 +1,15 @@
1
+ import { output } from '../output';
2
+
3
+ describe('output', () => {
4
+ it('should write all parsable data to stdout', () => {
5
+ const spyingStdout = jest.spyOn(process.stdout, 'write').mockImplementation();
6
+ const data = '{ "errors" : [] }';
7
+
8
+ output.write(data);
9
+
10
+ expect(spyingStdout).toBeCalledTimes(1);
11
+ expect(spyingStdout).toBeCalledWith(data);
12
+
13
+ spyingStdout.mockRestore();
14
+ });
15
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { isBrowser } from '../env';
6
+
7
+ describe('isBrowser', () => {
8
+ it('should be browser', () => {
9
+ expect(isBrowser).toBe(true);
10
+ });
11
+ });
@@ -5,6 +5,7 @@ import {
5
5
  getMatchingStatusCodeRange,
6
6
  doesYamlFileExist,
7
7
  } from '../utils';
8
+ import { isBrowser } from '../env';
8
9
  import * as fs from 'fs';
9
10
  import * as path from 'path';
10
11
 
@@ -122,5 +123,11 @@ describe('utils', () => {
122
123
  expect(doesYamlFileExist('redocly.yam')).toBe(false);
123
124
  });
124
125
  });
126
+
127
+ describe('isBrowser', () => {
128
+ it('should not be browser', () => {
129
+ expect(isBrowser).toBe(false);
130
+ });
131
+ });
125
132
  });
126
133
  });
@@ -15,7 +15,6 @@ const rebillyDocument = parseYamlToDocument(
15
15
  const config = makeConfigForRuleset({
16
16
  test: () => {
17
17
  return {
18
- // @ts-ignore
19
18
  Schema(schema, ctx) {
20
19
  if (schema.type === 'number') {
21
20
  ctx.report({
package/src/bundle.ts CHANGED
@@ -10,7 +10,7 @@ import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
10
10
  import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
11
11
  import { initRules } from './config/rules';
12
12
  import { reportUnresolvedRef } from './rules/no-unresolved-refs';
13
- import { isPlainObject } from './utils';
13
+ import { isPlainObject, isTruthy } from './utils';
14
14
  import { OasRef } from './typings/openapi';
15
15
  import { isRedoclyRegistryURL } from './redocly';
16
16
  import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components';
@@ -302,9 +302,12 @@ function makeBundleVisitor(
302
302
  if (!isPlainObject(resolved.node)) {
303
303
  ctx.parent[ctx.key] = resolved.node;
304
304
  } else {
305
+ // TODO: why $ref isn't optional in OasRef?
306
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
305
307
  // @ts-ignore
306
308
  delete ref.$ref;
307
- Object.assign(ref, resolved.node);
309
+ const obj = Object.assign({}, resolved.node, ref);
310
+ Object.assign(ref, obj); // assign ref itself again so ref fields take precedence
308
311
  }
309
312
  }
310
313
 
@@ -348,7 +351,7 @@ function makeBundleVisitor(
348
351
 
349
352
  let name = '';
350
353
 
351
- const refParts = pointer.slice(2).split('/').filter(Boolean); // slice(2) removes "#/"
354
+ const refParts = pointer.slice(2).split('/').filter(isTruthy); // slice(2) removes "#/"
352
355
  while (refParts.length > 0) {
353
356
  name = refParts.pop() + (name ? `-${name}` : '');
354
357
  if (
@@ -15,7 +15,7 @@ Object {
15
15
  "no-invalid-media-type-examples": "error",
16
16
  "no-server-example.com": "warn",
17
17
  "no-server-trailing-slash": "error",
18
- "no-servers-empty-enum": "error",
18
+ "no-server-variables-empty-enum": "error",
19
19
  "no-undefined-server-variable": "error",
20
20
  "no-unused-components": "warn",
21
21
  },
@@ -27,7 +27,7 @@ Object {
27
27
  "no-invalid-media-type-examples": "error",
28
28
  "no-server-example.com": "warn",
29
29
  "no-server-trailing-slash": "error",
30
- "no-servers-empty-enum": "error",
30
+ "no-server-variables-empty-enum": "error",
31
31
  "no-undefined-server-variable": "error",
32
32
  "no-unused-components": "warn",
33
33
  },
@@ -86,7 +86,7 @@ Object {
86
86
  "no-invalid-media-type-examples": "warn",
87
87
  "no-server-example.com": "warn",
88
88
  "no-server-trailing-slash": "error",
89
- "no-servers-empty-enum": "error",
89
+ "no-server-variables-empty-enum": "error",
90
90
  "no-undefined-server-variable": "error",
91
91
  "no-unused-components": "warn",
92
92
  },
@@ -98,7 +98,7 @@ Object {
98
98
  "no-invalid-media-type-examples": "warn",
99
99
  "no-server-example.com": "warn",
100
100
  "no-server-trailing-slash": "error",
101
- "no-servers-empty-enum": "error",
101
+ "no-server-variables-empty-enum": "error",
102
102
  "no-undefined-server-variable": "error",
103
103
  "no-unused-components": "warn",
104
104
  },
@@ -1,3 +1,4 @@
1
+ import { OasVersion } from '../../oas-types';
1
2
  import { Config, StyleguideConfig } from '../config';
2
3
  import { getMergedConfig } from '../utils';
3
4
 
@@ -242,3 +243,37 @@ describe('getMergedConfig', () => {
242
243
  `);
243
244
  });
244
245
  });
246
+
247
+ describe('StyleguideConfig.extendTypes', () => {
248
+ let oas3 = jest.fn();
249
+ let oas2 = jest.fn();
250
+ let testRawConfigStyleguide = {
251
+ plugins: [
252
+ {
253
+ id: 'test-types-plugin',
254
+ typeExtension: {
255
+ oas3,
256
+ oas2,
257
+ },
258
+ },
259
+ ],
260
+ };
261
+ it('should call only oas3 types extension', () => {
262
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
263
+ styleguideConfig.extendTypes({}, OasVersion.Version3_0);
264
+ expect(oas3).toHaveBeenCalledTimes(1);
265
+ expect(oas2).toHaveBeenCalledTimes(0);
266
+ });
267
+ it('should call only oas2 types extension', () => {
268
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
269
+ styleguideConfig.extendTypes({}, OasVersion.Version2);
270
+ expect(oas3).toHaveBeenCalledTimes(0);
271
+ expect(oas2).toHaveBeenCalledTimes(1);
272
+ });
273
+ it('should throw error if for oas version different from 2 and 3', () => {
274
+ const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
275
+ expect(() => styleguideConfig.extendTypes({}, 'something else' as OasVersion)).toThrowError(
276
+ 'Not implemented'
277
+ );
278
+ });
279
+ });
@@ -1,5 +1,7 @@
1
- import { loadConfig, findConfig, getConfig } from '../load';
1
+ import { loadConfig, findConfig, getConfig, createConfig } from '../load';
2
2
  import { RedoclyClient } from '../../redocly';
3
+ import { RuleConfig, RawConfig } from './../types';
4
+ import { Config } from '../config';
3
5
 
4
6
  const fs = require('fs');
5
7
  const path = require('path');
@@ -87,3 +89,79 @@ describe('getConfig', () => {
87
89
  expect(getConfig()).toEqual(Promise.resolve({}));
88
90
  });
89
91
  });
92
+
93
+ describe('createConfig', () => {
94
+ it('should create config from string', async () => {
95
+ const config = await createConfig(`
96
+ styleguide:
97
+ extends:
98
+ - recommended
99
+ rules:
100
+ info-license: off
101
+ `);
102
+
103
+ verifyExtendedConfig(config, {
104
+ extendsRuleSet: 'recommended',
105
+ overridesRules: { 'info-license': 'off' },
106
+ });
107
+ });
108
+
109
+ it('should create config from object', async () => {
110
+ const rawConfig: RawConfig = {
111
+ styleguide: {
112
+ extends: ['minimal'],
113
+ rules: {
114
+ 'info-license': 'off',
115
+ 'tag-description': 'off',
116
+ 'operation-2xx-response': 'off',
117
+ },
118
+ },
119
+ };
120
+ const config = await createConfig(rawConfig);
121
+
122
+ verifyExtendedConfig(config, {
123
+ extendsRuleSet: 'minimal',
124
+ overridesRules: rawConfig.styleguide!.rules as Record<string, RuleConfig>,
125
+ });
126
+ });
127
+ });
128
+
129
+ function verifyExtendedConfig(
130
+ config: Config,
131
+ {
132
+ extendsRuleSet,
133
+ overridesRules,
134
+ }: { extendsRuleSet: string; overridesRules: Record<string, RuleConfig> }
135
+ ) {
136
+ const defaultPlugin = config.styleguide.plugins.find((plugin) => plugin.id === '');
137
+ expect(defaultPlugin).toBeDefined();
138
+
139
+ const recommendedRules = defaultPlugin?.configs?.[extendsRuleSet];
140
+ expect(recommendedRules).toBeDefined();
141
+
142
+ verifyOasRules(config.styleguide.rules.oas2, overridesRules, recommendedRules?.rules || {});
143
+ verifyOasRules(
144
+ config.styleguide.rules.oas3_0,
145
+ overridesRules,
146
+ Object.assign({}, recommendedRules?.rules, recommendedRules?.oas3_0Rules)
147
+ );
148
+ verifyOasRules(
149
+ config.styleguide.rules.oas3_1,
150
+ overridesRules,
151
+ Object.assign({}, recommendedRules?.rules, recommendedRules?.oas3_1Rules)
152
+ );
153
+ }
154
+
155
+ function verifyOasRules(
156
+ finalRuleset: Record<string, RuleConfig>,
157
+ overridesRules: Record<string, RuleConfig>,
158
+ defaultRuleset: Record<string, RuleConfig>
159
+ ) {
160
+ Object.entries(finalRuleset).forEach(([ruleName, ruleValue]) => {
161
+ if (ruleName in overridesRules) {
162
+ expect(ruleValue).toBe(overridesRules[ruleName]);
163
+ } else {
164
+ expect(ruleValue).toBe(defaultRuleset[ruleName]);
165
+ }
166
+ });
167
+ }
package/src/config/all.ts CHANGED
@@ -51,7 +51,7 @@ export default {
51
51
  'no-example-value-and-externalValue': 'error',
52
52
  'no-unused-components': 'error',
53
53
  'no-undefined-server-variable': 'error',
54
- 'no-servers-empty-enum': 'error',
54
+ 'no-server-variables-empty-enum': 'error',
55
55
  },
56
56
  oas3_1Rules: {
57
57
  'no-server-example.com': 'error',
@@ -60,6 +60,6 @@ export default {
60
60
  'no-example-value-and-externalValue': 'error',
61
61
  'no-unused-components': 'error',
62
62
  'no-undefined-server-variable': 'error',
63
- 'no-servers-empty-enum': 'error',
63
+ 'no-server-variables-empty-enum': 'error',
64
64
  },
65
65
  } as PluginStyleguideConfig;