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: |
npm-${{ hashFiles('package-lock.json') }}
npm-
- name: Install dependencies
run: npm ci
env:
CI: true
- 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,
branches: 71,
functions: 68,
lines: 78,
lines: 79,
},
'packages/cli/': {
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__/",
"prettier": "npx prettier --write \"**/*.{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",
"watch": "tsc -b tsconfig.build.json --watch ",
"compile": "tsc -b tsconfig.build.json",
@@ -57,6 +58,9 @@
"devDependencies": {
"@types/jest": "^26.0.15",
"@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",
"null-loader": "^4.0.0",
"outdent": "^0.7.1",

View File

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

View File

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

View File

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

View File

@@ -99,6 +99,7 @@ export default async function startPreviewServer(
}
} else {
let filePath =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
{
'/hot.js': path.join(__dirname, 'hot.js'),
@@ -142,7 +143,7 @@ export default async function startPreviewServer(
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);
server.on('listening', () => {

View File

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

View File

@@ -1,6 +1,7 @@
import { red, blue, yellow, green } from 'colorette';
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 { performance } from 'perf_hooks';
const isEqual = require('lodash.isequal');
@@ -267,11 +268,11 @@ function gatherComponentsFiles(
componentName: string,
filename: string
) {
let inherits = [];
let inherits: string[] = [];
if (componentType === OPENAPI3_COMPONENT.Schemas) {
inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
.map((s: any) => s.$ref)
.filter(Boolean);
.map(({ $ref }) => $ref)
.filter(isTruthy);
}
componentsFiles[componentType] = componentsFiles[componentType] || {};
componentsFiles[componentType][componentName] = { inherits, filename };
@@ -301,7 +302,7 @@ function iteratePathItems(
continue;
}
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(
openapiDir,
'code_samples',
@@ -312,6 +313,7 @@ function iteratePathItems(
fs.mkdirSync(path.dirname(sampleFileName), { recursive: true });
fs.writeFileSync(sampleFileName, sample.source);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
sample.source = {
$ref: slash(path.relative(outDir, sampleFileName)),
@@ -340,6 +342,7 @@ function iterateComponents(
componentTypes.forEach(iterateAndGatherComponentsFiles);
componentTypes.forEach(iterateComponentTypes);
// eslint-disable-next-line no-inner-declarations
function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
const componentDirPath = path.join(componentsDir, 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) {
const componentDirPath = path.join(componentsDir, 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`);
// TODO: codeframe
} else {
// @ts-ignore
if (e instanceof CircularJSONNotSupportedError) {
process.stderr.write(
red(`Detected circular reference which can't be converted to JSON.\n`) +

View File

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

View File

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

View File

@@ -1,3 +1,4 @@
import { OasVersion } from '../../oas-types';
import { Config, StyleguideConfig } from '../config';
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[] {
if (!plugins) return [];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const requireFunc = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
@@ -166,7 +167,7 @@ export async function resolveApis({
resolver?: BaseResolver;
}): Promise<Record<string, ResolvedApi>> {
const { apis = {}, styleguide: styleguideConfig = {} } = rawConfig;
let resolvedApis: Record<string, ResolvedApi> = {};
const resolvedApis: Record<string, ResolvedApi> = {};
for (const [apiName, apiContent] of Object.entries(apis || {})) {
if (apiContent.styleguide?.extends?.some(isNotString)) {
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.`);
}
const preset = plugin.configs?.[configName]! as ResolvedStyleguideConfig;
const preset = plugin.configs?.[configName];
if (!preset) {
throw new Error(
pluginId

View File

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

View File

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

View File

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

View File

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

View File

@@ -185,7 +185,7 @@ export function formatProblems(
totals,
version,
problems: problems.map((p) => {
let problem = {
const problem = {
...p,
location: p.location.map((location: any) => ({
...location,
@@ -320,6 +320,7 @@ const groupByFiles = (problems: NormalizedProblem[]) => {
};
function xmlEscape(s: string): string {
// eslint-disable-next-line no-control-regex
return s.replace(/[<>&"'\x00-\x1F\x7F\u0080-\uFFFF]/gu, (char) => {
switch (char) {
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 { Oas3Types } from './types/oas3';
export { Oas2Types } from './types/oas2';
export { ConfigTypes } from './types/redocly-yaml';
export {
export type {
Oas3Definition,
Oas3_1Definition,
Oas3Components,
@@ -15,9 +15,10 @@ export {
Oas3Tag,
Oas3_1Webhooks,
Referenced,
OasRef,
} from './typings/openapi';
export { Oas2Definition } from './typings/swagger';
export { StatsAccumulator, StatsName } from './typings/common';
export type { Oas2Definition } from './typings/swagger';
export type { StatsAccumulator, StatsName } from './typings/common';
export { normalizeTypes } from './types';
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
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import { JSON_SCHEMA, types, LoadOptions, DumpOptions, load, dump } from 'js-yaml';

View File

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

View File

@@ -1,4 +1,3 @@
export namespace RegistryApiTypes {
interface VersionParams {
organizationId: string;
name: string;
@@ -31,4 +30,3 @@ export namespace RegistryApiTypes {
title: 'Not Found';
code: 'ORGANIZATION_NOT_FOUND' | 'API_VERSION_NOT_FOUND';
}
}

View File

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

View File

@@ -1,5 +1,6 @@
import { Source } from './resolve';
import { OasRef } from './typings/openapi';
import { isTruthy } from './utils';
export function joinPointer(base: string, key: string | number) {
if (base === '') base = '#/';
@@ -45,7 +46,7 @@ export function parseRef(ref: string): { uri: string | null; pointer: string[] }
const [uri, pointer] = ref.split('#/');
return {
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;
} catch (error) {
const resolvedRef = {
resolved: false as false,
resolved: false as const,
isRemote,
document: undefined,
error: error,
@@ -334,7 +334,7 @@ export async function resolveDocument(opts: {
}
let resolvedRef: ResolvedRef = {
resolved: true as true,
resolved: true as const,
document: targetDoc,
isRemote,
node: document.parsed,
@@ -344,7 +344,7 @@ export async function resolveDocument(opts: {
let target = targetDoc.parsed as any;
const segments = pointer;
for (let segment of segments) {
for (const segment of segments) {
if (typeof target !== 'object') {
target = undefined;
break;

View File

@@ -73,7 +73,7 @@ export function validateJsonSchema(
function beatifyErrorMessage(error: ErrorObject) {
let message = error.message;
let suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
const suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
if (suggest) {
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
const values = runOnValue(value) ? [value] : value;
const regx = regexFromString(condition);
for (let _val of values) {
for (const _val of values) {
if (!regx?.test(_val)) {
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) => {
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;
for (let _val of values) {
for (const _val of values) {
if (!condition.includes(_val)) {
return {
isValid: false,
@@ -81,7 +81,7 @@ export const asserts: Asserts = {
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
const values = runOnValue(value) ? [value] : value;
for (let _val of values) {
for (const _val of values) {
if (condition.includes(_val)) {
return {
isValid: false,
@@ -114,7 +114,7 @@ export const asserts: Asserts = {
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
const values: string[] = runOnValue(value) ? [value] : value;
for (let _val of values) {
for (const _val of values) {
let matchCase = false;
switch (condition) {
case 'camelCase':

View File

@@ -3,7 +3,7 @@ import { AssertToApply, buildSubjectVisitor, buildVisitorObject } from './utils'
import { Oas2Rule, Oas3Rule } from '../../../visitors';
export const Assertions: Oas3Rule | Oas2Rule = (opts: object) => {
let visitors: any[] = [];
const visitors: any[] = [];
// As 'Assertions' has an array of asserts,
// 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';
export const OperationSecurityDefined: Oas3Rule | Oas2Rule = () => {
let referencedSchemes = new Map<
const referencedSchemes = new Map<
string,
{
defined?: boolean;

View File

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

View File

@@ -4,7 +4,7 @@ import { Oas2Components } from '../../typings/swagger';
import { isEmptyObject } from '../../utils';
export const RemoveUnusedComponents: Oas2Rule = () => {
let components = new Map<
const components = new Map<
string,
{ used: boolean; componentType?: keyof Oas2Components; name: string }
>();
@@ -39,7 +39,7 @@ export const RemoveUnusedComponents: Oas2Rule = () => {
const data = ctx.getVisitorData() as { removedCount: number };
data.removedCount = 0;
let rootComponents = new Set<keyof Oas2Components>();
const rootComponents = new Set<keyof Oas2Components>();
components.forEach((usageInfo) => {
const { used, name, componentType } = usageInfo;
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;
const errors: enumError[] = [];
for (var variable in server.variables) {
for (const variable in server.variables) {
const serverVariable = server.variables[variable];
if (!serverVariable.enum) continue;

View File

@@ -2,7 +2,7 @@ import { Oas3Rule } from '../../visitors';
import { Location } from '../../ref-utils';
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 {
components.set(location.absolutePointer, {

View File

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

View File

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

View File

@@ -96,7 +96,7 @@ export function omitObjectProps<T extends Record<string, unknown>>(
export function splitCamelCaseIntoWords(str: string) {
const camel = str
.split(/(?:[-._])|([A-Z][a-z]+)/)
.filter(Boolean)
.filter(isTruthy)
.map((item) => item.toLocaleLowerCase());
const caps = str
.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>) {
for (let k of Object.keys(obj)) {
for (const k of Object.keys(obj)) {
if (target.hasOwnProperty(k)) {
target[k] = obj[k];
}
@@ -217,3 +217,9 @@ export function showWarningForDeprecatedField(deprecatedField: string, updatedFi
export function showErrorForDeprecatedField(deprecatedField: string, updatedField: string) {
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 VisitorNode<T extends any> = {
type VisitorNode<T> = {
ruleId: string;
severity: ProblemSeverity;
context: VisitorLevelContext | VisitorSkippedLevelContext;
@@ -309,7 +309,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
const possibleChildren = new Set<NormalizedNodeType>();
for (let type of Object.values(from.properties)) {
for (const type of Object.values(from.properties)) {
if (type === to) {
addWeakFromStack(ruleConf, stack);
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);
}
@@ -350,7 +350,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
visit: () => undefined,
depth: 0,
context: {
isSkippedLevel: true as true,
isSkippedLevel: true,
seen: new Set(),
parent: parentContext,
},
@@ -407,7 +407,7 @@ export function normalizeVisitors<T extends BaseVisitor>(
activatedOn: null,
type: types[typeName],
parent: parentContext,
isSkippedLevel: false as false,
isSkippedLevel: false,
};
if (typeof typeVisitor === 'object') {

View File

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