@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.
- package/lib/benchmark/benches/lint-with-top-level-rule-report.bench.js +0 -1
- package/lib/bundle.js +5 -2
- package/lib/config/all.js +2 -2
- package/lib/config/config-resolvers.js +31 -13
- package/lib/config/config.d.ts +3 -5
- package/lib/config/config.js +7 -4
- package/lib/config/load.d.ts +7 -0
- package/lib/config/load.js +14 -6
- package/lib/config/minimal.js +2 -2
- package/lib/config/recommended.js +2 -2
- package/lib/config/rules.d.ts +1 -1
- package/lib/config/utils.js +5 -5
- package/lib/decorators/common/registry-dependencies.js +1 -1
- package/lib/env.d.ts +3 -0
- package/lib/env.js +8 -0
- package/lib/format/codeframes.js +16 -10
- package/lib/format/format.js +28 -26
- package/lib/index.d.ts +5 -5
- package/lib/index.js +3 -1
- package/lib/js-yaml/index.js +1 -0
- package/lib/logger.d.ts +10 -0
- package/lib/logger.js +31 -0
- package/lib/output.d.ts +3 -0
- package/lib/output.js +9 -0
- package/lib/redocly/index.js +10 -9
- package/lib/redocly/registry-api-types.d.ts +28 -30
- package/lib/redocly/registry-api.d.ts +4 -3
- package/lib/redocly/registry-api.js +7 -2
- package/lib/ref-utils.js +2 -1
- package/lib/resolve.d.ts +1 -1
- package/lib/resolve.js +1 -1
- package/lib/rules/ajv.js +1 -1
- package/lib/rules/common/assertions/asserts.js +4 -4
- package/lib/rules/common/assertions/index.js +1 -1
- package/lib/rules/common/operation-security-defined.js +1 -1
- package/lib/rules/common/spec.js +2 -2
- package/lib/rules/oas2/remove-unused-components.js +2 -2
- package/lib/rules/oas3/index.js +2 -2
- package/lib/rules/oas3/no-server-variables-empty-enum.d.ts +2 -0
- package/lib/rules/oas3/{no-servers-empty-enum.js → no-server-variables-empty-enum.js} +4 -4
- package/lib/rules/oas3/no-unused-components.js +1 -1
- package/lib/rules/oas3/remove-unused-components.js +3 -3
- package/lib/rules/utils.d.ts +1 -1
- package/lib/rules/utils.js +1 -1
- package/lib/types/redocly-yaml.js +1 -1
- package/lib/utils.d.ts +3 -0
- package/lib/utils.js +15 -7
- package/lib/visitors.d.ts +1 -1
- package/lib/visitors.js +2 -2
- package/package.json +1 -1
- package/src/__tests__/logger-browser.test.ts +53 -0
- package/src/__tests__/logger.test.ts +47 -0
- package/src/__tests__/output-browser.test.ts +18 -0
- package/src/__tests__/output.test.ts +15 -0
- package/src/__tests__/utils-browser.test.ts +11 -0
- package/src/__tests__/utils.test.ts +7 -0
- package/src/benchmark/benches/lint-with-top-level-rule-report.bench.ts +0 -1
- package/src/bundle.ts +6 -3
- package/src/config/__tests__/__snapshots__/config-resolvers.test.ts.snap +4 -4
- package/src/config/__tests__/config.test.ts +35 -0
- package/src/config/__tests__/load.test.ts +79 -1
- package/src/config/all.ts +2 -2
- package/src/config/config-resolvers.ts +43 -17
- package/src/config/config.ts +10 -8
- package/src/config/load.ts +28 -5
- package/src/config/minimal.ts +2 -2
- package/src/config/recommended.ts +2 -2
- package/src/config/utils.ts +6 -5
- package/src/decorators/common/registry-dependencies.ts +1 -1
- package/src/env.ts +5 -0
- package/src/format/codeframes.ts +15 -9
- package/src/format/format.ts +28 -33
- package/src/index.ts +6 -4
- package/src/js-yaml/index.ts +1 -0
- package/src/logger.ts +34 -0
- package/src/output.ts +7 -0
- package/src/redocly/index.ts +7 -4
- package/src/redocly/registry-api-types.ts +27 -29
- package/src/redocly/registry-api.ts +16 -6
- package/src/ref-utils.ts +2 -1
- package/src/resolve.ts +4 -4
- package/src/rules/ajv.ts +1 -1
- package/src/rules/common/assertions/asserts.ts +4 -4
- package/src/rules/common/assertions/index.ts +1 -1
- package/src/rules/common/operation-security-defined.ts +1 -1
- package/src/rules/common/spec.ts +2 -2
- package/src/rules/oas2/remove-unused-components.ts +2 -2
- package/src/rules/oas3/__tests__/no-empty-enum-servers.com.test.ts +16 -16
- package/src/rules/oas3/index.ts +2 -2
- package/src/rules/oas3/{no-servers-empty-enum.ts → no-server-variables-empty-enum.ts} +2 -2
- package/src/rules/oas3/no-unused-components.ts +1 -1
- package/src/rules/oas3/remove-unused-components.ts +4 -4
- package/src/rules/utils.ts +1 -1
- package/src/types/redocly-yaml.ts +1 -1
- package/src/utils.ts +16 -6
- package/src/visitors.ts +5 -5
- package/tsconfig.tsbuildinfo +1 -1
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)) {
|
package/lib/rules/utils.d.ts
CHANGED
|
@@ -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" | "
|
|
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
|
*
|
package/lib/rules/utils.js
CHANGED
|
@@ -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 (
|
|
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' }),
|
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
|
|
20
|
-
const
|
|
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 ?
|
|
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(
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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 (
|
|
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
|
@@ -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
|
+
});
|
|
@@ -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
|
});
|
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(
|
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
63
|
+
'no-server-variables-empty-enum': 'error',
|
|
64
64
|
},
|
|
65
65
|
} as PluginStyleguideConfig;
|