@stoplight/spectral
Version:
A flexible object linter with out of the box support for OpenAPI v2 and v3.
113 lines • 4.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const json_1 = require("@stoplight/json");
const json_ref_resolver_1 = require("@stoplight/json-ref-resolver");
const types_1 = require("@stoplight/types");
const yaml_1 = require("@stoplight/yaml");
const lodash_1 = require("lodash");
const functions_1 = require("./functions");
const runner_1 = require("./runner");
tslib_1.__exportStar(require("./types"), exports);
class Spectral {
constructor(opts) {
this._rules = {};
this._functions = functions_1.functions;
this.resolver = opts && opts.resolver ? opts.resolver : new json_ref_resolver_1.Resolver();
}
run(target) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let results = [];
let parsedResult;
if (!exports.isParsedResult(target)) {
parsedResult = {
parsed: yaml_1.parseWithPointers(typeof target === 'string' ? target : json_1.safeStringify(target, undefined, 2)),
getLocationForJsonPath: yaml_1.getLocationForJsonPath,
};
results = results.concat(formatParserDiagnostics(parsedResult.parsed, parsedResult.source));
}
else {
parsedResult = target;
}
const { result: resolvedTarget, errors } = yield this.resolver.resolve(parsedResult.parsed.data);
return [
...results,
...formatResolverErrors(errors, parsedResult),
...runner_1.runRules(parsedResult, this.rules, this.functions, { resolvedTarget }),
];
});
}
get functions() {
return this._functions;
}
addFunctions(functions) {
Object.assign(this._functions, lodash_1.merge({}, functions));
}
get rules() {
const rules = {};
for (const name in this._rules) {
if (!this._rules.hasOwnProperty(name))
continue;
const rule = this._rules[name];
rules[name] = Object.assign({ name }, rule);
}
return rules;
}
addRules(rules) {
Object.assign(this._rules, lodash_1.merge({}, rules));
}
mergeRules(rules) {
for (const ruleName in lodash_1.merge({}, rules)) {
if (!rules.hasOwnProperty(ruleName))
continue;
const rule = rules[ruleName];
if (rule) {
this._rules[ruleName] = lodash_1.merge(this._rules[ruleName], rule);
}
}
}
applyRuleDeclarations(declarations) {
for (const ruleName in declarations) {
if (!declarations.hasOwnProperty(ruleName))
continue;
const declaration = declarations[ruleName];
const rule = this.rules[ruleName];
if (rule) {
if (typeof declaration === 'boolean') {
this._rules[ruleName].enabled = declaration;
}
}
}
}
}
exports.Spectral = Spectral;
exports.isParsedResult = (obj) => {
if (!obj || typeof obj !== 'object')
return false;
if (!obj.parsed || typeof obj.parsed !== 'object')
return false;
if (!obj.getLocationForJsonPath || typeof obj.getLocationForJsonPath !== 'function')
return false;
return true;
};
function formatParserDiagnostics(parsed, source) {
return parsed.diagnostics.map(diagnostic => (Object.assign({}, diagnostic, { path: [], source })));
}
const prettyPrintResolverError = (message) => message.replace(/^Error\s*:\s*/, '');
const formatResolverErrors = (resolveErrors, result) => {
return lodash_1.uniqBy(resolveErrors, 'message').reduce((errors, error) => {
const path = [...error.path, '$ref'];
const location = result.getLocationForJsonPath(result.parsed, path);
if (location) {
errors.push({
code: 'invalid-ref',
path,
message: prettyPrintResolverError(error.message),
severity: types_1.DiagnosticSeverity.Error,
range: location.range,
});
}
return errors;
}, []);
};
//# sourceMappingURL=spectral.js.map