@samiyev/guardian
Version:
Research-backed code quality guardian for AI-assisted development. Detects hardcodes, secrets, circular deps, framework leaks, entity exposure, and 9 architecture violations. Enforces Clean Architecture/DDD principles. Works with GitHub Copilot, Cursor, W
110 lines • 4.87 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AstVariableNameAnalyzer = void 0;
const NamingViolation_1 = require("../../../domain/value-objects/NamingViolation");
const constants_1 = require("../../../shared/constants");
const rules_1 = require("../../../shared/constants/rules");
const detectorPatterns_1 = require("../../constants/detectorPatterns");
/**
* AST-based analyzer for detecting variable naming violations
*
* Analyzes variable declarations to ensure proper naming conventions:
* - Regular variables: camelCase
* - Constants (exported UPPER_CASE): UPPER_SNAKE_CASE
* - Class properties: camelCase
* - Private properties with underscore prefix are allowed
*/
class AstVariableNameAnalyzer {
/**
* Analyzes a variable declaration node
*/
analyze(node, layer, filePath, _lines) {
const variableNodeTypes = [
constants_1.AST_VARIABLE_TYPES.VARIABLE_DECLARATOR,
constants_1.AST_VARIABLE_TYPES.REQUIRED_PARAMETER,
constants_1.AST_VARIABLE_TYPES.OPTIONAL_PARAMETER,
constants_1.AST_VARIABLE_TYPES.PUBLIC_FIELD_DEFINITION,
constants_1.AST_VARIABLE_TYPES.PROPERTY_SIGNATURE,
];
if (!variableNodeTypes.includes(node.type)) {
return null;
}
const nameNode = node.childForFieldName(constants_1.AST_FIELD_NAMES.NAME);
if (!nameNode) {
return null;
}
if (this.isDestructuringPattern(nameNode)) {
return null;
}
const variableName = nameNode.text;
const lineNumber = nameNode.startPosition.row + 1;
if (variableName.startsWith("_")) {
return null;
}
const isConstant = this.isConstantVariable(node);
if (isConstant) {
if (!/^[A-Z][A-Z0-9_]*$/.test(variableName)) {
return NamingViolation_1.NamingViolation.create(variableName, rules_1.NAMING_VIOLATION_TYPES.WRONG_CASE, layer, `${filePath}:${String(lineNumber)}`, detectorPatterns_1.NAMING_ERROR_MESSAGES.CONSTANT_UPPER_SNAKE_CASE, variableName, detectorPatterns_1.NAMING_ERROR_MESSAGES.USE_UPPER_SNAKE_CASE_CONSTANT);
}
}
else {
if (!/^[a-z][a-zA-Z0-9]*$/.test(variableName)) {
return NamingViolation_1.NamingViolation.create(variableName, rules_1.NAMING_VIOLATION_TYPES.WRONG_CASE, layer, `${filePath}:${String(lineNumber)}`, detectorPatterns_1.NAMING_ERROR_MESSAGES.VARIABLE_CAMEL_CASE, variableName, detectorPatterns_1.NAMING_ERROR_MESSAGES.USE_CAMEL_CASE_VARIABLE);
}
}
return null;
}
/**
* Checks if node is a destructuring pattern (object or array)
*/
isDestructuringPattern(node) {
return (node.type === constants_1.AST_PATTERN_TYPES.OBJECT_PATTERN ||
node.type === constants_1.AST_PATTERN_TYPES.ARRAY_PATTERN);
}
/**
* Checks if a variable is a constant (exported UPPER_CASE)
*/
isConstantVariable(node) {
const variableName = node.childForFieldName(constants_1.AST_FIELD_NAMES.NAME)?.text;
if (!variableName || !/^[A-Z]/.test(variableName)) {
return false;
}
if (node.type === constants_1.AST_VARIABLE_TYPES.PUBLIC_FIELD_DEFINITION ||
node.type === constants_1.AST_FIELD_TYPES.FIELD_DEFINITION) {
return this.hasConstModifiers(node);
}
let current = node.parent;
while (current) {
if (current.type === constants_1.AST_STATEMENT_TYPES.LEXICAL_DECLARATION) {
const firstChild = current.child(0);
if (firstChild?.type === constants_1.AST_MODIFIER_TYPES.CONST) {
return true;
}
}
if (current.type === constants_1.AST_VARIABLE_TYPES.PUBLIC_FIELD_DEFINITION ||
current.type === constants_1.AST_FIELD_TYPES.FIELD_DEFINITION) {
return this.hasConstModifiers(current);
}
current = current.parent;
}
return false;
}
/**
* Checks if field has readonly or static modifiers (indicating a constant)
*/
hasConstModifiers(fieldNode) {
for (let i = 0; i < fieldNode.childCount; i++) {
const child = fieldNode.child(i);
const childText = child?.text;
if (child?.type === constants_1.AST_MODIFIER_TYPES.READONLY ||
child?.type === constants_1.AST_MODIFIER_TYPES.STATIC ||
childText === constants_1.AST_MODIFIER_TYPES.READONLY ||
childText === constants_1.AST_MODIFIER_TYPES.STATIC) {
return true;
}
}
return false;
}
}
exports.AstVariableNameAnalyzer = AstVariableNameAnalyzer;
//# sourceMappingURL=AstVariableNameAnalyzer.js.map