@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
package/src/logger.ts ADDED
@@ -0,0 +1,34 @@
1
+ import * as colorette from 'colorette';
2
+ export { options as colorOptions } from 'colorette';
3
+
4
+ import { isBrowser } from './env';
5
+ import { identity } from './utils';
6
+
7
+ export const colorize = new Proxy(colorette, {
8
+ get(target: typeof colorette, prop: string): typeof identity {
9
+ if (isBrowser) {
10
+ return identity;
11
+ }
12
+
13
+ return (target as any)[prop];
14
+ },
15
+ });
16
+ class Logger {
17
+ protected stderr(str: string) {
18
+ return process.stderr.write(str);
19
+ }
20
+
21
+ info(str: string) {
22
+ return isBrowser ? console.log(str) : this.stderr(str);
23
+ }
24
+
25
+ warn(str: string) {
26
+ return isBrowser ? console.warn(str) : this.stderr(colorize.yellow(str));
27
+ }
28
+
29
+ error(str: string) {
30
+ return isBrowser ? console.error(str) : this.stderr(colorize.red(str));
31
+ }
32
+ }
33
+
34
+ export const logger = new Logger();
package/src/output.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { isBrowser } from './env';
2
+
3
+ export const output = {
4
+ write(str: string) {
5
+ return isBrowser ? undefined : process.stdout.write(str);
6
+ },
7
+ };
@@ -1,11 +1,12 @@
1
1
  import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
2
2
  import { resolve } from 'path';
3
3
  import { homedir } from 'os';
4
- import { green } from 'colorette';
5
4
  import { RegistryApi } from './registry-api';
6
- import { DEFAULT_REGION, DOMAINS, AVAILABLE_REGIONS, env } from '../config/config';
5
+ import { DEFAULT_REGION, DOMAINS, AVAILABLE_REGIONS } from '../config/config';
6
+ import { env } from '../env';
7
7
  import { RegionalToken, RegionalTokenWithValidity } from './redocly-client-types';
8
8
  import { isNotEmptyObject } from '../utils';
9
+ import { colorize } from '../logger';
9
10
 
10
11
  import type { AccessTokens, Region } from '../config/types';
11
12
 
