@redocly/openapi-core 1.5.0 → 1.7.0
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.
- package/CHANGELOG.md +13 -0
- package/lib/bundle.d.ts +8 -6
- package/lib/bundle.js +48 -14
- package/lib/config/config-resolvers.d.ts +9 -1
- package/lib/config/config-resolvers.js +22 -1
- package/lib/config/load.d.ts +11 -3
- package/lib/config/load.js +12 -6
- package/lib/lint.d.ts +6 -3
- package/lib/lint.js +14 -3
- package/lib/rules/ajv.d.ts +2 -2
- package/lib/rules/ajv.js +2 -2
- package/lib/rules/utils.d.ts +2 -2
- package/lib/types/asyncapi.js +2 -8
- package/lib/types/portal-config-schema.d.ts +22 -2465
- package/lib/types/portal-config-schema.js +56 -38
- package/lib/types/redocly-yaml.js +4 -4
- package/lib/typings/openapi.d.ts +14 -11
- package/lib/visitors.d.ts +5 -5
- package/lib/visitors.js +4 -6
- package/lib/walk.d.ts +3 -3
- package/lib/walk.js +2 -2
- package/package.json +2 -2
- package/src/__tests__/lint.test.ts +2 -2
- package/src/bundle.ts +67 -26
- package/src/config/__tests__/fixtures/resolve-refs-in-config/config-with-refs.yaml +8 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/rules.yaml +2 -0
- package/src/config/__tests__/fixtures/resolve-refs-in-config/seo.yaml +1 -0
- package/src/config/__tests__/load.test.ts +80 -1
- package/src/config/config-resolvers.ts +41 -11
- package/src/config/load.ts +36 -19
- package/src/lint.ts +32 -10
- package/src/rules/ajv.ts +6 -4
- package/src/rules/utils.ts +2 -2
- package/src/types/asyncapi.ts +2 -8
- package/src/types/portal-config-schema.ts +65 -42
- package/src/types/redocly-yaml.ts +2 -4
- package/src/typings/openapi.ts +15 -11
- package/src/visitors.ts +20 -22
- package/src/walk.ts +8 -8
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.rootRedoclyConfigSchema = exports.environmentSchema = exports.redoclyConfigSchema = exports.apiConfigSchema = exports.ssoConfigSchema = void 0;
|
|
3
|
+
exports.rootRedoclyConfigSchema = exports.environmentSchema = exports.redoclyConfigSchema = exports.PortalConfigNodeTypes = exports.apiConfigSchema = exports.ssoConfigSchema = void 0;
|
|
4
4
|
const config_1 = require("../config");
|
|
5
5
|
const oidcIssuerMetadataSchema = {
|
|
6
6
|
type: 'object',
|
|
@@ -80,6 +80,7 @@ const authProviderConfigSchema = {
|
|
|
80
80
|
};
|
|
81
81
|
exports.ssoConfigSchema = {
|
|
82
82
|
type: 'object',
|
|
83
|
+
properties: {},
|
|
83
84
|
additionalProperties: authProviderConfigSchema,
|
|
84
85
|
};
|
|
85
86
|
const redirectConfigSchema = {
|
|
@@ -89,7 +90,12 @@ const redirectConfigSchema = {
|
|
|
89
90
|
type: { type: 'number', default: 301 },
|
|
90
91
|
},
|
|
91
92
|
required: ['to'],
|
|
92
|
-
|
|
93
|
+
};
|
|
94
|
+
const redirectsConfigSchema = {
|
|
95
|
+
type: 'object',
|
|
96
|
+
properties: {},
|
|
97
|
+
additionalProperties: 'redirectConfigSchema',
|
|
98
|
+
default: {},
|
|
93
99
|
};
|
|
94
100
|
exports.apiConfigSchema = {
|
|
95
101
|
type: 'object',
|
|
@@ -136,13 +142,16 @@ const seoConfigSchema = {
|
|
|
136
142
|
},
|
|
137
143
|
},
|
|
138
144
|
},
|
|
139
|
-
additionalProperties: false,
|
|
140
145
|
};
|
|
141
|
-
const rbacScopeItemsSchema = {
|
|
146
|
+
const rbacScopeItemsSchema = {
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {},
|
|
149
|
+
additionalProperties: { type: 'string' },
|
|
150
|
+
};
|
|
142
151
|
const rbacConfigSchema = {
|
|
143
152
|
type: 'object',
|
|
144
153
|
properties: {
|
|
145
|
-
defaults: rbacScopeItemsSchema,
|
|
154
|
+
defaults: 'rbacScopeItemsSchema',
|
|
146
155
|
},
|
|
147
156
|
additionalProperties: rbacScopeItemsSchema,
|
|
148
157
|
};
|
|
@@ -207,7 +216,6 @@ const devOnboardingAdapterConfigSchema = {
|
|
|
207
216
|
const devOnboardingConfigSchema = {
|
|
208
217
|
type: 'object',
|
|
209
218
|
required: ['adapters'],
|
|
210
|
-
additionalProperties: false,
|
|
211
219
|
properties: {
|
|
212
220
|
adapters: {
|
|
213
221
|
type: 'array',
|
|
@@ -215,6 +223,30 @@ const devOnboardingConfigSchema = {
|
|
|
215
223
|
},
|
|
216
224
|
},
|
|
217
225
|
};
|
|
226
|
+
const i18ConfigSchema = {
|
|
227
|
+
type: 'object',
|
|
228
|
+
properties: {
|
|
229
|
+
defaultLocale: {
|
|
230
|
+
type: 'string',
|
|
231
|
+
},
|
|
232
|
+
locales: {
|
|
233
|
+
type: 'array',
|
|
234
|
+
items: {
|
|
235
|
+
type: 'object',
|
|
236
|
+
properties: {
|
|
237
|
+
code: {
|
|
238
|
+
type: 'string',
|
|
239
|
+
},
|
|
240
|
+
name: {
|
|
241
|
+
type: 'string',
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
required: ['code'],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
required: ['defaultLocale', 'locales'],
|
|
249
|
+
};
|
|
218
250
|
const responseHeaderSchema = {
|
|
219
251
|
type: 'object',
|
|
220
252
|
properties: {
|
|
@@ -224,14 +256,24 @@ const responseHeaderSchema = {
|
|
|
224
256
|
additionalProperties: false,
|
|
225
257
|
required: ['name', 'value'],
|
|
226
258
|
};
|
|
259
|
+
exports.PortalConfigNodeTypes = {
|
|
260
|
+
seoConfigSchema,
|
|
261
|
+
rbacConfigSchema,
|
|
262
|
+
rbacScopeItemsSchema,
|
|
263
|
+
ssoConfigSchema: exports.ssoConfigSchema,
|
|
264
|
+
devOnboardingConfigSchema,
|
|
265
|
+
i18ConfigSchema,
|
|
266
|
+
redirectsConfigSchema,
|
|
267
|
+
redirectConfigSchema,
|
|
268
|
+
// TODO: Extract other types that need to be linted in the config
|
|
269
|
+
};
|
|
227
270
|
exports.redoclyConfigSchema = {
|
|
228
271
|
type: 'object',
|
|
229
272
|
properties: {
|
|
230
273
|
licenseKey: { type: 'string' },
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
rbac: rbacConfigSchema,
|
|
274
|
+
redirects: 'redirectsConfigSchema',
|
|
275
|
+
seo: 'seoConfigSchema',
|
|
276
|
+
rbac: 'rbacConfigSchema',
|
|
235
277
|
responseHeaders: {
|
|
236
278
|
type: 'object',
|
|
237
279
|
additionalProperties: {
|
|
@@ -253,37 +295,12 @@ exports.redoclyConfigSchema = {
|
|
|
253
295
|
type: 'object',
|
|
254
296
|
additionalProperties: exports.apiConfigSchema,
|
|
255
297
|
},
|
|
256
|
-
sso:
|
|
257
|
-
developerOnboarding: devOnboardingConfigSchema,
|
|
258
|
-
i18n:
|
|
259
|
-
type: 'object',
|
|
260
|
-
properties: {
|
|
261
|
-
defaultLocale: {
|
|
262
|
-
type: 'string',
|
|
263
|
-
},
|
|
264
|
-
locales: {
|
|
265
|
-
type: 'array',
|
|
266
|
-
items: {
|
|
267
|
-
type: 'object',
|
|
268
|
-
properties: {
|
|
269
|
-
code: {
|
|
270
|
-
type: 'string',
|
|
271
|
-
},
|
|
272
|
-
name: {
|
|
273
|
-
type: 'string',
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
required: ['code'],
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
additionalProperties: false,
|
|
281
|
-
required: ['defaultLocale', 'locales'],
|
|
282
|
-
},
|
|
298
|
+
sso: 'ssoConfigSchema',
|
|
299
|
+
developerOnboarding: 'devOnboardingConfigSchema',
|
|
300
|
+
i18n: 'i18ConfigSchema',
|
|
283
301
|
metadata: metadataConfigSchema,
|
|
284
302
|
},
|
|
285
303
|
default: {},
|
|
286
|
-
additionalProperties: true,
|
|
287
304
|
};
|
|
288
305
|
exports.environmentSchema = {
|
|
289
306
|
oneOf: [
|
|
@@ -300,5 +317,6 @@ exports.environmentSchema = {
|
|
|
300
317
|
};
|
|
301
318
|
exports.rootRedoclyConfigSchema = Object.assign(Object.assign({}, exports.redoclyConfigSchema), { properties: Object.assign(Object.assign({}, exports.redoclyConfigSchema.properties), { env: {
|
|
302
319
|
type: 'object',
|
|
320
|
+
properties: {},
|
|
303
321
|
additionalProperties: exports.environmentSchema,
|
|
304
322
|
} }), default: {}, required: ['redirects'] });
|
|
@@ -4,8 +4,8 @@ exports.ConfigTypes = void 0;
|
|
|
4
4
|
const portal_config_schema_1 = require("./portal-config-schema");
|
|
5
5
|
const theme_config_1 = require("./theme-config");
|
|
6
6
|
const _1 = require(".");
|
|
7
|
-
const oas3_1_1 = require("./oas3_1");
|
|
8
7
|
const utils_1 = require("../utils");
|
|
8
|
+
const portal_config_schema_2 = require("./portal-config-schema");
|
|
9
9
|
const builtInCommonRules = [
|
|
10
10
|
'spec',
|
|
11
11
|
'info-contact',
|
|
@@ -196,7 +196,7 @@ const ConfigApisProperties = {
|
|
|
196
196
|
items: {
|
|
197
197
|
type: 'string',
|
|
198
198
|
},
|
|
199
|
-
}
|
|
199
|
+
} }), ConfigStyleguide.properties), { 'features.openapi': 'ConfigReferenceDocs', 'features.mockServer': 'ConfigMockServer', theme: 'ConfigRootTheme', files: {
|
|
200
200
|
type: 'array',
|
|
201
201
|
items: {
|
|
202
202
|
type: 'string',
|
|
@@ -838,7 +838,7 @@ const ConfigMockServer = {
|
|
|
838
838
|
errorIfForcedExampleNotFound: { type: 'boolean' },
|
|
839
839
|
},
|
|
840
840
|
};
|
|
841
|
-
exports.ConfigTypes = Object.assign(
|
|
841
|
+
exports.ConfigTypes = Object.assign({ Assert,
|
|
842
842
|
ConfigRoot,
|
|
843
843
|
ConfigApis,
|
|
844
844
|
ConfigApisProperties,
|
|
@@ -901,4 +901,4 @@ exports.ConfigTypes = Object.assign(Object.assign({}, oas3_1_1.Oas3_1Types), { A
|
|
|
901
901
|
Heading,
|
|
902
902
|
Typography,
|
|
903
903
|
AssertionDefinitionAssertions,
|
|
904
|
-
AssertionDefinitionSubject });
|
|
904
|
+
AssertionDefinitionSubject }, portal_config_schema_2.PortalConfigNodeTypes);
|
package/lib/typings/openapi.d.ts
CHANGED
|
@@ -106,16 +106,15 @@ export interface Oas3Xml {
|
|
|
106
106
|
attribute?: string;
|
|
107
107
|
wrapped?: string;
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
interface Oas3XSchemaBase<T> {
|
|
110
110
|
$ref?: string;
|
|
111
|
-
type?: string;
|
|
112
111
|
properties?: {
|
|
113
|
-
[name: string]:
|
|
112
|
+
[name: string]: T;
|
|
114
113
|
};
|
|
115
|
-
additionalProperties?: boolean |
|
|
114
|
+
additionalProperties?: boolean | T;
|
|
116
115
|
description?: string;
|
|
117
116
|
default?: any;
|
|
118
|
-
items?:
|
|
117
|
+
items?: T;
|
|
119
118
|
required?: string[];
|
|
120
119
|
readOnly?: boolean;
|
|
121
120
|
writeOnly?: boolean;
|
|
@@ -124,10 +123,10 @@ export interface Oas3Schema {
|
|
|
124
123
|
externalDocs?: Oas3ExternalDocs;
|
|
125
124
|
discriminator?: Oas3Discriminator;
|
|
126
125
|
nullable?: boolean;
|
|
127
|
-
oneOf?:
|
|
128
|
-
anyOf?:
|
|
129
|
-
allOf?:
|
|
130
|
-
not?:
|
|
126
|
+
oneOf?: T[];
|
|
127
|
+
anyOf?: T[];
|
|
128
|
+
allOf?: T[];
|
|
129
|
+
not?: T;
|
|
131
130
|
title?: string;
|
|
132
131
|
multipleOf?: number;
|
|
133
132
|
maximum?: number;
|
|
@@ -147,11 +146,14 @@ export interface Oas3Schema {
|
|
|
147
146
|
xml?: Oas3Xml;
|
|
148
147
|
'x-tags'?: string[];
|
|
149
148
|
}
|
|
150
|
-
export
|
|
149
|
+
export interface Oas3Schema extends Oas3XSchemaBase<Oas3Schema> {
|
|
150
|
+
type?: string;
|
|
151
|
+
}
|
|
152
|
+
export interface Oas3_1Schema extends Oas3XSchemaBase<Oas3_1Schema> {
|
|
151
153
|
type?: string | string[];
|
|
152
154
|
examples?: any[];
|
|
153
155
|
prefixItems?: Oas3_1Schema[];
|
|
154
|
-
}
|
|
156
|
+
}
|
|
155
157
|
export interface Oas3_1Definition extends Oas3Definition {
|
|
156
158
|
webhooks?: Oas3_1Webhooks;
|
|
157
159
|
}
|
|
@@ -298,3 +300,4 @@ export interface Oas3License {
|
|
|
298
300
|
name: string;
|
|
299
301
|
url?: string;
|
|
300
302
|
}
|
|
303
|
+
export {};
|
package/lib/visitors.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { Oas2Definition, Oas2Tag, Oas2ExternalDocs, Oas2SecurityRequirement, Oas2Info, Oas2Contact, Oas2License, Oas2PathItem, Oas2Operation, Oas2Header, Oas2Response, Oas2Schema, Oas2Xml, Oas2Parameter, Oas2SecurityScheme } from './typings/swagger';
|
|
3
|
-
import { NormalizedNodeType } from './types';
|
|
1
|
+
import type { NormalizedNodeType } from './types';
|
|
4
2
|
import type { Stack } from './utils';
|
|
5
3
|
import type { UserContext, ResolveResult, ProblemSeverity } from './walk';
|
|
6
4
|
import type { Location } from './ref-utils';
|
|
7
|
-
import {
|
|
5
|
+
import type { Oas3Definition, Oas3ExternalDocs, Oas3Info, Oas3Contact, Oas3Components, Oas3License, Oas3Schema, Oas3Header, Oas3Parameter, Oas3Operation, Oas3PathItem, Oas3ServerVariable, Oas3Server, Oas3MediaType, Oas3Response, Oas3Example, Oas3RequestBody, Oas3Tag, OasRef, Oas3SecurityScheme, Oas3SecurityRequirement, Oas3Encoding, Oas3Link, Oas3Xml, Oas3Discriminator, Oas3Callback } from './typings/openapi';
|
|
6
|
+
import type { Oas2Definition, Oas2Tag, Oas2ExternalDocs, Oas2SecurityRequirement, Oas2Info, Oas2Contact, Oas2License, Oas2PathItem, Oas2Operation, Oas2Header, Oas2Response, Oas2Schema, Oas2Xml, Oas2Parameter, Oas2SecurityScheme } from './typings/swagger';
|
|
7
|
+
import type { Async2Definition } from './typings/asyncapi';
|
|
8
8
|
export type SkipFunctionContext = Pick<UserContext, 'location' | 'rawNode' | 'resolve' | 'rawLocation'>;
|
|
9
9
|
export type VisitFunction<T> = (node: T, ctx: UserContext & {
|
|
10
10
|
ignoreNextVisitorsOnNode: () => void;
|
|
@@ -184,6 +184,6 @@ export type RuleInstanceConfig = {
|
|
|
184
184
|
severity: ProblemSeverity;
|
|
185
185
|
};
|
|
186
186
|
export declare function normalizeVisitors<T extends BaseVisitor>(visitorsConfig: (RuleInstanceConfig & {
|
|
187
|
-
visitor: NestedVisitObject<
|
|
187
|
+
visitor: NestedVisitObject<unknown, T>;
|
|
188
188
|
})[], types: Record<keyof T, NormalizedNodeType>): NormalizedOasVisitors<T>;
|
|
189
189
|
export {};
|
package/lib/visitors.js
CHANGED
|
@@ -77,12 +77,10 @@ function normalizeVisitors(visitorsConfig, types) {
|
|
|
77
77
|
}
|
|
78
78
|
function addWeakFromStack(ruleConf, stack) {
|
|
79
79
|
for (const interType of stack.slice(1)) {
|
|
80
|
-
normalizedVisitors[interType.name] =
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
leave: [],
|
|
85
|
-
};
|
|
80
|
+
normalizedVisitors[interType.name] = normalizedVisitors[interType.name] || {
|
|
81
|
+
enter: [],
|
|
82
|
+
leave: [],
|
|
83
|
+
};
|
|
86
84
|
normalizedVisitors[interType.name].enter.push(Object.assign(Object.assign({}, ruleConf), { visit: () => undefined, depth: 0, context: {
|
|
87
85
|
isSkippedLevel: true,
|
|
88
86
|
seen: new Set(),
|
package/lib/walk.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { Location } from './ref-utils';
|
|
2
|
+
import { ResolveError, YamlParseError, Source } from './resolve';
|
|
3
|
+
import { SpecVersion } from './oas-types';
|
|
1
4
|
import type { Referenced } from './typings/openapi';
|
|
2
5
|
import type { NormalizedOasVisitors, BaseVisitor } from './visitors';
|
|
3
6
|
import type { ResolvedRefMap, Document } from './resolve';
|
|
4
7
|
import type { NormalizedNodeType } from './types';
|
|
5
8
|
import type { RuleSeverity } from './config';
|
|
6
|
-
import { Location } from './ref-utils';
|
|
7
|
-
import { ResolveError, YamlParseError, Source } from './resolve';
|
|
8
|
-
import { SpecVersion } from './oas-types';
|
|
9
9
|
export type NonUndefined = string | number | boolean | symbol | bigint | object | Record<string, any>;
|
|
10
10
|
export type ResolveResult<T extends NonUndefined> = {
|
|
11
11
|
node: T;
|
package/lib/walk.js
CHANGED
|
@@ -86,7 +86,7 @@ function walkDocument(opts) {
|
|
|
86
86
|
const currentEnterVisitors = anyEnterVisitors.concat(((_c = normalizedVisitors[type.name]) === null || _c === void 0 ? void 0 : _c.enter) || []);
|
|
87
87
|
const activatedContexts = [];
|
|
88
88
|
for (const { context, visit, skip, ruleId, severity } of currentEnterVisitors) {
|
|
89
|
-
if (ignoredNodes.has(currentLocation.pointer))
|
|
89
|
+
if (ignoredNodes.has(`${currentLocation.absolutePointer}${currentLocation.pointer}`))
|
|
90
90
|
break;
|
|
91
91
|
if (context.isSkippedLevel) {
|
|
92
92
|
if (context.parent.activatedOn &&
|
|
@@ -248,7 +248,7 @@ function walkDocument(opts) {
|
|
|
248
248
|
parentLocations: collectParentsLocations(context),
|
|
249
249
|
oasVersion: ctx.oasVersion,
|
|
250
250
|
ignoreNextVisitorsOnNode: () => {
|
|
251
|
-
ignoredNodes.add(currentLocation.pointer);
|
|
251
|
+
ignoredNodes.add(`${currentLocation.absolutePointer}${currentLocation.pointer}`);
|
|
252
252
|
},
|
|
253
253
|
getVisitorData: getVisitorDataFn.bind(undefined, ruleId),
|
|
254
254
|
}, collectParents(context), context);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/openapi-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"engines": {
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@redocly/ajv": "^8.11.0",
|
|
38
|
-
"@types/node": "^14.11.8",
|
|
39
38
|
"colorette": "^1.2.0",
|
|
40
39
|
"js-levenshtein": "^1.1.6",
|
|
41
40
|
"js-yaml": "^4.1.0",
|
|
@@ -50,6 +49,7 @@
|
|
|
50
49
|
"@types/js-yaml": "^4.0.3",
|
|
51
50
|
"@types/lodash.isequal": "^4.5.5",
|
|
52
51
|
"@types/minimatch": "^3.0.5",
|
|
52
|
+
"@types/node": "^20.11.5",
|
|
53
53
|
"@types/node-fetch": "^2.5.7",
|
|
54
54
|
"@types/pluralize": "^0.0.29",
|
|
55
55
|
"typescript": "^5.2.2"
|
|
@@ -22,7 +22,7 @@ describe('lint', () => {
|
|
|
22
22
|
servers:
|
|
23
23
|
- url: http://redocly-example.com
|
|
24
24
|
paths: {}
|
|
25
|
-
|
|
25
|
+
`,
|
|
26
26
|
config: await loadConfig(),
|
|
27
27
|
});
|
|
28
28
|
|
|
@@ -139,8 +139,8 @@ describe('lint', () => {
|
|
|
139
139
|
"ruleId": "configuration spec",
|
|
140
140
|
"severity": "error",
|
|
141
141
|
"suggest": [
|
|
142
|
-
"theme",
|
|
143
142
|
"env",
|
|
143
|
+
"theme",
|
|
144
144
|
"seo",
|
|
145
145
|
"sso",
|
|
146
146
|
],
|
package/src/bundle.ts
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import isEqual = require('lodash.isequal');
|
|
2
|
+
import { BaseResolver, resolveDocument, makeRefId, makeDocumentFromString } from './resolve';
|
|
3
|
+
import { normalizeVisitors } from './visitors';
|
|
4
|
+
import { normalizeTypes } from './types';
|
|
5
|
+
import { walkDocument } from './walk';
|
|
2
6
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from './resolve';
|
|
10
|
-
import { Oas3Rule, normalizeVisitors, Oas3Visitor, Oas2Visitor } from './visitors';
|
|
11
|
-
import { NormalizedNodeType, normalizeTypes, NodeType } from './types';
|
|
12
|
-
import { WalkContext, walkDocument, UserContext, ResolveResult, NormalizedProblem } from './walk';
|
|
13
|
-
import { detectSpec, getTypes, getMajorSpecVersion, SpecMajorVersion } from './oas-types';
|
|
7
|
+
detectSpec,
|
|
8
|
+
getTypes,
|
|
9
|
+
getMajorSpecVersion,
|
|
10
|
+
SpecMajorVersion,
|
|
11
|
+
SpecVersion,
|
|
12
|
+
} from './oas-types';
|
|
14
13
|
import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
|
|
15
14
|
import { initRules } from './config/rules';
|
|
16
15
|
import { reportUnresolvedRef } from './rules/no-unresolved-refs';
|
|
17
16
|
import { isPlainObject, isTruthy } from './utils';
|
|
18
|
-
import { OasRef } from './typings/openapi';
|
|
19
17
|
import { isRedoclyRegistryURL } from './redocly';
|
|
20
18
|
import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './decorators/oas2/remove-unused-components';
|
|
21
19
|
import { RemoveUnusedComponents as RemoveUnusedComponentsOas3 } from './decorators/oas3/remove-unused-components';
|
|
20
|
+
import { ConfigTypes } from './types/redocly-yaml';
|
|
22
21
|
|
|
22
|
+
import type { Oas3Rule, Oas3Visitor, Oas2Visitor } from './visitors';
|
|
23
|
+
import type { NormalizedNodeType, NodeType } from './types';
|
|
24
|
+
import type { WalkContext, UserContext, ResolveResult, NormalizedProblem } from './walk';
|
|
23
25
|
import type { Config, StyleguideConfig } from './config';
|
|
26
|
+
import type { OasRef } from './typings/openapi';
|
|
27
|
+
import type { Document, ResolvedRefMap } from './resolve';
|
|
24
28
|
|
|
25
29
|
export type Oas3RuleSet = Record<string, Oas3Rule>;
|
|
26
30
|
|
|
@@ -33,12 +37,50 @@ export type BundleOptions = {
|
|
|
33
37
|
externalRefResolver?: BaseResolver;
|
|
34
38
|
config: Config;
|
|
35
39
|
dereference?: boolean;
|
|
36
|
-
base?: string;
|
|
40
|
+
base?: string | null;
|
|
37
41
|
skipRedoclyRegistryRefs?: boolean;
|
|
38
42
|
removeUnusedComponents?: boolean;
|
|
39
43
|
keepUrlRefs?: boolean;
|
|
40
44
|
};
|
|
41
45
|
|
|
46
|
+
export async function bundleConfig(document: Document, resolvedRefMap: ResolvedRefMap) {
|
|
47
|
+
const types = normalizeTypes(ConfigTypes);
|
|
48
|
+
|
|
49
|
+
const ctx: BundleContext = {
|
|
50
|
+
problems: [],
|
|
51
|
+
oasVersion: SpecVersion.OAS3_0,
|
|
52
|
+
refTypes: new Map<string, NormalizedNodeType>(),
|
|
53
|
+
visitorsData: {},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const bundleVisitor = normalizeVisitors(
|
|
57
|
+
[
|
|
58
|
+
{
|
|
59
|
+
severity: 'error',
|
|
60
|
+
ruleId: 'configBundler',
|
|
61
|
+
visitor: {
|
|
62
|
+
ref: {
|
|
63
|
+
leave(node: OasRef, ctx: UserContext, resolved: ResolveResult<any>) {
|
|
64
|
+
replaceRef(node, resolved, ctx);
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
types
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
walkDocument({
|
|
74
|
+
document,
|
|
75
|
+
rootType: types.ConfigRoot,
|
|
76
|
+
normalizedVisitors: bundleVisitor,
|
|
77
|
+
resolvedRefMap,
|
|
78
|
+
ctx,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return document.parsed ?? {};
|
|
82
|
+
}
|
|
83
|
+
|
|
42
84
|
export async function bundle(
|
|
43
85
|
opts: {
|
|
44
86
|
ref?: string;
|
|
@@ -254,6 +296,18 @@ export function mapTypeToComponent(typeName: string, version: SpecMajorVersion)
|
|
|
254
296
|
}
|
|
255
297
|
}
|
|
256
298
|
|
|
299
|
+
function replaceRef(ref: OasRef, resolved: ResolveResult<any>, ctx: UserContext) {
|
|
300
|
+
if (!isPlainObject(resolved.node)) {
|
|
301
|
+
ctx.parent[ctx.key] = resolved.node;
|
|
302
|
+
} else {
|
|
303
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
304
|
+
// @ts-ignore
|
|
305
|
+
delete ref.$ref;
|
|
306
|
+
const obj = Object.assign({}, resolved.node, ref);
|
|
307
|
+
Object.assign(ref, obj); // assign ref itself again so ref fields take precedence
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
257
311
|
// function oas3Move
|
|
258
312
|
|
|
259
313
|
function makeBundleVisitor(
|
|
@@ -351,19 +405,6 @@ function makeBundleVisitor(
|
|
|
351
405
|
});
|
|
352
406
|
}
|
|
353
407
|
|
|
354
|
-
function replaceRef(ref: OasRef, resolved: ResolveResult<any>, ctx: UserContext) {
|
|
355
|
-
if (!isPlainObject(resolved.node)) {
|
|
356
|
-
ctx.parent[ctx.key] = resolved.node;
|
|
357
|
-
} else {
|
|
358
|
-
// TODO: why $ref isn't optional in OasRef?
|
|
359
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
360
|
-
// @ts-ignore
|
|
361
|
-
delete ref.$ref;
|
|
362
|
-
const obj = Object.assign({}, resolved.node, ref);
|
|
363
|
-
Object.assign(ref, obj); // assign ref itself again so ref fields take precedence
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
408
|
function saveComponent(
|
|
368
409
|
componentType: string,
|
|
369
410
|
target: { node: any; location: Location },
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
title: 1 # fail
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { loadConfig, findConfig, getConfig, createConfig } from '../load';
|
|
2
2
|
import { RedoclyClient } from '../../redocly';
|
|
3
|
-
import { RuleConfig, FlatRawConfig } from './../types';
|
|
4
3
|
import { Config } from '../config';
|
|
4
|
+
import { lintConfig } from '../../lint';
|
|
5
|
+
import { replaceSourceWithRef } from '../../../__tests__/utils';
|
|
6
|
+
import type { RuleConfig, FlatRawConfig } from './../types';
|
|
7
|
+
import type { NormalizedProblem } from '../../walk';
|
|
5
8
|
|
|
6
9
|
const fs = require('fs');
|
|
7
10
|
const path = require('path');
|
|
@@ -91,6 +94,82 @@ describe('getConfig', () => {
|
|
|
91
94
|
it('should return empty object if there is no configPath and config file is not found', () => {
|
|
92
95
|
expect(getConfig()).toEqual(Promise.resolve({}));
|
|
93
96
|
});
|
|
97
|
+
|
|
98
|
+
it('should resolve refs in config', async () => {
|
|
99
|
+
let problems: NormalizedProblem[];
|
|
100
|
+
const result = await getConfig({
|
|
101
|
+
configPath: path.join(__dirname, './fixtures/resolve-refs-in-config/config-with-refs.yaml'),
|
|
102
|
+
processRawConfig: async (config, resolvedRefMap) => {
|
|
103
|
+
problems = await lintConfig({
|
|
104
|
+
document: config,
|
|
105
|
+
severity: 'warn',
|
|
106
|
+
resolvedRefMap,
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
expect(result).toEqual({
|
|
111
|
+
seo: {
|
|
112
|
+
title: 1,
|
|
113
|
+
},
|
|
114
|
+
styleguide: {
|
|
115
|
+
rules: {
|
|
116
|
+
'info-license': 'error',
|
|
117
|
+
'non-existing-rule': 'warn',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
expect(replaceSourceWithRef(problems!, __dirname)).toMatchInlineSnapshot(`
|
|
122
|
+
[
|
|
123
|
+
{
|
|
124
|
+
"from": {
|
|
125
|
+
"pointer": "#/seo",
|
|
126
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
127
|
+
},
|
|
128
|
+
"location": [
|
|
129
|
+
{
|
|
130
|
+
"pointer": "#/title",
|
|
131
|
+
"reportOnKey": false,
|
|
132
|
+
"source": "fixtures/resolve-refs-in-config/seo.yaml",
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
"message": "Expected type \`string\` but got \`integer\`.",
|
|
136
|
+
"ruleId": "configuration spec",
|
|
137
|
+
"severity": "warn",
|
|
138
|
+
"suggest": [],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"from": {
|
|
142
|
+
"pointer": "#/rules",
|
|
143
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
144
|
+
},
|
|
145
|
+
"location": [
|
|
146
|
+
{
|
|
147
|
+
"pointer": "#/non-existing-rule",
|
|
148
|
+
"reportOnKey": true,
|
|
149
|
+
"source": "fixtures/resolve-refs-in-config/rules.yaml",
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
"message": "Property \`non-existing-rule\` is not expected here.",
|
|
153
|
+
"ruleId": "configuration spec",
|
|
154
|
+
"severity": "warn",
|
|
155
|
+
"suggest": [],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"location": [
|
|
159
|
+
{
|
|
160
|
+
"pointer": "#/theme",
|
|
161
|
+
"reportOnKey": false,
|
|
162
|
+
"source": "fixtures/resolve-refs-in-config/config-with-refs.yaml",
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
"message": "Can't resolve $ref: ENOENT: no such file or directory 'fixtures/resolve-refs-in-config/wrong-ref.yaml'",
|
|
166
|
+
"ruleId": "configuration no-unresolved-refs",
|
|
167
|
+
"severity": "warn",
|
|
168
|
+
"suggest": [],
|
|
169
|
+
},
|
|
170
|
+
]
|
|
171
|
+
`);
|
|
172
|
+
});
|
|
94
173
|
});
|
|
95
174
|
|
|
96
175
|
describe('createConfig', () => {
|