@redocly/openapi-core 1.0.0-beta.77 → 1.0.0-beta.81

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 (108) hide show
  1. package/__tests__/utils.ts +11 -1
  2. package/lib/bundle.d.ts +3 -1
  3. package/lib/bundle.js +30 -8
  4. package/lib/config/builtIn.js +18 -4
  5. package/lib/config/config.d.ts +2 -9
  6. package/lib/{rules → decorators}/common/info-description-override.d.ts +0 -0
  7. package/lib/{rules → decorators}/common/info-description-override.js +0 -0
  8. package/lib/{rules → decorators}/common/operation-description-override.d.ts +0 -0
  9. package/lib/{rules → decorators}/common/operation-description-override.js +0 -0
  10. package/lib/{rules → decorators}/common/registry-dependencies.d.ts +0 -0
  11. package/lib/{rules → decorators}/common/registry-dependencies.js +0 -0
  12. package/lib/decorators/common/remove-x-internal.d.ts +2 -0
  13. package/lib/decorators/common/remove-x-internal.js +58 -0
  14. package/lib/{rules → decorators}/common/tag-description-override.d.ts +0 -0
  15. package/lib/{rules → decorators}/common/tag-description-override.js +0 -0
  16. package/lib/decorators/oas2/index.d.ts +8 -0
  17. package/lib/decorators/oas2/index.js +15 -0
  18. package/lib/decorators/oas3/index.d.ts +8 -0
  19. package/lib/decorators/oas3/index.js +15 -0
  20. package/lib/js-yaml/index.js +1 -1
  21. package/lib/redocly/registry-api.js +4 -1
  22. package/lib/resolve.d.ts +1 -0
  23. package/lib/resolve.js +7 -3
  24. package/lib/rules/common/operation-security-defined.js +1 -4
  25. package/lib/rules/common/spec.js +18 -3
  26. package/lib/rules/oas2/index.d.ts +1 -7
  27. package/lib/rules/oas2/index.js +1 -11
  28. package/lib/rules/oas2/remove-unused-components.d.ts +2 -0
  29. package/lib/rules/oas2/remove-unused-components.js +73 -0
  30. package/lib/rules/oas3/index.d.ts +0 -7
  31. package/lib/rules/oas3/index.js +1 -11
  32. package/lib/rules/oas3/no-invalid-media-type-examples.js +12 -11
  33. package/lib/rules/oas3/remove-unused-components.d.ts +2 -0
  34. package/lib/rules/oas3/remove-unused-components.js +83 -0
  35. package/lib/types/index.d.ts +4 -0
  36. package/lib/types/oas2.js +42 -35
  37. package/lib/types/oas3.js +68 -139
  38. package/lib/types/oas3_1.js +52 -49
  39. package/lib/typings/swagger.d.ts +14 -0
  40. package/lib/utils.d.ts +2 -0
  41. package/lib/utils.js +10 -2
  42. package/lib/walk.js +5 -8
  43. package/package.json +1 -1
  44. package/src/__tests__/js-yaml.test.ts +29 -5
  45. package/src/__tests__/lint.test.ts +1 -3
  46. package/src/bundle.ts +37 -8
  47. package/src/config/builtIn.ts +20 -7
  48. package/src/config/config.ts +2 -3
  49. package/src/decorators/__tests__/remove-x-internal.test.ts +316 -0
  50. package/src/{rules → decorators}/common/info-description-override.ts +0 -0
  51. package/src/{rules → decorators}/common/operation-description-override.ts +0 -0
  52. package/src/{rules → decorators}/common/registry-dependencies.ts +0 -0
  53. package/src/decorators/common/remove-x-internal.ts +59 -0
  54. package/src/{rules → decorators}/common/tag-description-override.ts +0 -0
  55. package/src/decorators/oas2/index.ts +14 -0
  56. package/src/decorators/oas3/index.ts +14 -0
  57. package/src/js-yaml/index.ts +1 -2
  58. package/src/redocly/registry-api.ts +7 -1
  59. package/src/resolve.ts +6 -6
  60. package/src/rules/__tests__/no-unresolved-refs.test.ts +1 -4
  61. package/src/rules/common/__tests__/info-description.test.ts +1 -3
  62. package/src/rules/common/__tests__/info-license.test.ts +1 -2
  63. package/src/rules/common/__tests__/license-url.test.ts +1 -2
  64. package/src/rules/common/__tests__/no-ambiguous-paths.test.ts +1 -2
  65. package/src/rules/common/__tests__/no-enum-type-mismatch.test.ts +1 -2
  66. package/src/rules/common/__tests__/no-identical-paths.test.ts +1 -2
  67. package/src/rules/common/__tests__/no-path-trailing-slash.test.ts +1 -2
  68. package/src/rules/common/__tests__/operation-2xx-response.test.ts +1 -2
  69. package/src/rules/common/__tests__/operation-4xx-response.test.ts +1 -2
  70. package/src/rules/common/__tests__/operation-operationId-unique.test.ts +1 -2
  71. package/src/rules/common/__tests__/operation-operationId-url-safe.test.ts +1 -2
  72. package/src/rules/common/__tests__/operation-parameters-unique.test.ts +1 -2
  73. package/src/rules/common/__tests__/operation-security-defined.test.ts +1 -2
  74. package/src/rules/common/__tests__/operation-singular-tag.test.ts +1 -2
  75. package/src/rules/common/__tests__/path-http-verbs-order.test.ts +1 -2
  76. package/src/rules/common/__tests__/path-not-include-query.test.ts +1 -2
  77. package/src/rules/common/__tests__/path-params-defined.test.ts +1 -2
  78. package/src/rules/common/__tests__/paths-kebab-case.test.ts +1 -2
  79. package/src/rules/common/__tests__/tag-description.test.ts +1 -2
  80. package/src/rules/common/__tests__/tags-alphabetical.test.ts +1 -2
  81. package/src/rules/common/operation-security-defined.ts +4 -5
  82. package/src/rules/common/spec.ts +18 -0
  83. package/src/rules/oas2/__tests__/boolean-parameter-prefixes.test.ts +1 -2
  84. package/src/rules/oas2/__tests__/spec/referenceableScalars.test.ts +1 -2
  85. package/src/rules/oas2/index.ts +1 -11
  86. package/src/rules/oas2/remove-unused-components.ts +76 -0
  87. package/src/rules/oas3/__tests__/boolean-parameter-prefixes.test.ts +1 -2
  88. package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +1 -2
  89. package/src/rules/oas3/__tests__/no-example-value-and-externalValue.test.ts +1 -2
  90. package/src/rules/oas3/__tests__/no-invalid-media-type-examples.test.ts +1 -2
  91. package/src/rules/oas3/__tests__/no-server-example.com.test.ts +1 -2
  92. package/src/rules/oas3/__tests__/no-server-trailing-slash.test.ts +1 -2
  93. package/src/rules/oas3/__tests__/no-unused-components.test.ts +1 -2
  94. package/src/rules/oas3/index.ts +0 -12
  95. package/src/rules/oas3/no-invalid-media-type-examples.ts +17 -21
  96. package/src/rules/oas3/remove-unused-components.ts +84 -0
  97. package/src/types/index.ts +4 -0
  98. package/src/types/oas2.ts +42 -43
  99. package/src/types/oas3.ts +68 -141
  100. package/src/types/oas3_1.ts +51 -50
  101. package/src/typings/swagger.ts +7 -0
  102. package/src/utils.ts +9 -1
  103. package/src/walk.ts +6 -13
  104. package/tsconfig.tsbuildinfo +1 -1
  105. package/lib/rules/builtin.d.ts +0 -20
  106. package/lib/rules/builtin.js +0 -17
  107. package/src/rules/__tests__/config.ts +0 -10
  108. package/src/rules/builtin.ts +0 -18
