From aa22a8dfec796998c4f3d52c7154a3dd643c458a Mon Sep 17 00:00:00 2001 From: Oriol Puig Date: Fri, 19 Jul 2024 14:34:00 +0200 Subject: [PATCH] feat(packages/eslint-plugin-sui): Create rules for @Deprecated() decorator --- packages/eslint-plugin-sui/src/index.js | 6 +- .../decorator-deprecated-remark-method.js | 51 ++++++++ .../src/rules/decorator-deprecated.js | 115 ++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 packages/eslint-plugin-sui/src/rules/decorator-deprecated-remark-method.js create mode 100644 packages/eslint-plugin-sui/src/rules/decorator-deprecated.js diff --git a/packages/eslint-plugin-sui/src/index.js b/packages/eslint-plugin-sui/src/index.js index 1620724c4..a4bbe561a 100644 --- a/packages/eslint-plugin-sui/src/index.js +++ b/packages/eslint-plugin-sui/src/index.js @@ -2,6 +2,8 @@ const FactoryPattern = require('./rules/factory-pattern.js') const SerializeDeserialize = require('./rules/serialize-deserialize.js') const CommonJS = require('./rules/commonjs.js') const Decorators = require('./rules/decorators.js') +const DecoratorDeprecated = require('./rules/decorator-deprecated.js') +const DecoratorDeprecatedRemarkMethod = require('./rules/decorator-deprecated-remark-method.js') const LayersArch = require('./rules/layers-architecture.js') // ------------------------------------------------------------------------------ @@ -15,6 +17,8 @@ module.exports = { 'serialize-deserialize': SerializeDeserialize, commonjs: CommonJS, decorators: Decorators, - 'layers-arch': LayersArch + 'layers-arch': LayersArch, + 'decorator-deprecated': DecoratorDeprecated, + 'decorator-deprecated-remark-method': DecoratorDeprecatedRemarkMethod } } diff --git a/packages/eslint-plugin-sui/src/rules/decorator-deprecated-remark-method.js b/packages/eslint-plugin-sui/src/rules/decorator-deprecated-remark-method.js new file mode 100644 index 000000000..5c47a7e90 --- /dev/null +++ b/packages/eslint-plugin-sui/src/rules/decorator-deprecated-remark-method.js @@ -0,0 +1,51 @@ +/** + * @fileoverview Ensure that method using @Deprecated() displays a warning alert + */ +'use strict' + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Ensure that method using @Deprecated() displays a warning alert', + recommended: true, + url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements' + }, + fixable: 'code', + schema: [], + messages: {} + }, + create: function (context) { + // TODO: Check using decorator in a Class. + + return { + MethodDefinition(node) { + // Method + const method = node + + // Method decorators + const methodDecorators = method.decorators + const hasDecorators = methodDecorators?.length > 0 + + if (!hasDecorators) return + + // Get the @Deprecated() decorator from method + const deprecatedDecoratorNode = + hasDecorators && methodDecorators?.find(decorator => decorator?.expression?.callee?.name === 'Deprecated') + + if (!deprecatedDecoratorNode) return + + // RULE: Mark method with a warning + context.report({ + node: method.key, + message: 'This method is marked as a deprecated.' + }) + } + } + } +} diff --git a/packages/eslint-plugin-sui/src/rules/decorator-deprecated.js b/packages/eslint-plugin-sui/src/rules/decorator-deprecated.js new file mode 100644 index 000000000..11465ddd6 --- /dev/null +++ b/packages/eslint-plugin-sui/src/rules/decorator-deprecated.js @@ -0,0 +1,115 @@ +/** + * @fileoverview Ensure that @Deprecated() decorator is used as expected + */ +'use strict' + +const dedent = require('string-dedent') + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + docs: { + description: 'Ensure that @Deprecated() decorator is used as expected', + recommended: true, + url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements' + }, + fixable: 'code', + schema: [], + messages: { + notFoundDecoratorArgumentError: dedent` + The @Deprecated() decorator must have arguments. + `, + notFoundKeyDecoratorArgumentError: dedent` + The @Deprecated() decorator must have a key property. + `, + notFoundMessageDecoratorArgumentError: dedent` + The @Deprecated() decorator must have a message property. + ` + } + }, + create: function (context) { + // TODO: Check using decorator in a Class. + return { + MethodDefinition(node) { + // Method + const method = node + const methodName = method.key?.name + + // Method decorators + const methodDecorators = method.decorators + const hasDecorators = methodDecorators?.length > 0 + + if (!hasDecorators) return + + // Get the @Deprecated() decorator from method + const deprecatedDecoratorNode = + hasDecorators && methodDecorators?.find(decorator => decorator?.expression?.callee?.name === 'Deprecated') + + if (!deprecatedDecoratorNode) return + + const methodArguments = deprecatedDecoratorNode?.expression?.arguments + const hasArgument = methodArguments.length === 1 + const argumentDecorator = hasArgument && methodArguments[0] + const isObjectExpression = hasArgument && argumentDecorator.type === 'ObjectExpression' + const argumentsAreInvalid = !hasArgument || !isObjectExpression + + // Get decorator arguments: key and message + const keyProperty = + !argumentsAreInvalid && argumentDecorator.properties?.find(prop => prop?.key?.name === 'key') + const messageProperty = + !argumentsAreInvalid && argumentDecorator.properties?.find(prop => prop?.key?.name === 'message') + + // RULE: Decorator must have 1 argument as an object with Key and Message properties + if (argumentsAreInvalid || (!keyProperty && !messageProperty)) { + context.report({ + node: deprecatedDecoratorNode, + messageId: 'notFoundDecoratorArgumentError', + *fix(fixer) { + yield fixer.insertTextBefore( + deprecatedDecoratorNode, + `\n @Deprecated({key: '${methodName}', message: 'The ${methodName} function is deprecated.'})` + ) + yield fixer.remove(deprecatedDecoratorNode) + } + }) + return + } + + // RULE: Decorator must have a key property and generates it if it doesn't exist + if (!keyProperty && messageProperty) { + context.report({ + node: deprecatedDecoratorNode, + messageId: 'notFoundKeyDecoratorArgumentError', + *fix(fixer) { + yield fixer.insertTextBefore( + deprecatedDecoratorNode, + `\n @Deprecated({key: '${methodName}', message: '${messageProperty.value.value}'})` + ) + yield fixer.remove(deprecatedDecoratorNode) + } + }) + } + + // RULE: Decorator must have a message property and generates it if it doesn't exist + if (keyProperty && !messageProperty) { + context.report({ + node: deprecatedDecoratorNode, + messageId: 'notFoundMessageDecoratorArgumentError', + *fix(fixer) { + yield fixer.insertTextBefore( + deprecatedDecoratorNode, + `\n @Deprecated({key: '${keyProperty.value.value}', message: 'The ${methodName} function is deprecated.'})` + ) + yield fixer.remove(deprecatedDecoratorNode) + } + }) + } + } + } + } +}