Chore/configure eslint (#808)

This commit is contained in:
Andrew Tatomyr
2022-08-18 14:22:31 +03:00
committed by GitHub
parent 229f54222b
commit 4dc87c3f18
41 changed files with 1949 additions and 121 deletions

29
.eslintrc.yml Normal file
View File

@@ -0,0 +1,29 @@
env:
browser: true
es2021: true
node: true
extends:
- eslint:recommended
- plugin:@typescript-eslint/recommended
overrides: []
parser: '@typescript-eslint/parser'
parserOptions:
ecmaVersion: latest
sourceType: module
plugins:
- '@typescript-eslint'
rules:
'@typescript-eslint/no-unused-vars':
- error
- argsIgnorePattern: ^_
varsIgnorePattern: ^_
'@typescript-eslint/no-var-requires': off
'@typescript-eslint/no-empty-function': off
'@typescript-eslint/no-inferrable-types': off
'@typescript-eslint/ban-types': warn
no-prototype-builtins: off
no-useless-escape: warn
ignorePatterns:
- '**/__tests__/'
- 'packages/*/lib/'
- '*.js'

View File

@@ -101,4 +101,26 @@ jobs:
restore-keys: | restore-keys: |
npm-${{ hashFiles('package-lock.json') }} npm-${{ hashFiles('package-lock.json') }}
npm- npm-
- name: Install dependencies
run: npm ci
env:
CI: true
- run: npm run prettier:check - run: npm run prettier:check
eslint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: |
npm-${{ hashFiles('package-lock.json') }}
npm-
- name: Install dependencies
run: npm ci
env:
CI: true
- run: npm run eslint

View File

@@ -14,7 +14,7 @@ module.exports = {
statements: 79, statements: 79,
branches: 71, branches: 71,
functions: 68, functions: 68,
lines: 78, lines: 79,
}, },
'packages/cli/': { 'packages/cli/': {
statements: 37, statements: 37,

1720
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@
"e2e": "npm run webpack-bundle -- --mode=none && jest --roots=./__tests__/", "e2e": "npm run webpack-bundle -- --mode=none && jest --roots=./__tests__/",
"prettier": "npx prettier --write \"**/*.{ts,js}\"", "prettier": "npx prettier --write \"**/*.{ts,js}\"",
"prettier:check": "npx prettier --check \"**/*.{ts,js}\"", "prettier:check": "npx prettier --check \"**/*.{ts,js}\"",
"eslint": "eslint packages/**",
"clean": "rm -rf packages/**/lib packages/**/node_modules packages/**/*.tsbuildinfo package-lock.json node_modules", "clean": "rm -rf packages/**/lib packages/**/node_modules packages/**/*.tsbuildinfo package-lock.json node_modules",
"watch": "tsc -b tsconfig.build.json --watch ", "watch": "tsc -b tsconfig.build.json --watch ",
"compile": "tsc -b tsconfig.build.json", "compile": "tsc -b tsconfig.build.json",
@@ -57,6 +58,9 @@
"devDependencies": { "devDependencies": {
"@types/jest": "^26.0.15", "@types/jest": "^26.0.15",
"@types/node": "^17.0.31", "@types/node": "^17.0.31",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"eslint": "^8.22.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"null-loader": "^4.0.0", "null-loader": "^4.0.0",
"outdent": "^0.7.1", "outdent": "^0.7.1",

View File

@@ -131,8 +131,8 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
} }
} }
let joinedDef: any = {}; const joinedDef: any = {};
let potentialConflicts = { const potentialConflicts = {
tags: {}, tags: {},
paths: {}, paths: {},
components: {}, components: {},
@@ -343,9 +343,7 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
if (!potentialConflicts.paths.hasOwnProperty(path)) { if (!potentialConflicts.paths.hasOwnProperty(path)) {
potentialConflicts.paths[path] = {}; potentialConflicts.paths[path] = {};
} }
for (const operation of Object.keys(paths[path])) { for (const [operation, pathOperation] of Object.entries(paths[path])) {
// @ts-ignore
const pathOperation = paths[path][operation];
joinedDef.paths[path][operation] = pathOperation; joinedDef.paths[path][operation] = pathOperation;
potentialConflicts.paths[path][operation] = [ potentialConflicts.paths[path][operation] = [
...(potentialConflicts.paths[path][operation] || []), ...(potentialConflicts.paths[path][operation] || []),
@@ -361,7 +359,7 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
api, api,
]; ];
} }
let { tags, security } = joinedDef.paths[path][operation]; const { tags, security } = joinedDef.paths[path][operation];
if (tags) { if (tags) {
joinedDef.paths[path][operation].tags = tags.map((tag: string) => joinedDef.paths[path][operation].tags = tags.map((tag: string) =>
addPrefix(tag, tagsPrefix) addPrefix(tag, tagsPrefix)
@@ -412,13 +410,11 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
if (!joinedDef.hasOwnProperty(COMPONENTS)) { if (!joinedDef.hasOwnProperty(COMPONENTS)) {
joinedDef[COMPONENTS] = {}; joinedDef[COMPONENTS] = {};
} }
for (const component of Object.keys(components)) { for (const [component, componentObj] of Object.entries(components)) {
if (!potentialConflicts[COMPONENTS].hasOwnProperty(component)) { if (!potentialConflicts[COMPONENTS].hasOwnProperty(component)) {
potentialConflicts[COMPONENTS][component] = {}; potentialConflicts[COMPONENTS][component] = {};
joinedDef[COMPONENTS][component] = {}; joinedDef[COMPONENTS][component] = {};
} }
// @ts-ignore
const componentObj = components[component];
for (const item of Object.keys(componentObj)) { for (const item of Object.keys(componentObj)) {
const componentPrefix = addPrefix(item, componentsPrefix!); const componentPrefix = addPrefix(item, componentsPrefix!);
potentialConflicts.components[component][componentPrefix] = [ potentialConflicts.components[component][componentPrefix] = [
@@ -436,7 +432,6 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
{ apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext { apiFilename, api, potentialConflicts, tagsPrefix, componentsPrefix }: JoinDocumentContext
) { ) {
const xWebhooks = 'x-webhooks'; const xWebhooks = 'x-webhooks';
// @ts-ignore
const openapiXWebhooks = openapi[xWebhooks]; const openapiXWebhooks = openapi[xWebhooks];
if (openapiXWebhooks) { if (openapiXWebhooks) {
if (!joinedDef.hasOwnProperty(xWebhooks)) { if (!joinedDef.hasOwnProperty(xWebhooks)) {
@@ -455,7 +450,7 @@ export async function handleJoin(argv: JoinArgv, packageVersion: string) {
]; ];
} }
for (const operationKey of Object.keys(joinedDef[xWebhooks][webhook])) { for (const operationKey of Object.keys(joinedDef[xWebhooks][webhook])) {
let { tags } = joinedDef[xWebhooks][webhook][operationKey]; const { tags } = joinedDef[xWebhooks][webhook][operationKey];
if (tags) { if (tags) {
joinedDef[xWebhooks][webhook][operationKey].tags = tags.map((tag: string) => joinedDef[xWebhooks][webhook][operationKey].tags = tags.map((tag: string) =>
addPrefix(tag, tagsPrefix) addPrefix(tag, tagsPrefix)
@@ -498,7 +493,7 @@ function doesComponentsDiffer(curr: object, next: object) {
function validateComponentsDifference(files: any) { function validateComponentsDifference(files: any) {
let isDiffer = false; let isDiffer = false;
for (let i = 0, len = files.length; i < len; i++) { for (let i = 0, len = files.length; i < len; i++) {
let next = files[i + 1]; const next = files[i + 1];
if (next && doesComponentsDiffer(files[i], next)) { if (next && doesComponentsDiffer(files[i], next)) {
isDiffer = true; isDiffer = true;
} }

View File

@@ -84,7 +84,7 @@ export async function handleLint(argv: LintOptions, version: string) {
totals.ignored += fileTotals.ignored; totals.ignored += fileTotals.ignored;
if (argv['generate-ignore-file']) { if (argv['generate-ignore-file']) {
for (let m of results) { for (const m of results) {
config.styleguide.addIgnore(m); config.styleguide.addIgnore(m);
totalIgnored++; totalIgnored++;
} }

View File

@@ -23,7 +23,7 @@ export async function previewDocs(
force?: boolean; force?: boolean;
} & Omit<Skips, 'skip-rule'> } & Omit<Skips, 'skip-rule'>
) { ) {
let isAuthorizedWithRedocly: boolean = false; let isAuthorizedWithRedocly = false;
let redocOptions: any = {}; let redocOptions: any = {};
let config = await reloadConfig(); let config = await reloadConfig();
@@ -129,7 +129,7 @@ export async function previewDocs(
}); });
async function reloadConfig() { async function reloadConfig() {
let config = await loadConfig(argv.config); const config = await loadConfig(argv.config);
const redoclyClient = new RedoclyClient(); const redoclyClient = new RedoclyClient();
isAuthorizedWithRedocly = await redoclyClient.isAuthorizedWithRedocly(); isAuthorizedWithRedocly = await redoclyClient.isAuthorizedWithRedocly();
const resolvedConfig = getMergedConfig(config, argv.api); const resolvedConfig = getMergedConfig(config, argv.api);
@@ -152,7 +152,9 @@ export function debounce(func: Function, wait: number, immediate?: boolean) {
let timeout: NodeJS.Timeout | null; let timeout: NodeJS.Timeout | null;
return function executedFunction(...args: any[]) { return function executedFunction(...args: any[]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-this-alias
const context = this; const context = this;
const later = () => { const later = () => {

View File

@@ -99,6 +99,7 @@ export default async function startPreviewServer(
} }
} else { } else {
let filePath = let filePath =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
{ {
'/hot.js': path.join(__dirname, 'hot.js'), '/hot.js': path.join(__dirname, 'hot.js'),
@@ -142,7 +143,7 @@ export default async function startPreviewServer(
console.timeEnd(colorette.dim(`GET ${request.url}`)); console.timeEnd(colorette.dim(`GET ${request.url}`));
}; };
let wsPort = await portfinder.getPortPromise({ port: 32201 }); const wsPort = await portfinder.getPortPromise({ port: 32201 });
const server = startHttpServer(port, host, handler); const server = startHttpServer(port, host, handler);
server.on('listening', () => { server.on('listening', () => {

View File

@@ -119,7 +119,7 @@ export async function handlePush(argv: PushArgs): Promise<void> {
let uploaded = 0; let uploaded = 0;
for (let file of filesToUpload.files) { for (const file of filesToUpload.files) {
const { signedUploadUrl, filePath } = await client.registryApi.prepareFileUpload({ const { signedUploadUrl, filePath } = await client.registryApi.prepareFileUpload({
organizationId, organizationId,
name, name,
@@ -206,7 +206,7 @@ function getFilesList(dir: string, files?: any): string[] {
} }
async function collectFilesToUpload(api: string, config: Config) { async function collectFilesToUpload(api: string, config: Config) {
let files: { filePath: string; keyOnS3: string; contents?: Buffer }[] = []; const files: { filePath: string; keyOnS3: string; contents?: Buffer }[] = [];
const [{ path: apiPath }] = await getFallbackApisOrExit([api], config); const [{ path: apiPath }] = await getFallbackApisOrExit([api], config);
process.stdout.write('Bundling definition\n'); process.stdout.write('Bundling definition\n');
@@ -246,7 +246,7 @@ async function collectFilesToUpload(api: string, config: Config) {
const fileList = getFilesList(dir, []); const fileList = getFilesList(dir, []);
files.push(...fileList.map((f) => getFileEntry(f))); files.push(...fileList.map((f) => getFileEntry(f)));
} }
let pluginFiles = new Set<string>(); const pluginFiles = new Set<string>();
for (const plugin of config.styleguide.pluginPaths) { for (const plugin of config.styleguide.pluginPaths) {
if (typeof plugin !== 'string') continue; if (typeof plugin !== 'string') continue;
const fileList = getFilesList(getFolder(plugin), []); const fileList = getFilesList(getFolder(plugin), []);
@@ -282,7 +282,7 @@ function getFolder(filePath: string) {
} }
function hashFiles(filePaths: { filePath: string }[]) { function hashFiles(filePaths: { filePath: string }[]) {
let sum = createHash('sha256'); const sum = createHash('sha256');
filePaths.forEach((file) => sum.update(fs.readFileSync(file.filePath))); filePaths.forEach((file) => sum.update(fs.readFileSync(file.filePath)));
return sum.digest('hex'); return sum.digest('hex');
} }
@@ -318,7 +318,7 @@ type BarePushArgs = Omit<PushArgs, 'api' | 'destination' | 'branchName'> & {
export const transformPush = export const transformPush =
(callback: typeof handlePush) => (callback: typeof handlePush) =>
({ maybeApiOrDestination, maybeDestination, maybeBranchName, branch, ...rest }: BarePushArgs) => { ({ maybeApiOrDestination, maybeDestination, maybeBranchName, branch, ...rest }: BarePushArgs) => {
if (!!maybeBranchName) { if (maybeBranchName) {
process.stderr.write( process.stderr.write(
yellow( yellow(
'Deprecation warning: Do not use the third parameter as a branch name. Please use a separate --branch option instead.' 'Deprecation warning: Do not use the third parameter as a branch name. Please use a separate --branch option instead.'
@@ -353,7 +353,7 @@ function uploadFileToS3(url: string, filePathOrBuffer: string | Buffer) {
typeof filePathOrBuffer === 'string' typeof filePathOrBuffer === 'string'
? fs.statSync(filePathOrBuffer).size ? fs.statSync(filePathOrBuffer).size
: filePathOrBuffer.byteLength; : filePathOrBuffer.byteLength;
let readStream = const readStream =
typeof filePathOrBuffer === 'string' ? fs.createReadStream(filePathOrBuffer) : filePathOrBuffer; typeof filePathOrBuffer === 'string' ? fs.createReadStream(filePathOrBuffer) : filePathOrBuffer;
return fetch(url, { return fetch(url, {

View File

@@ -1,6 +1,7 @@
import { red, blue, yellow, green } from 'colorette'; import { red, blue, yellow, green } from 'colorette';
import * as fs from 'fs'; import * as fs from 'fs';
import { parseYaml, slash, isRef } from '@redocly/openapi-core'; import { parseYaml, slash, isRef, isTruthy } from '@redocly/openapi-core';
import type { OasRef } from '@redocly/openapi-core';
import * as path from 'path'; import * as path from 'path';
import { performance } from 'perf_hooks'; import { performance } from 'perf_hooks';
const isEqual = require('lodash.isequal'); const isEqual = require('lodash.isequal');
@@ -267,11 +268,11 @@ function gatherComponentsFiles(
componentName: string, componentName: string,
filename: string filename: string
) { ) {
let inherits = []; let inherits: string[] = [];
if (componentType === OPENAPI3_COMPONENT.Schemas) { if (componentType === OPENAPI3_COMPONENT.Schemas) {
inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || []) inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
.map((s: any) => s.$ref) .map(({ $ref }) => $ref)
.filter(Boolean); .filter(isTruthy);
} }
componentsFiles[componentType] = componentsFiles[componentType] || {}; componentsFiles[componentType] = componentsFiles[componentType] || {};
componentsFiles[componentType][componentName] = { inherits, filename }; componentsFiles[componentType][componentName] = { inherits, filename };
@@ -301,7 +302,7 @@ function iteratePathItems(
continue; continue;
} }
for (const sample of methodDataXCode) { for (const sample of methodDataXCode) {
if (sample.source && (sample.source as any).$ref) continue; if (sample.source && (sample.source as unknown as OasRef).$ref) continue;
const sampleFileName = path.join( const sampleFileName = path.join(
openapiDir, openapiDir,
'code_samples', 'code_samples',
@@ -312,6 +313,7 @@ function iteratePathItems(
fs.mkdirSync(path.dirname(sampleFileName), { recursive: true }); fs.mkdirSync(path.dirname(sampleFileName), { recursive: true });
fs.writeFileSync(sampleFileName, sample.source); fs.writeFileSync(sampleFileName, sample.source);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
sample.source = { sample.source = {
$ref: slash(path.relative(outDir, sampleFileName)), $ref: slash(path.relative(outDir, sampleFileName)),
@@ -340,6 +342,7 @@ function iterateComponents(
componentTypes.forEach(iterateAndGatherComponentsFiles); componentTypes.forEach(iterateAndGatherComponentsFiles);
componentTypes.forEach(iterateComponentTypes); componentTypes.forEach(iterateComponentTypes);
// eslint-disable-next-line no-inner-declarations
function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) { function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
const componentDirPath = path.join(componentsDir, componentType); const componentDirPath = path.join(componentsDir, componentType);
for (const componentName of Object.keys(components?.[componentType] || {})) { for (const componentName of Object.keys(components?.[componentType] || {})) {
@@ -348,6 +351,7 @@ function iterateComponents(
} }
} }
// eslint-disable-next-line no-inner-declarations
function iterateComponentTypes(componentType: Oas3ComponentName) { function iterateComponentTypes(componentType: Oas3ComponentName) {
const componentDirPath = path.join(componentsDir, componentType); const componentDirPath = path.join(componentsDir, componentType);
createComponentDir(componentDirPath, componentType); createComponentDir(componentDirPath, componentType);

View File

@@ -177,7 +177,6 @@ export function handleError(e: Error, ref: string) {
process.stderr.write(`Failed to parse api definition at ${ref}:\n\n - ${e.message}.\n\n`); process.stderr.write(`Failed to parse api definition at ${ref}:\n\n - ${e.message}.\n\n`);
// TODO: codeframe // TODO: codeframe
} else { } else {
// @ts-ignore
if (e instanceof CircularJSONNotSupportedError) { if (e instanceof CircularJSONNotSupportedError) {
process.stderr.write( process.stderr.write(
red(`Detected circular reference which can't be converted to JSON.\n`) + red(`Detected circular reference which can't be converted to JSON.\n`) +

View File

@@ -15,7 +15,6 @@ const rebillyDocument = parseYamlToDocument(
const config = makeConfigForRuleset({ const config = makeConfigForRuleset({
test: () => { test: () => {
return { return {
// @ts-ignore
Schema(schema, ctx) { Schema(schema, ctx) {
if (schema.type === 'number') { if (schema.type === 'number') {
ctx.report({ ctx.report({

View File

@@ -10,7 +10,7 @@ import { detectOpenAPI, openAPIMajor, OasMajorVersion } from './oas-types';
import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils'; import { isAbsoluteUrl, isRef, Location, refBaseName } from './ref-utils';
import { initRules } from './config/rules'; import { initRules } from './config/rules';
import { reportUnresolvedRef } from './rules/no-unresolved-refs'; import { reportUnresolvedRef } from './rules/no-unresolved-refs';
import { isPlainObject } from './utils'; import { isPlainObject, isTruthy } from './utils';
import { OasRef } from './typings/openapi'; import { OasRef } from './typings/openapi';
import { isRedoclyRegistryURL } from './redocly'; import { isRedoclyRegistryURL } from './redocly';
import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components'; import { RemoveUnusedComponents as RemoveUnusedComponentsOas2 } from './rules/oas2/remove-unused-components';
@@ -302,6 +302,8 @@ function makeBundleVisitor(
if (!isPlainObject(resolved.node)) { if (!isPlainObject(resolved.node)) {
ctx.parent[ctx.key] = resolved.node; ctx.parent[ctx.key] = resolved.node;
} else { } else {
// TODO: why $ref isn't optional in OasRef?
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
delete ref.$ref; delete ref.$ref;
Object.assign(ref, resolved.node); Object.assign(ref, resolved.node);
@@ -348,7 +350,7 @@ function makeBundleVisitor(
let name = ''; let name = '';
const refParts = pointer.slice(2).split('/').filter(Boolean); // slice(2) removes "#/" const refParts = pointer.slice(2).split('/').filter(isTruthy); // slice(2) removes "#/"
while (refParts.length > 0) { while (refParts.length > 0) {
name = refParts.pop() + (name ? `-${name}` : ''); name = refParts.pop() + (name ? `-${name}` : '');
if ( if (

View File

@@ -1,3 +1,4 @@
import { OasVersion } from '../../oas-types';
import { Config, StyleguideConfig } from '../config'; import { Config, StyleguideConfig } from '../config';
import { getMergedConfig } from '../utils'; import { getMergedConfig } from '../utils';
@@ -242,3 +243,37 @@ describe('getMergedConfig', () => {
`); `);
}); });
}); });
describe('StyleguideConfig.extendTypes', () => {
let oas3 = jest.fn();
let oas2 = jest.fn();
let testRawConfigStyleguide = {
plugins: [
{
id: 'test-types-plugin',
typeExtension: {
oas3,
oas2,
},
},
],
};
it('should call only oas3 types extension', () => {
const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
styleguideConfig.extendTypes({}, OasVersion.Version3_0);
expect(oas3).toHaveBeenCalledTimes(1);
expect(oas2).toHaveBeenCalledTimes(0);
});
it('should call only oas2 types extension', () => {
const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
styleguideConfig.extendTypes({}, OasVersion.Version2);
expect(oas3).toHaveBeenCalledTimes(0);
expect(oas2).toHaveBeenCalledTimes(1);
});
it('should throw error if for oas version different from 2 and 3', () => {
const styleguideConfig = new StyleguideConfig(testRawConfigStyleguide);
expect(() => styleguideConfig.extendTypes({}, 'something else' as OasVersion)).toThrowError(
'Not implemented'
);
});
});

View File

@@ -71,6 +71,7 @@ export function resolvePlugins(
): Plugin[] { ): Plugin[] {
if (!plugins) return []; if (!plugins) return [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require; const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
@@ -166,7 +167,7 @@ export async function resolveApis({
resolver?: BaseResolver; resolver?: BaseResolver;
}): Promise<Record<string, ResolvedApi>> { }): Promise<Record<string, ResolvedApi>> {
const { apis = {}, styleguide: styleguideConfig = {} } = rawConfig; const { apis = {}, styleguide: styleguideConfig = {} } = rawConfig;
let resolvedApis: Record<string, ResolvedApi> = {}; const resolvedApis: Record<string, ResolvedApi> = {};
for (const [apiName, apiContent] of Object.entries(apis || {})) { for (const [apiName, apiContent] of Object.entries(apis || {})) {
if (apiContent.styleguide?.extends?.some(isNotString)) { if (apiContent.styleguide?.extends?.some(isNotString)) {
throw new Error( throw new Error(
@@ -287,7 +288,7 @@ export function resolvePreset(presetName: string, plugins: Plugin[]): ResolvedSt
throw new Error(`Invalid config ${red(presetName)}: plugin ${pluginId} is not included.`); throw new Error(`Invalid config ${red(presetName)}: plugin ${pluginId} is not included.`);
} }
const preset = plugin.configs?.[configName]! as ResolvedStyleguideConfig; const preset = plugin.configs?.[configName];
if (!preset) { if (!preset) {
throw new Error( throw new Error(
pluginId pluginId

View File

@@ -172,9 +172,11 @@ export class StyleguideConfig {
case OasVersion.Version3_1: case OasVersion.Version3_1:
if (!plugin.typeExtension.oas3) continue; if (!plugin.typeExtension.oas3) continue;
extendedTypes = plugin.typeExtension.oas3(extendedTypes, version); extendedTypes = plugin.typeExtension.oas3(extendedTypes, version);
break;
case OasVersion.Version2: case OasVersion.Version2:
if (!plugin.typeExtension.oas2) continue; if (!plugin.typeExtension.oas2) continue;
extendedTypes = plugin.typeExtension.oas2(extendedTypes, version); extendedTypes = plugin.typeExtension.oas2(extendedTypes, version);
break;
default: default:
throw new Error('Not implemented'); throw new Error('Not implemented');
} }
@@ -192,7 +194,7 @@ export class StyleguideConfig {
severity: settings, severity: settings,
}; };
} else { } else {
return { severity: 'error' as 'error', ...settings }; return { severity: 'error', ...settings };
} }
} }
@@ -203,10 +205,10 @@ export class StyleguideConfig {
const settings = this.preprocessors[oasVersion][ruleId] || 'off'; const settings = this.preprocessors[oasVersion][ruleId] || 'off';
if (typeof settings === 'string') { if (typeof settings === 'string') {
return { return {
severity: settings === 'on' ? ('error' as 'error') : settings, severity: settings === 'on' ? 'error' : settings,
}; };
} else { } else {
return { severity: 'error' as 'error', ...settings }; return { severity: 'error', ...settings };
} }
} }
@@ -216,10 +218,10 @@ export class StyleguideConfig {
const settings = this.decorators[oasVersion][ruleId] || 'off'; const settings = this.decorators[oasVersion][ruleId] || 'off';
if (typeof settings === 'string') { if (typeof settings === 'string') {
return { return {
severity: settings === 'on' ? ('error' as 'error') : settings, severity: settings === 'on' ? 'error' : settings,
}; };
} else { } else {
return { severity: 'error' as 'error', ...settings }; return { severity: 'error', ...settings };
} }
} }
@@ -250,12 +252,14 @@ export class StyleguideConfig {
getRulesForOasVersion(version: OasMajorVersion) { getRulesForOasVersion(version: OasMajorVersion) {
switch (version) { switch (version) {
case OasMajorVersion.Version3: case OasMajorVersion.Version3:
// eslint-disable-next-line no-case-declarations
const oas3Rules: Oas3RuleSet[] = []; // default ruleset const oas3Rules: Oas3RuleSet[] = []; // default ruleset
this.plugins.forEach((p) => p.preprocessors?.oas3 && oas3Rules.push(p.preprocessors.oas3)); this.plugins.forEach((p) => p.preprocessors?.oas3 && oas3Rules.push(p.preprocessors.oas3));
this.plugins.forEach((p) => p.rules?.oas3 && oas3Rules.push(p.rules.oas3)); this.plugins.forEach((p) => p.rules?.oas3 && oas3Rules.push(p.rules.oas3));
this.plugins.forEach((p) => p.decorators?.oas3 && oas3Rules.push(p.decorators.oas3)); this.plugins.forEach((p) => p.decorators?.oas3 && oas3Rules.push(p.decorators.oas3));
return oas3Rules; return oas3Rules;
case OasMajorVersion.Version2: case OasMajorVersion.Version2:
// eslint-disable-next-line no-case-declarations
const oas2Rules: Oas2RuleSet[] = []; // default ruleset const oas2Rules: Oas2RuleSet[] = []; // default ruleset
this.plugins.forEach((p) => p.preprocessors?.oas2 && oas2Rules.push(p.preprocessors.oas2)); this.plugins.forEach((p) => p.preprocessors?.oas2 && oas2Rules.push(p.preprocessors.oas2));
this.plugins.forEach((p) => p.rules?.oas2 && oas2Rules.push(p.rules.oas2)); this.plugins.forEach((p) => p.rules?.oas2 && oas2Rules.push(p.rules.oas2));

View File

@@ -1,6 +1,7 @@
import { yellow } from 'colorette'; import { yellow } from 'colorette';
import { import {
assignExisting, assignExisting,
isTruthy,
showErrorForDeprecatedField, showErrorForDeprecatedField,
showWarningForDeprecatedField, showWarningForDeprecatedField,
} from '../utils'; } from '../utils';
@@ -83,7 +84,7 @@ export function mergeExtends(rulesConfList: ResolvedStyleguideConfig[]) {
extendPaths: [], extendPaths: [],
}; };
for (let rulesConf of rulesConfList) { for (const rulesConf of rulesConfList) {
if (rulesConf.extends) { if (rulesConf.extends) {
throw new Error( throw new Error(
`'extends' is not supported in shared configs yet: ${JSON.stringify(rulesConf, null, 2)}.` `'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 {
config.rawConfig?.styleguide?.extendPaths, config.rawConfig?.styleguide?.extendPaths,
] ]
.flat() .flat()
.filter(Boolean) as string[]; .filter(isTruthy);
const pluginPaths = [ const pluginPaths = [
...Object.values(config.apis).map((api) => api?.styleguide?.pluginPaths), ...Object.values(config.apis).map((api) => api?.styleguide?.pluginPaths),
config.rawConfig?.styleguide?.pluginPaths, config.rawConfig?.styleguide?.pluginPaths,
] ]
.flat() .flat()
.filter(Boolean) as string[]; .filter(isTruthy);
return apiName return apiName
? new Config( ? new Config(

View File

@@ -4,7 +4,7 @@ import { isRedoclyRegistryURL } from '../../redocly';
import { Oas3Decorator, Oas2Decorator } from '../../visitors'; import { Oas3Decorator, Oas2Decorator } from '../../visitors';
export const RegistryDependencies: Oas3Decorator | Oas2Decorator = () => { export const RegistryDependencies: Oas3Decorator | Oas2Decorator = () => {
let registryDependencies = new Set<string>(); const registryDependencies = new Set<string>();
return { return {
DefinitionRoot: { DefinitionRoot: {

View File

@@ -36,8 +36,8 @@ export function getCodeframe(location: LineColLocationObject, color: boolean) {
if (skipLines > 0 && i >= endLineNum - skipLines) break; if (skipLines > 0 && i >= endLineNum - skipLines) break;
const line = lines[i - 1] || ''; const line = lines[i - 1] || '';
if (line !== '') currentPad = padSize(line); if (line !== '') currentPad = padSize(line);
let startIdx = i === startLineNum ? start.col - 1 : currentPad; const startIdx = i === startLineNum ? start.col - 1 : currentPad;
let endIdx = i === endLineNum ? end.col - 1 : line.length; const endIdx = i === endLineNum ? end.col - 1 : line.length;
prefixedLines.push([`${i}`, markLine(line, startIdx, endIdx, red)]); prefixedLines.push([`${i}`, markLine(line, startIdx, endIdx, red)]);
if (!color) prefixedLines.push(['', underlineLine(line, startIdx, endIdx)]); if (!color) prefixedLines.push(['', underlineLine(line, startIdx, endIdx)]);

View File

@@ -185,7 +185,7 @@ export function formatProblems(
totals, totals,
version, version,
problems: problems.map((p) => { problems: problems.map((p) => {
let problem = { const problem = {
...p, ...p,
location: p.location.map((location: any) => ({ location: p.location.map((location: any) => ({
...location, ...location,
@@ -320,6 +320,7 @@ const groupByFiles = (problems: NormalizedProblem[]) => {
}; };
function xmlEscape(s: string): string { function xmlEscape(s: string): string {
// eslint-disable-next-line no-control-regex
return s.replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, (char) => { return s.replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, (char) => {
switch (char) { switch (char) {
case '<': case '<':

View File

@@ -1,9 +1,9 @@
export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist } from './utils'; export { BundleOutputFormat, readFileFromUrl, slash, doesYamlFileExist, isTruthy } from './utils';
export { Oas3_1Types } from './types/oas3_1'; export { Oas3_1Types } from './types/oas3_1';
export { Oas3Types } from './types/oas3'; export { Oas3Types } from './types/oas3';
export { Oas2Types } from './types/oas2'; export { Oas2Types } from './types/oas2';
export { ConfigTypes } from './types/redocly-yaml'; export { ConfigTypes } from './types/redocly-yaml';
export { export type {
Oas3Definition, Oas3Definition,
Oas3_1Definition, Oas3_1Definition,
Oas3Components, Oas3Components,
@@ -15,9 +15,10 @@ export {
Oas3Tag, Oas3Tag,
Oas3_1Webhooks, Oas3_1Webhooks,
Referenced, Referenced,
OasRef,
} from './typings/openapi'; } from './typings/openapi';
export { Oas2Definition } from './typings/swagger'; export type { Oas2Definition } from './typings/swagger';
export { StatsAccumulator, StatsName } from './typings/common'; export type { StatsAccumulator, StatsName } from './typings/common';
export { normalizeTypes } from './types'; export { normalizeTypes } from './types';
export { Stats } from './rules/other/stats'; export { Stats } from './rules/other/stats';

View File

@@ -1,4 +1,5 @@
// TODO: add a type for "types" https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/js-yaml/index.d.ts // TODO: add a type for "types" https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/js-yaml/index.d.ts
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
import { JSON_SCHEMA, types, LoadOptions, DumpOptions, load, dump } from 'js-yaml'; import { JSON_SCHEMA, types, LoadOptions, DumpOptions, load, dump } from 'js-yaml';

View File

@@ -152,7 +152,7 @@ export class RedoclyClient {
const credentials = { const credentials = {
...this.readCredentialsFile(credentialsPath), ...this.readCredentialsFile(credentialsPath),
[this.region!]: accessToken, [this.region]: accessToken,
token: accessToken, // FIXME: backward compatibility, remove on 1.0.0 token: accessToken, // FIXME: backward compatibility, remove on 1.0.0
}; };
this.accessTokens = credentials; this.accessTokens = credentials;

View File

@@ -1,34 +1,32 @@
export namespace RegistryApiTypes { interface VersionParams {
interface VersionParams { organizationId: string;
organizationId: string; name: string;
name: string; version: string;
version: string; }
}
export interface PrepareFileuploadParams extends VersionParams {
export interface PrepareFileuploadParams extends VersionParams { filesHash: string;
filesHash: string; filename: string;
filename: string; isUpsert?: boolean;
isUpsert?: boolean; }
}
export interface PushApiParams extends VersionParams {
export interface PushApiParams extends VersionParams { rootFilePath: string;
rootFilePath: string; filePaths: string[];
filePaths: string[]; branch?: string;
branch?: string; isUpsert?: boolean;
isUpsert?: boolean; isPublic?: boolean;
isPublic?: boolean; batchId?: string;
batchId?: string; batchSize?: number;
batchSize?: number; }
}
export interface PrepareFileuploadOKResponse {
export interface PrepareFileuploadOKResponse { filePath: string;
filePath: string; signedUploadUrl: string;
signedUploadUrl: string; }
}
export interface NotFoundProblemResponse {
export interface NotFoundProblemResponse { status: 404;
status: 404; title: 'Not Found';
title: 'Not Found'; code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
}
} }

View File

@@ -1,11 +1,15 @@
import fetch, { RequestInit, HeadersInit } from 'node-fetch'; import fetch, { RequestInit, HeadersInit } from 'node-fetch';
import { RegistryApiTypes } from './registry-api-types'; import type {
NotFoundProblemResponse,
PrepareFileuploadOKResponse,
PrepareFileuploadParams,
PushApiParams,
} from './registry-api-types';
import type { AccessTokens, Region } from '../config/types';
import { DEFAULT_REGION, DOMAINS } from '../config/config'; import { DEFAULT_REGION, DOMAINS } from '../config/config';
import { isNotEmptyObject } from '../utils'; import { isNotEmptyObject } from '../utils';
const version = require('../../package.json').version; const version = require('../../package.json').version;
import type { AccessTokens, Region } from '../config/types';
export class RegistryApi { export class RegistryApi {
constructor(private accessTokens: AccessTokens, private region: Region) {} constructor(private accessTokens: AccessTokens, private region: Region) {}
@@ -39,7 +43,7 @@ export class RegistryApi {
} }
if (response.status === 404) { if (response.status === 404) {
const body: RegistryApiTypes.NotFoundProblemResponse = await response.json(); const body: NotFoundProblemResponse = await response.json();
throw new Error(body.code); throw new Error(body.code);
} }
@@ -71,7 +75,7 @@ export class RegistryApi {
filesHash, filesHash,
filename, filename,
isUpsert, isUpsert,
}: RegistryApiTypes.PrepareFileuploadParams): Promise<RegistryApiTypes.PrepareFileuploadOKResponse> { }: PrepareFileuploadParams): Promise<PrepareFileuploadOKResponse> {
const response = await this.request( const response = await this.request(
`/${organizationId}/${name}/${version}/prepare-file-upload`, `/${organizationId}/${name}/${version}/prepare-file-upload`,
{ {
@@ -107,7 +111,7 @@ export class RegistryApi {
isPublic, isPublic,
batchId, batchId,
batchSize, batchSize,
}: RegistryApiTypes.PushApiParams) { }: PushApiParams) {
const response = await this.request( const response = await this.request(
`/${organizationId}/${name}/${version}`, `/${organizationId}/${name}/${version}`,
{ {

View File

@@ -1,5 +1,6 @@
import { Source } from './resolve'; import { Source } from './resolve';
import { OasRef } from './typings/openapi'; import { OasRef } from './typings/openapi';
import { isTruthy } from './utils';
export function joinPointer(base: string, key: string | number) { export function joinPointer(base: string, key: string | number) {
if (base === '') base = '#/'; if (base === '') base = '#/';
@@ -45,7 +46,7 @@ export function parseRef(ref: string): { uri: string | null; pointer: string[] }
const [uri, pointer] = ref.split('#/'); const [uri, pointer] = ref.split('#/');
return { return {
uri: uri || null, uri: uri || null,
pointer: pointer ? pointer.split('/').map(unescapePointer).filter(Boolean) : [], pointer: pointer ? pointer.split('/').map(unescapePointer).filter(isTruthy) : [],
}; };
} }

View File

@@ -323,7 +323,7 @@ export async function resolveDocument(opts: {
: document; : document;
} catch (error) { } catch (error) {
const resolvedRef = { const resolvedRef = {
resolved: false as false, resolved: false as const,
isRemote, isRemote,
document: undefined, document: undefined,
error: error, error: error,
@@ -334,7 +334,7 @@ export async function resolveDocument(opts: {
} }
let resolvedRef: ResolvedRef = { let resolvedRef: ResolvedRef = {
resolved: true as true, resolved: true as const,
document: targetDoc, document: targetDoc,
isRemote, isRemote,
node: document.parsed, node: document.parsed,
@@ -344,7 +344,7 @@ export async function resolveDocument(opts: {
let target = targetDoc.parsed as any; let target = targetDoc.parsed as any;
const segments = pointer; const segments = pointer;
for (let segment of segments) { for (const segment of segments) {
if (typeof target !== 'object') { if (typeof target !== 'object') {
target = undefined; target = undefined;
break; break;

View File

@@ -73,7 +73,7 @@ export function validateJsonSchema(
function beatifyErrorMessage(error: ErrorObject) { function beatifyErrorMessage(error: ErrorObject) {
let message = error.message; let message = error.message;
let suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined; const suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
if (suggest) { if (suggest) {
message += ` ${suggest.map((e: any) => `"${e}"`).join(', ')}`; message += ` ${suggest.map((e: any) => `"${e}"`).join(', ')}`;
} }

View File

@@ -46,7 +46,7 @@ export const asserts: Asserts = {
if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
const values = runOnValue(value) ? [value] : value; const values = runOnValue(value) ? [value] : value;
const regx = regexFromString(condition); const regx = regexFromString(condition);
for (let _val of values) { for (const _val of values) {
if (!regx?.test(_val)) { if (!regx?.test(_val)) {
return { isValid: false, location: runOnValue(value) ? baseLocation : baseLocation.key() }; return { isValid: false, location: runOnValue(value) ? baseLocation : baseLocation.key() };
} }
@@ -56,7 +56,7 @@ export const asserts: Asserts = {
enum: (value: string | string[], condition: string[], baseLocation: Location) => { enum: (value: string | string[], condition: string[], baseLocation: Location) => {
if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
const values = runOnValue(value) ? [value] : value; const values = runOnValue(value) ? [value] : value;
for (let _val of values) { for (const _val of values) {
if (!condition.includes(_val)) { if (!condition.includes(_val)) {
return { return {
isValid: false, isValid: false,
@@ -81,7 +81,7 @@ export const asserts: Asserts = {
disallowed: (value: string | string[], condition: string[], baseLocation: Location) => { disallowed: (value: string | string[], condition: string[], baseLocation: Location) => {
if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
const values = runOnValue(value) ? [value] : value; const values = runOnValue(value) ? [value] : value;
for (let _val of values) { for (const _val of values) {
if (condition.includes(_val)) { if (condition.includes(_val)) {
return { return {
isValid: false, isValid: false,
@@ -114,7 +114,7 @@ export const asserts: Asserts = {
casing: (value: string | string[], condition: string, baseLocation: Location) => { casing: (value: string | string[], condition: string, baseLocation: Location) => {
if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert if (typeof value === 'undefined') return { isValid: true }; // property doesn't exist, no need to lint it with this assert
const values: string[] = runOnValue(value) ? [value] : value; const values: string[] = runOnValue(value) ? [value] : value;
for (let _val of values) { for (const _val of values) {
let matchCase = false; let matchCase = false;
switch (condition) { switch (condition) {
case 'camelCase': case 'camelCase':

View File

@@ -3,7 +3,7 @@ import { AssertToApply, buildSubjectVisitor, buildVisitorObject } from './utils'
import { Oas2Rule, Oas3Rule } from '../../../visitors'; import { Oas2Rule, Oas3Rule } from '../../../visitors';
export const Assertions: Oas3Rule | Oas2Rule = (opts: object) => { export const Assertions: Oas3Rule | Oas2Rule = (opts: object) => {
let visitors: any[] = []; const visitors: any[] = [];
// As 'Assertions' has an array of asserts, // As 'Assertions' has an array of asserts,
// that array spreads into an 'opts' object on init rules phase here // that array spreads into an 'opts' object on init rules phase here

View File

@@ -5,7 +5,7 @@ import { Oas2SecurityScheme } from '../../typings/swagger';
import { Oas3SecurityScheme } from '../../typings/openapi'; import { Oas3SecurityScheme } from '../../typings/openapi';
export const OperationSecurityDefined: Oas3Rule | Oas2Rule = () => { export const OperationSecurityDefined: Oas3Rule | Oas2Rule = () => {
let referencedSchemes = new Map< const referencedSchemes = new Map<
string, string,
{ {
defined?: boolean; defined?: boolean;

View File

@@ -28,7 +28,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
const required = const required =
typeof type.required === 'function' ? type.required(node, key) : type.required; typeof type.required === 'function' ? type.required(node, key) : type.required;
for (let propName of required || []) { for (const propName of required || []) {
if (!(node as object).hasOwnProperty(propName)) { if (!(node as object).hasOwnProperty(propName)) {
report({ report({
message: `The field \`${propName}\` must be present on this level.`, message: `The field \`${propName}\` must be present on this level.`,
@@ -57,7 +57,7 @@ export const OasSpec: Oas3Rule | Oas2Rule = () => {
const requiredOneOf = type.requiredOneOf || null; const requiredOneOf = type.requiredOneOf || null;
if (requiredOneOf) { if (requiredOneOf) {
let hasProperty = false; let hasProperty = false;
for (let propName of requiredOneOf || []) { for (const propName of requiredOneOf || []) {
if ((node as object).hasOwnProperty(propName)) { if ((node as object).hasOwnProperty(propName)) {
hasProperty = true; hasProperty = true;
} }

View File

@@ -4,7 +4,7 @@ import { Oas2Components } from '../../typings/swagger';
import { isEmptyObject } from '../../utils'; import { isEmptyObject } from '../../utils';
export const RemoveUnusedComponents: Oas2Rule = () => { export const RemoveUnusedComponents: Oas2Rule = () => {
let components = new Map< const components = new Map<
string, string,
{ used: boolean; componentType?: keyof Oas2Components; name: string } { used: boolean; componentType?: keyof Oas2Components; name: string }
>(); >();
@@ -39,7 +39,7 @@ export const RemoveUnusedComponents: Oas2Rule = () => {
const data = ctx.getVisitorData() as { removedCount: number }; const data = ctx.getVisitorData() as { removedCount: number };
data.removedCount = 0; data.removedCount = 0;
let rootComponents = new Set<keyof Oas2Components>(); const rootComponents = new Set<keyof Oas2Components>();
components.forEach((usageInfo) => { components.forEach((usageInfo) => {
const { used, name, componentType } = usageInfo; const { used, name, componentType } = usageInfo;
if (!used && componentType) { if (!used && componentType) {

View File

@@ -48,7 +48,7 @@ function checkEnumVariables(server: Oas3Server): enumError[] | undefined {
if (server.variables && Object.keys(server.variables).length === 0) return; if (server.variables && Object.keys(server.variables).length === 0) return;
const errors: enumError[] = []; const errors: enumError[] = [];
for (var variable in server.variables) { for (const variable in server.variables) {
const serverVariable = server.variables[variable]; const serverVariable = server.variables[variable];
if (!serverVariable.enum) continue; if (!serverVariable.enum) continue;

View File

@@ -2,7 +2,7 @@ import { Oas3Rule } from '../../visitors';
import { Location } from '../../ref-utils'; import { Location } from '../../ref-utils';
export const NoUnusedComponents: Oas3Rule = () => { export const NoUnusedComponents: Oas3Rule = () => {
let components = new Map<string, { used: boolean; location: Location; name: string }>(); const components = new Map<string, { used: boolean; location: Location; name: string }>();
function registerComponent(location: Location, name: string): void { function registerComponent(location: Location, name: string): void {
components.set(location.absolutePointer, { components.set(location.absolutePointer, {

View File

@@ -4,7 +4,7 @@ import { Oas3Components } from '../../typings/openapi';
import { isEmptyObject } from '../../utils'; import { isEmptyObject } from '../../utils';
export const RemoveUnusedComponents: Oas3Rule = () => { export const RemoveUnusedComponents: Oas3Rule = () => {
let components = new Map< const components = new Map<
string, string,
{ used: boolean; componentType?: keyof Oas3Components; name: string } { used: boolean; componentType?: keyof Oas3Components; name: string }
>(); >();
@@ -45,12 +45,12 @@ export const RemoveUnusedComponents: Oas3Rule = () => {
components.forEach((usageInfo) => { components.forEach((usageInfo) => {
const { used, componentType, name } = usageInfo; const { used, componentType, name } = usageInfo;
if (!used && componentType) { if (!used && componentType && root.components) {
let componentChild = root.components![componentType]; const componentChild = root.components[componentType];
delete componentChild![name]; delete componentChild![name];
data.removedCount++; data.removedCount++;
if (isEmptyObject(componentChild)) { if (isEmptyObject(componentChild)) {
delete root.components![componentType]; delete root.components[componentType];
} }
} }
}); });

View File

@@ -104,7 +104,7 @@ export function validateExample(
allowAdditionalProperties allowAdditionalProperties
); );
if (!valid) { if (!valid) {
for (let error of errors) { for (const error of errors) {
report({ report({
message: `Example value must conform to the schema: ${error.message}.`, message: `Example value must conform to the schema: ${error.message}.`,
location: { location: {

View File

@@ -96,7 +96,7 @@ export function omitObjectProps<T extends Record<string, unknown>>(
export function splitCamelCaseIntoWords(str: string) { export function splitCamelCaseIntoWords(str: string) {
const camel = str const camel = str
.split(/(?:[-._])|([A-Z][a-z]+)/) .split(/(?:[-._])|([A-Z][a-z]+)/)
.filter(Boolean) .filter(isTruthy)
.map((item) => item.toLocaleLowerCase()); .map((item) => item.toLocaleLowerCase());
const caps = str const caps = str
.split(/([A-Z]{2,})/) .split(/([A-Z]{2,})/)
@@ -183,7 +183,7 @@ export function isNotString<T>(value: string | T): value is T {
} }
export function assignExisting<T>(target: Record<string, T>, obj: Record<string, T>) { export function assignExisting<T>(target: Record<string, T>, obj: Record<string, T>) {
for (let k of Object.keys(obj)) { for (const k of Object.keys(obj)) {
if (target.hasOwnProperty(k)) { if (target.hasOwnProperty(k)) {
target[k] = obj[k]; target[k] = obj[k];
} }
@@ -217,3 +217,9 @@ export function showWarningForDeprecatedField(deprecatedField: string, updatedFi
export function showErrorForDeprecatedField(deprecatedField: string, updatedField: string) { export function showErrorForDeprecatedField(deprecatedField: string, updatedField: string) {
throw new Error(`Do not use '${deprecatedField}' field. Use '${updatedField}' instead.\n`); throw new Error(`Do not use '${deprecatedField}' field. Use '${updatedField}' instead.\n`);
} }
export type Falsy = undefined | null | false | '' | 0;
export function isTruthy<Truthy>(value: Truthy | Falsy): value is Truthy {
return !!value;
}

View File

@@ -70,7 +70,7 @@ type NestedVisitObject<T, P> = VisitObject<T> & NestedVisitor<P>;
type VisitFunctionOrObject<T> = VisitFunction<T> | VisitObject<T>; type VisitFunctionOrObject<T> = VisitFunction<T> | VisitObject<T>;
type VisitorNode<T extends any> = { type VisitorNode<T> = {
ruleId: string; ruleId: string;
severity: ProblemSeverity; severity: ProblemSeverity;
context: VisitorLevelContext | VisitorSkippedLevelContext; context: VisitorLevelContext | VisitorSkippedLevelContext;
@@ -309,7 +309,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
const possibleChildren = new Set<NormalizedNodeType>(); const possibleChildren = new Set<NormalizedNodeType>();
for (let type of Object.values(from.properties)) { for (const type of Object.values(from.properties)) {
if (type === to) { if (type === to) {
addWeakFromStack(ruleConf, stack); addWeakFromStack(ruleConf, stack);
continue; continue;
@@ -333,7 +333,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
} }
} }
for (let fromType of Array.from(possibleChildren.values())) { for (const fromType of Array.from(possibleChildren.values())) {
addWeakNodes(ruleConf, fromType, to, parentContext, stack); addWeakNodes(ruleConf, fromType, to, parentContext, stack);
} }
@@ -350,7 +350,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
visit: () => undefined, visit: () => undefined,
depth: 0, depth: 0,
context: { context: {
isSkippedLevel: true as true, isSkippedLevel: true,
seen: new Set(), seen: new Set(),
parent: parentContext, parent: parentContext,
}, },
@@ -407,7 +407,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
activatedOn: null, activatedOn: null,
type: types[typeName], type: types[typeName],
parent: parentContext, parent: parentContext,
isSkippedLevel: false as false, isSkippedLevel: false,
}; };
if (typeof typeVisitor === 'object') { if (typeof typeVisitor === 'object') {

View File

@@ -8,8 +8,6 @@
"noImplicitReturns": true, "noImplicitReturns": true,
"noImplicitThis": true, "noImplicitThis": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"strictPropertyInitialization": true, "strictPropertyInitialization": true,
"strictNullChecks": true, "strictNullChecks": true,
"strictFunctionTypes": true, "strictFunctionTypes": true,