Skip to content

Commit

Permalink
Merge pull request #1753 from SUI-Components/sui-lint-add-fitness-boa…
Browse files Browse the repository at this point in the history
…rd-rules-part-ii

Add more custom Lint Rules for the Golden Path
  • Loading branch information
carlosvillu authored Apr 30, 2024
2 parents b809285 + ea666e0 commit 6dd9adc
Show file tree
Hide file tree
Showing 6 changed files with 474 additions and 47 deletions.
6 changes: 5 additions & 1 deletion packages/eslint-plugin-sui/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +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 @@ -9,6 +11,8 @@ const SerializeDeserialize = require('./rules/serialize-deserialize.js')
module.exports = {
rules: {
'factory-pattern': FactoryPattern,
'serialize-deserialize': SerializeDeserialize
'serialize-deserialize': SerializeDeserialize,
commonjs: CommonJS,
decorators: Decorators
}
}
118 changes: 118 additions & 0 deletions packages/eslint-plugin-sui/src/rules/commonjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* @fileoverview Ensure your code is not using CommonJS signatures like module.exports or moduel.exports.foo or require() or require.resolve()
*/
'use strict'

const dedent = require('string-dedent')

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

/** @type {import('eslint').Rule.RuleModule} */
module.exports = {
meta: {
type: 'problem',
docs: {
description: 'Ensure that your code is using ems over commonjs modules',
recommended: true,
url: 'https://github.mpi-internal.com/scmspain/es-td-agreements/blob/master/30-Frontend/00-agreements'
},
fixable: null,
schema: [],
messages: {
forbiddenExports: dedent`
Use module.* should be avoid.
`,
forbiddenRequires: dedent`
Use require function should be avoid.
`,
forbiddenModuleRequire: dedent`
Use module.require function should be avoid.
`,
forbiddenRequiresObjects: dedent`
Use require.cache or require.extensions or require.main should be avoid.
`,
forbiddenRequireResolve: dedent`
Use require.resolve function should be avoid.
`,
forbidden__filename: dedent`
__filename should be avoid
`,
forbidden__dirname: dedent`
__dirname should be avoid
`
}
},
create: function (context) {
return {
CallExpression(node) {
const isRequire = node.callee?.name === 'require'
const isResolve = node.callee?.object?.name === 'require' && node.callee?.property?.name === 'resolve'
const isModule = node.callee?.object?.name === 'module' && node.callee?.property?.name === 'require'

const isRequireFormCreateRequire = node.parent?.parent?.body
?.filter(node => node.type === 'ImportDeclaration')
?.some(
node =>
node.source?.value === 'module' && node.specifiers?.some(spec => spec.imported?.name === 'createRequire')
)

isRequire &&
!isRequireFormCreateRequire &&
context.report({
node,
messageId: 'forbiddenRequires'
})

isResolve &&
context.report({
node,
messageId: 'forbiddenRequireResolve'
})

isModule &&
context.report({
node,
messageId: 'forbiddenModuleRequire'
})
},
MemberExpression(node) {
const isModule =
node.object?.name === 'module' &&
['children', 'exports', 'filename', 'id', 'isPreloading', 'loaded', 'parent', 'path', 'paths'].some(
property => node.property?.name === property
)

const isRequire =
node.object?.name === 'require' &&
['cache', 'extensions', 'main'].some(property => node.property?.name === property)

isModule &&
context.report({
node,
messageId: 'forbiddenExports'
})

isRequire &&
context.report({
node,
messageId: 'forbiddenRequiresObjects'
})
},
Identifier(node) {
node.name === '__filename' &&
context.report({
node,
messageId: 'forbidden__filename'
})

node.name === '__dirname' &&
context.report({
node,
messageId: 'forbidden__dirname'
})
}
}
}
}
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}'})`)
}
})
}
}
}
}
46 changes: 0 additions & 46 deletions packages/eslint-plugin-sui/src/rules/forbidden-require.js

This file was deleted.

Loading

0 comments on commit 6dd9adc

Please sign in to comment.