diff --git a/packages/eslint-plugin-sui/src/rules/decorators-adr.js b/packages/eslint-plugin-sui/src/rules/decorators-adr.js index ff8f9416b..19a8708b8 100644 --- a/packages/eslint-plugin-sui/src/rules/decorators-adr.js +++ b/packages/eslint-plugin-sui/src/rules/decorators-adr.js @@ -1,5 +1,5 @@ /** - * @fileoverview Ensure that at least all your UseCases are using @AsyncInlineError decorator from sui + * @fileoverview Ensure that at least all your UseCases, Services and Repositories are using @AsyncInlineError decorator from sui */ 'use strict' @@ -22,57 +22,110 @@ module.exports = { schema: [], messages: { notFoundAsyncInlineErrorDecorator: dedent` - You have to use the @AsyncInlineError() decorator in your UseCase execute method + The execute method of an UseCase or Service and the public Repository methods should use the @AsyncInlineError() decorator in order to follow the Adevinta domain code guidelines. `, asyncInlineErrorDecoratorIsNotFirst: dedent` - The @AsyncInlineError() decorator should be the first one + The @AsyncInlineError() decorator must be the first one assigned into the method to avoid inconsistence with other decorators. ` } }, create: function (context) { - // Estamos en una clase UseCase - return { ClassDeclaration(node) { - const executeFn = node?.body?.body?.find( - methodDefinition => methodDefinition.type === 'MethodDefinition' && methodDefinition.key.name === 'execute' - ) - - // Pepe - // AsyncInlineErrror - // function() - - if (executeFn) { - const decorators = executeFn?.decorators - const isAsyncInlineErrorLastDecorator = decorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError' - const asyncInlineErrorDecoratorNode = decorators?.find( - decorator => decorator.expression.callee.name === 'AsyncInlineError' + const className = node.id.name + const superClassName = node.superClass?.name + + console.log('ClassName: ', className) + + // UseCase + const containUseCase = className.endsWith('UseCase') + const extendsUseCase = superClassName === 'UseCase' + const isUsecase = containUseCase || extendsUseCase + + // Service + const containService = className.endsWith('Service') + const extendsService = superClassName === 'Service' + const isService = containService || extendsService + + // Repository + const containRepository = className.endsWith('Repository') + const extendsRepository = superClassName === 'Repository' + const isRepository = containRepository || extendsRepository + + // Skip if it's not a UseCase, Service or Repository + if (!isUsecase && !isService && !isRepository) return + + // UseCases and Services always have a execute method + if (isUsecase || isService) { + const executeFn = node?.body?.body?.find( + methodDefinition => methodDefinition.type === 'MethodDefinition' && methodDefinition.key.name === 'execute' ) - // TODO validar en services - // TODO validar en repositories - // TODO quickFix implementar - if (!asyncInlineErrorDecoratorNode) { - context.report({ - node: executeFn.key, - messageId: 'notFoundAsyncInlineErrorDecorator', - fix: fixer => { - return fixer.insertTextBefore(executeFn, '@AsyncInlineError()\n') - } - }) - } + if (executeFn) { + const decorators = executeFn?.decorators + const isAsyncInlineErrorLastDecorator = decorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError' + const asyncInlineErrorDecoratorNode = decorators?.find( + decorator => decorator?.expression?.callee?.name === 'AsyncInlineError' + ) - if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) { - context.report({ - node: asyncInlineErrorDecoratorNode, - messageId: 'asyncInlineErrorDecoratorIsNotFirst', - *fix(fixer) { - yield fixer.remove(asyncInlineErrorDecoratorNode) - yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()') - } - }) + if (!asyncInlineErrorDecoratorNode) { + context.report({ + node: executeFn.key, + messageId: 'notFoundAsyncInlineErrorDecorator' + }) + } + + if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) { + context.report({ + node: asyncInlineErrorDecoratorNode, + messageId: 'asyncInlineErrorDecoratorIsNotFirst', + *fix(fixer) { + yield fixer.remove(asyncInlineErrorDecoratorNode) + yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()') + } + }) + } } } + + if (isRepository) { + // Get all public methods from the repository + const publicMethods = node?.body?.body?.filter( + methodDefinition => + methodDefinition.type === 'MethodDefinition' && + methodDefinition.key.name !== 'constructor' && // skip constructor method + !methodDefinition.key.name.startsWith('_') && // Remove methods with _ prefix + methodDefinition.key.type !== 'PrivateIdentifier' // Remove methods with # decorator + ) + + publicMethods?.forEach(method => { + const methodDecorators = method.decorators + const decorators = method?.decorators + const isAsyncInlineErrorLastDecorator = + methodDecorators?.at(-1)?.expression?.callee?.name === 'AsyncInlineError' + const asyncInlineErrorDecoratorNode = methodDecorators?.find( + decorator => decorator?.expression?.callee?.name === 'AsyncInlineError' + ) + + if (!asyncInlineErrorDecoratorNode) { + context.report({ + node: method.key, + messageId: 'notFoundAsyncInlineErrorDecorator' + }) + } + + if (asyncInlineErrorDecoratorNode && !isAsyncInlineErrorLastDecorator) { + context.report({ + node: asyncInlineErrorDecoratorNode, + messageId: 'asyncInlineErrorDecoratorIsNotFirst', + *fix(fixer) { + yield fixer.remove(asyncInlineErrorDecoratorNode) + yield fixer.insertTextAfter(decorators.at(-1), '\n@AsyncInlineError()') + } + }) + } + }) + } } } }