chore: refactored cli commands handlers

This commit is contained in:
Andriy Leliv
2020-11-17 17:47:24 +02:00
parent 6abc6501a2
commit 009db6f640
8 changed files with 410 additions and 347 deletions

59
package-lock.json generated
View File

@@ -2014,6 +2014,19 @@
"fsevents": "~2.1.2"
}
},
"node_modules/chokidar/node_modules/fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
@@ -3526,9 +3539,10 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz",
"integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==",
"dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -8315,6 +8329,18 @@
"node": ">=4.2.0"
}
},
"node_modules/uglify-js": {
"version": "3.11.6",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.6.tgz",
"integrity": "sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==",
"extraneous": true,
"bin": {
"uglifyjs": "bin/uglifyjs"
},
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/union-value": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
@@ -9746,11 +9772,10 @@
}
},
"packages/cli": {
"name": "@redocly/openapi-cli",
"version": "1.0.0-beta.22",
"license": "MIT",
"dependencies": {
"@redocly/openapi-core": "^1.0.0-beta.19",
"@redocly/openapi-core": "^1.0.0-beta.22",
"@types/node": "^14.11.8",
"chokidar": "^3.4.0",
"colorette": "^1.2.0",
@@ -9774,7 +9799,6 @@
}
},
"packages/core": {
"name": "@redocly/openapi-core",
"version": "1.0.0-beta.22",
"license": "MIT",
"dependencies": {
@@ -10442,7 +10466,7 @@
"@redocly/openapi-cli": {
"version": "file:packages/cli",
"requires": {
"@redocly/openapi-core": "^1.0.0-beta.19",
"@redocly/openapi-core": "^1.0.0-beta.22",
"@types/node": "^14.11.8",
"@types/yargs": "^15.0.5",
"chokidar": "^3.4.0",
@@ -11543,6 +11567,14 @@
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
"readdirp": "~3.5.0"
},
"dependencies": {
"fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"optional": true
}
}
},
"chownr": {
@@ -12798,9 +12830,10 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
"integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.2.1.tgz",
"integrity": "sha512-bTLYHSeC0UH/EFXS9KqWnXuOl/wHK5Z/d+ghd5AsFMYN7wIGkUCOJyzy88+wJKkZPGON8u4Z9f6U4FdgURE9qA==",
"dev": true,
"optional": true
},
"function-bind": {
@@ -16567,6 +16600,12 @@
"integrity": "sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==",
"dev": true
},
"uglify-js": {
"version": "3.11.6",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.11.6.tgz",
"integrity": "sha512-oASI1FOJ7BBFkSCNDZ446EgkSuHkOZBuqRFrwXIKWCoXw8ZXQETooTQjkAcBS03Acab7ubCKsXnwuV2svy061g==",
"extraneous": true
},
"union-value": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",

View File

@@ -34,7 +34,7 @@
"Andriy Leliv <andriy@redoc.ly> (https://redoc.ly/)"
],
"dependencies": {
"@redocly/openapi-core": "^1.0.0-beta.19",
"@redocly/openapi-core": "^1.0.0-beta.22",
"@types/node": "^14.11.8",
"yargs": "^15.3.1",
"colorette": "^1.2.0",

View File

@@ -0,0 +1,108 @@
import { bundle, formatProblems, loadConfig, OutputFormat } from '@redocly/openapi-core';
import {
dumpBundle,
getExecutionTime,
getFallbackEntryPointsOrExit,
getOutputFileName,
getTotals, handleError, printUnusedWarnings,
saveBundle,
} from '../utils';
import { OutputExtensions, Totals } from '../types';
import { performance } from "perf_hooks";
import { blue, gray, green, yellow } from 'colorette';
export async function handleBundle (argv: {
entrypoints: string[];
output?: string;
ext: OutputExtensions;
'max-problems'?: number;
'skip-rule'?: string[];
'skip-preprocessor'?: string[];
'skip-decorator'?: string[];
dereferenced?: boolean;
force?: boolean;
config?: string;
format: OutputFormat;
},
version: string
) {
const config = await loadConfig(argv.config);
config.lint.skipRules(argv['skip-rule']);
config.lint.skipPreprocessors(argv['skip-preprocessor']);
config.lint.skipDecorators(argv['skip-decorator']);
const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoints, config);
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
for (const entrypoint of entrypoints) {
try {
const startedAt = performance.now();
process.stderr.write(gray(`bundling ${entrypoint}...\n`));
const { bundle: result, problems } = await bundle({
config,
ref: entrypoint,
dereference: argv.dereferenced,
});
const fileTotals = getTotals(problems);
const { outputFile, ext } = getOutputFileName(
entrypoint,
entrypoints.length,
argv.output,
argv.ext,
);
if (fileTotals.errors === 0 || argv.force) {
if (!argv.output) {
const output = dumpBundle(result, argv.ext || 'yaml', argv.dereferenced);
process.stdout.write(output);
} else {
const output = dumpBundle(result, ext, argv.dereferenced);
saveBundle(outputFile, output);
}
}
totals.errors += fileTotals.errors;
totals.warnings += fileTotals.warnings;
totals.ignored += fileTotals.ignored;
formatProblems(problems, {
format: argv.format,
maxProblems: argv['max-problems'],
totals: fileTotals,
version,
});
const elapsed = getExecutionTime(startedAt);
if (fileTotals.errors > 0) {
if (argv.force) {
process.stderr.write(
`❓ Created a bundle for ${blue(entrypoint)} at ${blue(
outputFile,
)} with errors ${green(elapsed)}.\n${yellow(
'Errors ignored because of --force',
)}.\n`,
);
} else {
process.stderr.write(
`❌ Errors encountered while bundling ${blue(
entrypoint,
)}: bundle not created (use --force to ignore errors).\n`,
);
}
} else {
process.stderr.write(
`📦 Created a bundle for ${blue(entrypoint)} at ${blue(outputFile)} ${green(
elapsed,
)}.\n`,
);
}
} catch (e) {
handleError(e, entrypoint);
}
}
printUnusedWarnings(config.lint);
process.exit(totals.errors === 0 || argv.force ? 0 : 1);
}

