UNPKG

@stoplight/spectral-cli

Version:

[![](https://raw.githubusercontent.com/stoplightio/spectral/develop/docs/img/readme-header.svg)](https://stoplight.io/api-governance?utm_source=github&utm_medium=spectral&utm_campaign=readme) [![CircleCI](https://img.shields.io/circleci/build/github/stopl

231 lines 9.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.severeEnoughToFail = void 0; const tslib_1 = require("tslib"); const types_1 = require("@stoplight/types"); const json_1 = require("@stoplight/json"); const spectral_core_1 = require("@stoplight/spectral-core"); const lodash_1 = require("lodash"); const process = (0, tslib_1.__importStar)(require("process")); const chalk_1 = (0, tslib_1.__importDefault)(require("chalk")); const stacktracey_1 = (0, tslib_1.__importDefault)(require("stacktracey")); const linter_1 = require("../services/linter"); const output_1 = require("../services/output"); const config_1 = require("../services/config"); const errors_1 = require("../errors"); const formatOptions = Object.values(config_1.OutputFormat); const lintCommand = { describe: 'lint JSON/YAML documents from files or URLs', command: 'lint [documents..]', builder: yargs => yargs .strict() .positional('documents', { description: 'Location of JSON/YAML documents. Can be either a file, a glob or fetchable resource(s) on the web.', coerce(values) { if (Array.isArray(values) && values.length > 0) { return values; } if (process.stdin.isTTY) { return []; } return [process.stdin.fd]; }, }) .middleware((argv) => { const formats = argv.format; if (argv.output === void 0) { argv.output = { [formats[0]]: '<stdout>' }; } else if (typeof argv.output === 'string') { argv.output = { [formats[0]]: argv.output }; } else { const output = argv.output; if (Object.keys(output).length >= formats.length) { return; } const firstMissingFormat = formats.find(f => !(f in output)); if (firstMissingFormat !== void 0) { output[firstMissingFormat] = '<stdout>'; } } }) .check((argv) => { if (!Array.isArray(argv.documents) || argv.documents.length === 0) { throw new errors_1.CLIError('No documents provided.'); } const format = argv.format; const output = argv.output; if (format.length === 1) { if (output === void 0 || Object.keys(output).length === 1) { return true; } throw new errors_1.CLIError('Output must be either string or unspecified when a single format is specified'); } if (!(0, json_1.isPlainObject)(output)) { throw new errors_1.CLIError('Multiple outputs have to be provided when more than a single format is specified'); } const keys = Object.keys(output); if (format.length !== keys.length) { throw new errors_1.CLIError('The number of outputs must match the number of formats'); } const diff = (0, lodash_1.difference)(format, keys); if (diff.length !== 0) { throw new errors_1.CLIError(`Missing outputs for the following formats: ${diff.join(', ')}`); } return true; }) .options({ encoding: { alias: 'e', description: 'text encoding to use', type: 'string', default: 'utf8', choices: ['utf8', 'ascii', 'utf-8', 'utf16le', 'ucs2', 'ucs-2', 'base64', 'latin1'], }, format: { alias: 'f', description: 'formatters to use for outputting results, more than one can be provided by using multiple flags', choices: formatOptions, default: config_1.OutputFormat.STYLISH, type: 'string', coerce(values) { return Array.isArray(values) ? values : [values]; }, }, output: { alias: 'o', description: `where to output results, can be a single file name, multiple "output.<format>" or missing to print to stdout`, type: 'string', }, 'stdin-filepath': { description: 'path to a file to pretend that stdin comes from', type: 'string', }, resolver: { description: 'path to custom json-ref-resolver instance', type: 'string', }, ruleset: { alias: 'r', description: 'path/URL to a ruleset file', type: 'string', }, 'fail-severity': { alias: 'F', description: 'results of this level or above will trigger a failure exit code', choices: ['error', 'warn', 'info', 'hint'], default: 'error', type: 'string', }, 'display-only-failures': { alias: 'D', description: 'only output results equal to or greater than --fail-severity', type: 'boolean', default: false, }, 'ignore-unknown-format': { description: 'do not warn about unmatched formats', type: 'boolean', default: false, }, 'fail-on-unmatched-globs': { description: 'fail on unmatched glob patterns', type: 'boolean', default: false, }, verbose: { alias: 'v', description: 'increase verbosity', type: 'boolean', }, quiet: { alias: 'q', description: 'no logging - output only', type: 'boolean', }, }), async handler(args) { const { documents, failSeverity, displayOnlyFailures, ruleset, stdinFilepath, format, output, encoding, ignoreUnknownFormat, failOnUnmatchedGlobs, ...config } = args; try { const linterResult = await (0, linter_1.lint)(documents, { format, output, encoding, ignoreUnknownFormat, failOnUnmatchedGlobs, ruleset, stdinFilepath, ...(0, lodash_1.pick)(config, ['verbose', 'quiet', 'resolver']), }); if (displayOnlyFailures) { linterResult.results = filterResultsBySeverity(linterResult.results, failSeverity); } await Promise.all(format.map(f => { var _a; const formattedOutput = (0, output_1.formatOutput)(linterResult.results, f, { failSeverity: (0, spectral_core_1.getDiagnosticSeverity)(failSeverity) }, linterResult.resolvedRuleset); return (0, output_1.writeOutput)(formattedOutput, (_a = output === null || output === void 0 ? void 0 : output[f]) !== null && _a !== void 0 ? _a : '<stdout>'); })); if (linterResult.results.length > 0) { process.exit((0, exports.severeEnoughToFail)(linterResult.results, failSeverity) ? 1 : 0); } else if (config.quiet !== true) { const isErrorSeverity = (0, spectral_core_1.getDiagnosticSeverity)(failSeverity) === types_1.DiagnosticSeverity.Error; process.stdout.write(`No results with a severity of '${failSeverity}' ${isErrorSeverity ? '' : 'or higher '}found!\n`); } } catch (ex) { fail((0, lodash_1.isError)(ex) ? ex : new Error(String(ex)), config.verbose === true); } }, }; const fail = (error, verbose) => { if (error instanceof errors_1.CLIError) { process.stderr.write(chalk_1.default.red(error.message)); process.exit(2); } const errors = 'errors' in error ? error.errors : [error]; process.stderr.write(chalk_1.default.red('Error running Spectral!\n')); if (!verbose) { process.stderr.write(chalk_1.default.red('Use --verbose flag to print the error stack.\n')); } for (const [i, error] of errors.entries()) { const actualError = (0, lodash_1.isError)(error) && 'cause' in error ? error.cause : error; const message = (0, lodash_1.isError)(actualError) ? actualError.message : String(actualError); const info = `Error #${i + 1}: `; process.stderr.write(`${info}${chalk_1.default.red(message)}\n`); if (verbose && (0, lodash_1.isError)(actualError)) { process.stderr.write(`${chalk_1.default.red(printErrorStacks(actualError, info.length))}\n`); } } process.exit(2); }; function getWidth(ratio) { return Math.min(20, Math.floor(ratio * process.stderr.columns)); } function printErrorStacks(error, padding) { return new stacktracey_1.default(error) .slice(0, 5) .withSources() .asTable({ maxColumnWidths: { callee: getWidth(0.2), file: getWidth(0.4), sourceLine: getWidth(0.4), }, }) .split('\n') .map(error => `${' '.repeat(padding)}${error}`) .join('\n'); } const filterResultsBySeverity = (results, failSeverity) => { const diagnosticSeverity = (0, spectral_core_1.getDiagnosticSeverity)(failSeverity); return results.filter(r => r.severity <= diagnosticSeverity); }; const severeEnoughToFail = (results, failSeverity) => { const diagnosticSeverity = (0, spectral_core_1.getDiagnosticSeverity)(failSeverity); return results.some(r => r.severity <= diagnosticSeverity); }; exports.severeEnoughToFail = severeEnoughToFail; exports.default = lintCommand; //# sourceMappingURL=lint.js.map