@sun-asterisk/sunlint
Version:
☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards
147 lines (130 loc) • 5.25 kB
JavaScript
/**
* Custom ESLint rule for: C017 – Limit constructor logic
* Rule ID: custom/c017
* Purpose: Enforce minimal logic in constructors to maintain clean initialization
*/
module.exports = {
meta: {
type: "suggestion",
docs: {
description: "Limit constructor logic",
recommended: false
},
schema: [],
messages: {
constructorLogic: "Constructor contains complex logic: {{description}}. Move to initialization methods",
tooManyStatements: "Constructor has too many statements ({{count}}). Consider simplifying or moving logic to initialization methods"
}
},
create(context) {
function isSimpleAssignment(node) {
// Simple property assignments like this.prop = value
if (node.type === "ExpressionStatement" &&
node.expression.type === "AssignmentExpression" &&
node.expression.left.type === "MemberExpression" &&
node.expression.left.object.type === "ThisExpression") {
// Check if right side is a simple value (not complex computation)
const right = node.expression.right;
return right.type === "Literal" ||
right.type === "Identifier" ||
(right.type === "MemberExpression" && right.property.name === "length") ||
(right.type === "CallExpression" && right.callee.type === "Identifier" &&
['require', 'process'].includes(right.callee.name));
}
return false;
}
function isSimpleDeclaration(node) {
// Simple variable declarations
return node.type === "VariableDeclaration";
}
function isSuperCall(node) {
// super() calls
return node.type === "ExpressionStatement" &&
node.expression.type === "CallExpression" &&
node.expression.callee.type === "Super";
}
function isComplexLogic(node) {
// Complex patterns that shouldn't be in constructor
switch (node.type) {
case "IfStatement":
case "WhileStatement":
case "ForStatement":
case "SwitchStatement":
case "TryStatement":
return { type: "control_flow", description: "control flow statements" };
case "ExpressionStatement":
if (node.expression.type === "CallExpression") {
const callee = node.expression.callee;
// Method calls that aren't simple setters
if (callee.type === "MemberExpression") {
const methodName = callee.property.name;
// Allow simple MobX observable setup
if (methodName === "makeObservable" || methodName === "makeAutoObservable") {
return null;
}
// Flag complex method calls
if (!['push', 'set', 'add'].includes(methodName)) {
return { type: "method_call", description: "complex method calls" };
}
}
// Direct function calls (not method calls)
if (callee.type === "Identifier" &&
!['require', 'parseInt', 'parseFloat', 'Boolean', 'Number', 'String'].includes(callee.name)) {
return { type: "function_call", description: "function calls" };
}
}
// Complex assignments with computations
if (node.expression.type === "AssignmentExpression") {
const right = node.expression.right;
if (right.type === "BinaryExpression" ||
right.type === "ConditionalExpression" ||
(right.type === "CallExpression" &&
right.callee.type === "MemberExpression" &&
!['map', 'filter', 'toString', 'slice'].includes(right.callee.property.name))) {
return { type: "complex_assignment", description: "complex computations" };
}
}
break;
}
return null;
}
return {
MethodDefinition(node) {
if (node.kind === "constructor" && node.value && node.value.body) {
const statements = node.value.body.body;
let complexLogicCount = 0;
for (const stmt of statements) {
// Skip simple assignments, declarations, and super calls
if (isSimpleAssignment(stmt) ||
isSimpleDeclaration(stmt) ||
isSuperCall(stmt)) {
continue;
}
// Check for complex logic
const complexity = isComplexLogic(stmt);
if (complexity) {
context.report({
node: stmt,
messageId: "constructorLogic",
data: {
description: complexity.description
}
});
complexLogicCount++;
}
}
// Also flag constructors with too many statements overall
if (statements.length > 10) {
context.report({
node: node,
messageId: "tooManyStatements",
data: {
count: statements.length
}
});
}
}
}
};
}
};