@@ -29,7 +30,9 @@ export class RedoclyClient {
29
30
  loadRegion(region?: Region) {
30
31
  if (region && !DOMAINS[region]) {
31
32
  throw new Error(
32
- `Invalid argument: region in config file.\nGiven: ${green(region)}, choices: "us", "eu".`
33
+ `Invalid argument: region in config file.\nGiven: ${colorize.green(
34
+ region
35
+ )}, choices: "us", "eu".`
33
36
  );
34
37
  }
35
38
 
@@ -152,7 +155,7 @@ export class RedoclyClient {
152
155
 
153
156
  const credentials = {
154
157
  ...this.readCredentialsFile(credentialsPath),
155
- [this.region!]: accessToken,
158
+ [this.region]: accessToken,
156
159
  token: accessToken, // FIXME: backward compatibility, remove on 1.0.0
157
160
  };
158
161
  this.accessTokens = credentials;
@@ -1,34 +1,32 @@
1
- export namespace RegistryApiTypes {
2
- interface VersionParams {
3
- organizationId: string;
4
- name: string;
5
- version: string;
6
- }
1
+ interface VersionParams {
2
+ organizationId: string;
3
+ name: string;
4
+ version: string;
5
+ }
7
6
 
8
- export interface PrepareFileuploadParams extends VersionParams {
9
- filesHash: string;
10
- filename: string;
11
- isUpsert?: boolean;
12
- }
7
+ export interface PrepareFileuploadParams extends VersionParams {
8
+ filesHash: string;
9
+ filename: string;
10
+ isUpsert?: boolean;
11
+ }
13
12
 
14
- export interface PushApiParams extends VersionParams {
15
- rootFilePath: string;
16
- filePaths: string[];
17
- branch?: string;
18
- isUpsert?: boolean;
19
- isPublic?: boolean;
20
- batchId?: string;
21
- batchSize?: number;
22
- }
13
+ export interface PushApiParams extends VersionParams {
14
+ rootFilePath: string;
15
+ filePaths: string[];
16
+ branch?: string;
17
+ isUpsert?: boolean;
18
+ isPublic?: boolean;
19
+ batchId?: string;
20
+ batchSize?: number;
21
+ }
23
22
 
24
- export interface PrepareFileuploadOKResponse {
25
- filePath: string;
26
- signedUploadUrl: string;
27
- }
23
+ export interface PrepareFileuploadOKResponse {
24
+ filePath: string;
25
+ signedUploadUrl: string;
26
+ }
28
27
 
29
- export interface NotFoundProblemResponse {
30
- status: 404;
31
- title: 'Not Found';
32
- code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
33
- }
28
+ export interface NotFoundProblemResponse {
29
+ status: 404;
30
+ title: 'Not Found';
31
+ code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
34
32
  }
@@ -1,10 +1,17 @@
1
1
  import fetch, { RequestInit, HeadersInit } from 'node-fetch';
2
- import { RegistryApiTypes } from './registry-api-types';
2
+ import type {
3
+ NotFoundProblemResponse,
4
+ PrepareFileuploadOKResponse,
5
+ PrepareFileuploadParams,
6
+ PushApiParams,
7
+ } from './registry-api-types';
8
+ import type { AccessTokens, Region } from '../config/types';
3
9
  import { DEFAULT_REGION, DOMAINS } from '../config/config';
4
10
  import { isNotEmptyObject } from '../utils';
5
11
  const version = require('../../package.json').version;
6
12
 
7
- import type { AccessTokens, Region } from '../config/types';
13
+ export const currentCommand =
14
+ typeof process !== 'undefined' ? process.env?.REDOCLY_CLI_COMMAND || '' : '';
8
15
 
9
16
  export class RegistryApi {
10
17
  constructor(private accessTokens: AccessTokens, private region: Region) {}
@@ -23,7 +30,10 @@ export class RegistryApi {
23
30
  }
24
31
 
25
32
  private async request(path = '', options: RequestInit = {}, region?: Region) {
26
- const headers = Object.assign({}, options.headers || {}, { 'x-redocly-cli-version': version });
33
+ const headers = Object.assign({}, options.headers || {}, {
34
+ 'x-redocly-cli-version': version,
35
+ 'user-agent': `redocly-cli / ${version} ${currentCommand}`,
36
+ });
27
37
 
28
38
  if (!headers.hasOwnProperty('authorization')) {
29
39
  throw new Error('Unauthorized');
@@ -39,7 +49,7 @@ export class RegistryApi {
39
49
  }
40
50
 
41
51
  if (response.status === 404) {
42
- const body: RegistryApiTypes.NotFoundProblemResponse = await response.json();
52
+ const body: NotFoundProblemResponse = await response.json();
43
53
  throw new Error(body.code);
44
54
  }
45
55
 
@@ -71,7 +81,7 @@ export class RegistryApi {
71
81
  filesHash,
72
82
  filename,
73
83
  isUpsert,
74
- }: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse> {
84
+ }: PrepareFileuploadParams): Promise<PrepareFileuploadOKResponse> {
75
85
  const response = await this.request(
76
86
  `/${organizationId}/${name}/${version}/prepare-file-upload`,
77
87
  {
@@ -107,7 +117,7 @@ export class RegistryApi {
107
117
  isPublic,
108
118
  batchId,
109
119
  batchSize,
110
- }: RegistryApiTypes.PushApiParams) {
120
+ }: PushApiParams) {
111
121
  const response = await this.request(
112
122
  `/${organizationId}/${name}/${version}`,
113
123
  {
package/src/ref-utils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Source } from './resolve';
2
2
  import { OasRef } from './typings/openapi';
3
+ import { isTruthy } from './utils';
3
4
 
4
5
  export function joinPointer(base: string, key: string | number) {
5
6
  if (base === '') base = '#/';
@@ -45,7 +46,7 @@ export function parseRef(ref: string): { uri: string | null; pointer: string[] }
45
46
  const [uri, pointer] = ref.split('#/');
46
47
  return {
47
48
  uri: uri || null,
48
- pointer: pointer ? pointer.split('/').map(unescapePointer).filter(Boolean) : [],
49
+ pointer: pointer ? pointer.split('/').map(unescapePointer).filter(isTruthy) : [],
49
50
  };
50
51
  }
51
52
 
package/src/resolve.ts CHANGED
@@ -91,7 +91,7 @@ export function makeDocumentFromString(sourceString: string, absoluteRef: string
91
91
  export class BaseResolver {
92
92
  cache: Map<string, Promise<Document | ResolveError>> = new Map();
93
93
 
94
- constructor(private config: ResolveConfig = { http: { headers: [] } }) {}
94
+ constructor(protected config: ResolveConfig = { http: { headers: [] } }) {}
95
95
 
96
96
  getFiles() {
97
97
  return new Set(Array.from(this.cache.keys()));
@@ -323,7 +323,7 @@ export async function resolveDocument(opts: {
323
323
  : document;
324
324
  } catch (error) {
325
325
  const resolvedRef = {
326
- resolved: false as false,
326
+ resolved: false as const,
327
327
  isRemote,
328
328
  document: undefined,
329
329
  error: error,
@@ -334,7 +334,7 @@ export async function resolveDocument(opts: {
334
334
  }
335
335
 
336
336
  let resolvedRef: ResolvedRef = {
337
- resolved: true as true,
337
+ resolved: true as const,
338
338
  document: targetDoc,
339
339
  isRemote,
340
340
  node: document.parsed,
@@ -344,7 +344,7 @@ export async function resolveDocument(opts: {
344
344
  let target = targetDoc.parsed as any;
345
345
 
346
346
  const segments = pointer;
347
- for (let segment of segments) {
347
+ for (const segment of segments) {
348
348
  if (typeof target !== 'object') {
349
349
  target = undefined;
350
350
  break;
package/src/rules/ajv.ts CHANGED
@@ -73,7 +73,7 @@ export function validateJsonSchema(
73
73
 
74
74
  function beatifyErrorMessage(error: ErrorObject) {
75
75
  let message = error.message;
76
- let suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
76
+ const suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
77
77
  if (suggest) {
78
78
  message += ` ${suggest.map((e: any) => `"${e}"`).join(', ')}`;
79
79
  }
@@ -46,7 +46,7 @@ export const asserts: Asserts = {
46
46
  if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
47
47
  const values = runOnValue(value) ? [value] : value;
48
48
  const regx = regexFromString(condition);
49
- for (let _val of values) {
49
+ for (const _val of values) {
50
50
  if (!regx?.test(_val)) {
51
51
  return { isValid: false, location: runOnValue(value) ? baseLocation : baseLocation.key() };
52
52
  }
@@ -56,7 +56,7 @@ export const asserts: Asserts = {
56
56
  enum: (value: string | string[], condition: string[], baseLocation: Location) => {
57
57
  if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
58
58
  const values = runOnValue(value) ? [value] : value;
59
- for (let _val of values) {
59
+ for (const _val of values) {
60
60
  if (!condition.includes(_val)) {
61
61
  return {
62
62
  isValid: false,
@@ -81,7 +81,7 @@ export const asserts: Asserts = {
81
81
  disallowed: (value: string | string[], condition: string[], baseLocation: Location) => {
82
82
  if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
83
83
  const values = runOnValue(value) ? [value] : value;
84
- for (let _val of values) {
84
+ for (const _val of values) {
85
85
  if (condition.includes(_val)) {
86
86
  return {
87
87
  isValid: false,
@@ -114,7 +114,7 @@ export const asserts: Asserts = {
114
114
  casing: (value: string | string[], condition: string, baseLocation: Location) => {
115
115
  if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
116
116
  const values: string[] = runOnValue(value) ? [value] : value;
117
- for (let _val of values) {
117
+ for (const _val of values) {
118
118
  let matchCase = false;
119
119
  switch (condition) {
120
120
  case 'camelCase':
@@ -3,7 +3,7 @@ import { AssertToApply, buildSubjectVisitor, buildVisitorObject } from './utils'
3
3
  import { Oas2Rule, Oas3Rule } from '../../../visitors';
4
4
 
5
5
  export const Assertions: Oas3Rule | Oas2Rule = (opts: object) => {
6
- let visitors: any[] = [];
6
+ const visitors: any[] = [];
7
7
 
8
8
  // As 'Assertions' has an array of asserts,
9
9
  // that array spreads into an 'opts' object on init rules phase here
@@ -5,7 +5,7 @@ import { Oas2SecurityScheme } from '../../typings/swagger';
5
5
  import { Oas3SecurityScheme } from '../../typings/openapi';
6
6
 
7
7
  export const OperationSecurityDefined: Oas3Rule | Oas2Rule = () => {
8
- let referencedSchemes = new Map<
8
+ const referencedSchemes = new Map<
9
9
  string,
10
10
  {
11
11
  defined?: boolean;
@@ -28,7 +28,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
28
28
  const required =
29
29
  typeof type.required === 'function' ? type.required(node, key) : type.required;
30
30
 
31
- for (let propName of required || []) {
31
+ for (const propName of required || []) {
32
32
  if (!(node as object).hasOwnProperty(propName)) {
33
33
  report({
34
34
  message: `The field \`${propName}\` must be present on this level.`,
@@ -57,7 +57,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
57
57
  const requiredOneOf = type.requiredOneOf || null;
58
58
  if (requiredOneOf) {
59
59
  let hasProperty = false;
60
- for (let propName of requiredOneOf || []) {
60
+ for (const propName of requiredOneOf || []) {
61
61
  if ((node as object).hasOwnProperty(propName)) {
62
62
  hasProperty = true;
63
63
  }
@@ -4,7 +4,7 @@ import { Oas2Components } from '../../typings/swagger';
4
4
  import { isEmptyObject } from '../../utils';
5
5
 
6
6
  export const RemoveUnusedComponents: Oas2Rule = () => {
7
- let components = new Map<
7
+ const components = new Map<
8
8
  string,
9
9
  { used: boolean; componentType?: keyof Oas2Components; name: string }
10
10
  >();
@@ -39,7 +39,7 @@ export const RemoveUnusedComponents: Oas2Rule = () => {
39
39
  const data = ctx.getVisitorData() as { removedCount: number };
40
40
  data.removedCount = 0;
41
41
 
42
- let rootComponents = new Set<keyof Oas2Components>();
42
+ const rootComponents = new Set<keyof Oas2Components>();
43
43
  components.forEach((usageInfo) => {
44
44
  const { used, name, componentType } = usageInfo;
45
45
  if (!used && componentType) {
@@ -3,8 +3,8 @@ import { lintDocument } from '../../../lint';
3
3
  import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../../../__tests__/utils';
4
4
  import { BaseResolver } from '../../../resolve';
5
5
 
6
- describe('Oas3 as3-no-servers-empty-enum', () => {
7
- it('oas3-no-servers-empty-enum: should report on server object with empty enum and unknown enum value', async () => {
6
+ describe('Oas3 as3-no-server-variables-empty-enum', () => {
7
+ it('oas3-no-server-variables-empty-enum: should report on server object with empty enum and unknown enum value', async () => {
8
8
  const document = parseYamlToDocument(
9
9
  outdent`
10
10
  openapi: 3.0.0
@@ -24,7 +24,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
24
24
  const results = await lintDocument({
25
25
  externalRefResolver: new BaseResolver(),
26
26
  document,
27
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
27
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
28
28
  });
29
29
 
30
30
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -38,7 +38,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
38
38
  },
39
39
  ],
40
40
  "message": "Server variable with \`enum\` must be a non-empty array.",
41
- "ruleId": "no-servers-empty-enum",
41
+ "ruleId": "no-server-variables-empty-enum",
42
42
  "severity": "error",
43
43
  "suggest": Array [],
44
44
  },
@@ -51,7 +51,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
51
51
  },
52
52
  ],
53
53
  "message": "Server variable define \`enum\` and \`default\`. \`enum\` must include default value",
54
- "ruleId": "no-servers-empty-enum",
54
+ "ruleId": "no-server-variables-empty-enum",
55
55
  "severity": "error",
56
56
  "suggest": Array [],
57
57
  },
@@ -59,7 +59,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
59
59
  `);
60
60
  });
61
61
 
62
- it('oas3-no-servers-empty-enum: should report on server object with empty enum', async () => {
62
+ it('oas3-no-server-variables-empty-enum: should report on server object with empty enum', async () => {
63
63
  const document = parseYamlToDocument(
64
64
  outdent`
65
65
  openapi: 3.0.0
@@ -78,7 +78,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
78
78
  const results = await lintDocument({
79
79
  externalRefResolver: new BaseResolver(),
80
80
  document,
81
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
81
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
82
82
  });
83
83
 
84
84
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
@@ -92,7 +92,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
92
92
  },
93
93
  ],
94
94
  "message": "Server variable with \`enum\` must be a non-empty array.",
95
- "ruleId": "no-servers-empty-enum",
95
+ "ruleId": "no-server-variables-empty-enum",
96
96
  "severity": "error",
97
97
  "suggest": Array [],
98
98
  },
@@ -100,7 +100,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
100
100
  `);
101
101
  });
102
102
 
103
- it('oas3-no-servers-empty-enum: should be success because variables is empty object', async () => {
103
+ it('oas3-no-server-variables-empty-enum: should be success because variables is empty object', async () => {
104
104
  const document = parseYamlToDocument(
105
105
  outdent`
106
106
  openapi: 3.0.0
@@ -117,13 +117,13 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
117
117
  const results = await lintDocument({
118
118
  externalRefResolver: new BaseResolver(),
119
119
  document,
120
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
120
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
121
121
  });
122
122
 
123
123
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
124
124
  });
125
125
 
126
- it('oas3-no-servers-empty-enum: should be success because variable is empty object', async () => {
126
+ it('oas3-no-server-variables-empty-enum: should be success because variable is empty object', async () => {
127
127
  const document = parseYamlToDocument(
128
128
  outdent`
129
129
  openapi: 3.0.0
@@ -141,13 +141,13 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
141
141
  const results = await lintDocument({
142
142
  externalRefResolver: new BaseResolver(),
143
143
  document,
144
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
144
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
145
145
  });
146
146
 
147
147
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
148
148
  });
149
149
 
150
- it('oas3-no-servers-empty-enum: should be success because enum contains default value', async () => {
150
+ it('oas3-no-server-variables-empty-enum: should be success because enum contains default value', async () => {
151
151
  const document = parseYamlToDocument(
152
152
  outdent`
153
153
  openapi: 3.0.0
@@ -168,13 +168,13 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
168
168
  const results = await lintDocument({
169
169
  externalRefResolver: new BaseResolver(),
170
170
  document,
171
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
171
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
172
172
  });
173
173
 
174
174
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
175
175
  });
176
176
 
177
- it('oas3-no-servers-empty-enum: should be success because enum contains default value', async () => {
177
+ it('oas3-no-server-variables-empty-enum: should be success because enum contains default value', async () => {
178
178
  const document = parseYamlToDocument(
179
179
  outdent`
180
180
  openapi: 3.0.0
@@ -197,7 +197,7 @@ describe('Oas3 as3-no-servers-empty-enum', () => {
197
197
  const results = await lintDocument({
198
198
  externalRefResolver: new BaseResolver(),
199
199
  document,
200
- config: await makeConfig({ 'no-servers-empty-enum': 'error' }),
200
+ config: await makeConfig({ 'no-server-variables-empty-enum': 'error' }),
201
201
  });
202
202
 
203
203
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`Array []`);
@@ -37,7 +37,7 @@ import { NoUndefinedServerVariable } from './no-undefined-server-variable';
37
37
  import { OperationOperationId } from '../common/operation-operationId';
38
38
  import { OperationSummary } from '../common/operation-summary';
39
39
  import { NoAmbiguousPaths } from '../common/no-ambiguous-paths';
40
- import { NoEmptyEnumServers } from './no-servers-empty-enum';
40
+ import { NoServerVariablesEmptyEnum } from './no-server-variables-empty-enum';
41
41
  import { NoHttpVerbsInPaths } from '../common/no-http-verbs-in-paths';
42
42
  import { RequestMimeType } from './request-mime-type';
43
43
  import { ResponseMimeType } from './response-mime-type';
@@ -89,7 +89,7 @@ export const rules = {
89
89
  'no-identical-paths': NoIdenticalPaths,
90
90
  'no-ambiguous-paths': NoAmbiguousPaths,
91
91
  'no-undefined-server-variable': NoUndefinedServerVariable,
92
- 'no-servers-empty-enum': NoEmptyEnumServers,
92
+ 'no-server-variables-empty-enum': NoServerVariablesEmptyEnum,
93
93
  'no-http-verbs-in-paths': NoHttpVerbsInPaths,
94
94
  'path-excludes-patterns': PathExcludesPatterns,
95
95
  'request-mime-type': RequestMimeType,
@@ -6,7 +6,7 @@ enum enumError {
6
6
  invalidDefaultValue = 'invalidDefaultValue',
7
7
  }
8
8
 
9
- export const NoEmptyEnumServers: Oas3Rule = () => {
9
+ export const NoServerVariablesEmptyEnum: Oas3Rule = () => {
10
10
  return {
11
11
  DefinitionRoot(root, { report, location }) {
12
12
  if (!root.servers || root.servers.length === 0) return;
@@ -48,7 +48,7 @@ function checkEnumVariables(server: Oas3Server): enumError[] | undefined {
48
48
  if (server.variables && Object.keys(server.variables).length === 0) return;
49
49
 
50
50
  const errors: enumError[] = [];
51
- for (var variable in server.variables) {
51
+ for (const variable in server.variables) {
52
52
  const serverVariable = server.variables[variable];
53
53
  if (!serverVariable.enum) continue;
54
54
 
@@ -2,7 +2,7 @@ import { Oas3Rule } from '../../visitors';
2
2
  import { Location } from '../../ref-utils';
3
3
 
4
4
  export const NoUnusedComponents: Oas3Rule = () => {
5
- let components = new Map<string, { used: boolean; location: Location; name: string }>();
5
+ const components = new Map<string, { used: boolean; location: Location; name: string }>();
6
6
 
7
7
  function registerComponent(location: Location, name: string): void {
8
8
  components.set(location.absolutePointer, {
@@ -4,7 +4,7 @@ import { Oas3Components } from '../../typings/openapi';
4
4
  import { isEmptyObject } from '../../utils';
5
5
 
6
6
  export const RemoveUnusedComponents: Oas3Rule = () => {
7
- let components = new Map<
7
+ const components = new Map<
8
8
  string,
9
9
  { used: boolean; componentType?: keyof Oas3Components; name: string }
10
10
  >();
@@ -45,12 +45,12 @@ export const RemoveUnusedComponents: Oas3Rule = () => {
45
45
 
46
46
  components.forEach((usageInfo) => {
47
47
  const { used, componentType, name } = usageInfo;
48
- if (!used && componentType) {
49
- let componentChild = root.components![componentType];
48
+ if (!used && componentType && root.components) {
49
+ const componentChild = root.components[componentType];
50
50
  delete componentChild![name];
51
51
  data.removedCount++;
52
52
  if (isEmptyObject(componentChild)) {
53
- delete root.components![componentType];
53
+ delete root.components[componentType];
54
54
  }
55
55
  }
56
56
  });
@@ -104,7 +104,7 @@ export function validateExample(
104
104
  allowAdditionalProperties
105
105
  );
106
106
  if (!valid) {
107
- for (let error of errors) {
107
+ for (const error of errors) {
108
108
  report({
109
109
  message: `Example value must conform to the schema: ${error.message}.`,
110
110
  location: {
@@ -40,7 +40,7 @@ const builtInRulesList = [
40
40
  'no-identical-paths',
41
41
  'no-ambiguous-paths',
42
42
  'no-undefined-server-variable',
43
- 'no-servers-empty-enum',
43
+ 'no-server-variables-empty-enum',
44
44
  'no-http-verbs-in-paths',
45
45
  'path-excludes-patterns',
46
46
  'request-mime-type',
package/src/utils.ts CHANGED
@@ -6,8 +6,8 @@ import * as pluralize from 'pluralize';
6
6
  import { parseYaml } from './js-yaml';
7
7
  import { UserContext } from './walk';
8
8
  import { HttpResolveConfig } from './config';
9
- import { env } from './config';
10
- import { green, yellow } from 'colorette';
9
+ import { env } from './env';
10
+ import { logger, colorize } from './logger';
11
11
 
12
12
  export { parseYaml, stringifyYaml } from './js-yaml';
13
13
 
@@ -96,7 +96,7 @@ export function omitObjectProps<T extends Record<string, unknown>>(
96
96
  export function splitCamelCaseIntoWords(str: string) {
97
97
  const camel = str
98
98
  .split(/(?:[-._])|([A-Z][a-z]+)/)
99
- .filter(Boolean)
99
+ .filter(isTruthy)
100
100
  .map((item) => item.toLocaleLowerCase());
101
101
  const caps = str
102
102
  .split(/([A-Z]{2,})/)
@@ -183,7 +183,7 @@ export function isNotString<T>(value: string | T): value is T {
183
183
  }
184
184
 
185
185
  export function assignExisting<T>(target: Record<string, T>, obj: Record<string, T>) {
186
- for (let k of Object.keys(obj)) {
186
+ for (const k of Object.keys(obj)) {
187
187
  if (target.hasOwnProperty(k)) {
188
188
  target[k] = obj[k];
189
189
  }
@@ -207,8 +207,8 @@ export function doesYamlFileExist(filePath: string): boolean {
207
207
  }
208
208
 
209
209
  export function showWarningForDeprecatedField(deprecatedField: string, updatedField: string) {
210
- process.stderr.write(
211
- `The ${yellow(deprecatedField)} field is deprecated. Use ${green(
210
+ logger.warn(
211
+ `The ${colorize.red(deprecatedField)} field is deprecated. Use ${colorize.green(
212
212
  updatedField
213
213
  )} instead. Read more about this change: https://redocly.com/docs/api-registry/guides/migration-guide-config-file/#changed-properties\n`
214
214
  );
@@ -217,3 +217,13 @@ export function showWarningForDeprecatedField(deprecatedField: string, updatedFi
217
217
  export function showErrorForDeprecatedField(deprecatedField: string, updatedField: string) {
218
218
  throw new Error(`Do not use '${deprecatedField}' field. Use '${updatedField}' instead.\n`);
219
219
  }
220
+
221
+ export type Falsy = undefined | null | false | '' | 0;
222
+
223
+ export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
224
+ return !!value;
225
+ }
226
+
227
+ export function identity<T>(value: T): T {
228
+ return value;
229
+ }