diff --git a/packages/eslint-plugin-sui/src/index.js b/packages/eslint-plugin-sui/src/index.js index 1620724c4..db3f19651 100644 --- a/packages/eslint-plugin-sui/src/index.js +++ b/packages/eslint-plugin-sui/src/index.js @@ -3,6 +3,7 @@ const SerializeDeserialize = require('./rules/serialize-deserialize.js') const CommonJS = require('./rules/commonjs.js') const Decorators = require('./rules/decorators.js') const LayersArch = require('./rules/layers-architecture.js') +const IsEmptyMethodModel = require('./rules/isEmpty-method-model.js') // ------------------------------------------------------------------------------ // Plugin Definition @@ -15,6 +16,7 @@ module.exports = { 'serialize-deserialize': SerializeDeserialize, commonjs: CommonJS, decorators: Decorators, - 'layers-arch': LayersArch + 'layers-arch': LayersArch, + 'isEmpty-method-model': IsEmptyMethodModel } } diff --git a/packages/eslint-plugin-sui/src/rules/isEmpty-method-model.js b/packages/eslint-plugin-sui/src/rules/isEmpty-method-model.js new file mode 100644 index 000000000..7428a6900 --- /dev/null +++ b/packages/eslint-plugin-sui/src/rules/isEmpty-method-model.js @@ -0,0 +1,77 @@ +/** + * @fileoverview ensure domain model has isEmpty method when we can create it without validation + */ +'use strict' + +const dedent = require('string-dedent') +const path = require('path') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'Ensure domain model has isEmpty method when we can create it without validation', + recommended: false, + url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements' + }, + fixable: null, + schema: [], + messages: { + missingIsEmptyMethod: dedent` + If your class is a domain model (Value Object or Entity), and the constructor has a skipValidation param you have to define a 'isEmpty' method. + ` + } + }, + + create(context) { + const filePath = context.getFilename() + const relativePath = path.relative(context.getCwd(), filePath) + + // Check if the file is inside requierd folders (useCases, services, repositories, ...) + const valueObjectPattern = /valueObjects|valueobjects|ValueObjects|Valueobjects/i + const isValueObjectPath = valueObjectPattern.test(relativePath) + + const entityPattern = /entity|Entity/i + const isEntityPath = entityPattern.test(relativePath) + + return { + ClassDeclaration(node) { + const constructor = node.body?.body?.find(i => i.key.name === 'constructor') + + const className = node.id?.name ?? '' + + const allowedWords = ['VO', 'ValueObject', 'Entity'] + + const isDomainModelName = allowedWords.some(allowWord => className.includes(allowWord)) + + if (!isDomainModelName && !isValueObjectPath) return + if (!isDomainModelName && !isEntityPath) return + + if (!constructor) return + + const hasConstructorSkipValidationParam = constructor.value?.params[0]?.properties?.some(param => { + return param.key?.name === 'skipValidation' + }) + + if (!hasConstructorSkipValidationParam) return + + const classMethods = node.body.body.filter(node => { + return node.type === 'MethodDefinition' || node.value?.type === 'ArrowFunctionExpression' + }) + + const hasIsEmptyMethod = classMethods.some(i => i.key.name === 'isEmpty') + + if (!hasIsEmptyMethod) + return context.report({ + node: node.id, + messageId: 'missingIsEmptyMethod' + }) + } + } + } +} diff --git a/packages/sui-lint/eslintrc.js b/packages/sui-lint/eslintrc.js index 06787f9b6..f176b81b3 100644 --- a/packages/sui-lint/eslintrc.js +++ b/packages/sui-lint/eslintrc.js @@ -239,7 +239,8 @@ module.exports = { rules: { 'sui/factory-pattern': RULES.WARNING, 'sui/serialize-deserialize': RULES.WARNING, - 'sui/decorators': RULES.WARNING + 'sui/decorators': RULES.WARNING, + 'sui/isEmpty-method-model': RULES.WARNING } }, {