@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
223 lines • 7.83 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AstContextChecker = void 0;
const ast_node_types_1 = require("../../shared/constants/ast-node-types");
/**
* AST context checker for analyzing node contexts
*
* Provides reusable methods to check if a node is in specific contexts
* like exports, type declarations, function calls, etc.
*/
class AstContextChecker {
/**
* Checks if node is in an exported constant with "as const"
*/
isInExportedConstant(node) {
let current = node.parent;
while (current) {
if (current.type === "export_statement") {
if (this.checkExportedConstant(current)) {
return true;
}
}
current = current.parent;
}
return false;
}
/**
* Helper to check if export statement contains "as const"
*/
checkExportedConstant(exportNode) {
const declaration = exportNode.childForFieldName(ast_node_types_1.AST_FIELD_NAMES.DECLARATION);
if (!declaration) {
return false;
}
if (declaration.type !== "lexical_declaration") {
return false;
}
const declarator = this.findDescendant(declaration, ast_node_types_1.AST_VARIABLE_TYPES.VARIABLE_DECLARATOR);
if (!declarator) {
return false;
}
const value = declarator.childForFieldName(ast_node_types_1.AST_FIELD_NAMES.VALUE);
if (value?.type !== "as_expression") {
return false;
}
const asType = value.children.find((c) => c.type === ast_node_types_1.AST_MODIFIER_TYPES.CONST);
return asType !== undefined;
}
/**
* Checks if node is in a type context (union type, type alias, interface)
*/
isInTypeContext(node) {
let current = node.parent;
while (current) {
if (current.type === "type_alias_declaration" ||
current.type === "union_type" ||
current.type === "literal_type" ||
current.type === "interface_declaration" ||
current.type === "type_annotation") {
return true;
}
current = current.parent;
}
return false;
}
/**
* Checks if node is in an import statement or import() call
*/
isInImportStatement(node) {
let current = node.parent;
while (current) {
if (current.type === "import_statement") {
return true;
}
if (current.type === "call_expression") {
const functionNode = current.childForFieldName(ast_node_types_1.AST_FIELD_NAMES.FUNCTION) ||
current.children.find((c) => c.type === ast_node_types_1.AST_IDENTIFIER_TYPES.IDENTIFIER ||
c.type === ast_node_types_1.AST_IDENTIFIER_TYPES.IMPORT);
if (functionNode &&
(functionNode.text === "import" ||
functionNode.type === ast_node_types_1.AST_IDENTIFIER_TYPES.IMPORT)) {
return true;
}
}
current = current.parent;
}
return false;
}
/**
* Checks if node is in a test description (test(), describe(), it())
*/
isInTestDescription(node) {
let current = node.parent;
while (current) {
if (current.type === "call_expression") {
const callee = current.childForFieldName("function");
if (callee?.type === "identifier") {
const funcName = callee.text;
if (funcName === "test" ||
funcName === "describe" ||
funcName === "it" ||
funcName === "expect") {
return true;
}
}
}
current = current.parent;
}
return false;
}
/**
* Checks if node is in a console.log or console.error call
*/
isInConsoleCall(node) {
let current = node.parent;
while (current) {
if (current.type === "call_expression") {
const callee = current.childForFieldName("function");
if (callee?.type === "member_expression") {
const object = callee.childForFieldName("object");
const property = callee.childForFieldName("property");
if (object?.text === "console" &&
property &&
(property.text === "log" ||
property.text === "error" ||
property.text === "warn")) {
return true;
}
}
}
current = current.parent;
}
return false;
}
/**
* Checks if node is in a Symbol() call
*/
isInSymbolCall(node) {
let current = node.parent;
while (current) {
if (current.type === "call_expression") {
const callee = current.childForFieldName("function");
if (callee?.type === "identifier" && callee.text === "Symbol") {
return true;
}
}
current = current.parent;
}
return false;
}
/**
* Checks if node is in a typeof check
*/
isInTypeofCheck(node) {
let current = node.parent;
while (current) {
if (current.type === "binary_expression") {
const left = current.childForFieldName("left");
const right = current.childForFieldName("right");
if (left?.type === "unary_expression") {
const operator = left.childForFieldName("operator");
if (operator?.text === "typeof") {
return true;
}
}
if (right?.type === "unary_expression") {
const operator = right.childForFieldName("operator");
if (operator?.text === "typeof") {
return true;
}
}
}
current = current.parent;
}
return false;
}
/**
* Checks if parent is a call expression with specific function names
*/
isInCallExpression(parent, functionNames) {
if (parent.type === "arguments") {
const callExpr = parent.parent;
if (callExpr?.type === "call_expression") {
const callee = callExpr.childForFieldName("function");
if (callee?.type === "identifier") {
return functionNames.includes(callee.text);
}
}
}
return false;
}
/**
* Gets context text around a node
*/
getNodeContext(node) {
let current = node;
while (current &&
current.type !== "lexical_declaration" &&
current.type !== "pair" &&
current.type !== "call_expression" &&
current.type !== "return_statement") {
current = current.parent;
}
return current ? current.text.toLowerCase() : "";
}
/**
* Finds a descendant node by type
*/
findDescendant(node, type) {
if (node.type === type) {
return node;
}
for (const child of node.children) {
const result = this.findDescendant(child, type);
if (result) {
return result;
}
}
return null;
}
}
exports.AstContextChecker = AstContextChecker;
//# sourceMappingURL=AstContextChecker.js.map