UNPKG

@stoplight/spectral

Version:

A flexible object linter with out of the box support for OpenAPI v2 and v3.

240 lines 8.59 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const command_1 = require("@oclif/command"); const yaml_1 = require("@stoplight/yaml"); const fs_1 = require("fs"); const lodash_1 = require("lodash"); const path_1 = require("path"); const util_1 = require("util"); const configLoader_1 = require("../../config/configLoader"); const formatters_1 = require("../../formatters"); const reader_1 = require("../../fs/reader"); const oas2_1 = require("../../rulesets/oas2"); const oas3_1 = require("../../rulesets/oas3"); const reader_2 = require("../../rulesets/reader"); const spectral_1 = require("../../spectral"); const config_1 = require("../../types/config"); const writeFileAsync = util_1.promisify(fs_1.writeFile); class Lint extends command_1.Command { run() { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { args, flags } = this.parse(Lint); const { config: configFileFlag } = flags; let config = mergeConfig(configLoader_1.createEmptyConfig(), flags); const configFile = configFileFlag || configLoader_1.getDefaultConfigFile(process.cwd()) || null; if (configFile) { try { const loadedConfig = yield configLoader_1.load(configFile, config_1.ConfigCommand.LINT); config = mergeConfig(loadedConfig, flags); } catch (ex) { this.error(`Cannot load provided config file. ${ex.message}.`); } } const { ruleset } = config; let rules; if (ruleset) { rules = yield tryReadOrLog(this, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return reader_2.readRulesFromRulesets(...ruleset); })); } if (args.source) { try { yield lint(args.source, config, this, rules); } catch (ex) { this.error(ex.message); } } else { this.error('You must specify a document to lint'); } }); } } Lint.description = 'lint a JSON/YAML document from a file or URL'; Lint.examples = [ `$ spectral lint .openapi.yaml linting ./openapi.yaml `, ]; Lint.flags = { help: command_1.flags.help({ char: 'h' }), config: command_1.flags.string({ char: 'c', description: 'path to a config file', }), encoding: command_1.flags.string({ char: 'e', description: 'text encoding to use', }), format: command_1.flags.string({ char: 'f', description: 'formatter to use for outputting results', options: ['json', 'stylish'], }), output: command_1.flags.string({ char: 'o', description: 'output to a file instead of stdout', }), 'max-results': command_1.flags.integer({ description: '[default: all] maximum results to show', }), maxResults: command_1.flags.integer({ char: 'm', description: 'deprecated: use --max-results instead', }), ruleset: command_1.flags.string({ char: 'r', description: 'path to a ruleset file (supports remote files)', multiple: true, }), 'skip-rule': command_1.flags.string({ char: 's', description: 'ignore certain rules if they are causing trouble', multiple: true, }), verbose: command_1.flags.boolean({ char: 'v', description: 'increase verbosity', }), }; Lint.args = [{ name: 'source' }]; exports.default = Lint; function tryReadOrLog(command, reader) { return tslib_1.__awaiter(this, void 0, void 0, function* () { try { return yield reader(); } catch (ex) { if (ex.messages) { command.log(ex.messages[0]); command.error(ex.messages[1]); } else { command.error(ex); } } }); } function lint(name, flags, command, rules) { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (flags.verbose) { command.log(`Linting ${name}`); } const spec = yield reader_1.readParsable(name, flags.encoding); const spectral = new spectral_1.Spectral(); if (parseInt(spec.data.swagger) === 2) { command.log('Adding OpenAPI 2.0 (Swagger) functions'); spectral.addFunctions(oas2_1.oas2Functions()); } else if (parseInt(spec.data.openapi) === 3) { command.log('Adding OpenAPI 3.x functions'); spectral.addFunctions(oas3_1.oas3Functions()); } if (rules) { if (flags.verbose) { command.log(`Found ${Object.keys(rules).length} rules`); } } else { if (flags.verbose) { command.log('No rules loaded, attempting to detect document type'); } if (parseInt(spec.data.swagger) === 2) { command.log('OpenAPI 2.0 (Swagger) detected'); rules = yield tryReadOrLog(command, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield oas2_1.rules(); })); } else if (parseInt(spec.data.openapi) === 3) { command.log('OpenAPI 3.x detected'); rules = yield tryReadOrLog(command, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield oas3_1.rules(); })); } } if (flags.skipRule) { rules = skipRules(Object.assign({}, rules), flags, command); } if (!rules) { throw new Error('No rules provided, and document type does not have any default rules, so lint has nothing to do'); } spectral.addRules(rules); let results = []; try { const parsedResult = { source: path_1.resolve(process.cwd(), name), parsed: spec, getLocationForJsonPath: yaml_1.getLocationForJsonPath, }; results = yield spectral.run(parsedResult); if (results.length === 0) { command.log('No errors or warnings found!'); return; } } catch (ex) { process.exitCode = 2; throw new Error(ex); } const output = yield formatOutput(results, flags); try { yield writeOutput(output, flags, command); process.exitCode = 1; } catch (ex) { process.exitCode = 2; throw new Error(ex); } }); } const skipRules = (rules, flags, command) => { const skippedRules = []; const invalidRules = []; for (const rule of flags.skipRule) { if (rule in rules) { delete rules[rule]; skippedRules.push(rule); } else { invalidRules.push(rule); } } if (invalidRules.length !== 0) { command.warn(`ignoring invalid ${invalidRules.length > 1 ? 'rules' : 'rule'} "${invalidRules.join(', ')}"`); } if (skippedRules.length !== 0 && flags.verbose) { command.log(`INFO: skipping ${skippedRules.length > 1 ? 'rules' : 'rule'} "${skippedRules.join(', ')}"`); } return rules; }; function formatOutput(results, flags) { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (flags.maxResults) { results = results.slice(0, flags.maxResults); } return { json: () => formatters_1.json(results), stylish: () => formatters_1.stylish(results), }[flags.format](); }); } function writeOutput(outputStr, flags, command) { return tslib_1.__awaiter(this, void 0, void 0, function* () { if (flags.output) { return writeFileAsync(flags.output, outputStr); } command.log(outputStr); }); } exports.writeOutput = writeOutput; function mergeConfig(config, flags) { return Object.assign({}, config.lint, lodash_1.omitBy({ encoding: flags.encoding, format: flags.format, output: flags.output, maxResults: flags.maxResults > 0 ? flags.maxResults : flags['max-results'], verbose: flags.verbose, ruleset: flags.ruleset, skipRule: flags['skip-rule'], }, lodash_1.isNil)); } //# sourceMappingURL=lint.js.map