From f503b4b169f4f263e3b5ae343e17f6f86b740c5d Mon Sep 17 00:00:00 2001 From: aleix-ferrer Date: Mon, 22 Jul 2024 14:29:58 +0200 Subject: [PATCH 1/3] feat(packages/eslint-plugin-sui): add new linter rule --- packages/eslint-plugin-sui/src/index.js | 4 +- .../src/rules/value-attribute-VO.js | 124 ++++++++++++++++++ 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin-sui/src/rules/value-attribute-VO.js diff --git a/packages/eslint-plugin-sui/src/index.js b/packages/eslint-plugin-sui/src/index.js index 1620724c4..3e1d31fda 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 ValueAttributeVO = require('./rules/value-attribute-VO.js') // ------------------------------------------------------------------------------ // Plugin Definition @@ -15,6 +16,7 @@ module.exports = { 'serialize-deserialize': SerializeDeserialize, commonjs: CommonJS, decorators: Decorators, - 'layers-arch': LayersArch + 'layers-arch': LayersArch, + 'value-attribute-VO': ValueAttributeVO } } diff --git a/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js b/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js new file mode 100644 index 000000000..d8f57f3c8 --- /dev/null +++ b/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js @@ -0,0 +1,124 @@ +/** + * @fileoverview ensure value object has a value attribute + */ +'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 value object has a value attribute', + recommended: false, + url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements' + }, + fixable: null, + schema: [], + messages: { + missingValueAttribute: dedent` + If your class is a value object, you have to define a 'value' attribute. + `, + missingPrivateValueAttribute: dedent` + The 'value' attribute has to be private. + `, + missingValueAttributeGetter: dedent` + The 'value' attribute has to have a getter. + You can define a native getter (get {{attributeName}}) or a custom getter ({{customGetterName}}). + ` + } + }, + + // If your class is a value object, you have to define a private 'value' attribute and the associated getter. + + 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 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 + + const attributes = node.body.body.filter(node => { + return node?.type === 'PropertyDefinition' && node?.value?.type !== 'ArrowFunctionExpression' + }) + + if (attributes.length > 1) return + + // Check if exists value attribute + const valueAttribute = node?.body?.body?.find(node => { + return node?.type === 'PropertyDefinition' && node?.key?.name === 'value' + }) + + if (!valueAttribute) { + return context.report({ + node: node?.id, + messageId: 'missingValueAttribute' + }) + } + + // Check if value attribute is private + + const isPrivateValueAttribute = valueAttribute?.key?.type === 'PrivateIdentifier' + + if (!isPrivateValueAttribute) { + return context.report({ + node: node?.id, + messageId: 'missingPrivateValueAttribute' + }) + } + + // Check if a value attribute has a public accessor + const classMethods = node.body.body.filter(node => { + return node?.type === 'MethodDefinition' || node?.value?.type === 'ArrowFunctionExpression' + }) + + let hasGetter = false + const customGetterName = `get + ${valueAttribute?.key?.name?.charAt(0).toUpperCase()} + ${valueAttribute?.key?.name?.slice(1)} + ` + + classMethods.forEach(method => { + const existNativeGetterWithAttributeKey = + method?.key?.name === valueAttribute?.key?.name && + method?.kind === 'get' + const existCustomGetterWithAttributeKey = method?.key?.name === customGetterName + + if (existNativeGetterWithAttributeKey || existCustomGetterWithAttributeKey) { + hasGetter = true + } + }) + + if (!hasGetter) { + context.report({ + node: valueAttribute, + messageId: 'missingValueAttributeGetter', + data: { + attributeName: valueAttribute?.key?.name, + customGetterName + } + }) + } + } + } + } +} From dba062e52f25c4fd062a9d6d6cddee568bdfba52 Mon Sep 17 00:00:00 2001 From: aleix-ferrer Date: Mon, 22 Jul 2024 14:30:07 +0200 Subject: [PATCH 2/3] feat(packages/eslint-plugin-sui): add new linter rule --- packages/eslint-plugin-sui/src/rules/value-attribute-VO.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js b/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js index d8f57f3c8..4beec0ffa 100644 --- a/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js +++ b/packages/eslint-plugin-sui/src/rules/value-attribute-VO.js @@ -98,9 +98,8 @@ module.exports = { ` classMethods.forEach(method => { - const existNativeGetterWithAttributeKey = - method?.key?.name === valueAttribute?.key?.name && - method?.kind === 'get' + const existNativeGetterWithAttributeKey = + method?.key?.name === valueAttribute?.key?.name && method?.kind === 'get' const existCustomGetterWithAttributeKey = method?.key?.name === customGetterName if (existNativeGetterWithAttributeKey || existCustomGetterWithAttributeKey) { From 35d96741f65d62d2c12f95dab2a74b248955cc0f Mon Sep 17 00:00:00 2001 From: aleix-ferrer Date: Mon, 22 Jul 2024 14:30:22 +0200 Subject: [PATCH 3/3] feat(packages/sui-lint): add rule to config --- packages/sui-lint/eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sui-lint/eslintrc.js b/packages/sui-lint/eslintrc.js index 06787f9b6..f5594276e 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/value-attribute-VO': RULES.WARNING } }, {