@arabasta/eslint-plugin-tsoa
Version:
ESLint plugin for tsoa rules
124 lines (123 loc) • 5.24 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_fs_1 = __importDefault(require("node:fs"));
const utils_1 = require("../utils");
exports.default = (0, utils_1.createRule)({
name: 'require-security-metadata',
meta: {
messages: {
securityDefinitionNotDefinedInConfig: "The following security definitions are missing from the TSOA config '{{ missingSecurityDefinitions }}'",
},
type: 'problem',
docs: {
description: 'require all security definitions used by the `@Security` decorator to be present in the `TSOA` config',
recommended: true,
},
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
tsoaConfigFilePath: {
type: 'string',
description: "Where the TSOA's config file is located.",
},
},
},
],
},
defaultOptions: [
{
tsoaConfigFilePath: './tsoa.json',
},
],
create: (context, [{ tsoaConfigFilePath }]) => {
const decorators = [];
function getDefinedTsoaSecurityDefinitions(tsoaConfig) {
var _a;
if (typeof ((_a = tsoaConfig.spec) === null || _a === void 0 ? void 0 : _a.securityDefinitions) === 'object') {
return Object.keys(tsoaConfig.spec.securityDefinitions);
}
return [];
}
return {
Decorator(node) {
if (node.expression.type !== 'CallExpression' ||
node.expression.callee.type !== 'Identifier' ||
node.expression.callee.name !== 'Security') {
return;
}
decorators.push(node);
},
'Program:exit': () => {
var _a;
if (decorators.length === 0) {
return;
}
let tsoaConfigJson;
try {
tsoaConfigJson = node_fs_1.default.readFileSync(tsoaConfigFilePath, {
encoding: 'utf-8',
});
}
catch (error) {
console.warn(error);
return;
}
let tsoaConfig;
try {
tsoaConfig = JSON.parse(tsoaConfigJson);
}
catch (error) {
console.warn(error);
return;
}
if (typeof tsoaConfig !== 'object') {
console.warn(`Expected tsoa config to be an object but got '${typeof tsoaConfig}' instead.`);
return;
}
const tsoaSecurityDefinitions = getDefinedTsoaSecurityDefinitions(tsoaConfig);
for (const decorator of decorators) {
const expression = decorator.expression;
if (!('arguments' in expression) ||
!Array.isArray(expression.arguments) ||
!expression.arguments[0]) {
continue;
}
const missingSecurityDefinitions = [];
const securityDefinitionArgument = expression.arguments[0];
if (securityDefinitionArgument.type === 'Literal' &&
typeof securityDefinitionArgument.value === 'string') {
if (!tsoaSecurityDefinitions.includes(securityDefinitionArgument.value)) {
missingSecurityDefinitions.push(securityDefinitionArgument.value);
}
}
if (securityDefinitionArgument.type === 'ObjectExpression' &&
Array.isArray(securityDefinitionArgument.properties)) {
for (const property of securityDefinitionArgument.properties) {
if (property.type !== 'Property' ||
((_a = property.key) === null || _a === void 0 ? void 0 : _a.type) !== 'Identifier') {
continue;
}
if (!tsoaSecurityDefinitions.includes(property.key.name)) {
missingSecurityDefinitions.push(property.key.name);
}
}
}
if (missingSecurityDefinitions.length > 0) {
context.report({
node: decorator,
messageId: 'securityDefinitionNotDefinedInConfig',
data: {
missingSecurityDefinitions: missingSecurityDefinitions.join(', '),
},
});
}
}
},
};
},
});