mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 04:21:09 +00:00
Chore/configure eslint (#808)
This commit is contained in:
29
.eslintrc.yml
Normal file
29
.eslintrc.yml
Normal 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'
|
||||||
22
.github/workflows/tests.yaml
vendored
22
.github/workflows/tests.yaml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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
1720
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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++;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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', () => {
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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`) +
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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)]);
|
||||||
|
|||||||
@@ -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 '<':
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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}`,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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) : [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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(', ')}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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':
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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, {
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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') {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user