@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
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import { blue, red } from 'colorette';
|
|
3
2
|
import { isAbsoluteUrl } from '../ref-utils';
|
|
4
3
|
import { BaseResolver } from '../resolve';
|
|
5
4
|
import { defaultPlugin } from './builtIn';
|
|
@@ -21,8 +20,10 @@ import type {
|
|
|
21
20
|
RuleConfig,
|
|
22
21
|
DeprecatedInRawConfig,
|
|
23
22
|
} from './types';
|
|
23
|
+
import { isBrowser } from '../env';
|
|
24
24
|
import { isNotString, isString, notUndefined, parseYaml } from '../utils';
|
|
25
25
|
import { Config } from './config';
|
|
26
|
+
import { colorize, logger } from '../logger';
|
|
26
27
|
|
|
27
28
|
export async function resolveConfig(rawConfig: RawConfig, configPath?: string): Promise<Config> {
|
|
28
29
|
if (rawConfig.styleguide?.extends?.some(isNotString)) {
|
|
@@ -71,34 +72,57 @@ export function resolvePlugins(
|
|
|
71
72
|
): Plugin[] {
|
|
72
73
|
if (!plugins) return [];
|
|
73
74
|
|
|
74
|
-
//
|
|
75
|
-
const requireFunc =
|
|
75
|
+
// TODO: implement or reuse Resolver approach so it will work in node and browser envs
|
|
76
|
+
const requireFunc = (plugin: string | Plugin): Plugin | undefined => {
|
|
77
|
+
if (isBrowser && isString(plugin)) {
|
|
78
|
+
logger.error(`Cannot load ${plugin}. Plugins aren't supported in browser yet.`);
|
|
79
|
+
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isString(plugin)) {
|
|
84
|
+
const absoltePluginPath = path.resolve(path.dirname(configPath), plugin);
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
return typeof __webpack_require__ === 'function'
|
|
88
|
+
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
89
|
+
// @ts-ignore
|
|
90
|
+
__non_webpack_require__(absoltePluginPath)
|
|
91
|
+
: require(absoltePluginPath);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return plugin;
|
|
95
|
+
};
|
|
76
96
|
|
|
77
97
|
const seenPluginIds = new Map<string, string>();
|
|
78
98
|
|
|
79
99
|
return plugins
|
|
80
100
|
.map((p) => {
|
|
81
101
|
if (isString(p) && isAbsoluteUrl(p)) {
|
|
82
|
-
throw new Error(red(`We don't support remote plugins yet.`));
|
|
102
|
+
throw new Error(colorize.red(`We don't support remote plugins yet.`));
|
|
83
103
|
}
|
|
84
104
|
|
|
85
105
|
// TODO: resolve npm packages similar to eslint
|
|
86
|
-
const pluginModule =
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
const pluginModule = requireFunc(p);
|
|
107
|
+
|
|
108
|
+
if (!pluginModule) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
89
111
|
|
|
90
112
|
const id = pluginModule.id;
|
|
91
113
|
if (typeof id !== 'string') {
|
|
92
|
-
throw new Error(
|
|
114
|
+
throw new Error(
|
|
115
|
+
colorize.red(`Plugin must define \`id\` property in ${colorize.blue(p.toString())}.`)
|
|
116
|
+
);
|
|
93
117
|
}
|
|
94
118
|
|
|
95
119
|
if (seenPluginIds.has(id)) {
|
|
96
120
|
const pluginPath = seenPluginIds.get(id)!;
|
|
97
121
|
throw new Error(
|
|
98
|
-
red(
|
|
99
|
-
`Plugin "id" must be unique. Plugin ${
|
|
100
|
-
|
|
101
|
-
)}" already seen in ${blue(pluginPath)}`
|
|
122
|
+
colorize.red(
|
|
123
|
+
`Plugin "id" must be unique. Plugin ${colorize.blue(
|
|
124
|
+
p.toString()
|
|
125
|
+
)} uses id "${colorize.blue(id)}" already seen in ${colorize.blue(pluginPath)}`
|
|
102
126
|
)
|
|
103
127
|
);
|
|
104
128
|
}
|
|
@@ -166,7 +190,7 @@ export async function resolveApis({
|
|
|
166
190
|
resolver?: BaseResolver;
|
|
167
191
|
}): Promise<Record<string, ResolvedApi>> {
|
|
168
192
|
const { apis = {}, styleguide: styleguideConfig = {} } = rawConfig;
|
|
169
|
-
|
|
193
|
+
const resolvedApis: Record<string, ResolvedApi> = {};
|
|
170
194
|
for (const [apiName, apiContent] of Object.entries(apis || {})) {
|
|
171
195
|
if (apiContent.styleguide?.extends?.some(isNotString)) {
|
|
172
196
|
throw new Error(
|
|
@@ -284,17 +308,19 @@ export function resolvePreset(presetName: string, plugins: Plugin[]): ResolvedSt
|
|
|
284
308
|
const { pluginId, configName } = parsePresetName(presetName);
|
|
285
309
|
const plugin = plugins.find((p) => p.id === pluginId);
|
|
286
310
|
if (!plugin) {
|
|
287
|
-
throw new Error(
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Invalid config ${colorize.red(presetName)}: plugin ${pluginId} is not included.`
|
|
313
|
+
);
|
|
288
314
|
}
|
|
289
315
|
|
|
290
|
-
const preset = plugin.configs?.[configName]
|
|
316
|
+
const preset = plugin.configs?.[configName];
|
|
291
317
|
if (!preset) {
|
|
292
318
|
throw new Error(
|
|
293
319
|
pluginId
|
|
294
|
-
? `Invalid config ${red(
|
|
320
|
+
? `Invalid config ${colorize.red(
|
|
295
321
|
presetName
|
|
296
322
|
)}: plugin ${pluginId} doesn't export config with name ${configName}.`
|
|
297
|
-
: `Invalid config ${red(presetName)}: there is no such built-in config.`
|
|
323
|
+
: `Invalid config ${colorize.red(presetName)}: there is no such built-in config.`
|
|
298
324
|
);
|
|
299
325
|
}
|
|
300
326
|
return preset;
|
package/src/config/config.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { parseYaml, stringifyYaml } from '../js-yaml';
|
|
|
4
4
|
import { slash, doesYamlFileExist } from '../utils';
|
|
5
5
|
import { NormalizedProblem } from '../walk';
|
|
6
6
|
import { OasVersion, OasMajorVersion, Oas2RuleSet, Oas3RuleSet } from '../oas-types';
|
|
7
|
+
import { env } from '../env';
|
|
7
8
|
|
|
8
9
|
import type { NodeType } from '../types';
|
|
9
10
|
import type {
|
|
@@ -19,9 +20,6 @@ import type {
|
|
|
19
20
|
} from './types';
|
|
20
21
|
import { getResolveConfig } from './utils';
|
|
21
22
|
|
|
22
|
-
// Alias environment here so this file can work in browser environments too.
|
|
23
|
-
export const env = typeof process !== 'undefined' ? process.env || {} : {};
|
|
24
|
-
|
|
25
23
|
export const IGNORE_FILE = '.redocly.lint-ignore.yaml';
|
|
26
24
|
const IGNORE_BANNER =
|
|
27
25
|
`# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.\n` +
|
|
@@ -172,9 +170,11 @@ export class StyleguideConfig {
|
|
|
172
170
|
case OasVersion.Version3_1:
|
|
173
171
|
if (!plugin.typeExtension.oas3) continue;
|
|
174
172
|
extendedTypes = plugin.typeExtension.oas3(extendedTypes, version);
|
|
173
|
+
break;
|
|
175
174
|
case OasVersion.Version2:
|
|
176
175
|
if (!plugin.typeExtension.oas2) continue;
|
|
177
176
|
extendedTypes = plugin.typeExtension.oas2(extendedTypes, version);
|
|
177
|
+
break;
|
|
178
178
|
default:
|
|
179
179
|
throw new Error('Not implemented');
|
|
180
180
|
}
|
|
@@ -192,7 +192,7 @@ export class StyleguideConfig {
|
|
|
192
192
|
severity: settings,
|
|
193
193
|
};
|
|
194
194
|
} else {
|
|
195
|
-
return { severity: 'error'
|
|
195
|
+
return { severity: 'error', ...settings };
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
|
|
@@ -203,10 +203,10 @@ export class StyleguideConfig {
|
|
|
203
203
|
const settings = this.preprocessors[oasVersion][ruleId] || 'off';
|
|
204
204
|
if (typeof settings === 'string') {
|
|
205
205
|
return {
|
|
206
|
-
severity: settings === 'on' ?
|
|
206
|
+
severity: settings === 'on' ? 'error' : settings,
|
|
207
207
|
};
|
|
208
208
|
} else {
|
|
209
|
-
return { severity: 'error'
|
|
209
|
+
return { severity: 'error', ...settings };
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -216,10 +216,10 @@ export class StyleguideConfig {
|
|
|
216
216
|
const settings = this.decorators[oasVersion][ruleId] || 'off';
|
|
217
217
|
if (typeof settings === 'string') {
|
|
218
218
|
return {
|
|
219
|
-
severity: settings === 'on' ?
|
|
219
|
+
severity: settings === 'on' ? 'error' : settings,
|
|
220
220
|
};
|
|
221
221
|
} else {
|
|
222
|
-
return { severity: 'error'
|
|
222
|
+
return { severity: 'error', ...settings };
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -250,12 +250,14 @@ export class StyleguideConfig {
|
|
|
250
250
|
getRulesForOasVersion(version: OasMajorVersion) {
|
|
251
251
|
switch (version) {
|
|
252
252
|
case OasMajorVersion.Version3:
|
|
253
|
+
// eslint-disable-next-line no-case-declarations
|
|
253
254
|
const oas3Rules: Oas3RuleSet[] = []; // default ruleset
|
|
254
255
|
this.plugins.forEach((p) => p.preprocessors?.oas3 && oas3Rules.push(p.preprocessors.oas3));
|
|
255
256
|
this.plugins.forEach((p) => p.rules?.oas3 && oas3Rules.push(p.rules.oas3));
|
|
256
257
|
this.plugins.forEach((p) => p.decorators?.oas3 && oas3Rules.push(p.decorators.oas3));
|
|
257
258
|
return oas3Rules;
|
|
258
259
|
case OasMajorVersion.Version2:
|
|
260
|
+
// eslint-disable-next-line no-case-declarations
|
|
259
261
|
const oas2Rules: Oas2RuleSet[] = []; // default ruleset
|
|
260
262
|
this.plugins.forEach((p) => p.preprocessors?.oas2 && oas2Rules.push(p.preprocessors.oas2));
|
|
261
263
|
this.plugins.forEach((p) => p.rules?.oas2 && oas2Rules.push(p.rules.oas2));
|
package/src/config/load.ts
CHANGED
|
@@ -2,20 +2,24 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { RedoclyClient } from '../redocly';
|
|
4
4
|
import { isEmptyObject, loadYaml, doesYamlFileExist } from '../utils';
|
|
5
|
+
import { parseYaml } from '../js-yaml';
|
|
5
6
|
import { Config, DOMAINS } from './config';
|
|
6
7
|
import { transformConfig } from './utils';
|
|
7
8
|
import { resolveConfig } from './config-resolvers';
|
|
8
9
|
|
|
9
10
|
import type { DeprecatedInRawConfig, RawConfig, Region } from './types';
|
|
11
|
+
import { RegionalTokenWithValidity } from '../redocly/redocly-client-types';
|
|
10
12
|
|
|
11
13
|
async function addConfigMetadata({
|
|
12
14
|
rawConfig,
|
|
13
15
|
customExtends,
|
|
14
16
|
configPath,
|
|
17
|
+
tokens,
|
|
15
18
|
}: {
|
|
16
19
|
rawConfig: RawConfig;
|
|
17
20
|
customExtends?: string[];
|
|
18
21
|
configPath?: string;
|
|
22
|
+
tokens?: RegionalTokenWithValidity[];
|
|
19
23
|
}): Promise<Config> {
|
|
20
24
|
if (customExtends !== undefined) {
|
|
21
25
|
rawConfig.styleguide = rawConfig.styleguide || {};
|
|
@@ -25,10 +29,7 @@ async function addConfigMetadata({
|
|
|
25
29
|
// rawConfig.styleguide = { extends: ['recommended'], recommendedFallback: true };
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
const tokens = await redoclyClient.getTokens();
|
|
30
|
-
|
|
31
|
-
if (tokens.length) {
|
|
32
|
+
if (tokens?.length) {
|
|
32
33
|
if (!rawConfig.resolve) rawConfig.resolve = {};
|
|
33
34
|
if (!rawConfig.resolve.http) rawConfig.resolve.http = {};
|
|
34
35
|
rawConfig.resolve.http.headers = [...(rawConfig.resolve.http.headers ?? [])];
|
|
@@ -69,10 +70,15 @@ export async function loadConfig(
|
|
|
69
70
|
if (typeof processRawConfig === 'function') {
|
|
70
71
|
await processRawConfig(rawConfig);
|
|
71
72
|
}
|
|
72
|
-
|
|
73
|
+
|
|
74
|
+
const redoclyClient = new RedoclyClient();
|
|
75
|
+
const tokens = await redoclyClient.getTokens();
|
|
76
|
+
|
|
77
|
+
return addConfigMetadata({
|
|
73
78
|
rawConfig,
|
|
74
79
|
customExtends,
|
|
75
80
|
configPath,
|
|
81
|
+
tokens,
|
|
76
82
|
});
|
|
77
83
|
}
|
|
78
84
|
|
|
@@ -102,3 +108,20 @@ export async function getConfig(configPath: string | undefined = findConfig()):
|
|
|
102
108
|
throw new Error(`Error parsing config file at '${configPath}': ${e.message}`);
|
|
103
109
|
}
|
|
104
110
|
}
|
|
111
|
+
|
|
112
|
+
type CreateConfigOptions = {
|
|
113
|
+
extends?: string[];
|
|
114
|
+
tokens?: RegionalTokenWithValidity[];
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export async function createConfig(
|
|
118
|
+
config: string | RawConfig,
|
|
119
|
+
options?: CreateConfigOptions
|
|
120
|
+
): Promise<Config> {
|
|
121
|
+
return addConfigMetadata({
|
|
122
|
+
rawConfig: transformConfig(
|
|
123
|
+
typeof config === 'string' ? (parseYaml(config) as RawConfig) : config
|
|
124
|
+
),
|
|
125
|
+
...options,
|
|
126
|
+
});
|
|
127
|
+
}
|
package/src/config/minimal.ts
CHANGED
|
@@ -44,7 +44,7 @@ export default {
|
|
|
44
44
|
'no-example-value-and-externalValue': 'warn',
|
|
45
45
|
'no-unused-components': 'warn',
|
|
46
46
|
'no-undefined-server-variable': 'warn',
|
|
47
|
-
'no-
|
|
47
|
+
'no-server-variables-empty-enum': 'error',
|
|
48
48
|
},
|
|
49
49
|
oas3_1Rules: {
|
|
50
50
|
'no-invalid-media-type-examples': 'warn',
|
|
@@ -54,6 +54,6 @@ export default {
|
|
|
54
54
|
'no-example-value-and-externalValue': 'warn',
|
|
55
55
|
'no-unused-components': 'warn',
|
|
56
56
|
'no-undefined-server-variable': 'warn',
|
|
57
|
-
'no-
|
|
57
|
+
'no-server-variables-empty-enum': 'error',
|
|
58
58
|
},
|
|
59
59
|
} as PluginStyleguideConfig;
|
|
@@ -44,7 +44,7 @@ export default {
|
|
|
44
44
|
'no-example-value-and-externalValue': 'error',
|
|
45
45
|
'no-unused-components': 'warn',
|
|
46
46
|
'no-undefined-server-variable': 'error',
|
|
47
|
-
'no-
|
|
47
|
+
'no-server-variables-empty-enum': 'error',
|
|
48
48
|
},
|
|
49
49
|
oas3_1Rules: {
|
|
50
50
|
'no-invalid-media-type-examples': 'warn',
|
|
@@ -54,6 +54,6 @@ export default {
|
|
|
54
54
|
'no-example-value-and-externalValue': 'error',
|
|
55
55
|
'no-unused-components': 'warn',
|
|
56
56
|
'no-undefined-server-variable': 'error',
|
|
57
|
-
'no-
|
|
57
|
+
'no-server-variables-empty-enum': 'error',
|
|
58
58
|
},
|
|
59
59
|
} as PluginStyleguideConfig;
|
package/src/config/utils.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { yellow } from 'colorette';
|
|
2
1
|
import {
|
|
3
2
|
assignExisting,
|
|
3
|
+
isTruthy,
|
|
4
4
|
showErrorForDeprecatedField,
|
|
5
5
|
showWarningForDeprecatedField,
|
|
6
6
|
} from '../utils';
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
ResolvedStyleguideConfig,
|
|
18
18
|
RulesFields,
|
|
19
19
|
} from './types';
|
|
20
|
+
import { logger, colorize } from '../logger';
|
|
20
21
|
|
|
21
22
|
export function parsePresetName(presetName: string): { pluginId: string; configName: string } {
|
|
22
23
|
if (presetName.indexOf('/') > -1) {
|
|
@@ -83,7 +84,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
|
|
|
83
84
|
extendPaths: [],
|
|
84
85
|
};
|
|
85
86
|
|
|
86
|
-
for (
|
|
87
|
+
for (const rulesConf of rulesConfList) {
|
|
87
88
|
if (rulesConf.extends) {
|
|
88
89
|
throw new Error(
|
|
89
90
|
`'extends' is not supported in shared configs yet: ${JSON.stringify(rulesConf, null, 2)}.`
|
|
@@ -128,14 +129,14 @@ export function getMergedConfig(config: Config, apiName?: string): Config {
|
|
|
128
129
|
config.rawConfig?.styleguide?.extendPaths,
|
|
129
130
|
]
|
|
130
131
|
.flat()
|
|
131
|
-
.filter(
|
|
132
|
+
.filter(isTruthy);
|
|
132
133
|
|
|
133
134
|
const pluginPaths = [
|
|
134
135
|
...Object.values(config.apis).map((api) => api?.styleguide?.pluginPaths),
|
|
135
136
|
config.rawConfig?.styleguide?.pluginPaths,
|
|
136
137
|
]
|
|
137
138
|
.flat()
|
|
138
|
-
.filter(
|
|
139
|
+
.filter(isTruthy);
|
|
139
140
|
|
|
140
141
|
return apiName
|
|
141
142
|
? new Config(
|
|
@@ -221,7 +222,7 @@ export function getUniquePlugins(plugins: Plugin[]): Plugin[] {
|
|
|
221
222
|
results.push(p);
|
|
222
223
|
seen.add(p.id);
|
|
223
224
|
} else if (p.id) {
|
|
224
|
-
|
|
225
|
+
logger.warn(`Duplicate plugin id "${colorize.red(p.id)}".\n`);
|
|
225
226
|
}
|
|
226
227
|
}
|
|
227
228
|
return results;
|
|
@@ -4,7 +4,7 @@ import { isRedoclyRegistryURL } from '../../redocly';
|
|
|
4
4
|
import { Oas3Decorator, Oas2Decorator } from '../../visitors';
|
|
5
5
|
|
|
6
6
|
export const RegistryDependencies: Oas3Decorator | Oas2Decorator = () => {
|
|
7
|
-
|
|
7
|
+
const registryDependencies = new Set<string>();
|
|
8
8
|
|
|
9
9
|
return {
|
|
10
10
|
DefinitionRoot: {
|
package/src/env.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export const isBrowser =
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
typeof window !== 'undefined' || typeof self !== 'undefined' || typeof process === 'undefined'; // main and worker thread
|
|
5
|
+
export const env = isBrowser ? {} : process.env || {};
|
package/src/format/codeframes.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { gray, red, options as colorOptions } from 'colorette';
|
|
2
1
|
import * as yamlAst from 'yaml-ast-parser';
|
|
3
2
|
import { unescapePointer } from '../ref-utils';
|
|
4
3
|
import { LineColLocationObject, Loc, LocationObject } from '../walk';
|
|
4
|
+
import { colorize, colorOptions } from '../logger';
|
|
5
5
|
|
|
6
6
|
type YAMLMapping = yamlAst.YAMLMapping & { kind: yamlAst.Kind.MAPPING };
|
|
7
7
|
type YAMLMap = yamlAst.YamlMap & { kind: yamlAst.Kind.MAP };
|
|
@@ -36,17 +36,23 @@ export function getCodeframe(location: LineColLocationObject, color: boolean) {
|
|
|
36
36
|
if (skipLines > 0 && i >= endLineNum - skipLines) break;
|
|
37
37
|
const line = lines[i - 1] || '';
|
|
38
38
|
if (line !== '') currentPad = padSize(line);
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
const startIdx = i === startLineNum ? start.col - 1 : currentPad;
|
|
40
|
+
const endIdx = i === endLineNum ? end.col - 1 : line.length;
|
|
41
41
|
|
|
42
|
-
prefixedLines.push([`${i}`, markLine(line, startIdx, endIdx, red)]);
|
|
42
|
+
prefixedLines.push([`${i}`, markLine(line, startIdx, endIdx, colorize.red)]);
|
|
43
43
|
if (!color) prefixedLines.push(['', underlineLine(line, startIdx, endIdx)]);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
if (skipLines > 0) {
|
|
47
|
-
prefixedLines.push([
|
|
47
|
+
prefixedLines.push([
|
|
48
|
+
`…`,
|
|
49
|
+
`${whitespace(currentPad)}${colorize.gray(`< ${skipLines} more lines >`)}`,
|
|
50
|
+
]);
|
|
48
51
|
// print last line
|
|
49
|
-
prefixedLines.push([
|
|
52
|
+
prefixedLines.push([
|
|
53
|
+
`${endLineNum}`,
|
|
54
|
+
markLine(lines[endLineNum - 1], -1, end.col - 1, colorize.red),
|
|
55
|
+
]);
|
|
50
56
|
|
|
51
57
|
if (!color) prefixedLines.push(['', underlineLine(lines[endLineNum - 1], -1, end.col - 1)]);
|
|
52
58
|
}
|
|
@@ -63,7 +69,7 @@ export function getCodeframe(location: LineColLocationObject, color: boolean) {
|
|
|
63
69
|
line: string,
|
|
64
70
|
startIdx: number = -1,
|
|
65
71
|
endIdx: number = +Infinity,
|
|
66
|
-
variant = gray
|
|
72
|
+
variant = colorize.gray
|
|
67
73
|
) {
|
|
68
74
|
if (!color) return line;
|
|
69
75
|
if (!line) return line;
|
|
@@ -90,7 +96,7 @@ function printPrefixedLines(lines: [string, string][]): string {
|
|
|
90
96
|
return existingLines
|
|
91
97
|
.map(
|
|
92
98
|
([prefix, line]) =>
|
|
93
|
-
gray(leftPad(padLen, prefix) + ' |') +
|
|
99
|
+
colorize.gray(leftPad(padLen, prefix) + ' |') +
|
|
94
100
|
(line ? ' ' + limitLineLength(line.substring(dedentLen)) : '')
|
|
95
101
|
)
|
|
96
102
|
.join('\n');
|
|
@@ -99,7 +105,7 @@ function printPrefixedLines(lines: [string, string][]): string {
|
|
|
99
105
|
function limitLineLength(line: string, maxLen: number = MAX_LINE_LENGTH) {
|
|
100
106
|
const overflowLen = line.length - maxLen;
|
|
101
107
|
if (overflowLen > 0) {
|
|
102
|
-
const charsMoreText = gray(`...<${overflowLen} chars>`);
|
|
108
|
+
const charsMoreText = colorize.gray(`...<${overflowLen} chars>`);
|
|
103
109
|
return line.substring(0, maxLen - charsMoreText.length) + charsMoreText;
|
|
104
110
|
} else {
|
|
105
111
|
return line;
|
package/src/format/format.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
gray,
|
|
5
|
-
blue,
|
|
6
|
-
bgRed,
|
|
7
|
-
bgYellow,
|
|
8
|
-
black,
|
|
9
|
-
yellow,
|
|
10
|
-
red,
|
|
11
|
-
} from 'colorette';
|
|
2
|
+
import { colorOptions, colorize, logger } from '../logger';
|
|
3
|
+
import { output } from '../output';
|
|
12
4
|
|
|
13
5
|
const coreVersion = require('../../package.json').version;
|
|
14
6
|
|
|
15
7
|
import { NormalizedProblem, ProblemSeverity, LineColLocationObject, LocationObject } from '../walk';
|
|
16
8
|
import { getCodeframe, getLineColLocation } from './codeframes';
|
|
17
|
-
import { env } from '../
|
|
9
|
+
import { env } from '../env';
|
|
18
10
|
|
|
19
11
|
export type Totals = {
|
|
20
12
|
errors: number;
|
|
@@ -27,13 +19,13 @@ const ERROR_MESSAGE = {
|
|
|
27
19
|
};
|
|
28
20
|
|
|
29
21
|
const BG_COLORS = {
|
|
30
|
-
warn: (str: string) => bgYellow(black(str)),
|
|
31
|
-
error: bgRed,
|
|
22
|
+
warn: (str: string) => colorize.bgYellow(colorize.black(str)),
|
|
23
|
+
error: colorize.bgRed,
|
|
32
24
|
};
|
|
33
25
|
|
|
34
26
|
const COLORS = {
|
|
35
|
-
warn: yellow,
|
|
36
|
-
error: red,
|
|
27
|
+
warn: colorize.yellow,
|
|
28
|
+
error: colorize.red,
|
|
37
29
|
};
|
|
38
30
|
|
|
39
31
|
const SEVERITY_NAMES = {
|
|
@@ -114,7 +106,7 @@ export function formatProblems(
|
|
|
114
106
|
case 'codeframe':
|
|
115
107
|
for (let i = 0; i < problems.length; i++) {
|
|
116
108
|
const problem = problems[i];
|
|
117
|
-
|
|
109
|
+
logger.info(`${formatCodeframe(problem, i)}\n`);
|
|
118
110
|
}
|
|
119
111
|
break;
|
|
120
112
|
case 'stylish': {
|
|
@@ -122,30 +114,30 @@ export function formatProblems(
|
|
|
122
114
|
for (const [file, { ruleIdPad, locationPad: positionPad, fileProblems }] of Object.entries(
|
|
123
115
|
groupedByFile
|
|
124
116
|
)) {
|
|
125
|
-
|
|
117
|
+
logger.info(`${colorize.blue(path.relative(cwd, file))}:\n`);
|
|
126
118
|
|
|
127
119
|
for (let i = 0; i < fileProblems.length; i++) {
|
|
128
120
|
const problem = fileProblems[i];
|
|
129
|
-
|
|
121
|
+
logger.info(`${formatStylish(problem, positionPad, ruleIdPad)}\n`);
|
|
130
122
|
}
|
|
131
123
|
|
|
132
|
-
|
|
124
|
+
logger.info('\n');
|
|
133
125
|
}
|
|
134
126
|
break;
|
|
135
127
|
}
|
|
136
128
|
case 'checkstyle': {
|
|
137
129
|
const groupedByFile = groupByFiles(problems);
|
|
138
130
|
|
|
139
|
-
|
|
140
|
-
|
|
131
|
+
output.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
|
132
|
+
output.write('<checkstyle version="4.3">\n');
|
|
141
133
|
|
|
142
134
|
for (const [file, { fileProblems }] of Object.entries(groupedByFile)) {
|
|
143
|
-
|
|
135
|
+
output.write(`<file name="${xmlEscape(path.relative(cwd, file))}">\n`);
|
|
144
136
|
fileProblems.forEach(formatCheckstyle);
|
|
145
|
-
|
|
137
|
+
output.write(`</file>\n`);
|
|
146
138
|
}
|
|
147
139
|
|
|
148
|
-
|
|
140
|
+
output.write(`</checkstyle>\n`);
|
|
149
141
|
break;
|
|
150
142
|
}
|
|
151
143
|
case 'codeclimate':
|
|
@@ -154,8 +146,8 @@ export function formatProblems(
|
|
|
154
146
|
}
|
|
155
147
|
|
|
156
148
|
if (totalProblems - ignoredProblems > maxProblems) {
|
|
157
|
-
|
|
158
|
-
`< ... ${totalProblems - maxProblems} more problems hidden > ${gray(
|
|
149
|
+
logger.info(
|
|
150
|
+
`< ... ${totalProblems - maxProblems} more problems hidden > ${colorize.gray(
|
|
159
151
|
'increase with `--max-problems N`'
|
|
160
152
|
)}\n`
|
|
161
153
|
);
|
|
@@ -177,7 +169,7 @@ export function formatProblems(
|
|
|
177
169
|
fingerprint: `${p.ruleId}${p.location.length > 0 ? '-' + p.location[0].pointer : ''}`,
|
|
178
170
|
};
|
|
179
171
|
});
|
|
180
|
-
|
|
172
|
+
output.write(JSON.stringify(issues, null, 2));
|
|
181
173
|
}
|
|
182
174
|
|
|
183
175
|
function outputJSON() {
|
|
@@ -185,7 +177,7 @@ export function formatProblems(
|
|
|
185
177
|
totals,
|
|
186
178
|
version,
|
|
187
179
|
problems: problems.map((p) => {
|
|
188
|
-
|
|
180
|
+
const problem = {
|
|
189
181
|
...p,
|
|
190
182
|
location: p.location.map((location: any) => ({
|
|
191
183
|
...location,
|
|
@@ -211,7 +203,7 @@ export function formatProblems(
|
|
|
211
203
|
return problem;
|
|
212
204
|
}),
|
|
213
205
|
};
|
|
214
|
-
|
|
206
|
+
output.write(JSON.stringify(resultObject, null, 2));
|
|
215
207
|
}
|
|
216
208
|
|
|
217
209
|
function getBgColor(problem: NormalizedProblem) {
|
|
@@ -227,7 +219,7 @@ export function formatProblems(
|
|
|
227
219
|
const location = problem.location[0]; // TODO: support multiple locations
|
|
228
220
|
const relativePath = path.relative(cwd, location.source.absoluteRef);
|
|
229
221
|
const loc = getLineColLocation(location);
|
|
230
|
-
const atPointer = location.pointer ? gray(`at ${location.pointer}`) : '';
|
|
222
|
+
const atPointer = location.pointer ? colorize.gray(`at ${location.pointer}`) : '';
|
|
231
223
|
const fileWithLoc = `${relativePath}:${loc.start.line}:${loc.start.col}`;
|
|
232
224
|
return (
|
|
233
225
|
`[${idx + 1}] ${bgColor(fileWithLoc)} ${atPointer}\n\n` +
|
|
@@ -236,7 +228,9 @@ export function formatProblems(
|
|
|
236
228
|
getCodeframe(loc, color) +
|
|
237
229
|
'\n\n' +
|
|
238
230
|
formatFrom(cwd, problem.from) +
|
|
239
|
-
`${SEVERITY_NAMES[problem.severity]} was generated by the ${blue(
|
|
231
|
+
`${SEVERITY_NAMES[problem.severity]} was generated by the ${colorize.blue(
|
|
232
|
+
problem.ruleId
|
|
233
|
+
)} rule.\n\n`
|
|
240
234
|
);
|
|
241
235
|
}
|
|
242
236
|
|
|
@@ -257,7 +251,7 @@ export function formatProblems(
|
|
|
257
251
|
const severity = problem.severity == 'warn' ? 'warning' : 'error';
|
|
258
252
|
const message = xmlEscape(problem.message);
|
|
259
253
|
const source = xmlEscape(problem.ruleId);
|
|
260
|
-
|
|
254
|
+
output.write(
|
|
261
255
|
`<error line="${line}" column="${col}" severity="${severity}" message="${message}" source="${source}" />\n`
|
|
262
256
|
);
|
|
263
257
|
}
|
|
@@ -269,7 +263,7 @@ function formatFrom(cwd: string, location?: LocationObject) {
|
|
|
269
263
|
const loc = getLineColLocation(location);
|
|
270
264
|
const fileWithLoc = `${relativePath}:${loc.start.line}:${loc.start.col}`;
|
|
271
265
|
|
|
272
|
-
return `referenced from ${blue(fileWithLoc)}\n\n`;
|
|
266
|
+
return `referenced from ${colorize.blue(fileWithLoc)}\n\n`;
|
|
273
267
|
}
|
|
274
268
|
|
|
275
269
|
function formatDidYouMean(problem: NormalizedProblem) {
|
|
@@ -320,6 +314,7 @@ const groupByFiles = (problems: NormalizedProblem[]) => {
|
|
|
320
314
|
};
|
|
321
315
|
|
|
322
316
|
function xmlEscape(s: string): string {
|
|
317
|
+
// eslint-disable-next-line no-control-regex
|
|
323
318
|
return s.replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, (char) => {
|
|
324
319
|
switch (char) {
|
|
325
320
|
case '<':
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist } from './utils';
|
|
1
|
+
export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist, isTruthy } from './utils';
|
|
2
2
|
export { Oas3_1Types } from './types/oas3_1';
|
|
3
3
|
export { Oas3Types } from './types/oas3';
|
|
4
4
|
export { Oas2Types } from './types/oas2';
|
|
5
5
|
export { ConfigTypes } from './types/redocly-yaml';
|
|
6
|
-
export {
|
|
6
|
+
export type {
|
|
7
7
|
Oas3Definition,
|
|
8
8
|
Oas3_1Definition,
|
|
9
9
|
Oas3Components,
|
|
@@ -15,9 +15,10 @@ export {
|
|
|
15
15
|
Oas3Tag,
|
|
16
16
|
Oas3_1Webhooks,
|
|
17
17
|
Referenced,
|
|
18
|
+
OasRef,
|
|
18
19
|
} from './typings/openapi';
|
|
19
|
-
export { Oas2Definition } from './typings/swagger';
|
|
20
|
-
export { StatsAccumulator, StatsName } from './typings/common';
|
|
20
|
+
export type { Oas2Definition } from './typings/swagger';
|
|
21
|
+
export type { StatsAccumulator, StatsName } from './typings/common';
|
|
21
22
|
export { normalizeTypes } from './types';
|
|
22
23
|
export { Stats } from './rules/other/stats';
|
|
23
24
|
|
|
@@ -34,6 +35,7 @@ export {
|
|
|
34
35
|
findConfig,
|
|
35
36
|
CONFIG_FILE_NAMES,
|
|
36
37
|
RuleSeverity,
|
|
38
|
+
createConfig,
|
|
37
39
|
} from './config';
|
|
38
40
|
|
|
39
41
|
export { RedoclyClient, isRedoclyRegistryURL } from './redocly';
|
package/src/js-yaml/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// TODO: add a type for "types" https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/js-yaml/index.d.ts
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
2
3
|
// @ts-ignore
|
|
3
4
|
import { JSON_SCHEMA, types, LoadOptions, DumpOptions, load, dump } from 'js-yaml';
|
|
4
5
|
|