@arabasta/eslint-plugin-tsoa
Version:
ESLint plugin for tsoa rules
147 lines (146 loc) • 6.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const utils_js_1 = require("../utils.js");
exports.default = (0, utils_js_1.createRule)({
name: 'valid-alternative-response',
meta: {
messages: {
noCorrectResponseDecorator: "Using '{{ functionName }}' inside a method requires you to have '@Response({{ status }})' decorator on the method",
},
type: 'problem',
docs: {
description: 'require correct response decorators when using alternative responses',
recommended: true,
},
fixable: 'code',
schema: [
{
type: 'object',
additionalProperties: false,
properties: {
functionNames: {
items: {
type: 'string',
},
type: 'array',
description: 'The list of functions that are alternative response',
},
specialStatuses: {
items: {
type: 'object',
additionalProperties: false,
properties: {
functionName: {
type: 'string',
description: 'The alternative response function name',
},
statusCode: {
oneOf: [
{
type: 'string',
},
{
type: 'number',
},
],
description: "The alternative response's status code",
},
},
},
type: 'array',
description: 'The list of alternative responses function names and special statuses',
},
},
},
],
},
defaultOptions: [
{
functionNames: ['this.errorResult', 'this.noContentResult'],
specialStatuses: [
{
functionName: 'this.noContentResult',
statusCode: 204,
},
],
},
],
create(context, [{ functionNames, specialStatuses }]) {
const specialStatusesMap = new Map();
if (specialStatuses) {
for (const specialStatus of specialStatuses) {
specialStatusesMap.set(specialStatus.functionName, specialStatus.statusCode);
}
}
let usedFunctions = [];
function recordUsedFunctions(functionNode) {
var _a;
const fullFunctionName = (0, utils_js_1.getFullFunctionName)(functionNode.callee);
usedFunctions.push({
fullFunctionName,
arguments: functionNode.arguments,
typeArguments: (_a = functionNode.typeArguments) === null || _a === void 0 ? void 0 : _a.params,
node: functionNode,
});
}
let currentClassDeclarationNode;
return {
ClassDeclaration(node) {
currentClassDeclarationNode = node;
},
'MethodDefinition FunctionExpression > BlockStatement CallExpression': (node) => {
recordUsedFunctions(node);
},
'MethodDefinition:exit': (node) => {
const responseDecorators = [
...(0, utils_js_1.getAllDecoratorsWithName)({
node,
decoratorName: 'Response',
}),
...(0, utils_js_1.getAllDecoratorsWithName)({
node,
decoratorName: 'SuccessResponse',
}),
...(0, utils_js_1.getAllDecoratorsWithName)({
node: currentClassDeclarationNode,
decoratorName: 'Response',
}),
];
for (const usedFunction of usedFunctions) {
if (!functionNames ||
!functionNames.includes(usedFunction.fullFunctionName)) {
continue;
}
let status;
if (specialStatusesMap.has(usedFunction.fullFunctionName)) {
status = specialStatusesMap.get(usedFunction.fullFunctionName);
}
else {
if (!Array.isArray(usedFunction.arguments) ||
usedFunction.arguments.length === 0 ||
!('value' in usedFunction.arguments[0]) ||
typeof usedFunction.arguments[0].value !== 'number') {
continue;
}
status = usedFunction.arguments[0].value;
}
if (!Array.isArray(responseDecorators) ||
!(0, utils_js_1.hasResponseDecoratorWithStatus)({
status,
decorators: responseDecorators,
})) {
context.report({
node,
messageId: 'noCorrectResponseDecorator',
data: {
functionName: usedFunction.fullFunctionName,
status,
},
});
}
}
usedFunctions = [];
},
};
},
});