@stoplight/spectral
Version:
A flexible object linter with out of the box support for OpenAPI v2 and v3.
148 lines • 4.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const types_1 = require("@stoplight/types");
const jp = require("jsonpath");
const lodash_1 = require("lodash");
const message_1 = require("./rulesets/message");
exports.lintNode = (node, rule, then, apply, opts, parsedResult) => {
const givenPath = node.path[0] === '$' ? node.path.slice(1) : node.path;
const conditioning = exports.whatShouldBeLinted(givenPath, node.value, rule);
if (!conditioning.lint) {
return [];
}
const { parsed } = parsedResult;
const targetValue = conditioning.value;
const targets = [];
if (then && then.field) {
if (then.field === '@key') {
for (const key of Object.keys(targetValue)) {
targets.push({
path: key,
value: key,
});
}
}
else if (then.field[0] === '$') {
const nodes = jp.nodes(targetValue, then.field);
for (const n of nodes) {
targets.push({
path: n.path,
value: n.value,
});
}
}
else {
targets.push({
path: typeof then.field === 'string' ? then.field.split('.') : then.field,
value: lodash_1.get(targetValue, then.field),
});
}
}
else {
targets.push({
path: [],
value: targetValue,
});
}
if (!targets.length) {
targets.push({
path: [],
value: undefined,
});
}
let results = [];
for (const target of targets) {
const targetPath = givenPath.concat(target.path);
const targetResults = apply(target.value, then.functionOptions || {}, {
given: givenPath,
target: targetPath,
}, {
original: node.value,
given: node.value,
resolved: opts.resolvedTarget,
}) || [];
const severity = rule.severity !== undefined ? rule.severity : types_1.DiagnosticSeverity.Warning;
results = results.concat(targetResults.map(result => {
const path = result.path || targetPath;
const location = parsedResult.getLocationForJsonPath(parsed, path, true);
return Object.assign({ code: rule.name, get summary() {
return this.message;
}, message: rule.message === undefined
? rule.summary || result.message
: message_1.message(rule.message, {
error: result.message,
property: path.length > 0 ? path[path.length - 1] : '',
description: rule.description,
}), path,
severity, source: parsedResult.source }, (location || {
range: {
start: {
character: 0,
line: 0,
},
end: {
character: 0,
line: 0,
},
},
}));
}));
}
return results;
};
exports.whatShouldBeLinted = (path, originalValue, rule) => {
const leaf = path[path.length - 1];
const when = rule.when;
if (!when) {
return {
lint: true,
value: originalValue,
};
}
const pattern = when.pattern;
const field = when.field;
const isKey = field === '@key';
if (!pattern) {
if (isKey) {
return {
lint: false,
value: originalValue,
};
}
return {
lint: lodash_1.has(originalValue, field),
value: originalValue,
};
}
if (isKey && pattern) {
return keyAndOptionalPattern(leaf, pattern, originalValue);
}
const fieldValue = String(lodash_1.get(originalValue, when.field));
return {
lint: fieldValue.match(pattern) !== null,
value: originalValue,
};
};
function keyAndOptionalPattern(key, pattern, value) {
if (typeof key === 'number' && typeof value === 'object') {
for (const k of Object.keys(value)) {
if (String(k).match(pattern)) {
return {
lint: true,
value,
};
}
}
}
else if (String(key).match(pattern)) {
return {
lint: true,
value,
};
}
return {
lint: false,
value,
};
}
//# sourceMappingURL=linter.js.map