@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
112 lines • 4.12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.AstNumberAnalyzer = void 0;
const HardcodedValue_1 = require("../../domain/value-objects/HardcodedValue");
const rules_1 = require("../../shared/constants/rules");
const ast_node_types_1 = require("../../shared/constants/ast-node-types");
const defaults_1 = require("../constants/defaults");
/**
* AST-based analyzer for detecting magic numbers
*
* Analyzes number literal nodes in the AST to determine if they are
* hardcoded values that should be extracted to constants.
*/
class AstNumberAnalyzer {
contextChecker;
constructor(contextChecker) {
this.contextChecker = contextChecker;
}
/**
* Analyzes a number node and returns a violation if it's a magic number
*/
analyze(node, lines) {
const value = parseInt(node.text, 10);
if (defaults_1.ALLOWED_NUMBERS.has(value)) {
return null;
}
if (this.contextChecker.isInExportedConstant(node)) {
return null;
}
if (!this.shouldDetect(node, value)) {
return null;
}
return this.createViolation(node, value, lines);
}
/**
* Checks if number should be detected based on context
*/
shouldDetect(node, value) {
const parent = node.parent;
if (!parent) {
return false;
}
if (this.contextChecker.isInCallExpression(parent, [
ast_node_types_1.TIMER_FUNCTIONS.SET_TIMEOUT,
ast_node_types_1.TIMER_FUNCTIONS.SET_INTERVAL,
])) {
return true;
}
if (parent.type === "variable_declarator") {
const identifier = parent.childForFieldName("name");
if (identifier && this.hasConfigKeyword(identifier.text.toLowerCase())) {
return true;
}
}
if (parent.type === "pair") {
const key = parent.childForFieldName("key");
if (key && this.hasConfigKeyword(key.text.toLowerCase())) {
return true;
}
}
if (value >= 100) {
const context = this.contextChecker.getNodeContext(node);
return this.looksLikeMagicNumber(context);
}
return false;
}
/**
* Checks if name contains configuration keywords
*/
hasConfigKeyword(name) {
const keywords = [
defaults_1.DETECTION_KEYWORDS.TIMEOUT,
defaults_1.DETECTION_KEYWORDS.DELAY,
defaults_1.DETECTION_KEYWORDS.RETRY,
defaults_1.DETECTION_KEYWORDS.LIMIT,
defaults_1.DETECTION_KEYWORDS.MAX,
defaults_1.DETECTION_KEYWORDS.MIN,
defaults_1.DETECTION_KEYWORDS.PORT,
defaults_1.DETECTION_KEYWORDS.INTERVAL,
];
return (keywords.some((keyword) => name.includes(keyword)) ||
name.includes("retries") ||
name.includes("attempts"));
}
/**
* Checks if context suggests a magic number
*/
looksLikeMagicNumber(context) {
const configKeywords = [
defaults_1.DETECTION_KEYWORDS.TIMEOUT,
defaults_1.DETECTION_KEYWORDS.DELAY,
defaults_1.DETECTION_KEYWORDS.RETRY,
defaults_1.DETECTION_KEYWORDS.LIMIT,
defaults_1.DETECTION_KEYWORDS.MAX,
defaults_1.DETECTION_KEYWORDS.MIN,
defaults_1.DETECTION_KEYWORDS.PORT,
defaults_1.DETECTION_KEYWORDS.INTERVAL,
];
return configKeywords.some((keyword) => context.includes(keyword));
}
/**
* Creates a HardcodedValue violation from a number node
*/
createViolation(node, value, lines) {
const lineNumber = node.startPosition.row + 1;
const column = node.startPosition.column;
const context = lines[node.startPosition.row]?.trim() ?? "";
return HardcodedValue_1.HardcodedValue.create(value, rules_1.HARDCODE_TYPES.MAGIC_NUMBER, lineNumber, column, context);
}
}
exports.AstNumberAnalyzer = AstNumberAnalyzer;
//# sourceMappingURL=AstNumberAnalyzer.js.map