View File

@@ -9,11 +9,11 @@ import {
BaseResolver,
Document,
LintConfig,
Oas3Tag,
loadConfig,
formatProblems,
validateDocument,
detectOpenAPI,
Oas3Tag,
detectOpenAPI
} from '@redocly/openapi-core';
import {
@@ -38,7 +38,6 @@ type JoinDocumentContext = {
componentsPrefix: string | undefined
}
export async function handleJoin (argv: {
entrypoints: string[],
lint?: boolean,

View File

@@ -0,0 +1,91 @@
import { Config, formatProblems, loadConfig, OutputFormat, validate } from '@redocly/openapi-core';
import {
getExecutionTime,
getFallbackEntryPointsOrExit,
getTotals,
handleError,
pluralize,
printLintTotals,
printUnusedWarnings
} from '../utils';
import { Totals } from '../types';
import { blue, gray } from 'colorette';
import { performance } from "perf_hooks";
export async function handleLint (argv: {
entrypoints: string[];
'max-problems'?: number;
'generate-ignore-file'?: boolean;
'skip-rule'?: string[];
'skip-preprocessor'?: string[];
extends?: string[];
config?: string;
format: OutputFormat;
},
version: string
) {
const config: Config = await loadConfig(argv.config, argv.extends);
config.lint.skipRules(argv['skip-rule']);
config.lint.skipPreprocessors(argv['skip-preprocessor']);
const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoints, config);
if (argv['generate-ignore-file']) {
config.lint.ignore = {}; // clear ignore
}
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
let totalIgnored = 0;
if (config.lint.recommendedFallback) {
process.stderr.write(
`No configurations were defined in extends -- using built in ${blue('recommended')} configuration by default.\n\n`,
);
}
// TODO: use shared externalRef resolver, blocked by preprocessors now as they can mutate documents
for (const entryPoint of entrypoints) {
try {
const startedAt = performance.now();
process.stderr.write(gray(`validating ${entryPoint}...\n`));
const results = await validate({
ref: entryPoint,
config,
});
const fileTotals = getTotals(results);
totals.errors += fileTotals.errors;
totals.warnings += fileTotals.warnings;
totals.ignored += fileTotals.ignored;
if (argv['generate-ignore-file']) {
for (let m of results) {
config.lint.addIgnore(m);
totalIgnored++;
}
} else {
formatProblems(results, {
format: argv.format,
maxProblems: argv['max-problems'],
totals: fileTotals,
version
});
}
const elapsed = getExecutionTime(startedAt);
process.stderr.write(gray(`${entryPoint}: validated in ${elapsed}\n\n`));
} catch (e) {
totals.errors++;
handleError(e, entryPoint);
}
}
if (argv['generate-ignore-file']) {
config.lint.saveIgnore();
process.stderr.write(
`Generated ignore file with ${totalIgnored} ${pluralize('problem', totalIgnored)}.\n\n`,
);
} else {
printLintTotals(totals, entrypoints.length);
}
printUnusedWarnings(config.lint);
process.exit(totals.errors === 0 || argv['generate-ignore-file'] ? 0 : 1);
}

View File

@@ -1,39 +1,17 @@
#!/usr/bin/env node
import * as yargs from 'yargs';
import { extname, basename, dirname, join } from 'path';
import { green, yellow, blue, gray } from 'colorette';
import { performance } from 'perf_hooks';
import { Totals } from './types';
import {
BundleOutputFormat,
validate,
bundle,
loadConfig,
LintConfig,
RedoclyClient,
formatProblems,
OutputFormat,
} from "@redocly/openapi-core";
import {
getFallbackEntryPointsOrExit,
getExecutionTime,
getTotals,
dumpBundle,
saveBundle,
promptUser,
pluralize,
printLintTotals,
handleError
} from './utils';
import { green, blue } from 'colorette';
import { outputExtensions } from './types';
import { RedoclyClient, OutputFormat } from "@redocly/openapi-core";
import { promptUser } from './utils';
import { previewDocs } from './commands/preview-docs';
import { handleStats } from './commands/stats';
import { handleSplit } from './commands/split';
import { handleJoin } from './commands/join';
import { handleLint } from './commands/lint';
import { handleBundle } from './commands/bundle';
const version = require('../package.json').version;
const outputExtensions = ['json', 'yaml', 'yml'] as ReadonlyArray<BundleOutputFormat>;
yargs
.version('version', 'Show version number.', version)
.help('help', 'Show help.')
@@ -47,18 +25,19 @@ yargs
choices: ['stylish', 'json'] as ReadonlyArray<OutputFormat>,
default: 'stylish' as OutputFormat,
}
}
),
async (argv) => { handleStats(argv) }
}),
(argv) => { handleStats(argv) }
)
.command('split [entrypoint]', 'Split definition into a multi-file structure',
(yargs) => yargs
.positional('entrypoint', { type: 'string' })
.option({ outDir: {
.option({
outDir: {
description: 'Output directory where files will be saved',
required: true,
type: 'string'
}}),
}
}),
(argv) => { handleSplit(argv) }
)
.command('join [entrypoints...]', 'Join definitions [experimental]',
@@ -88,261 +67,101 @@ yargs
}),
(argv) => { handleJoin(argv, version) }
)
.command(
'lint [entrypoints...]',
'Lint definition.',
(yargs) =>
yargs
.positional('entrypoints', {
array: true,
type: 'string',
demandOption: true,
})
.option('format', {
.command('lint [entrypoints...]', 'Lint definition.',
(yargs) => yargs
.positional('entrypoints', { array: true, type: 'string', demandOption: true })
.option({
format: {
description: 'Use a specific output format.',
choices: ['stylish', 'codeframe', 'json'] as ReadonlyArray<OutputFormat>,
default: 'codeframe' as OutputFormat,
})
.option('max-problems', {
},
'max-problems': {
requiresArg: true,
description: 'Reduce output to max N problems.',
type: 'number',
default: 100,
})
.option('generate-ignore-file', {
},
'generate-ignore-file': {
description: 'Generate ignore file.',
type: 'boolean',
})
.option('skip-rule', {
},
'skip-rule': {
description: 'Ignore certain rules.',
array: true,
type: 'string',
})
.option('skip-preprocessor', {
},
'skip-preprocessor': {
description: 'Ignore certain preprocessors.',
array: true,
type: 'string',
})
.option('config', {
},
config: {
description: 'Specify path to the config file.',
requiresArg: true,
type: 'string',
})
.option('extends', {
},
extends: {
description: 'Override extends configurations (defaults or config file settings).',
requiresArg: true,
array: true,
type: 'string',
}
}),
async (argv) => {
const config = await loadConfig(argv.config, argv.extends);
config.lint.skipRules(argv['skip-rule']);
config.lint.skipPreprocessors(argv['skip-preprocessor']);
const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoints, config);
if (argv['generate-ignore-file']) {
config.lint.ignore = {}; // clear ignore
}
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
let totalIgnored = 0;
if (config.lint.recommendedFallback) {
process.stderr.write(
`No configurations were defined in extends -- using built in ${blue('recommended')} configuration by default.\n\n`,
);
}
// TODO: use shared externalRef resolver, blocked by preprocessors now as they can mutate documents
for (const entryPoint of entrypoints) {
try {
const startedAt = performance.now();
process.stderr.write(gray(`validating ${entryPoint}...\n`));
const results = await validate({
ref: entryPoint,
config,
});
const fileTotals = getTotals(results);
totals.errors += fileTotals.errors;
totals.warnings += fileTotals.warnings;
totals.ignored += fileTotals.ignored;
if (argv['generate-ignore-file']) {
for (let m of results) {
config.lint.addIgnore(m);
totalIgnored++;
}
} else {
formatProblems(results, {
format: argv.format,
maxProblems: argv['max-problems'],
totals: fileTotals,
version
});
}
const elapsed = getExecutionTime(startedAt);
process.stderr.write(gray(`${entryPoint}: validated in ${elapsed}\n\n`));
} catch (e) {
totals.errors++;
handleError(e, entryPoint);
}
}
if (argv['generate-ignore-file']) {
config.lint.saveIgnore();
process.stderr.write(
`Generated ignore file with ${totalIgnored} ${pluralize('problem', totalIgnored)}.\n\n`,
);
} else {
printLintTotals(totals, entrypoints.length);
}
printUnusedWarnings(config.lint);
process.exit(totals.errors === 0 || argv['generate-ignore-file'] ? 0 : 1);
},
(argv) => { handleLint(argv, version) }
)
.command(
'bundle [entrypoints...]',
'Bundle definition.',
(yargs) =>
yargs
.positional('entrypoints', {
array: true,
type: 'string',
demandOption: true,
})
.command('bundle [entrypoints...]', 'Bundle definition.',
(yargs) => yargs
.positional('entrypoints', { array: true, type: 'string', demandOption: true })
.options({
output: { type: 'string', alias: 'o' },
})
.option('format', {
format: {
description: 'Use a specific output format.',
choices: ['stylish', 'codeframe', 'json'] as ReadonlyArray<OutputFormat>,
default: 'codeframe' as OutputFormat,
})
.option('max-problems', {
},
'max-problems': {
requiresArg: true,
description: 'Reduce output to max N problems.',
type: 'number',
default: 100,
})
.option('ext', {
},
ext: {
description: 'Bundle file extension.',
requiresArg: true,
choices: outputExtensions,
})
.option('skip-rule', {
},
'skip-rule': {
description: 'Ignore certain rules.',
array: true,
type: 'string',
})
.option('skip-preprocessor', {
},
'skip-preprocessor': {
description: 'Ignore certain preprocessors.',
array: true,
type: 'string',
})
.option('skip-decorator', {
},
'skip-decorator': {
description: 'Ignore certain decorators.',
array: true,
type: 'string',
})
.option('dereferenced', {
},
dereferenced: {
alias: 'd',
type: 'boolean',
description: 'Produce fully dereferenced bundle.',
})
.option('force', {
},
force: {
alias: 'f',
type: 'boolean',
description: 'Produce bundle output even when errors occur.',
})
.option('config', {
},
config: {
description: 'Specify path to the config file.',
type: 'string',
}
}),
async (argv) => {
const config = await loadConfig(argv.config);
config.lint.skipRules(argv['skip-rule']);
config.lint.skipPreprocessors(argv['skip-preprocessor']);
config.lint.skipDecorators(argv['skip-decorator']);
const entrypoints = await getFallbackEntryPointsOrExit(argv.entrypoints, config);
const totals: Totals = { errors: 0, warnings: 0, ignored: 0 };
for (const entrypoint of entrypoints) {
try {
const startedAt = performance.now();
process.stderr.write(gray(`bundling ${entrypoint}...\n`));
const { bundle: result, problems } = await bundle({
config,
ref: entrypoint,
dereference: argv.dereferenced,
});
const fileTotals = getTotals(problems);
const { outputFile, ext } = getOutputFileName(
entrypoint,
entrypoints.length,
argv.output,
argv.ext,
);
if (fileTotals.errors === 0 || argv.force) {
if (!argv.output) {
const output = dumpBundle(result, argv.ext || 'yaml', argv.dereferenced);
process.stdout.write(output);
} else {
const output = dumpBundle(result, ext, argv.dereferenced);
saveBundle(outputFile, output);
}
}
totals.errors += fileTotals.errors;
totals.warnings += fileTotals.warnings;
totals.ignored += fileTotals.ignored;
formatProblems(problems, {
format: argv.format,
maxProblems: argv['max-problems'],
totals: fileTotals,
version,
});
const elapsed = getExecutionTime(startedAt);
if (fileTotals.errors > 0) {
if (argv.force) {
process.stderr.write(
`❓ Created a bundle for ${blue(entrypoint)} at ${blue(
outputFile,
)} with errors ${green(elapsed)}.\n${yellow(
'Errors ignored because of --force',
)}.\n`,
);
} else {
process.stderr.write(
`❌ Errors encountered while bundling ${blue(
entrypoint,
)}: bundle not created (use --force to ignore errors).\n`,
);
}
} else {
process.stderr.write(
`📦 Created a bundle for ${blue(entrypoint)} at ${blue(outputFile)} ${green(
elapsed,
)}.\n`,
);
}
} catch (e) {
handleError(e, entrypoint);
}
}
printUnusedWarnings(config.lint);
process.exit(totals.errors === 0 || argv.force ? 0 : 1);
},
(argv) => { handleBundle(argv, version) }
)
.command('login', 'Login to the Redoc.ly API registry with an access token.', async () => {
const clientToken = await promptUser(
@@ -359,109 +178,41 @@ yargs
const client = new RedoclyClient();
client.logout();
})
.command(
'preview-docs [entrypoint]',
'Preview API reference docs for the specified definition.',
(yargs) =>
yargs
.positional('entrypoint', {
type: 'string',
})
.option('port', {
.command('preview-docs [entrypoint]', 'Preview API reference docs for the specified definition.',
(yargs) => yargs
.positional('entrypoint', { type: 'string' })
.options({
port: {
alias: 'p',
type: 'number',
default: 8080,
description: 'Preview port.',
})
.option('skip-preprocessor', {
description: 'Preview port.'
},
'skip-preprocessor': {
description: 'Ignore certain preprocessors.',
array: true,
type: 'string',
})
.option('skip-decorator', {
},
'skip-decorator': {
description: 'Ignore certain decorators.',
array: true,
type: 'string',
})
.option('use-community-edition', {
},
'use-community-edition': {
description: 'Force using Redoc CE for docs preview.',
type: 'boolean',
})
.option('force', {
},
'force': {
alias: 'f',
type: 'boolean',
description: 'Produce bundle output even when errors occur.',
})
.option('config', {
},
'config': {
description: 'Specify path to the config file.',
type: 'string',
}
}),
async (argv) => {
previewDocs(argv);
},
async (argv) => { previewDocs(argv) },
)
.demandCommand(1)
.strict().argv;
function getOutputFileName(
entrypoint: string,
entries: number,
output?: string,
ext?: BundleOutputFormat,
) {
if (!output) {
return { outputFile: 'stdout', ext: ext || 'yaml' };
}
let outputFile = output;
if (entries > 1) {
ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
if (!outputExtensions.includes(ext as any)) {
throw new Error(`Invalid file extension: ${ext}.`);
}
outputFile = join(output, basename(entrypoint, extname(entrypoint))) + '.' + ext;
} else {
if (output) {
ext = ext || (extname(output).substring(1) as BundleOutputFormat);
}
ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
if (!outputExtensions.includes(ext as any)) {
throw new Error(`Invalid file extension: ${ext}.`);
}
outputFile = join(dirname(outputFile), basename(outputFile, extname(outputFile))) + '.' + ext;
}
return { outputFile, ext };
}
function printUnusedWarnings(config: LintConfig) {
const { preprocessors, rules, decorators } = config.getUnusedRules();
if (rules.length) {
process.stderr.write(
yellow(
`[WARNING] Unused rules found in ${blue(config.configFile || '')}: ${rules.join(', ')}.\n`,
),
);
}
if (preprocessors.length) {
process.stderr.write(
yellow(
`[WARNING] Unused preprocessors found in ${blue(
config.configFile || '',
)}: ${preprocessors.join(', ')}.\n`,
),
);
}
if (decorators.length) {
process.stderr.write(
yellow(
`[WARNING] Unused decorators found in ${blue(config.configFile || '')}: ${decorators.join(
', ',
)}.\n`,
),
);
}
if (rules.length || preprocessors.length) {
process.stderr.write(`Check the spelling and verify you added plugin prefix.\n`);
}
}

View File

@@ -1,5 +1,10 @@
import { BundleOutputFormat } from '@redocly/openapi-core';
export type Totals = {
errors: number;
warnings: number;
ignored: number;
}
export const outputExtensions = ['json', 'yaml', 'yml'] as ReadonlyArray<BundleOutputFormat>;
export type OutputExtensions = 'json' | 'yaml' | 'yml' | undefined;

View File

@@ -1,3 +1,5 @@
import { basename, dirname, extname, join, resolve } from 'path';
import { blue, gray, green, red, yellow } from 'colorette';
import { performance } from "perf_hooks";
import * as colors from 'colorette';
import * as glob from 'glob-promise';
@@ -5,10 +7,15 @@ import * as yaml from 'js-yaml';
import * as fs from 'fs';
import * as path from 'path';
import * as readline from 'readline';
import { BundleOutputFormat, Config, NormalizedProblem, ResolveError, YamlParseError } from '@redocly/openapi-core';
import { dirname, resolve } from 'path';
import { Totals } from './types';
import { blue, gray, green, red, yellow } from 'colorette';
import {
BundleOutputFormat,
Config,
LintConfig,
NormalizedProblem,
ResolveError,
YamlParseError,
} from '@redocly/openapi-core';
import { Totals, outputExtensions } from './types';
export async function getFallbackEntryPointsOrExit(argsEntrypoints: string[] | undefined, config: Config) {
const { apiDefinitions } = config;
@@ -207,3 +214,66 @@ export function printLintTotals(totals: Totals, definitionsCount: number) {
process.stderr.write('\n');
}
export function getOutputFileName(
entrypoint: string,
entries: number,
output?: string,
ext?: BundleOutputFormat,
) {
if (!output) {
return { outputFile: 'stdout', ext: ext || 'yaml' };
}
let outputFile = output;
if (entries > 1) {
ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
if (!outputExtensions.includes(ext as any)) {
throw new Error(`Invalid file extension: ${ext}.`);
}
outputFile = join(output, basename(entrypoint, extname(entrypoint))) + '.' + ext;
} else {
if (output) {
ext = ext || (extname(output).substring(1) as BundleOutputFormat);
}
ext = ext || (extname(entrypoint).substring(1) as BundleOutputFormat);
if (!outputExtensions.includes(ext as any)) {
throw new Error(`Invalid file extension: ${ext}.`);
}
outputFile = join(dirname(outputFile), basename(outputFile, extname(outputFile))) + '.' + ext;
}
return { outputFile, ext };
}
export function printUnusedWarnings(config: LintConfig) {
const { preprocessors, rules, decorators } = config.getUnusedRules();
if (rules.length) {
process.stderr.write(
yellow(
`[WARNING] Unused rules found in ${blue(config.configFile || '')}: ${rules.join(', ')}.\n`,
),
);
}
if (preprocessors.length) {
process.stderr.write(
yellow(
`[WARNING] Unused preprocessors found in ${blue(
config.configFile || '',
)}: ${preprocessors.join(', ')}.\n`,
),
);
}
if (decorators.length) {
process.stderr.write(
yellow(
`[WARNING] Unused decorators found in ${blue(config.configFile || '')}: ${decorators.join(
', ',
)}.\n`,
),
);
}
if (rules.length || preprocessors.length) {
process.stderr.write(`Check the spelling and verify you added plugin prefix.\n`);
}
}