@@ -21,35 +21,19 @@ const DefinitionRoot = {
21
21
  };
22
22
  const License = {
23
23
  properties: {
24
- name: {
25
- type: 'string',
26
- },
27
- url: {
28
- type: 'string',
29
- },
30
- identifier: {
31
- type: 'string',
32
- },
24
+ name: { type: 'string' },
25
+ url: { type: 'string' },
26
+ identifier: { type: 'string' },
33
27
  },
34
28
  required: ['name'],
35
29
  };
36
30
  const Info = {
37
31
  properties: {
38
- title: {
39
- type: 'string',
40
- },
41
- version: {
42
- type: 'string',
43
- },
44
- description: {
45
- type: 'string',
46
- },
47
- termsOfService: {
48
- type: 'string',
49
- },
50
- summary: {
51
- type: 'string',
52
- },
32
+ title: { type: 'string' },
33
+ version: { type: 'string' },
34
+ description: { type: 'string' },
35
+ termsOfService: { type: 'string' },
36
+ summary: { type: 'string' },
53
37
  contact: 'Contact',
54
38
  license: 'License',
55
39
  },
@@ -84,9 +68,7 @@ const Operation = {
84
68
  servers: _1.listOf('Server'),
85
69
  requestBody: 'RequestBody',
86
70
  responses: 'ResponsesMap',
87
- deprecated: {
88
- type: 'boolean',
89
- },
71
+ deprecated: { type: 'boolean' },
90
72
  callbacks: _1.mapOf('Callback'),
91
73
  'x-codeSamples': _1.listOf('XCodeSample'),
92
74
  'x-code-samples': _1.listOf('XCodeSample'), // deprecated
@@ -137,7 +119,7 @@ const Schema = {
137
119
  then: 'Schema',
138
120
  else: 'Schema',
139
121
  dependentSchemas: _1.listOf('Schema'),
140
- prefixItems: { type: 'array' },
122
+ prefixItems: _1.listOf('Schema'),
141
123
  contains: 'Schema',
142
124
  patternProperties: { type: 'object' },
143
125
  propertyNames: 'Schema',
@@ -149,18 +131,16 @@ const Schema = {
149
131
  if (Array.isArray(value)) {
150
132
  return _1.listOf('Schema');
151
133
  }
152
- else {
153
- return 'Schema';
154
- }
155
- },
156
- additionalProperties: (value) => {
157
- if (typeof value === 'boolean') {
134
+ else if (typeof value === 'boolean') {
158
135
  return { type: 'boolean' };
159
136
  }
160
137
  else {
161
138
  return 'Schema';
162
139
  }
163
140
  },
141
+ additionalProperties: (value) => {
142
+ return typeof value === 'boolean' ? { type: 'boolean' } : 'Schema';
143
+ },
164
144
  description: { type: 'string' },
165
145
  format: { type: 'string' },
166
146
  contentEncoding: { type: 'string' },
@@ -181,30 +161,53 @@ const SecurityScheme = {
181
161
  type: { enum: ['apiKey', 'http', 'oauth2', 'openIdConnect', 'mutualTLS'] },
182
162
  description: { type: 'string' },
183
163
  name: { type: 'string' },
184
- in: { type: 'string' },
164
+ in: { type: 'string', enum: ['query', 'header', 'cookie'] },
185
165
  scheme: { type: 'string' },
186
166
  bearerFormat: { type: 'string' },
187
167
  flows: 'SecuritySchemeFlows',
188
168
  openIdConnectUrl: { type: 'string' },
189
169
  },
190
170
  required(value) {
191
- if (!(value === null || value === void 0 ? void 0 : value.type)) {
192
- return ['type'];
193
- }
194
- if (value.type === 'apiKey') {
195
- return ['type', 'name', 'in'];
171
+ switch (value === null || value === void 0 ? void 0 : value.type) {
172
+ case 'apiKey':
173
+ return ['type', 'name', 'in'];
174
+ case 'http':
175
+ return ['type', 'scheme'];
176
+ case 'oauth2':
177
+ return ['type', 'flows'];
178
+ case 'openIdConnect':
179
+ return ['type', 'openIdConnectUrl'];
180
+ default:
181
+ return ['type'];
196
182
  }
197
- else if (value.type === 'http') {
198
- return ['type', 'scheme'];
199
- }
200
- else if (value.type === 'oauth2') {
201
- return ['type', 'flows'];
202
- }
203
- else if (value.type === 'openIdConnect') {
204
- return ['type', 'openIdConnectUrl'];
183
+ },
184
+ allowed(value) {
185
+ switch (value === null || value === void 0 ? void 0 : value.type) {
186
+ case 'apiKey':
187
+ return ['type', 'name', 'in', 'description'];
188
+ case 'http':
189
+ return ['type', 'scheme', 'bearerFormat', 'description'];
190
+ case 'oauth2':
191
+ switch (value === null || value === void 0 ? void 0 : value.flows) {
192
+ case 'implicit':
193
+ return ['type', 'flows', 'authorizationUrl', 'refreshUrl', 'description', 'scopes'];
194
+ case 'password':
195
+ case 'clientCredentials':
196
+ return ['type', 'flows', 'tokenUrl', 'refreshUrl', 'description', 'scopes'];
197
+ case 'authorizationCode':
198
+ return ['type', 'flows', 'authorizationUrl', 'refreshUrl', 'tokenUrl', 'description', 'scopes'];
199
+ default:
200
+ return ['type', 'flows', 'authorizationUrl', 'refreshUrl', 'tokenUrl', 'description', 'scopes'];
201
+ }
202
+ case 'openIdConnect':
203
+ return ['type', 'openIdConnectUrl', 'description'];
204
+ case 'mutualTLS':
205
+ return ['type', 'description'];
206
+ default:
207
+ return ['type', 'description'];
205
208
  }
206
- return ['type'];
207
209
  },
210
+ extensionsPrefix: 'x-',
208
211
  };
209
212
  exports.Oas3_1Types = Object.assign(Object.assign({}, oas3_1.Oas3Types), { Info,
210
213
  DefinitionRoot,
@@ -16,6 +16,20 @@ export interface Oas2Definition {
16
16
  tags?: Oas2Tag[];
17
17
  externalDocs?: Oas2ExternalDocs;
18
18
  }
19
+ export interface Oas2Components {
20
+ definitions?: {
21
+ [name: string]: Record<string, Oas2Schema>;
22
+ };
23
+ securityDefinitions?: {
24
+ [name: string]: Record<string, Oas2SecurityScheme>;
25
+ };
26
+ responses?: {
27
+ [name: string]: Record<string, Oas2Response>;
28
+ };
29
+ parameters?: {
30
+ [name: string]: Record<string, Oas2Parameter>;
31
+ };
32
+ }
19
33
  export interface Oas2Info {
20
34
  title: string;
21
35
  version: string;
package/lib/utils.d.ts CHANGED
@@ -16,6 +16,8 @@ export declare type BundleOutputFormat = 'json' | 'yml' | 'yaml';
16
16
  export declare function loadYaml(filename: string): Promise<unknown>;
17
17
  export declare function notUndefined<T>(x: T | undefined): x is T;
18
18
  export declare function isPlainObject(value: any): value is object;
19
+ export declare function isEmptyObject(value: any): value is object;
20
+ export declare function isEmptyArray(value: any): boolean;
19
21
  export declare function readFileFromUrl(url: string, config: HttpResolveConfig): Promise<{
20
22
  body: any;
21
23
  mimeType: any;
package/lib/utils.js CHANGED
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.match = exports.readFileFromUrl = exports.isPlainObject = exports.notUndefined = exports.loadYaml = exports.popStack = exports.pushStack = exports.stringifyYaml = exports.parseYaml = void 0;
12
+ exports.isNotEmptyObject = exports.slash = exports.isPathParameter = exports.readFileAsStringSync = exports.isSingular = exports.validateMimeTypeOAS3 = exports.validateMimeType = exports.splitCamelCaseIntoWords = exports.omitObjectProps = exports.pickObjectProps = exports.match = 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 minimatch = require("minimatch");
15
15
  const node_fetch_1 = require("node-fetch");
@@ -42,6 +42,14 @@ function isPlainObject(value) {
42
42
  return value !== null && typeof value === 'object' && !Array.isArray(value);
43
43
  }
44
44
  exports.isPlainObject = isPlainObject;
45
+ function isEmptyObject(value) {
46
+ return isPlainObject(value) && Object.keys(value).length === 0;
47
+ }
48
+ exports.isEmptyObject = isEmptyObject;
49
+ function isEmptyArray(value) {
50
+ return Array.isArray(value) && value.length === 0;
51
+ }
52
+ exports.isEmptyArray = isEmptyArray;
45
53
  function readFileFromUrl(url, config) {
46
54
  return __awaiter(this, void 0, void 0, function* () {
47
55
  const headers = {};
@@ -130,7 +138,7 @@ function readFileAsStringSync(filePath) {
130
138
  }
131
139
  exports.readFileAsStringSync = readFileAsStringSync;
132
140
  function isPathParameter(pathSegment) {
133
- return pathSegment.startsWith('{') && pathSegment.endsWith('{');
141
+ return pathSegment.startsWith('{') && pathSegment.endsWith('}');
134
142
  }
135
143
  exports.isPathParameter = isPathParameter;
136
144
  /**
package/lib/walk.js CHANGED
@@ -101,10 +101,9 @@ function walkDocument(opts) {
101
101
  if (!activatedOn.skipped) {
102
102
  visitedBySome = true;
103
103
  enteredContexts.add(context);
104
- const ignoreNextVisitorsOnNode = visitWithContext(visit, resolvedNode, context, ruleId, severity);
105
- if (ignoreNextVisitorsOnNode) {
104
+ const { ignoreNextVisitorsOnNode } = visitWithContext(visit, resolvedNode, context, ruleId, severity);
105
+ if (ignoreNextVisitorsOnNode)
106
106
  break;
107
- }
108
107
  }
109
108
  }
110
109
  }
@@ -211,17 +210,15 @@ function walkDocument(opts) {
211
210
  key,
212
211
  parentLocations: collectParentsLocations(context),
213
212
  oasVersion: ctx.oasVersion,
214
- ignoreNextVisitorsOnNode: () => {
215
- ignoreNextVisitorsOnNode = true;
216
- },
213
+ ignoreNextVisitorsOnNode: () => { ignoreNextVisitorsOnNode = true; },
217
214
  getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
218
215
  }, collectParents(context), context);
219
- return ignoreNextVisitorsOnNode;
216
+ return { ignoreNextVisitorsOnNode };
220
217
  }
221
218
  function resolve(ref, from = currentLocation.source.absoluteRef) {
222
219
  if (!ref_utils_1.isRef(ref))
223
220
  return { location, node: ref };
224
- const refId = from + '::' + ref.$ref;
221
+ const refId = resolve_1.makeRefId(from, ref.$ref);
225
222
  const resolvedRef = resolvedRefMap.get(refId);
226
223
  if (!resolvedRef) {
227
224
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/openapi-core",
3
- "version": "1.0.0-beta.77",
3
+ "version": "1.0.0-beta.81",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "engines": {
@@ -1,3 +1,4 @@
1
+ import { outdent } from 'outdent';
1
2
  import { parseYaml, stringifyYaml } from '../js-yaml';
2
3
 
3
4
  const yaml = `
@@ -7,7 +8,8 @@ const yaml = `
7
8
  number: 1000
8
9
  decimal: 12.34
9
10
  boolean: true
10
- date: 2020-01-01
11
+ dateWithoutQuotes: 2020-01-01
12
+ dateWithQuotes: '2020-01-01'
11
13
  array:
12
14
  - 1
13
15
  - 2
@@ -16,6 +18,16 @@ const yaml = `
16
18
  key2: 2
17
19
  `;
18
20
 
21
+ const yamlToDump = outdent`
22
+ date: '2022-01-21T11:29:56.694Z'
23
+ dateWithoutQuotes: 2020-01-01
24
+ dateWithQuotes: '2020-01-01'
25
+ dateImplicit: !!str 2020-01-01
26
+ string: test
27
+ stringWithQuotes: 'test'
28
+ stringWithDoubleQuotes: "test"
29
+ `;
30
+
19
31
  const jsObject = {
20
32
  emptyValue: null,
21
33
  'spaces in keys': 'spaces in keys',
@@ -23,7 +35,8 @@ const jsObject = {
23
35
  number: 1000,
24
36
  decimal: 12.34,
25
37
  boolean: true,
26
- date: '2020-01-01',
38
+ dateWithoutQuotes: '2020-01-01',
39
+ dateWithQuotes: '2020-01-01',
27
40
  array: [1, 2],
28
41
  object: { key1: 1, key2: 2 },
29
42
  };
@@ -33,14 +46,25 @@ describe('js-yaml', () => {
33
46
  expect(parseYaml(yaml)).toEqual(jsObject);
34
47
  });
35
48
 
49
+ test('should correctly dump date and string', () => {
50
+ expect(stringifyYaml(parseYaml(yamlToDump))).toMatchInlineSnapshot(
51
+ `
52
+ "date: '2022-01-21T11:29:56.694Z'
53
+ dateWithoutQuotes: '2020-01-01'
54
+ dateWithQuotes: '2020-01-01'
55
+ dateImplicit: '2020-01-01'
56
+ string: test
57
+ stringWithQuotes: test
58
+ stringWithDoubleQuotes: test
59
+ "
60
+ `);
61
+ });
62
+
36
63
  test('parse and stringify', () => {
37
64
  expect(parseYaml(stringifyYaml(jsObject))).toEqual(jsObject);
38
65
  });
39
66
 
40
67
  test('should throw an error for unsupported types', () => {
41
- expect(() => stringifyYaml({ date: new Date() }))
42
- .toThrow('unacceptable kind of an object to dump [object Date]');
43
-
44
68
  expect(() => stringifyYaml({ foo: () => {} }))
45
69
  .toThrow('unacceptable kind of an object to dump [object Function]');
46
70
  });
@@ -3,9 +3,7 @@ import { outdent } from 'outdent';
3
3
  import { lintFromString, lintConfig, lintDocument } from '../lint';
4
4
  import { BaseResolver } from '../resolve';
5
5
  import { loadConfig } from '../config/load';
6
- import { parseYamlToDocument, replaceSourceWithRef } from '../../__tests__/utils';
7
- // todo create general utils for tests and move this config.
8
- import { makeConfig } from '../rules/__tests__/config';
6
+ import { parseYamlToDocument, replaceSourceWithRef, makeConfig } from '../../__tests__/utils';
9
7
 
10
8
  describe('lint', () => {
11
9
  it('lintFromString should work', async () => {
package/src/bundle.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import isEqual = require('lodash.isequal');
2
- import { BaseResolver, resolveDocument, Document } from './resolve';
2
+ import { BaseResolver, resolveDocument, Document, ResolvedRefMap, makeRefId } from './resolve';
3
3
  import { Oas3Rule, normalizeVisitors, Oas3Visitor, Oas2Visitor } from './visitors';
4
4
  import { Oas3Types } from './types/oas3';
5
5
  import { Oas2Types } from './types/oas2';
@@ -14,6 +14,8 @@ import { reportUnresolvedRef } from './rules/no-unresolved-refs';
14
14
  import { isPlainObject } from './utils';
15
15
  import { OasRef } from './typings/openapi';
16
16
  import { isRedoclyRegistryURL } from './redocly';
17
+ import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components';
18
+ import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './rules/oas3/remove-unused-components';
17
19
 
18
20
  export type Oas3RuleSet = Record<string, Oas3Rule>;
19
21
 
@@ -31,6 +33,7 @@ export async function bundle(opts: {
31
33
  dereference?: boolean;
32
34
  base?: string;
33
35
  skipRedoclyRegistryRefs?: boolean;
36
+ removeUnusedComponents?: boolean;
34
37
  }) {
35
38
  const {
36
39
  ref,
@@ -66,6 +69,7 @@ export async function bundleDocument(opts: {
66
69
  externalRefResolver: BaseResolver;
67
70
  dereference?: boolean;
68
71
  skipRedoclyRegistryRefs?: boolean;
72
+ removeUnusedComponents?: boolean;
69
73
  }) {
70
74
  const {
71
75
  document,
@@ -74,6 +78,7 @@ export async function bundleDocument(opts: {
74
78
  externalRefResolver,
75
79
  dereference = false,
76
80
  skipRedoclyRegistryRefs = false,
81
+ removeUnusedComponents = false,
77
82
  } = opts;
78
83
  const oasVersion = detectOpenAPI(document.parsed);
79
84
  const oasMajorVersion = openAPIMajor(oasVersion);
@@ -92,6 +97,7 @@ export async function bundleDocument(opts: {
92
97
 
93
98
  const preprocessors = initRules(rules as any, config, 'preprocessors', oasVersion);
94
99
  const decorators = initRules(rules as any, config, 'decorators', oasVersion);
100
+
95
101
  const ctx: BundleContext = {
96
102
  problems: [],
97
103
  oasVersion: oasVersion,
@@ -99,25 +105,35 @@ export async function bundleDocument(opts: {
99
105
  visitorsData: {},
100
106
  };
101
107
 
108
+ if (removeUnusedComponents) {
109
+ decorators.push({
110
+ severity: 'error',
111
+ ruleId: 'remove-unused-components',
112
+ visitor: oasMajorVersion === OasMajorVersion.Version2
113
+ ? RemoveUnusedComponentsOas2({})
114
+ : RemoveUnusedComponentsOas3({})
115
+ })
116
+ }
117
+
118
+ const resolvedRefMap = await resolveDocument({
119
+ rootDocument: document,
120
+ rootType: types.DefinitionRoot,
121
+ externalRefResolver,
122
+ });
123
+
102
124
  const bundleVisitor = normalizeVisitors(
103
125
  [
104
126
  ...preprocessors,
105
127
  {
106
128
  severity: 'error',
107
129
  ruleId: 'bundler',
108
- visitor: makeBundleVisitor(oasMajorVersion, dereference, skipRedoclyRegistryRefs, document),
130
+ visitor: makeBundleVisitor(oasMajorVersion, dereference, skipRedoclyRegistryRefs, document, resolvedRefMap),
109
131
  },
110
132
  ...decorators,
111
133
  ],
112
134
  types,
113
135
  );
114
136
 
115
- const resolvedRefMap = await resolveDocument({
116
- rootDocument: document,
117
- rootType: types.DefinitionRoot,
118
- externalRefResolver,
119
- });
120
-
121
137
  walkDocument({
122
138
  document,
123
139
  rootType: types.DefinitionRoot as NormalizedNodeType,
@@ -182,6 +198,7 @@ function makeBundleVisitor(
182
198
  dereference: boolean,
183
199
  skipRedoclyRegistryRefs: boolean,
184
200
  rootDocument: Document,
201
+ resolvedRefMap: ResolvedRefMap
185
202
  ) {
186
203
  let components: Record<string, Record<string, any>>;
187
204
 
@@ -214,6 +231,7 @@ function makeBundleVisitor(
214
231
  replaceRef(node, resolved, ctx);
215
232
  } else {
216
233
  node.$ref = saveComponent(componentType, resolved, ctx);
234
+ resolveBundledComponent(node, resolved, ctx);
217
235
  }
218
236
  }
219
237
  },
@@ -251,6 +269,17 @@ function makeBundleVisitor(
251
269
  };
252
270
  }
253
271
 
272
+ function resolveBundledComponent(node: OasRef, resolved: ResolveResult<any>, ctx: UserContext) {
273
+ const newRefId = makeRefId(ctx.location.source.absoluteRef, node.$ref)
274
+ resolvedRefMap.set(newRefId, {
275
+ document: rootDocument,
276
+ isRemote: false,
277
+ node: resolved.node,
278
+ nodePointer: node.$ref,
279
+ resolved: true,
280
+ });
281
+ }
282
+
254
283
  function replaceRef(ref: OasRef, resolved: ResolveResult<any>, ctx: UserContext) {
255
284
  if (!isPlainObject(resolved.node)) {
256
285
  ctx.parent[ctx.key] = resolved.node;
@@ -1,9 +1,13 @@
1
1
  import recommended from './recommended';
2
2
  import all from './all';
3
3
  import minimal from './minimal';
4
- import { LintRawConfig, Plugin } from './config';
5
-
6
- import * as builtinRules from '../rules/builtin';
4
+ import { CustomRulesConfig, LintRawConfig, Plugin } from './config';
5
+ import { rules as oas3Rules } from '../rules/oas3';
6
+ import { rules as oas2Rules } from '../rules/oas2';
7
+ import { preprocessors as oas3Preprocessors } from '../rules/oas3';
8
+ import { preprocessors as oas2Preprocessors } from '../rules/oas2';
9
+ import { decorators as oas3Decorators } from '../decorators/oas3';
10
+ import { decorators as oas2Decorators } from '../decorators/oas2';
7
11
 
8
12
  export const builtInConfigs: Record<string, LintRawConfig> = {
9
13
  recommended,
@@ -16,8 +20,17 @@ export const builtInConfigs: Record<string, LintRawConfig> = {
16
20
 
17
21
  export const defaultPlugin: Plugin = {
18
22
  id: '', // default plugin doesn't have id
19
- rules: builtinRules.rules,
20
- preprocessors: builtinRules.preprocessors,
21
- decorators: builtinRules.decorators,
23
+ rules: {
24
+ oas3: oas3Rules,
25
+ oas2: oas2Rules,
26
+ } as CustomRulesConfig,
27
+ preprocessors: {
28
+ oas3: oas3Preprocessors,
29
+ oas2: oas2Preprocessors,
30
+ },
31
+ decorators: {
32
+ oas3: oas3Decorators,
33
+ oas2: oas2Decorators,
34
+ },
22
35
  configs: builtInConfigs,
23
- }
36
+ }
@@ -38,10 +38,9 @@ export type PreprocessorConfig =
38
38
  | ProblemSeverity
39
39
  | 'off'
40
40
  | 'on'
41
- | {
41
+ | ({
42
42
  severity?: ProblemSeverity;
43
- options?: Record<string, any>;
44
- };
43
+ } & Record<string, any>);
45
44
 
46
45
  export type DecoratorConfig = PreprocessorConfig;
47
46