UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

332 lines (331 loc) 9.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.traverseAST = traverseAST; exports.visitEachChild = visitEachChild; exports.walkDescendants = walkDescendants; exports.traverseWithContext = traverseWithContext; exports.visitNodesOfKind = visitNodesOfKind; exports.traverseDepthFirst = traverseDepthFirst; exports.traverseBreadthFirst = traverseBreadthFirst; exports.visitWithCallback = visitWithCallback; exports.getNodePath = getNodePath; exports.getContainingFunction = getContainingFunction; exports.getContainingBlock = getContainingBlock; exports.getSourceFileForNode = getSourceFileForNode; exports.getParentOfKind = getParentOfKind; exports.getAncestor = getAncestor; exports.navigateToRoot = navigateToRoot; exports.findSiblings = findSiblings; exports.findNodes = findNodes; exports.findNodesInFiles = findNodesInFiles; exports.findNearestParent = findNearestParent; exports.findReferences = findReferences; exports.findReferencesInFiles = findReferencesInFiles; exports.findNodesWithContext = findNodesWithContext; exports.findComponentNode = findComponentNode; exports.findIdentifiersInFiles = findIdentifiersInFiles; const typescript_1 = __importDefault(require("typescript")); const reactSpecific_1 = require("./reactSpecific"); /** * Traverses AST nodes depth-first */ function traverseAST(node, callback) { callback(node); typescript_1.default.forEachChild(node, (child) => traverseAST(child, callback)); } /** * Visits each child of a node */ function visitEachChild(node, visitor) { typescript_1.default.forEachChild(node, visitor); } /** * Walks all descendant nodes */ function walkDescendants(node, predicate) { const results = []; const walk = (current) => { if (!predicate || predicate(current)) { results.push(current); } typescript_1.default.forEachChild(current, walk); }; typescript_1.default.forEachChild(node, walk); return results; } /** * Traverses with context information */ function traverseWithContext(node, visitor, context) { visitor(node, context); typescript_1.default.forEachChild(node, (child) => traverseWithContext(child, visitor, context)); } /** * Visits nodes of specific kind */ function visitNodesOfKind(node, kind, visitor) { if (node.kind === kind) { visitor(node); } typescript_1.default.forEachChild(node, (child) => visitNodesOfKind(child, kind, visitor)); } /** * Traverses depth-first with early exit */ function traverseDepthFirst(node, visitor) { const shouldContinue = visitor(node); if (shouldContinue === false) { return false; } let continueTraversal = true; typescript_1.default.forEachChild(node, (child) => { if (continueTraversal) { continueTraversal = traverseDepthFirst(child, visitor) !== false; } }); return continueTraversal; } /** * Traverses breadth-first */ function traverseBreadthFirst(node, visitor) { const queue = [node]; while (queue.length > 0) { const current = queue.shift(); visitor(current); typescript_1.default.forEachChild(current, (child) => queue.push(child)); } } /** * Visits with callback and collects results */ function visitWithCallback(node, callback) { const results = []; const visit = (current) => { const result = callback(current); if (result !== undefined) { results.push(result); } typescript_1.default.forEachChild(current, visit); }; visit(node); return results; } /** * Builds a path to a node from its ancestors */ function getNodePath(node) { const path = []; let current = node; while (current) { path.unshift(current); current = current.parent; } return path; } /** * Gets the containing function-like declaration */ function getContainingFunction(node) { return findNearestParent(node, (n) => typescript_1.default.isFunctionLike(n)); } /** * Gets the containing block scope */ function getContainingBlock(node) { return findNearestParent(node, typescript_1.default.isBlock); } /** * Gets the source file for a node */ function getSourceFileForNode(node, sourceFiles) { const fileName = node.getSourceFile()?.fileName; return fileName ? sourceFiles.get(fileName) : undefined; } /** * Gets the parent of a specific kind */ function getParentOfKind(node, kind) { let current = node.parent; while (current) { if (current.kind === kind) { return current; } current = current.parent; } return undefined; } /** * Gets ancestor at a specific level */ function getAncestor(node, level) { let current = node; let currentLevel = 0; while (current && currentLevel < level) { current = current.parent; currentLevel++; } return current; } /** * Navigates to the root node (SourceFile) */ function navigateToRoot(node) { let current = node; while (current.parent) { current = current.parent; } return current; } /** * Finds sibling nodes */ function findSiblings(node) { const parent = node.parent; if (!parent) return []; const siblings = []; typescript_1.default.forEachChild(parent, (child) => { if (child !== node) { siblings.push(child); } }); return siblings; } /** * Finds all nodes matching a predicate in a single file */ function findNodes(sourceFile, predicate) { const results = []; function visit(node) { if (predicate(node)) { results.push(node); } typescript_1.default.forEachChild(node, visit); } visit(sourceFile); return results; } /** * Finds all nodes matching a specific predicate across all source files */ function findNodesInFiles(sourceFiles, predicate) { const results = new Map(); sourceFiles.forEach((sourceFile, filePath) => { const fileResults = []; function visit(node) { if (predicate(node)) { fileResults.push(node); } typescript_1.default.forEachChild(node, visit); } visit(sourceFile); if (fileResults.length > 0) { results.set(filePath, fileResults); } }); return results; } /** * Gets the nearest parent matching a predicate */ function findNearestParent(node, predicate) { let current = node.parent; while (current) { if (predicate(current)) { return current; } current = current.parent; } return undefined; } /** * Finds all references to an identifier in a single file */ function findReferences(sourceFile, identifier) { return findNodes(sourceFile, typescript_1.default.isIdentifier).filter((id) => id.text === identifier); } /** * Finds all references to an identifier across all files */ function findReferencesInFiles(sourceFiles, identifier) { const results = new Map(); sourceFiles.forEach((sourceFile, filePath) => { const fileResults = findReferences(sourceFile, identifier); if (fileResults.length > 0) { results.set(filePath, fileResults); } }); return results; } /** * Finds nodes across multiple files with source file context */ function findNodesWithContext(sourceFiles, predicate) { const results = []; sourceFiles.forEach((sourceFile, filePath) => { function visit(node) { if (predicate(node)) { results.push({ node, sourceFile, filePath }); } typescript_1.default.forEachChild(node, visit); } visit(sourceFile); }); return results; } /** * Finds a component node in the source file by name */ function findComponentNode(node, componentName, typeChecker) { if (typescript_1.default.isFunctionDeclaration(node)) { // Function declaration component if (node.name && node.name.text === componentName && (0, reactSpecific_1.isReactComponent)(node, typeChecker)) { return node; } } else if (typescript_1.default.isVariableDeclaration(node)) { // Variable declaration (including arrow functions) if (typescript_1.default.isIdentifier(node.name) && node.name.text === componentName) { if (node.initializer) { if (typescript_1.default.isArrowFunction(node.initializer) || typescript_1.default.isFunctionExpression(node.initializer)) { return node.initializer; } } return node; } } else if (typescript_1.default.isExportAssignment(node)) { // Export default if (typescript_1.default.isIdentifier(node.expression) && node.expression.text === componentName) { return node.expression; } } let result; typescript_1.default.forEachChild(node, (child) => { if (!result) { result = findComponentNode(child, componentName, typeChecker); } }); return result; } /** * Gets all identifiers used in a node */ function findIdentifiersInFiles(sourceFiles) { const results = new Map(); sourceFiles.forEach((sourceFile, filePath) => { const fileResults = findNodes(sourceFile, typescript_1.default.isIdentifier); if (fileResults.length > 0) { results.set(filePath, fileResults); } }); return results; }