Skip to content

Commit

Permalink
feat(packages/eslint-plugin-sui): add basic decorators rules for UseC…
Browse files Browse the repository at this point in the history
…ases
  • Loading branch information
carlosvillu committed Apr 25, 2024
1 parent f210c46 commit 86ef4a2
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 1 deletion.
4 changes: 3 additions & 1 deletion packages/eslint-plugin-sui/src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
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')

// ------------------------------------------------------------------------------
// Plugin Definition
Expand All @@ -11,6 +12,7 @@ module.exports = {
rules: {
'factory-pattern': FactoryPattern,
'serialize-deserialize': SerializeDeserialize,
commonjs: CommonJS
commonjs: CommonJS,
decorators: Decorators
}
}
86 changes: 86 additions & 0 deletions packages/eslint-plugin-sui/src/rules/decorators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/**
* @fileoverview Ensure that at least all your UseCases are using @inlineError and @tracer decorator from sui
*/
'use strict'

const dedent = require('string-dedent')

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Ensure that at least all your UseCases are using @inlineError and @tracer decorator from sui',
recommended: true,
url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements'
},
fixable: 'code',
schema: [],
messages: {
missingInlineError: dedent`
All our UseCases must have an @inlineError decorator.
`,
missingTracer: dedent`
All our UseCases must have a @tracer() decorator.
`,
tracerMissCall: dedent`
Your tracer decorator should be call always with the name of your class
`,
inlineErrorMissplace: dedent`
the inlineError decorator should be always the first
`
}
},
create: function (context) {
return {
MethodDefinition(node) {
const className = node.parent?.parent?.id?.name
const shouldExtendFromUseCase = node.parent?.parent?.superClass?.name === 'UseCase'
const isExecute = node.key?.name === 'execute' && shouldExtendFromUseCase
const hasInlineError = node.decorators?.some(node => node.expression?.name === 'inlineError')
const tracerNode = node.decorators?.find(node => node.expression?.callee?.name === 'tracer')
const isTracerCalledWithClassName =
tracerNode?.expression?.callee?.name === 'tracer' &&
className + '#' + node.key?.name === tracerNode?.expression?.arguments[0]?.properties[0]?.value?.value &&
tracerNode?.expression?.arguments[0]?.properties[0]?.key?.name === 'metric'
const isInlineErrorTheFirst = node.decorators?.at(-1)?.expression?.name === 'inlineError'

isExecute &&
!hasInlineError &&
context.report({
node: node.key,
messageId: 'missingInlineError'
})

isExecute &&
hasInlineError &&
!isInlineErrorTheFirst &&
context.report({
node: node.key,
messageId: 'inlineErrorMissplace'
})

isExecute &&
!tracerNode &&
context.report({
node: node.key,
messageId: 'missingTracer'
})

tracerNode &&
!isTracerCalledWithClassName &&
context.report({
node: tracerNode,
messageId: 'tracerMissCall',
fix(fixer) {
return fixer.replaceText(tracerNode.expression, `tracer({metric: '${className}#${node.key?.name}'})`)
}
})
}
}
}
}
109 changes: 109 additions & 0 deletions packages/eslint-plugin-sui/test/server/decorators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import dedent from 'dedent'
import {RuleTester} from 'eslint'

import rule from '../../src/rules/decorators.js'

// ------------------------------------------------------------------------------
// Tests
// more info: https://eslint.org/docs/latest/integrate/nodejs-api#ruletester
// ------------------------------------------------------------------------------

const resolvedBabelPresetSui = require.resolve('babel-preset-sui')
const parser = require.resolve('@babel/eslint-parser')

const ruleTester = new RuleTester({parser, parserOptions: {babelOptions: {configFile: resolvedBabelPresetSui}}})
ruleTester.run('decorators', rule, {
valid: [
{
code: dedent`
class MyUseCase extends UseCase {
@tracer({metric: 'MyUseCase#execute'})
@inlineError
execute(){}
}
`
}
],

invalid: [
{
code: dedent`
class MyUseCase extends UseCase {
execute(){}
}
`,
errors: [
{
message: dedent`
All our UseCases must have an @inlineError decorator.
`
},
{
message: dedent`
All our UseCases must have a @tracer() decorator.
`
}
]
},
{
code: dedent`
class MyUseCase extends UseCase {
@inlineError
execute(){}
}
`,
errors: [
{
message: dedent`
All our UseCases must have a @tracer() decorator.
`
}
]
},
{
code: dedent`
class MyUseCase extends UseCase {
@tracer()
@inlineError
execute(){}
}
`,
output: dedent`
class MyUseCase extends UseCase {
@tracer({metric: 'MyUseCase#execute'})
@inlineError
execute(){}
}
`,
errors: [
{
message: dedent`
Your tracer decorator should be call always with the name of your class
`
}
]
},
{
code: dedent`
class MyUseCase extends UseCase {
@inlineError
@tracer({metric: 'MyUseCase#execute'})
execute(){}
}
`,
errors: [
{
message: dedent`
the inlineError decorator should be always the first
`
}
]
}
]
})

0 comments on commit 86ef4a2

Please sign in to comment.