UNPKG

@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
"use strict"; 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