ng-di-graph 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["code: ErrorCode","filePath?: string","context?: Record<string, unknown>","lines: string[]","circularDependencies: string[][]","edges: Edge[]","edge: Edge","_options: CliOptions","_logger?: Logger","Project","decoratedClasses: ParsedClass[]","SyntaxKind","dependencies: ParsedDependency[]","cacheKey: string | null","flags: EdgeFlags","token: string","result","elapsed","Command","cliOptions: CliOptions","formatter: JsonFormatter | MermaidFormatter"],"sources":["../../src/core/error-handler.ts","../../src/core/logger.ts","../../src/core/graph-builder.ts","../../src/core/graph-filter.ts","../../src/core/output-handler.ts","../../src/core/parser.ts","../../src/formatters/json-formatter.ts","../../src/formatters/mermaid-formatter.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * Comprehensive error handling infrastructure for ng-di-graph CLI\n * Implements FR-10 requirements from PRD Section 13\n */\n\n/**\n * Exit codes for ng-di-graph CLI\n * Implements PRD Section 13 error handling requirements\n */\nexport enum ExitCodes {\n SUCCESS = 0, // Successful execution\n GENERAL_ERROR = 1, // Generic error (uncaught exception)\n INVALID_ARGUMENTS = 2, // Invalid CLI arguments\n TSCONFIG_ERROR = 3, // tsconfig.json not found or invalid\n PARSING_ERROR = 4, // File parsing failure\n TYPE_RESOLUTION_ERROR = 5, // Type resolution failure\n MEMORY_ERROR = 6, // Memory limit exceeded\n FILE_NOT_FOUND = 7, // Required file not found\n PERMISSION_ERROR = 8, // Insufficient permissions\n}\n\n/**\n * Error codes for structured error handling\n * Each code maps to a specific error scenario\n */\nexport type ErrorCode =\n | 'TSCONFIG_NOT_FOUND' // tsconfig.json file not found\n | 'TSCONFIG_INVALID' // Invalid JSON or config\n | 'PROJECT_LOAD_FAILED' // ts-morph project loading failed\n | 'COMPILATION_ERROR' // TypeScript compilation errors\n | 'FILE_PARSE_ERROR' // Individual file parsing failed\n | 'TYPE_RESOLUTION_ERROR' // Cannot resolve dependency type\n | 'MEMORY_LIMIT_EXCEEDED' // Memory constraints exceeded\n | 'DEPENDENCY_NOT_FOUND' // Dependency cannot be resolved\n | 'ANONYMOUS_CLASS_SKIPPED' // Anonymous class encountered\n | 'INTERNAL_ERROR' // Unexpected internal error\n | 'INVALID_ARGUMENTS' // Invalid CLI arguments provided\n | 'FILE_NOT_FOUND' // Required file not found\n | 'PERMISSION_DENIED' // Insufficient file permissions\n | 'OUTPUT_WRITE_ERROR'; // Cannot write output file\n\n/**\n * Custom error class for ng-di-graph CLI\n * Provides structured error information with context\n */\nexport class CliError extends Error {\n public readonly name = 'CliError';\n\n constructor(\n message: string,\n public readonly code: ErrorCode,\n public readonly filePath?: string,\n public readonly context?: Record<string, unknown>\n ) {\n super(message);\n Object.setPrototypeOf(this, CliError.prototype);\n }\n\n /**\n * Check if error is fatal (should exit immediately)\n */\n isFatal(): boolean {\n const fatalCodes: ErrorCode[] = [\n 'TSCONFIG_NOT_FOUND',\n 'TSCONFIG_INVALID',\n 'PROJECT_LOAD_FAILED',\n 'MEMORY_LIMIT_EXCEEDED',\n 'INTERNAL_ERROR',\n 'INVALID_ARGUMENTS',\n 'PERMISSION_DENIED',\n 'COMPILATION_ERROR',\n ];\n return fatalCodes.includes(this.code);\n }\n\n /**\n * Check if error is recoverable (can continue processing)\n */\n isRecoverable(): boolean {\n return !this.isFatal();\n }\n}\n\n/**\n * ErrorHandler - Static utility class for error handling\n * Provides centralized error formatting and exit code management\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: Centralized error handling pattern\nexport class ErrorHandler {\n /**\n * Map error code to exit code\n * @param error CliError instance or null\n * @returns Exit code for process.exit()\n */\n static classifyExitCode(error: CliError | null): ExitCodes {\n if (!error) return ExitCodes.SUCCESS;\n\n switch (error.code) {\n case 'TSCONFIG_NOT_FOUND':\n case 'TSCONFIG_INVALID':\n case 'PROJECT_LOAD_FAILED':\n return ExitCodes.TSCONFIG_ERROR;\n\n case 'FILE_PARSE_ERROR':\n case 'COMPILATION_ERROR':\n return ExitCodes.PARSING_ERROR;\n\n case 'TYPE_RESOLUTION_ERROR':\n case 'DEPENDENCY_NOT_FOUND':\n return ExitCodes.TYPE_RESOLUTION_ERROR;\n\n case 'MEMORY_LIMIT_EXCEEDED':\n return ExitCodes.MEMORY_ERROR;\n\n case 'FILE_NOT_FOUND':\n return ExitCodes.FILE_NOT_FOUND;\n\n case 'PERMISSION_DENIED':\n return ExitCodes.PERMISSION_ERROR;\n\n case 'INVALID_ARGUMENTS':\n return ExitCodes.INVALID_ARGUMENTS;\n\n default:\n return ExitCodes.GENERAL_ERROR;\n }\n }\n\n /**\n * Format error for user-friendly display\n * @param error CliError to format\n * @param verbose Include stack traces and detailed info\n * @returns Formatted error message string\n */\n static formatError(error: CliError, verbose = false): string {\n const lines: string[] = [];\n\n // Error header with classification\n if (error.isFatal()) {\n lines.push('❌ Fatal Error');\n } else if (error.code === 'TYPE_RESOLUTION_ERROR') {\n lines.push('⚠️ Type Resolution Warning');\n } else {\n lines.push('⚠️ Warning');\n }\n\n // Main error message\n lines.push('');\n lines.push(`Message: ${error.message}`);\n\n // File context if available\n if (error.filePath) {\n lines.push(`File: ${error.filePath}`);\n }\n\n // Error code for debugging\n lines.push(`Code: ${error.code}`);\n\n // Additional context\n if (error.context && Object.keys(error.context).length > 0) {\n lines.push('');\n lines.push('Context:');\n for (const [key, value] of Object.entries(error.context)) {\n lines.push(` ${key}: ${JSON.stringify(value)}`);\n }\n }\n\n // Recovery guidance\n const guidance = ErrorHandler.getRecoveryGuidance(error);\n if (guidance) {\n lines.push('');\n lines.push('💡 Suggestions:');\n for (const line of guidance.split('\\n')) {\n if (line.trim()) {\n lines.push(` • ${line.trim()}`);\n }\n }\n }\n\n // Stack trace in verbose mode\n if (verbose && error.stack) {\n lines.push('');\n lines.push('🔍 Stack Trace:');\n lines.push(error.stack);\n }\n\n // Help reference\n lines.push('');\n lines.push('Run with --help for usage information');\n lines.push('Use --verbose for detailed debugging information');\n\n return lines.join('\\n');\n }\n\n /**\n * Get actionable recovery guidance for error\n * @param error CliError to provide guidance for\n * @returns Multi-line guidance string\n */\n static getRecoveryGuidance(error: CliError): string {\n switch (error.code) {\n case 'TSCONFIG_NOT_FOUND':\n return `Check that the file path is correct\nEnsure the tsconfig.json file exists\nTry using an absolute path instead of relative\nUse --project flag to specify correct path`;\n\n case 'TSCONFIG_INVALID':\n return `Validate JSON syntax with a JSON validator\nCheck TypeScript compiler options are valid\nEnsure all referenced files exist\nTry with a minimal tsconfig.json first`;\n\n case 'PROJECT_LOAD_FAILED':\n return `Check TypeScript compilation errors\nEnsure all dependencies are installed\nVerify import paths are correct\nTry cleaning node_modules and reinstalling`;\n\n case 'FILE_PARSE_ERROR':\n return `Check TypeScript syntax in the problematic file\nEnsure all imports are properly resolved\nTry excluding the file from tsconfig if not needed\nUse --verbose to see detailed parsing errors`;\n\n case 'TYPE_RESOLUTION_ERROR':\n return `Consider adding explicit type annotations\nCheck import statements are correct\nVerify the dependency is properly exported\nUse 'any' type as temporary workaround if needed`;\n\n case 'DEPENDENCY_NOT_FOUND':\n return `Check import statements in ${error.filePath || 'the file'}\nVerify all dependencies are properly installed\nEnsure the dependency is exported from its module\nConsider using --verbose for detailed analysis`;\n\n case 'MEMORY_LIMIT_EXCEEDED':\n return `Try processing smaller portions of the codebase\nUse --entry filtering to limit scope\nConsider increasing available memory (NODE_OPTIONS=\"--max-old-space-size=4096\")\nProcess files in batches rather than all at once`;\n\n case 'ANONYMOUS_CLASS_SKIPPED':\n return `Consider giving the class a name for better tracking\nThis is a non-fatal warning - processing will continue\nAnonymous classes cannot be included in dependency graphs`;\n\n case 'OUTPUT_WRITE_ERROR':\n return `Check file permissions for the output location\nEnsure the output directory exists\nTry writing to a different location\nUse stdout instead of file output as workaround`;\n\n case 'PERMISSION_DENIED':\n return `Check file and directory permissions\nTry running with appropriate user privileges\nEnsure you have read access to source files\nVerify write access to output location`;\n\n case 'COMPILATION_ERROR':\n return `Fix TypeScript compilation errors\nCheck compiler options in tsconfig.json\nEnsure all type definitions are available\nTry running tsc directly to see detailed errors`;\n\n case 'INVALID_ARGUMENTS':\n return `Check CLI argument syntax\nReview --help for valid options\nEnsure all required arguments are provided\nVerify argument values are in correct format`;\n\n case 'FILE_NOT_FOUND':\n return `Verify file path is correct\nCheck file exists in the specified location\nEnsure file permissions allow reading\nTry using absolute path instead of relative`;\n\n default:\n return `Review the error message for specific details\nTry running with --verbose for more information\nCheck project configuration and dependencies\nConsider filing an issue if the problem persists`;\n }\n }\n\n /**\n * Handle error and exit process (never returns)\n * @param error CliError to handle\n * @param verbose Include verbose error information\n */\n static handleError(error: CliError, verbose = false): never {\n const formattedError = ErrorHandler.formatError(error, verbose);\n console.error(formattedError);\n\n const exitCode = ErrorHandler.classifyExitCode(error);\n process.exit(exitCode);\n }\n\n /**\n * Factory method to create CliError\n * @param message Error message\n * @param code Error code\n * @param filePath Optional file path\n * @param context Optional context object\n * @returns CliError instance\n */\n static createError(\n message: string,\n code: ErrorCode,\n filePath?: string,\n context?: Record<string, unknown>\n ): CliError {\n return new CliError(message, code, filePath, context);\n }\n\n /**\n * Output warning without exiting\n * @param message Warning message\n * @param filePath Optional file path\n */\n static warn(message: string, filePath?: string): void {\n console.warn(`⚠️ Warning: ${message}${filePath ? ` (${filePath})` : ''}`);\n }\n}\n","/**\n * Logger Infrastructure for ng-di-graph\n * Provides structured logging with categories, performance timing, and memory tracking\n * Implements FR-12: Verbose mode support\n *\n * Usage:\n * ```typescript\n * const logger = createLogger(verbose);\n * logger?.info(LogCategory.FILE_PROCESSING, 'Processing file', { filePath: '/test.ts' });\n * logger?.time('operation');\n * // ... operation ...\n * const elapsed = logger?.timeEnd('operation');\n * const stats = logger?.getStats();\n * ```\n */\n\n/**\n * Log category enumeration for structured logging\n * Each category represents a distinct phase of ng-di-graph processing\n */\nexport enum LogCategory {\n FILE_PROCESSING = 'file-processing',\n AST_ANALYSIS = 'ast-analysis',\n TYPE_RESOLUTION = 'type-resolution',\n GRAPH_CONSTRUCTION = 'graph-construction',\n FILTERING = 'filtering',\n ERROR_RECOVERY = 'error-recovery',\n PERFORMANCE = 'performance',\n}\n\n/**\n * Optional context information attached to log messages\n * Provides additional debugging context for verbose mode\n */\nexport interface LogContext {\n filePath?: string; // Source file path being processed\n lineNumber?: number; // Line number in source file\n className?: string; // Class name being analyzed\n methodName?: string; // Method name being executed\n nodeId?: string; // Graph node identifier\n timing?: number; // Performance timing in milliseconds\n memoryUsage?: number; // Memory usage in bytes\n [key: string]: unknown; // Additional custom fields\n}\n\n/**\n * Logger interface for structured logging\n * Supports log levels, performance timing, and statistics tracking\n */\nexport interface Logger {\n debug(category: LogCategory, message: string, context?: LogContext): void;\n info(category: LogCategory, message: string, context?: LogContext): void;\n warn(category: LogCategory, message: string, context?: LogContext): void;\n error(category: LogCategory, message: string, context?: LogContext): void;\n time(label: string): void;\n timeEnd(label: string): number;\n getStats(): LoggingStats;\n}\n\n/**\n * Aggregated logging statistics\n * Provides insights into verbose mode execution\n */\nexport interface LoggingStats {\n totalLogs: number; // Total number of logs emitted\n categoryCounts: Record<LogCategory, number>; // Logs per category\n performanceMetrics: {\n totalTime: number; // Total execution time (ms)\n fileProcessingTime: number; // Time spent processing files (ms)\n graphBuildingTime: number; // Time spent building graph (ms)\n outputGenerationTime: number; // Time spent generating output (ms)\n };\n memoryUsage: {\n peakUsage: number; // Peak memory usage (bytes)\n currentUsage: number; // Current memory usage (bytes)\n };\n}\n\n/**\n * Internal timer tracking entry\n * Stores performance timer state\n */\nexport interface TimerEntry {\n startTime: number;\n category?: LogCategory;\n}\n\n/**\n * Factory function for Logger creation\n * Returns undefined when verbose is false (no-op pattern)\n *\n * @param verbose - Enable verbose logging\n * @returns Logger instance or undefined\n *\n * @example\n * ```typescript\n * const logger = createLogger(cliOptions.verbose);\n * logger?.info(LogCategory.FILE_PROCESSING, 'Starting processing');\n * ```\n */\nexport function createLogger(verbose: boolean): Logger | undefined {\n return verbose ? new LoggerImpl() : undefined;\n}\n\n/**\n * Logger implementation (internal)\n * Provides structured logging with minimal performance overhead\n */\nclass LoggerImpl implements Logger {\n private _timers: Map<string, TimerEntry>;\n private _stats: LoggingStats;\n private _peakMemory: number;\n\n constructor() {\n this._timers = new Map();\n this._stats = {\n totalLogs: 0,\n categoryCounts: {} as Record<LogCategory, number>,\n performanceMetrics: {\n totalTime: 0,\n fileProcessingTime: 0,\n graphBuildingTime: 0,\n outputGenerationTime: 0,\n },\n memoryUsage: {\n peakUsage: 0,\n currentUsage: 0,\n },\n };\n this._peakMemory = 0;\n }\n\n debug(category: LogCategory, message: string, context?: LogContext): void {\n this._log('DEBUG', category, message, context);\n }\n\n info(category: LogCategory, message: string, context?: LogContext): void {\n this._log('INFO', category, message, context);\n }\n\n warn(category: LogCategory, message: string, context?: LogContext): void {\n this._log('WARN', category, message, context);\n }\n\n error(category: LogCategory, message: string, context?: LogContext): void {\n this._log('ERROR', category, message, context);\n }\n\n time(label: string): void {\n this._timers.set(label, {\n startTime: performance.now(),\n });\n }\n\n timeEnd(label: string): number {\n const timer = this._timers.get(label);\n if (!timer) {\n throw new Error(`Timer '${label}' does not exist`);\n }\n const elapsed = performance.now() - timer.startTime;\n this._timers.delete(label);\n return elapsed;\n }\n\n getStats(): LoggingStats {\n // Update current memory usage\n const memUsage = process.memoryUsage();\n this._stats.memoryUsage.currentUsage = memUsage.heapUsed;\n\n return { ...this._stats };\n }\n\n private _log(level: string, category: LogCategory, message: string, context?: LogContext): void {\n // Update statistics\n this._stats.totalLogs++;\n this._stats.categoryCounts[category] = (this._stats.categoryCounts[category] || 0) + 1;\n\n // Track peak memory\n const memUsage = process.memoryUsage().heapUsed;\n if (memUsage > this._peakMemory) {\n this._peakMemory = memUsage;\n this._stats.memoryUsage.peakUsage = memUsage;\n }\n\n // Format and output log\n const formattedLog = this._formatLog(level, category, message, context);\n console.error(formattedLog); // Use stderr to separate from tool output\n }\n\n private _formatLog(\n level: string,\n category: LogCategory,\n message: string,\n context?: LogContext\n ): string {\n const timestamp = new Date().toISOString();\n const contextStr = context ? ` ${JSON.stringify(context)}` : '';\n return `[${timestamp}] [${level}] [${category}] ${message}${contextStr}`;\n }\n}\n","/**\n * Graph builder module for ng-di-graph CLI tool\n * Transforms parsed classes into graph data structure\n */\n\nimport type { Edge, Graph, Node, ParsedClass } from '../types';\nimport { LogCategory, type Logger } from './logger';\n\n/**\n * Validates input parameters for the buildGraph function\n * @param parsedClasses Array to validate\n * @throws Error if validation fails\n */\nfunction validateInput(parsedClasses: ParsedClass[]): void {\n // Check for null/undefined\n if (parsedClasses == null) {\n throw new Error('parsedClasses parameter cannot be null or undefined');\n }\n\n // Validate each ParsedClass\n for (let i = 0; i < parsedClasses.length; i++) {\n const parsedClass = parsedClasses[i];\n\n // Check name property\n if (typeof parsedClass.name !== 'string') {\n throw new Error('ParsedClass must have a valid name property');\n }\n\n if (parsedClass.name.trim() === '') {\n throw new Error('ParsedClass name cannot be empty');\n }\n\n // Check kind property\n if (typeof parsedClass.kind !== 'string') {\n throw new Error('ParsedClass must have a valid kind property');\n }\n\n // Check dependencies property\n if (parsedClass.dependencies == null) {\n throw new Error('ParsedClass must have a dependencies array');\n }\n\n if (!Array.isArray(parsedClass.dependencies)) {\n throw new Error('ParsedClass dependencies must be an array');\n }\n\n // Validate each dependency\n for (const dependency of parsedClass.dependencies) {\n if (typeof dependency.token !== 'string') {\n throw new Error('ParsedDependency must have a valid token property');\n }\n }\n }\n}\n\n/**\n * Detects circular dependencies using DFS (Depth-First Search)\n * @param edges Array of edges representing the dependency graph\n * @param nodes Array of nodes in the graph\n * @returns Array of circular dependency paths and edges marked as circular\n */\nfunction detectCircularDependencies(\n edges: Edge[],\n nodes: Node[]\n): {\n circularDependencies: string[][];\n circularEdges: Set<string>;\n} {\n const circularDependencies: string[][] = [];\n const circularEdges = new Set<string>();\n\n // Build adjacency list for efficient traversal\n const adjacencyList = new Map<string, string[]>();\n for (const node of nodes) {\n adjacencyList.set(node.id, []);\n }\n for (const edge of edges) {\n if (!adjacencyList.has(edge.from)) {\n adjacencyList.set(edge.from, []);\n }\n const neighbors = adjacencyList.get(edge.from);\n if (neighbors) {\n neighbors.push(edge.to);\n }\n }\n\n // Track visited nodes during DFS\n const recursionStack = new Set<string>();\n const processedNodes = new Set<string>();\n\n /**\n * DFS helper function to detect cycles\n */\n function dfs(node: string, path: string[]): void {\n if (processedNodes.has(node)) {\n return; // Already processed this node completely\n }\n\n if (recursionStack.has(node)) {\n // Found a cycle - extract the cycle path\n const cycleStartIndex = path.indexOf(node);\n const cyclePath = [...path.slice(cycleStartIndex), node];\n circularDependencies.push(cyclePath);\n\n // Mark all edges in this cycle as circular\n for (let i = 0; i < cyclePath.length - 1; i++) {\n const edgeKey = `${cyclePath[i]}->${cyclePath[i + 1]}`;\n circularEdges.add(edgeKey);\n }\n return;\n }\n\n recursionStack.add(node);\n const newPath = [...path, node];\n\n const neighbors = adjacencyList.get(node) || [];\n for (const neighbor of neighbors) {\n dfs(neighbor, newPath);\n }\n\n recursionStack.delete(node);\n processedNodes.add(node);\n }\n\n // Run DFS from each unvisited node\n for (const node of nodes) {\n if (!processedNodes.has(node.id)) {\n dfs(node.id, []);\n }\n }\n\n return { circularDependencies, circularEdges };\n}\n\n/**\n * Builds a dependency graph from parsed Angular classes\n * @param parsedClasses Array of parsed classes with their dependencies\n * @param logger Optional Logger instance for verbose mode logging\n * @returns Graph containing nodes and edges representing the dependency relationships\n */\nexport function buildGraph(parsedClasses: ParsedClass[], logger?: Logger): Graph {\n // Start performance timing\n logger?.time('buildGraph');\n logger?.info(LogCategory.GRAPH_CONSTRUCTION, 'Starting graph construction', {\n classCount: parsedClasses.length,\n });\n\n // Validate input parameters\n validateInput(parsedClasses);\n\n // Use Map for O(1) lookups during construction\n const nodeMap = new Map<string, Node>();\n const edges: Edge[] = [];\n\n // First pass: Create nodes for all parsed classes\n for (const parsedClass of parsedClasses) {\n if (!nodeMap.has(parsedClass.name)) {\n nodeMap.set(parsedClass.name, {\n id: parsedClass.name,\n kind: parsedClass.kind,\n });\n }\n }\n\n logger?.info(LogCategory.GRAPH_CONSTRUCTION, `Created ${nodeMap.size} nodes`, {\n nodeCount: nodeMap.size,\n });\n\n // Second pass: Create edges and unknown nodes for dependencies\n let unknownNodeCount = 0;\n for (const parsedClass of parsedClasses) {\n for (const dependency of parsedClass.dependencies) {\n // Create unknown node if dependency doesn't exist\n if (!nodeMap.has(dependency.token)) {\n nodeMap.set(dependency.token, {\n id: dependency.token,\n kind: 'unknown',\n });\n unknownNodeCount++;\n logger?.warn(LogCategory.GRAPH_CONSTRUCTION, `Created unknown node: ${dependency.token}`, {\n nodeId: dependency.token,\n referencedBy: parsedClass.name,\n });\n }\n\n // Create edge\n const edge: Edge = {\n from: parsedClass.name,\n to: dependency.token,\n };\n\n // Add flags if present\n if (dependency.flags) {\n edge.flags = dependency.flags;\n }\n\n edges.push(edge);\n }\n }\n\n logger?.info(LogCategory.GRAPH_CONSTRUCTION, `Created ${edges.length} edges`, {\n edgeCount: edges.length,\n unknownNodeCount,\n });\n\n // Convert map to array and sort for consistency\n const nodes = Array.from(nodeMap.values()).sort((a, b) => a.id.localeCompare(b.id));\n\n // Sort edges by from, then by to for consistency\n edges.sort((a, b) => {\n const fromCompare = a.from.localeCompare(b.from);\n if (fromCompare !== 0) return fromCompare;\n return a.to.localeCompare(b.to);\n });\n\n // Detect circular dependencies\n logger?.time('circularDetection');\n const { circularDependencies, circularEdges } = detectCircularDependencies(edges, nodes);\n const circularDetectionTime = logger?.timeEnd('circularDetection');\n\n if (circularDependencies.length > 0) {\n logger?.warn(\n LogCategory.GRAPH_CONSTRUCTION,\n `Circular dependencies detected: ${circularDependencies.length}`,\n {\n circularCount: circularDependencies.length,\n cycles: circularDependencies,\n detectionTime: circularDetectionTime,\n }\n );\n } else {\n logger?.info(LogCategory.GRAPH_CONSTRUCTION, 'No circular dependencies detected', {\n detectionTime: circularDetectionTime,\n });\n }\n\n // Mark circular edges\n for (const edge of edges) {\n const edgeKey = `${edge.from}->${edge.to}`;\n if (circularEdges.has(edgeKey)) {\n edge.isCircular = true;\n }\n }\n\n // Complete performance timing\n const duration = logger?.timeEnd('buildGraph');\n logger?.info(LogCategory.GRAPH_CONSTRUCTION, 'Graph construction complete', {\n duration,\n nodeCount: nodes.length,\n edgeCount: edges.length,\n circularCount: circularDependencies.length,\n });\n\n return {\n nodes,\n edges,\n circularDependencies,\n };\n}\n","/**\n * Graph filtering module for ng-di-graph CLI tool\n * Filters graphs based on entry points and direction\n */\n\nimport type { CliOptions, Graph } from '../types';\n\n/**\n * Helper function to validate entry points and perform traversal\n * @param entryPoints Array of entry point names to validate and traverse from\n * @param graph The graph containing all nodes\n * @param adjacencyList The adjacency list for traversal\n * @param resultSet The set to collect traversal results\n * @param options CLI options for verbose output\n */\nfunction validateAndTraverseEntryPoints(\n entryPoints: string[],\n graph: Graph,\n adjacencyList: Map<string, string[]>,\n resultSet: Set<string>,\n options: CliOptions\n): void {\n for (const entryPoint of entryPoints) {\n if (graph.nodes.some((n) => n.id === entryPoint)) {\n traverseFromEntry(entryPoint, adjacencyList, resultSet);\n } else if (options.verbose) {\n console.warn(`Entry point '${entryPoint}' not found in graph`);\n }\n }\n}\n\n/**\n * Filters a graph based on entry points and traversal direction\n * @param graph The graph to filter\n * @param options CLI options containing entry points and direction\n * @returns Filtered graph containing only nodes reachable from entry points\n */\nexport function filterGraph(graph: Graph, options: CliOptions): Graph {\n // If no entry points specified or empty array, return original graph\n if (!options.entry || options.entry.length === 0) {\n return graph;\n }\n\n const includedNodeIds = new Set<string>();\n\n // Handle bidirectional separately by combining upstream and downstream\n if (options.direction === 'both') {\n // Perform upstream traversal\n const upstreamAdjacencyList = buildAdjacencyList(graph, 'upstream');\n const upstreamNodes = new Set<string>();\n validateAndTraverseEntryPoints(\n options.entry,\n graph,\n upstreamAdjacencyList,\n upstreamNodes,\n options\n );\n\n // Perform downstream traversal\n const downstreamAdjacencyList = buildAdjacencyList(graph, 'downstream');\n const downstreamNodes = new Set<string>();\n validateAndTraverseEntryPoints(\n options.entry,\n graph,\n downstreamAdjacencyList,\n downstreamNodes,\n options\n );\n\n // Combine upstream and downstream results using Set constructor for optimal performance\n const combinedNodes = new Set([...upstreamNodes, ...downstreamNodes]);\n for (const nodeId of combinedNodes) {\n includedNodeIds.add(nodeId);\n }\n } else {\n // Handle single direction (upstream or downstream)\n const adjacencyList = buildAdjacencyList(graph, options.direction);\n validateAndTraverseEntryPoints(options.entry, graph, adjacencyList, includedNodeIds, options);\n }\n\n // Filter nodes and edges\n const filteredNodes = graph.nodes.filter((node) => includedNodeIds.has(node.id));\n const filteredEdges = graph.edges.filter(\n (edge) => includedNodeIds.has(edge.from) && includedNodeIds.has(edge.to)\n );\n\n // Filter circular dependencies - only include cycles where all nodes exist and edges form valid cycle\n const filteredCircularDeps = graph.circularDependencies.filter((cycle) => {\n // First check if cycle has valid length (minimum 2 for self-loop, minimum 3 for multi-node cycle)\n if (cycle.length < 2) {\n return false; // Invalid cycle - too short\n }\n\n // Check if it's a valid cycle format:\n // - Self-loop: ['A', 'A']\n // - Proper cycle with closing node: ['A', 'B', 'A']\n // - Proper cycle without closing node: ['A', 'B', 'C'] (where edges form A->B->C->A)\n const isSelfLoop = cycle.length === 2 && cycle[0] === cycle[1];\n const isProperCycleWithClosing = cycle.length >= 3 && cycle[0] === cycle[cycle.length - 1];\n const isProperCycleWithoutClosing = cycle.length >= 3 && cycle[0] !== cycle[cycle.length - 1];\n\n if (!isSelfLoop && !isProperCycleWithClosing && !isProperCycleWithoutClosing) {\n return false; // Invalid cycle format\n }\n\n // Check all nodes in cycle exist in filtered result\n if (!cycle.every((nodeId) => includedNodeIds.has(nodeId))) {\n return false;\n }\n\n // Then check that the cycle has valid edges between consecutive nodes in the ORIGINAL graph\n const edgesToCheck = isProperCycleWithClosing ? cycle.length - 1 : cycle.length;\n\n for (let i = 0; i < edgesToCheck; i++) {\n const fromNode = cycle[i];\n const toNode = isProperCycleWithClosing ? cycle[i + 1] : cycle[(i + 1) % cycle.length];\n\n // Check if there's an edge from fromNode to toNode in the original graph edges\n const hasEdgeInOriginal = graph.edges.some(\n (edge) => edge.from === fromNode && edge.to === toNode\n );\n if (!hasEdgeInOriginal) {\n return false; // Invalid cycle - missing edge in original graph\n }\n }\n\n return true;\n });\n\n if (options.verbose) {\n console.log(`Filtered graph: ${filteredNodes.length} nodes, ${filteredEdges.length} edges`);\n console.log(`Entry points: ${options.entry.join(', ')}`);\n }\n\n return {\n nodes: filteredNodes,\n edges: filteredEdges,\n circularDependencies: filteredCircularDeps,\n };\n}\n\n/**\n * Traverses the graph from a starting node using DFS\n * @param startNode The node to start traversal from\n * @param adjacencyList The adjacency list representation of the graph\n * @param visited Set to track visited nodes\n */\nfunction traverseFromEntry(\n startNode: string,\n adjacencyList: Map<string, string[]>,\n visited: Set<string>\n): void {\n const stack = [startNode];\n\n while (stack.length > 0) {\n const currentNode = stack.pop();\n if (!currentNode) break; // Defensive check, should never happen\n\n if (visited.has(currentNode)) {\n continue;\n }\n\n visited.add(currentNode);\n\n // Add neighbors to stack for traversal\n const neighbors = adjacencyList.get(currentNode) || [];\n for (const neighbor of neighbors) {\n if (!visited.has(neighbor)) {\n stack.push(neighbor);\n }\n }\n }\n}\n\n/**\n * Builds an adjacency list from the graph based on direction\n * @param graph The graph to build adjacency list from\n * @param direction The direction to follow edges\n * @returns Map representing adjacency list\n */\nfunction buildAdjacencyList(graph: Graph, direction: string): Map<string, string[]> {\n const adjacencyList = new Map<string, string[]>();\n\n // Initialize empty arrays for all nodes\n for (const node of graph.nodes) {\n adjacencyList.set(node.id, []);\n }\n\n // Populate from edges based on direction\n for (const edge of graph.edges) {\n if (direction === 'downstream') {\n const neighbors = adjacencyList.get(edge.from) || [];\n neighbors.push(edge.to);\n adjacencyList.set(edge.from, neighbors);\n } else if (direction === 'upstream') {\n const neighbors = adjacencyList.get(edge.to) || [];\n neighbors.push(edge.from);\n adjacencyList.set(edge.to, neighbors);\n }\n }\n\n return adjacencyList;\n}\n","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\n/**\n * Handles output writing to stdout or files\n * Supports directory creation and error handling\n */\nexport class OutputHandler {\n /**\n * Write output content to stdout or file\n * @param content The content to write\n * @param filePath Optional file path. If not provided, writes to stdout\n */\n async writeOutput(content: string, filePath?: string): Promise<void> {\n if (!filePath) {\n // Write to stdout\n process.stdout.write(content);\n return;\n }\n\n try {\n // Ensure directory exists\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Write to file\n writeFileSync(filePath, content, 'utf-8');\n } catch (error) {\n throw new Error(\n `Failed to write output file: ${error instanceof Error ? error.message : 'Unknown error'}`\n );\n }\n }\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport type {\n CallExpression,\n ClassDeclaration,\n Decorator,\n Node,\n ObjectLiteralExpression,\n ParameterDeclaration,\n PropertyAssignment,\n PropertyDeclaration,\n SourceFile,\n Type,\n TypeNode,\n} from 'ts-morph';\nimport { Project, SyntaxKind } from 'ts-morph';\nimport type {\n CliOptions,\n EdgeFlags,\n NodeKind,\n ParameterAnalysisResult,\n ParsedClass,\n ParsedDependency,\n StructuredWarnings,\n VerboseStats,\n Warning,\n} from '../types';\nimport { CliError, ErrorHandler } from './error-handler';\nimport { LogCategory, type Logger } from './logger';\n\n/**\n * AngularParser - Core TypeScript AST parsing using ts-morph\n * Implements FR-01: ts-morph project loading with comprehensive error handling\n * Implements FR-02: Decorated class collection\n * Implements FR-03: Constructor token resolution\n */\nconst GLOBAL_WARNING_KEYS = new Set<string>();\n\nexport class AngularParser {\n private _project?: Project;\n private _typeResolutionCache = new Map<string, string | null>();\n private _circularTypeRefs = new Set<string>();\n private _structuredWarnings: StructuredWarnings = {\n categories: {\n typeResolution: [],\n skippedTypes: [],\n unresolvedImports: [],\n circularReferences: [],\n performance: [],\n },\n totalCount: 0,\n };\n\n constructor(\n private _options: CliOptions,\n private _logger?: Logger\n ) {}\n\n /**\n * Reset global warning deduplication state (useful for testing)\n */\n static resetWarningState(): void {\n GLOBAL_WARNING_KEYS.clear();\n }\n\n /**\n * Get structured warnings for analysis (Task 3.3)\n * Returns a copy of the structured warnings collected during parsing\n * @returns StructuredWarnings object with categorized warnings\n */\n getStructuredWarnings(): StructuredWarnings {\n return {\n categories: {\n typeResolution: [...this._structuredWarnings.categories.typeResolution],\n skippedTypes: [...this._structuredWarnings.categories.skippedTypes],\n unresolvedImports: [...this._structuredWarnings.categories.unresolvedImports],\n circularReferences: [...this._structuredWarnings.categories.circularReferences],\n performance: [...this._structuredWarnings.categories.performance],\n },\n totalCount: this._structuredWarnings.totalCount,\n };\n }\n\n /**\n * Add structured warning to collection (Task 3.3)\n * Includes global deduplication for both structured warnings and console output\n * @param category Warning category\n * @param warning Warning object\n */\n private addStructuredWarning(\n category: keyof StructuredWarnings['categories'],\n warning: Warning\n ): void {\n // Deduplicate using global warning tracking for both structured warnings and console output\n const warnKey = `${category}_${warning.type}_${warning.file}_${warning.message}`;\n if (!GLOBAL_WARNING_KEYS.has(warnKey)) {\n // Add to structured warnings array (deduplicated)\n this._structuredWarnings.categories[category].push(warning);\n this._structuredWarnings.totalCount++;\n\n // Also output to console for immediate feedback\n const location = warning.line\n ? `${warning.file}:${warning.line}:${warning.column}`\n : warning.file;\n console.warn(`[${warning.severity.toUpperCase()}] ${warning.message} (${location})`);\n\n if (warning.suggestion && this._options.verbose) {\n console.warn(` Suggestion: ${warning.suggestion}`);\n }\n\n GLOBAL_WARNING_KEYS.add(warnKey);\n }\n }\n\n /**\n * Load TypeScript project using ts-morph\n * Implements FR-01 with error handling from PRD Section 13\n */\n loadProject(): void {\n // Validate tsconfig path exists\n if (!existsSync(this._options.project)) {\n throw ErrorHandler.createError(\n `tsconfig.json not found at: ${this._options.project}`,\n 'TSCONFIG_NOT_FOUND',\n this._options.project\n );\n }\n\n try {\n // First, try to validate the JSON syntax by reading and parsing it\n try {\n const configContent = readFileSync(this._options.project, 'utf8');\n JSON.parse(configContent);\n } catch (jsonError) {\n const errorMessage = jsonError instanceof Error ? jsonError.message : String(jsonError);\n throw ErrorHandler.createError(\n `Invalid tsconfig.json: ${errorMessage}`,\n 'TSCONFIG_INVALID',\n this._options.project\n );\n }\n\n // Load Project with ts-morph\n this._project = new Project({\n tsConfigFilePath: this._options.project,\n });\n\n // Validate project loaded successfully\n if (!this._project) {\n throw ErrorHandler.createError(\n 'Failed to load TypeScript project',\n 'PROJECT_LOAD_FAILED',\n this._options.project\n );\n }\n\n // Basic validation - try to get source files to ensure project is valid\n // This will catch TypeScript compilation/configuration errors\n this._project.getSourceFiles();\n\n // Additional validation for compiler options\n const program = this._project.getProgram();\n const diagnostics = program.getConfigFileParsingDiagnostics();\n\n if (diagnostics.length > 0) {\n const firstDiagnostic = diagnostics[0];\n const message = firstDiagnostic.getMessageText();\n\n throw ErrorHandler.createError(\n `TypeScript configuration error: ${message}`,\n 'PROJECT_LOAD_FAILED',\n this._options.project,\n { diagnosticCount: diagnostics.length }\n );\n }\n } catch (error) {\n // Re-throw CliError instances\n if (error instanceof CliError) {\n throw error;\n }\n\n if (error instanceof Error) {\n // Handle different types of ts-morph/TypeScript errors\n if (\n error.message.includes('JSON') ||\n error.message.includes('Unexpected token') ||\n error.message.includes('expected')\n ) {\n throw ErrorHandler.createError(\n `Invalid tsconfig.json: ${error.message}`,\n 'TSCONFIG_INVALID',\n this._options.project\n );\n }\n\n if (error.message.includes('TypeScript') || error.message.includes('Compiler option')) {\n throw ErrorHandler.createError(\n `TypeScript compilation failed: ${error.message}`,\n 'COMPILATION_ERROR',\n this._options.project\n );\n }\n\n // Generic project loading failure\n throw ErrorHandler.createError(\n `Failed to load TypeScript project: ${error.message}`,\n 'PROJECT_LOAD_FAILED',\n this._options.project\n );\n }\n\n // Unknown error type\n throw ErrorHandler.createError(\n 'Failed to load TypeScript project due to unknown error',\n 'PROJECT_LOAD_FAILED',\n this._options.project\n );\n }\n }\n\n /**\n * Get the loaded ts-morph Project instance\n * @returns Project instance\n * @throws Error if project not loaded\n */\n getProject(): Project {\n if (!this._project) {\n throw new Error('Project not loaded. Call loadProject() first.');\n }\n return this._project;\n }\n\n /**\n * Parse decorated classes from the loaded project\n * Auto-loads project if not already loaded\n * @returns Promise of parsed class information\n */\n async parseClasses(): Promise<ParsedClass[]> {\n if (!this._project) {\n this.loadProject();\n }\n\n // Use findDecoratedClasses to implement parseClasses\n return this.findDecoratedClasses();\n }\n\n /**\n * Find all classes decorated with @Injectable, @Component, or @Directive\n * Implements FR-02: Decorated Class Collection\n * @returns Promise<ParsedClass[]> List of decorated classes\n */\n async findDecoratedClasses(): Promise<ParsedClass[]> {\n if (!this._project) {\n this.loadProject();\n }\n\n if (!this._project) {\n throw ErrorHandler.createError(\n 'Failed to load TypeScript project',\n 'PROJECT_LOAD_FAILED',\n this._options.project\n );\n }\n\n const decoratedClasses: ParsedClass[] = [];\n const sourceFiles = this._project.getSourceFiles();\n let processedFiles = 0;\n let skippedFiles = 0;\n\n // Clear circular reference tracking for cross-class detection\n this._circularTypeRefs.clear();\n\n // Logger: Start timing file processing\n this._logger?.time('findDecoratedClasses');\n this._logger?.info(LogCategory.FILE_PROCESSING, 'Starting file processing', {\n fileCount: sourceFiles.length,\n });\n\n if (this._options.verbose) {\n console.log(`Processing ${sourceFiles.length} source files`);\n }\n\n for (const sourceFile of sourceFiles) {\n const filePath = sourceFile.getFilePath();\n try {\n if (this._options.verbose) {\n console.log(`🔍 Parsing file: ${filePath}`);\n }\n\n this._logger?.debug(LogCategory.FILE_PROCESSING, 'Processing file', { filePath });\n\n const classes = sourceFile.getClasses();\n\n if (this._options.verbose) {\n console.log(`File: ${filePath}, Classes: ${classes.length}`);\n }\n\n this._logger?.debug(LogCategory.AST_ANALYSIS, 'Analyzing classes in file', {\n filePath,\n classCount: classes.length,\n });\n\n // Process regular class declarations\n for (const classDeclaration of classes) {\n const parsedClass = this.parseClassDeclaration(classDeclaration);\n if (parsedClass) {\n decoratedClasses.push(parsedClass);\n if (this._options.verbose) {\n console.log(`Found decorated class: ${parsedClass.name} (${parsedClass.kind})`);\n }\n this._logger?.info(LogCategory.AST_ANALYSIS, 'Found decorated class', {\n className: parsedClass.name,\n kind: parsedClass.kind,\n filePath,\n });\n }\n }\n\n // Look for anonymous class expressions in variable declarations\n // Pattern: const X = Decorator()(class { ... })\n this.detectAnonymousClasses(sourceFile);\n\n processedFiles++;\n } catch (error) {\n // Graceful error recovery: warn and continue with next file (FR-14)\n skippedFiles++;\n\n if (error instanceof CliError) {\n if (!error.isFatal()) {\n ErrorHandler.warn(error.message, filePath);\n continue;\n }\n throw error;\n }\n\n // Non-fatal file parsing error - continue processing\n ErrorHandler.warn(\n `Failed to parse file (skipping): ${error instanceof Error ? error.message : 'Unknown error'}`,\n filePath\n );\n }\n }\n\n if (this._options.verbose) {\n console.log(`✅ Processed ${processedFiles} files, skipped ${skippedFiles} files`);\n }\n\n // Logger: End timing and log completion\n const elapsed = this._logger?.timeEnd('findDecoratedClasses') || 0;\n this._logger?.info(LogCategory.PERFORMANCE, 'File processing complete', {\n totalClasses: decoratedClasses.length,\n processedFiles,\n skippedFiles,\n timing: elapsed,\n });\n\n if (decoratedClasses.length === 0) {\n ErrorHandler.warn('No decorated classes found in the project');\n }\n\n return decoratedClasses;\n }\n\n /**\n * Parse a single class declaration for Angular decorators\n * @param classDeclaration ts-morph ClassDeclaration\n * @returns ParsedClass if decorated with Angular decorator, null otherwise\n */\n private parseClassDeclaration(classDeclaration: ClassDeclaration): ParsedClass | null {\n const className = classDeclaration.getName();\n\n // Skip anonymous classes with warning\n if (!className) {\n console.warn(\n 'Warning: Skipping anonymous class - classes must be named for dependency injection analysis'\n );\n return null;\n }\n\n const decorators = classDeclaration.getDecorators();\n\n if (this._options.verbose) {\n const decoratorNames = decorators.map((d) => this.getDecoratorName(d)).join(', ');\n console.log(`Class: ${className}, Decorators: ${decorators.length} [${decoratorNames}]`);\n }\n\n const angularDecorator = this.findAngularDecorator(decorators);\n\n if (!angularDecorator) {\n // Skip undecorated classes silently\n if (this._options.verbose && decorators.length > 0) {\n console.log(` No Angular decorator found for ${className}`);\n }\n return null;\n }\n\n const nodeKind = this.determineNodeKind(angularDecorator);\n const filePath = classDeclaration.getSourceFile().getFilePath();\n\n // FR-03: Extract constructor dependencies\n const dependencies = this.extractConstructorDependencies(classDeclaration);\n\n return {\n name: className,\n kind: nodeKind,\n filePath,\n dependencies,\n };\n }\n\n /**\n * Find Angular decorator (@Injectable, @Component, @Directive) from list of decorators\n * @param decorators Array of decorators from ts-morph\n * @returns Angular decorator if found, null otherwise\n */\n private findAngularDecorator(decorators: Decorator[]): Decorator | null {\n for (const decorator of decorators) {\n const decoratorName = this.getDecoratorName(decorator);\n\n if (\n decoratorName === 'Injectable' ||\n decoratorName === 'Component' ||\n decoratorName === 'Directive'\n ) {\n return decorator;\n }\n }\n\n return null;\n }\n\n /**\n * Extract decorator name from ts-morph Decorator\n * Handles various import patterns and aliases\n * @param decorator ts-morph Decorator\n * @returns Decorator name string\n */\n private getDecoratorName(decorator: Decorator): string {\n const callExpression = decorator.getCallExpression();\n if (!callExpression) {\n return '';\n }\n\n const expression = callExpression.getExpression();\n\n // Handle direct decorator names (e.g., @Injectable)\n if (expression.getKind() === SyntaxKind.Identifier) {\n const identifier = expression.asKindOrThrow(SyntaxKind.Identifier);\n const decoratorName = identifier.getText();\n\n // Resolve aliases by checking import declarations (cached for performance)\n const resolvedName = this.resolveDecoratorAlias(decorator.getSourceFile(), decoratorName);\n return resolvedName || decoratorName;\n }\n\n return '';\n }\n\n /**\n * Resolve decorator alias from import declarations with basic caching\n * @param sourceFile Source file containing the decorator\n * @param decoratorName Raw decorator name from AST\n * @returns Original decorator name if alias found, null otherwise\n */\n private resolveDecoratorAlias(sourceFile: SourceFile, decoratorName: string): string | null {\n const importDeclarations = sourceFile.getImportDeclarations();\n\n for (const importDecl of importDeclarations) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue();\n if (moduleSpecifier === '@angular/core') {\n const namedImports = importDecl.getNamedImports();\n\n for (const namedImport of namedImports) {\n const alias = namedImport.getAliasNode();\n if (alias && alias.getText() === decoratorName) {\n // Found alias, return the original name\n return namedImport.getName();\n }\n if (!alias && namedImport.getName() === decoratorName) {\n // Direct import without alias\n return decoratorName;\n }\n }\n }\n }\n\n return null;\n }\n\n /**\n * Determine NodeKind from Angular decorator\n * @param decorator Angular decorator\n * @returns NodeKind mapping\n */\n private determineNodeKind(decorator: Decorator): NodeKind {\n const decoratorName = this.getDecoratorName(decorator);\n\n switch (decoratorName) {\n case 'Injectable':\n return 'service';\n case 'Component':\n return 'component';\n case 'Directive':\n return 'directive';\n default:\n return 'unknown';\n }\n }\n\n /**\n * Detect and warn about anonymous class expressions\n * Handles patterns like: const X = Decorator()(class { ... })\n * @param sourceFile Source file to analyze\n */\n private detectAnonymousClasses(sourceFile: SourceFile): void {\n try {\n // More robust anonymous class detection using ts-morph's AST traversal\n sourceFile.forEachDescendant((node: Node) => {\n // Look for class expressions that might be decorated\n if (node.getKind() === SyntaxKind.ClassExpression) {\n const parent = node.getParent();\n\n // Check if this class expression is used in a decorator pattern\n if (parent && parent.getKind() === SyntaxKind.CallExpression) {\n const grandParent = parent.getParent();\n if (grandParent && grandParent.getKind() === SyntaxKind.CallExpression) {\n // Found pattern like Decorator()(class { ... })\n console.warn(\n 'Warning: Skipping anonymous class - classes must be named for dependency injection analysis'\n );\n if (this._options.verbose) {\n console.log(` Anonymous class found in ${sourceFile.getFilePath()}`);\n }\n }\n }\n }\n });\n } catch (error) {\n // Silent fallback - don't break parsing for this edge case\n if (this._options.verbose) {\n console.log(\n ` Could not detect anonymous classes in ${sourceFile.getFilePath()}: ${error}`\n );\n }\n }\n }\n\n /**\n * Extract constructor dependencies from a class declaration\n * Implements FR-03: Constructor parameter analysis\n * Implements TDD Cycle 2.1: inject() function detection\n * @param classDeclaration ts-morph ClassDeclaration\n * @returns Array of parsed dependencies\n */\n private extractConstructorDependencies(classDeclaration: ClassDeclaration): ParsedDependency[] {\n const dependencies: ParsedDependency[] = [];\n const verboseStats = {\n decoratorCounts: { optional: 0, self: 0, skipSelf: 0, host: 0 },\n skippedDecorators: [] as Array<{ name: string; reason: string }>,\n parametersWithDecorators: 0,\n parametersWithoutDecorators: 0,\n legacyDecoratorsUsed: 0,\n injectPatternsUsed: 0,\n totalProcessingTime: 0,\n totalParameters: 0,\n };\n\n const startTime = performance.now();\n\n if (this._options.verbose && this._options.includeDecorators) {\n console.log('=== Decorator Analysis ===');\n const className = classDeclaration.getName() || 'unknown';\n console.log(`Analyzing decorators for class: ${className}`);\n }\n\n if (this._options.verbose && !this._options.includeDecorators) {\n console.log('Decorator analysis disabled - --include-decorators flag not set');\n }\n\n // Extract constructor parameter dependencies (legacy approach)\n const constructors = classDeclaration.getConstructors();\n if (constructors.length > 0) {\n // Take first constructor (Angular classes should have only one)\n const constructorDecl = constructors[0];\n const parameters = constructorDecl.getParameters();\n verboseStats.totalParameters = parameters.length;\n\n for (const param of parameters) {\n const paramStartTime = performance.now();\n const dependency = this.parseConstructorParameter(param);\n const paramEndTime = performance.now();\n\n if (dependency) {\n dependencies.push(dependency);\n\n // Collect verbose statistics\n if (this._options.verbose && this._options.includeDecorators) {\n this.collectVerboseStats(param, dependency, verboseStats);\n }\n }\n\n verboseStats.totalProcessingTime += paramEndTime - paramStartTime;\n }\n }\n\n // Extract inject() function dependencies (modern approach)\n const injectDependencies = this.extractInjectFunctionDependencies(classDeclaration);\n dependencies.push(...injectDependencies);\n\n const endTime = performance.now();\n verboseStats.totalProcessingTime = endTime - startTime;\n\n // Output verbose analysis if enabled\n if (this._options.verbose) {\n this.outputVerboseAnalysis(dependencies, verboseStats, classDeclaration);\n }\n\n return dependencies;\n }\n\n /**\n * Check if type reference is circular (Task 3.3)\n * @param typeText Type text to check\n * @param typeNode TypeNode for context\n * @returns True if circular reference detected\n */\n private isCircularTypeReference(typeText: string, typeNode: TypeNode): boolean {\n // Basic circular reference detection\n const currentClass = typeNode.getFirstAncestorByKind(SyntaxKind.ClassDeclaration);\n if (currentClass) {\n const className = currentClass.getName();\n if (className === typeText) {\n return true;\n }\n }\n\n // Track type resolution chain\n if (this._circularTypeRefs.has(typeText)) {\n return true;\n }\n\n this._circularTypeRefs.add(typeText);\n return false;\n }\n\n /**\n * Check if type is generic (Task 3.3)\n * @param typeText Type text to check\n * @returns True if generic type\n */\n private isGenericType(typeText: string): boolean {\n return typeText.includes('<') && typeText.includes('>');\n }\n\n /**\n * Handle generic types (Task 3.3)\n * @param typeText Generic type text\n * @param filePath File path for context\n * @param lineNumber Line number\n * @param columnNumber Column number\n * @returns Token string or null\n */\n private handleGenericType(\n typeText: string,\n _filePath: string,\n _lineNumber: number,\n _columnNumber: number\n ): string | null {\n if (this._options.verbose) {\n console.log(`Processing generic type: ${typeText}`);\n }\n\n // For now, return the full generic type\n // Future enhancement: could extract base type\n return typeText;\n }\n\n /**\n * Check if type is union type (Task 3.3)\n * @param typeText Type text to check\n * @returns True if union type\n */\n private isUnionType(typeText: string): boolean {\n return typeText.includes(' | ') && !typeText.includes('<');\n }\n\n /**\n * Handle union types (Task 3.3)\n * @param typeText Union type text\n * @param filePath File path for context\n * @param lineNumber Line number\n * @param columnNumber Column number\n * @returns Null (union types are skipped)\n */\n private handleUnionType(\n typeText: string,\n filePath: string,\n lineNumber: number,\n columnNumber: number\n ): string | null {\n this.addStructuredWarning('skippedTypes', {\n type: 'complex_union_type',\n message: `Skipping complex union type: ${typeText}`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Consider using a single service type or interface',\n severity: 'info',\n });\n return null;\n }\n\n /**\n * Check if type can be resolved through imports (Task 3.3)\n * Handles module-scoped types (e.g., MyModule.ScopedService)\n * @param typeNode TypeNode to check\n * @returns True if type can be resolved\n */\n private canResolveType(typeNode: TypeNode): boolean {\n try {\n const sourceFile = typeNode.getSourceFile();\n const typeText = typeNode.getText();\n\n // First, check if the type itself is resolvable via TypeScript's type system\n const type = typeNode.getType();\n const typeTextFromSystem = type.getText();\n const symbol = type.getSymbol();\n\n // Check for error types first (unresolved imports/types show as 'error')\n if (typeTextFromSystem === 'error' || typeTextFromSystem === 'any' || type.isUnknown()) {\n return false;\n }\n\n // If no symbol and not a primitive, it's unresolved\n if (!symbol) {\n return false;\n }\n\n // Check if it's a known global type\n const globalTypes = ['Array', 'Promise', 'Observable', 'Date', 'Error'];\n if (globalTypes.includes(typeText)) {\n return true;\n }\n\n // Handle module-scoped types (e.g., MyModule.ScopedService)\n let simpleTypeText = typeText;\n if (typeText.includes('.')) {\n const parts = typeText.split('.');\n simpleTypeText = parts[0]; // Check if the namespace exists\n }\n\n // Check if type is imported\n const imports = sourceFile.getImportDeclarations();\n for (const importDecl of imports) {\n const namedImports = importDecl.getNamedImports();\n for (const namedImport of namedImports) {\n // Check both full type and simple type for module-scoped\n if (namedImport.getName() === typeText || namedImport.getName() === simpleTypeText) {\n return true;\n }\n }\n }\n\n // Check if type is declared in current file\n const typeAliases = sourceFile.getTypeAliases();\n const interfaces = sourceFile.getInterfaces();\n const classes = sourceFile.getClasses();\n\n // Check for both full type name and simple type\n const declared = [...typeAliases, ...interfaces, ...classes].some(\n (decl) => decl.getName() === typeText || decl.getName() === simpleTypeText\n );\n\n if (declared) {\n return true;\n }\n\n // Check for namespace declarations (for module-scoped types)\n if (typeText.includes('.')) {\n const namespaces = sourceFile.getModules();\n for (const namespace of namespaces) {\n if (namespace.getName() === simpleTypeText) {\n // Namespace exists, assume the nested type exists\n return true;\n }\n }\n }\n\n // If we reach here, the type wasn't found in imports, declarations, or namespaces\n // Even if we have a symbol, if we can't locate the declaration, treat it as unresolved\n // This prevents false positives for TypeScript error types that have symbols but are actually unresolved\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Enhanced type token extraction with advanced analysis (Task 3.3)\n * @param typeNode TypeNode to extract token from\n * @param filePath File path for context\n * @param lineNumber Line number\n * @param columnNumber Column number\n * @returns Token string or null\n */\n private extractTypeTokenEnhanced(\n typeNode: TypeNode,\n filePath: string,\n lineNumber: number,\n columnNumber: number\n ): string | null {\n const typeText = typeNode.getText();\n\n if (this._options.verbose) {\n console.log(\n `Type resolution steps: Processing '${typeText}' at ${filePath}:${lineNumber}:${columnNumber}`\n );\n }\n\n // Check for circular references\n if (this.isCircularTypeReference(typeText, typeNode)) {\n this.addStructuredWarning('circularReferences', {\n type: 'circular_type_reference',\n message: `Circular type reference detected: ${typeText}`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Consider using interfaces or breaking the circular dependency',\n severity: 'warning',\n });\n return null;\n }\n\n // Handle generic types\n if (this.isGenericType(typeText)) {\n return this.handleGenericType(typeText, filePath, lineNumber, columnNumber);\n }\n\n // Handle union types\n if (this.isUnionType(typeText)) {\n return this.handleUnionType(typeText, filePath, lineNumber, columnNumber);\n }\n\n // Standard validation\n if (this.shouldSkipType(typeText)) {\n this.addStructuredWarning('skippedTypes', {\n type: 'any_unknown_type',\n message: `Skipping parameter with any/unknown type: ${typeText}`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Consider adding explicit type annotation',\n severity: 'warning',\n });\n return null;\n }\n\n if (this.isPrimitiveType(typeText)) {\n this.addStructuredWarning('skippedTypes', {\n type: 'primitive_type',\n message: `Skipping primitive type: ${typeText}`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Use dependency injection for services, not primitive types',\n severity: 'info',\n });\n return null;\n }\n\n // Validate type resolution\n if (!this.canResolveType(typeNode)) {\n this.addStructuredWarning('unresolvedImports', {\n type: 'unresolved_type',\n message: `Unresolved type '${typeText}' - check imports`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: `Ensure ${typeText} is properly imported`,\n severity: 'warning',\n });\n return null;\n }\n\n return typeText;\n }\n\n /**\n * Resolve inferred types with enhanced validation (Task 3.3)\n * @param type Type object from ts-morph\n * @param typeText Type text representation\n * @param param Parameter declaration for context\n * @param filePath File path for warnings\n * @param lineNumber Line number for warnings\n * @param columnNumber Column number for warnings\n * @returns Resolved token or null\n */\n private resolveInferredTypeEnhanced(\n type: Type,\n typeText: string,\n param: ParameterDeclaration,\n filePath: string,\n lineNumber: number,\n columnNumber: number\n ): string | null {\n if (this._options.verbose) {\n console.log(`Attempting to resolve inferred type: ${typeText}`);\n }\n\n // Try symbol-based resolution\n const symbol = type.getSymbol?.();\n if (symbol) {\n const symbolName = symbol.getName();\n if (symbolName && symbolName !== '__type') {\n return symbolName;\n }\n }\n\n // Try type alias resolution\n const aliasSymbol = type.getAliasSymbol?.();\n if (aliasSymbol) {\n // Check if the alias symbol actually has declarations (unresolved types don't)\n const declarations = aliasSymbol.getDeclarations();\n if (declarations && declarations.length > 0) {\n const aliasName = aliasSymbol.getName();\n return aliasName;\n }\n }\n\n // Standard validation with structured warnings\n // Deduplication is now handled by addStructuredWarning()\n if (this.shouldSkipType(typeText)) {\n this.addStructuredWarning('skippedTypes', {\n type: 'inferred_any_unknown',\n message: `Skipping parameter '${param.getName()}' with inferred any/unknown type`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Add explicit type annotation to improve type safety',\n severity: 'warning',\n });\n return null;\n }\n\n if (this.isPrimitiveType(typeText)) {\n this.addStructuredWarning('skippedTypes', {\n type: 'inferred_primitive',\n message: `Skipping inferred primitive type parameter '${param.getName()}': ${typeText}`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Use dependency injection for services, not primitive types',\n severity: 'info',\n });\n return null;\n }\n\n // Check if type is resolvable (has symbol or is a known global)\n // If no symbol and no valid alias symbol (with declarations), the type is unresolved (missing import)\n const aliasSymbolCheck = type.getAliasSymbol?.();\n const hasValidAliasSymbol = aliasSymbolCheck && aliasSymbolCheck.getDeclarations().length > 0;\n\n if (!symbol && !hasValidAliasSymbol) {\n this.addStructuredWarning('unresolvedImports', {\n type: 'unresolved_type',\n message: `Unresolved type '${typeText}' - check imports`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: `Ensure ${typeText} is properly imported`,\n severity: 'warning',\n });\n return null;\n }\n\n return typeText;\n }\n\n /**\n * Parse a single constructor parameter to extract dependency token\n * Implements FR-03 token resolution priority: @Inject > type annotation > inferred type\n * Implements FR-04 parameter decorator handling\n * @param param ts-morph ParameterDeclaration\n * @returns ParsedDependency if valid dependency, null if should be skipped\n */\n private parseConstructorParameter(param: ParameterDeclaration): ParsedDependency | null {\n const parameterName = param.getName();\n const filePath = param.getSourceFile().getFilePath();\n const lineNumber = param.getStartLineNumber();\n const columnNumber = param.getStart() - param.getStartLinePos();\n\n // Performance tracking\n const startTime = performance.now();\n\n // Cache key for performance monitoring (needs to be in outer scope for finally block)\n let cacheKey: string | null = null;\n\n try {\n // Extract parameter decorators (FR-04)\n const flags = this.extractParameterDecorators(param);\n\n // Check for @Inject decorator first (highest priority)\n const injectDecorator = param.getDecorator('Inject');\n if (injectDecorator) {\n const token = this.extractInjectToken(injectDecorator);\n if (token) {\n return {\n token,\n flags,\n parameterName,\n };\n }\n }\n\n // Check for inject() function pattern (second priority)\n const initializer = param.getInitializer();\n if (initializer) {\n const injectResult = this.analyzeInjectCall(initializer);\n if (injectResult) {\n // If legacy decorators are present, they completely override inject() options\n // Otherwise, use inject() options\n const finalFlags = Object.keys(flags).length > 0 ? flags : injectResult.flags;\n return {\n token: injectResult.token,\n flags: finalFlags,\n parameterName,\n };\n }\n }\n\n // Fall back to type annotation with enhanced validation (medium priority)\n const typeNode_check = param.getTypeNode();\n if (typeNode_check) {\n const token = this.extractTypeTokenEnhanced(\n typeNode_check,\n filePath,\n lineNumber,\n columnNumber\n );\n if (token) {\n return {\n token,\n flags,\n parameterName,\n };\n }\n }\n\n // Handle inferred types with caching (lowest priority)\n const type = param.getType();\n const typeText = type.getText(param);\n cacheKey = `${filePath}:${parameterName}:${typeText}`;\n\n // Check cache first\n if (this._typeResolutionCache.has(cacheKey)) {\n const cachedResult = this._typeResolutionCache.get(cacheKey);\n\n if (this._options.verbose) {\n console.log(`Cache hit for parameter '${parameterName}': ${typeText}`);\n }\n\n return cachedResult ? { token: cachedResult, flags, parameterName } : null;\n }\n\n if (this._options.verbose) {\n console.log(`Cache miss for parameter '${parameterName}': ${typeText}`);\n }\n\n // Resolve and cache\n const resolvedToken = this.resolveInferredTypeEnhanced(\n type,\n typeText,\n param,\n filePath,\n lineNumber,\n columnNumber\n );\n\n this._typeResolutionCache.set(cacheKey, resolvedToken);\n\n if (resolvedToken) {\n return {\n token: resolvedToken,\n flags,\n parameterName,\n };\n }\n\n return null;\n } finally {\n // Performance monitoring (only for successfully resolved types)\n const duration = performance.now() - startTime;\n if (cacheKey && duration > 10) {\n // 10ms threshold\n // Only emit performance warning for successfully cached types (not null/skipped types)\n const cachedToken = this._typeResolutionCache.get(cacheKey);\n if (cachedToken) {\n this.addStructuredWarning('performance', {\n type: 'slow_type_resolution',\n message: `Slow type resolution for parameter '${parameterName}' (${duration.toFixed(2)}ms)`,\n file: filePath,\n line: lineNumber,\n column: columnNumber,\n suggestion: 'Consider adding explicit type annotation',\n severity: 'info',\n });\n }\n }\n }\n }\n\n /**\n * Extract token from @Inject decorator\n * @param decorator @Inject decorator\n * @returns Token string or null\n */\n private extractInjectToken(decorator: Decorator): string | null {\n const callExpr = decorator.getCallExpression();\n if (!callExpr) return null;\n\n const args = callExpr.getArguments();\n if (args.length === 0) return null;\n\n const firstArg = args[0];\n return firstArg.getText().replace(/['\"]/g, ''); // Remove quotes if string literal\n }\n\n /**\n * Check if type should be skipped (any/unknown types)\n * Implements FR-09: Skip dependencies whose type resolves to any/unknown\n * @param typeText Type text to check\n * @returns True if should be skipped\n */\n private shouldSkipType(typeText: string): boolean {\n const skipTypes = ['any', 'unknown', 'object', 'Object'];\n return skipTypes.includes(typeText);\n }\n\n /**\n * Check if type is primitive and should be skipped\n * @param typeText Type text to check\n * @returns True if primitive type\n */\n private isPrimitiveType(typeText: string): boolean {\n const primitives = ['string', 'number', 'boolean', 'symbol', 'bigint', 'undefined', 'null'];\n return primitives.includes(typeText);\n }\n\n /**\n * Extract parameter decorators from constructor parameter\n * Implements FR-04: Parameter decorator handling (@Optional, @Self, @SkipSelf, @Host)\n * Optimized for performance with early returns and minimal object allocation\n * @param param ts-morph ParameterDeclaration\n * @returns EdgeFlags object with detected decorators\n */\n private extractParameterDecorators(param: ParameterDeclaration): EdgeFlags {\n // Delegate to the new analyzeParameterDecorators method for consistency\n return this.analyzeParameterDecorators(param, this._options.includeDecorators);\n }\n\n /**\n * Extract dependencies from inject() function calls in class properties\n * Implements TDD Cycle 2.1: Modern Angular inject() pattern detection\n * @param classDeclaration ts-morph ClassDeclaration\n * @returns Array of parsed dependencies from inject() calls\n */\n private extractInjectFunctionDependencies(\n classDeclaration: ClassDeclaration\n ): ParsedDependency[] {\n const dependencies: ParsedDependency[] = [];\n\n try {\n // Get all property declarations in the class\n const properties = classDeclaration.getProperties();\n\n for (const property of properties) {\n const dependency = this.parseInjectProperty(property);\n if (dependency) {\n dependencies.push(dependency);\n }\n }\n } catch (error) {\n // Graceful error handling - don't break parsing for inject() issues\n if (this._options.verbose) {\n const className = classDeclaration.getName() || 'unknown';\n const filePath = classDeclaration.getSourceFile().getFilePath();\n console.warn(\n `Warning: Failed to extract inject() dependencies for class '${className}' in ${filePath}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n return dependencies;\n }\n\n /**\n * Parse a property declaration for inject() function calls\n * @param property ts-morph PropertyDeclaration\n * @returns ParsedDependency if inject() call found, null otherwise\n */\n private parseInjectProperty(property: PropertyDeclaration): ParsedDependency | null {\n try {\n const initializer = property.getInitializer();\n if (!initializer) {\n return null;\n }\n\n // Check if initializer is a call expression\n if (initializer.getKind() !== SyntaxKind.CallExpression) {\n return null;\n }\n\n const callExpression = initializer as CallExpression;\n const expression = callExpression.getExpression();\n\n // Check if the call is to the inject() function\n if (expression.getKind() !== SyntaxKind.Identifier) {\n return null;\n }\n\n const identifier = expression.getText();\n if (identifier !== 'inject') {\n return null;\n }\n\n // Additional validation: ensure inject is imported from @angular/core\n // This helps avoid false positives from other inject() functions\n if (!this.isAngularInjectImported(property.getSourceFile())) {\n return null;\n }\n\n // Extract token and options from inject() call\n const args = callExpression.getArguments();\n if (args.length === 0) {\n return null;\n }\n\n // First argument is the token\n const tokenArg = args[0];\n const token = tokenArg.getText().replace(/['\"]/g, ''); // Remove quotes if string literal\n\n // Skip if token should be filtered out\n if (this.shouldSkipType(token) || this.isPrimitiveType(token)) {\n return null;\n }\n\n // Second argument is options object (optional)\n let flags: EdgeFlags = {};\n if (args.length > 1 && this._options.includeDecorators) {\n flags = this.parseInjectOptions(args[1]);\n }\n\n const propertyName = property.getName() || 'unknown';\n\n return {\n token,\n flags,\n parameterName: propertyName,\n };\n } catch (error) {\n // Graceful error handling\n if (this._options.verbose) {\n const propertyName = property.getName() || 'unknown';\n const filePath = property.getSourceFile().getFilePath();\n console.warn(\n `Warning: Failed to parse inject() property '${propertyName}' in ${filePath}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n return null;\n }\n }\n\n /**\n * Parse options object from inject() function call\n * @param optionsArg Options argument from inject() call\n * @returns EdgeFlags object with parsed options\n */\n private parseInjectOptions(optionsArg: Node): EdgeFlags {\n const flags: EdgeFlags = {};\n\n try {\n if (optionsArg.getKind() !== SyntaxKind.ObjectLiteralExpression) {\n return flags;\n }\n\n const objectLiteral = optionsArg as ObjectLiteralExpression;\n const properties = objectLiteral.getProperties();\n\n // Cache for performance - avoid repeated string comparisons\n const supportedOptions = new Set(['optional', 'self', 'skipSelf', 'host']);\n\n for (const prop of properties) {\n if (prop.getKind() !== SyntaxKind.PropertyAssignment) {\n continue;\n }\n\n const propertyAssignment = prop as PropertyAssignment;\n const name = propertyAssignment.getName();\n\n // Only process known inject() options for performance\n if (!supportedOptions.has(name)) {\n // Warn about unknown options but continue processing\n if (this._options.verbose) {\n console.warn(`Unknown inject() option: '${name}' - ignoring`);\n }\n continue;\n }\n\n // Check if the property value is true (literal boolean)\n const initializer = propertyAssignment.getInitializer();\n if (initializer && initializer.getText() === 'true') {\n // Set flags based on known property names\n switch (name) {\n case 'optional':\n flags.optional = true;\n break;\n case 'self':\n flags.self = true;\n break;\n case 'skipSelf':\n flags.skipSelf = true;\n break;\n case 'host':\n flags.host = true;\n break;\n }\n }\n }\n } catch (error) {\n // Graceful error handling - return empty flags on error\n if (this._options.verbose) {\n console.warn(\n `Warning: Failed to parse inject() options: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n }\n\n return flags;\n }\n\n /**\n * Analyze parameter decorators for TDD Cycle 1.1\n * Legacy parameter decorator detection method for @Optional, @Self, @SkipSelf, @Host\n * @param parameter ParameterDeclaration to analyze\n * @param includeDecorators Whether to include decorators in analysis\n * @returns EdgeFlags object with detected decorators\n */\n private analyzeParameterDecorators(\n parameter: ParameterDeclaration,\n includeDecorators: boolean,\n verboseStats?: VerboseStats\n ): EdgeFlags {\n // Early return for disabled decorator detection\n if (!includeDecorators) {\n return {};\n }\n\n const decorators = parameter.getDecorators();\n\n // Early return if no decorators present\n if (decorators.length === 0) {\n return {};\n }\n\n const flags: EdgeFlags = {};\n\n try {\n // Cache for performance - avoid repeated name resolution\n const supportedDecorators = new Set(['Optional', 'Self', 'SkipSelf', 'Host']);\n\n for (const decorator of decorators) {\n const decoratorName = this.getDecoratorName(decorator);\n\n // Skip @Inject as it's handled separately for token extraction\n if (decoratorName === 'Inject') {\n continue;\n }\n\n // Only process known Angular DI decorators\n if (supportedDecorators.has(decoratorName)) {\n switch (decoratorName) {\n case 'Optional':\n flags.optional = true;\n break;\n case 'Self':\n flags.self = true;\n break;\n case 'SkipSelf':\n flags.skipSelf = true;\n break;\n case 'Host':\n flags.host = true;\n break;\n }\n } else if (decoratorName) {\n // Track unknown decorators for verbose stats\n if (verboseStats) {\n verboseStats.skippedDecorators.push({\n name: decoratorName,\n reason: 'Unknown or unsupported decorator',\n });\n }\n\n // Warn about unknown decorators to help with debugging\n console.warn(\n `Unknown or unsupported decorator: @${decoratorName}() - This decorator is not recognized as an Angular DI decorator and will be ignored.`\n );\n }\n }\n } catch (error) {\n // Graceful error handling - don't break parsing for decorator issues\n if (this._options.verbose) {\n const paramName = parameter.getName();\n const filePath = parameter.getSourceFile().getFilePath();\n console.warn(\n `Warning: Failed to extract decorators for parameter '${paramName}' in ${filePath}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n // Return empty flags object on error\n return {};\n }\n\n return flags;\n }\n\n /**\n * Check if inject() function is imported from @angular/core\n * Prevents false positives from custom inject() functions\n * @param sourceFile Source file to check imports\n * @returns True if Angular inject is imported\n */\n private isAngularInjectImported(sourceFile: SourceFile): boolean {\n try {\n const importDeclarations = sourceFile.getImportDeclarations();\n\n for (const importDecl of importDeclarations) {\n const moduleSpecifier = importDecl.getModuleSpecifierValue();\n if (moduleSpecifier === '@angular/core') {\n const namedImports = importDecl.getNamedImports();\n\n for (const namedImport of namedImports) {\n const importName = namedImport.getName();\n const alias = namedImport.getAliasNode();\n\n // Check for direct import or aliased import\n if (importName === 'inject' || (alias && alias.getText() === 'inject')) {\n return true;\n }\n }\n }\n }\n\n return false;\n } catch (error) {\n // Conservative approach: assume it's valid if we can't determine\n if (this._options.verbose) {\n console.warn(\n `Warning: Could not verify inject() import in ${sourceFile.getFilePath()}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n return true; // Assume valid to avoid false negatives\n }\n }\n\n /**\n * Analyze inject() function call expression to extract token and options\n * Implements TDD Cycle 2.1 - Modern Angular inject() pattern support\n * @param expression Expression to analyze (should be a CallExpression)\n * @returns ParameterAnalysisResult or null if not a valid inject() call\n */\n private analyzeInjectCall(expression: Node | undefined): ParameterAnalysisResult | null {\n // Early return for missing expression\n if (!expression) {\n return null;\n }\n\n // Must be a CallExpression\n if (expression.getKind() !== SyntaxKind.CallExpression) {\n return null;\n }\n\n const callExpression = expression as CallExpression;\n\n try {\n // Verify it's actually an inject() call\n const callIdentifier = callExpression.getExpression();\n if (callIdentifier.getKind() !== SyntaxKind.Identifier) {\n return null;\n }\n\n const functionName = callIdentifier.getText();\n if (functionName !== 'inject') {\n return null;\n }\n\n // Verify inject is imported from @angular/core\n const sourceFile = expression.getSourceFile();\n if (!this.isAngularInjectImported(sourceFile)) {\n return null;\n }\n\n const args = callExpression.getArguments();\n if (args.length === 0) {\n // inject() called without arguments\n if (this._options.verbose) {\n console.warn('inject() called without token parameter - skipping');\n }\n return null;\n }\n\n // Extract token from first argument\n const tokenArg = args[0];\n let token: string;\n\n // Handle various token argument types with validation\n if (tokenArg.getKind() === SyntaxKind.StringLiteral) {\n // String token: inject('MY_TOKEN', ...)\n token = tokenArg.getText().slice(1, -1); // Remove quotes\n if (!token) {\n // Empty string token\n if (this._options.verbose) {\n console.warn('inject() called with empty string token - skipping');\n }\n return null;\n }\n } else if (tokenArg.getKind() === SyntaxKind.Identifier) {\n // Class token: inject(MyService, ...)\n token = tokenArg.getText();\n if (token === 'undefined' || token === 'null') {\n // Explicit undefined or null token\n if (this._options.verbose) {\n console.warn(`inject() called with ${token} token - skipping`);\n }\n return null;\n }\n } else if (tokenArg.getKind() === SyntaxKind.NullKeyword) {\n // Direct null literal\n if (this._options.verbose) {\n console.warn('inject() called with null token - skipping');\n }\n return null;\n } else {\n // Complex expression - use text representation\n token = tokenArg.getText();\n if (!token) {\n if (this._options.verbose) {\n console.warn('inject() called with invalid token expression - skipping');\n }\n return null;\n }\n }\n\n // Parse options from second argument if present\n let flags: EdgeFlags = {};\n if (args.length > 1 && this._options.includeDecorators) {\n const optionsArg = args[1];\n if (optionsArg.getKind() === SyntaxKind.ObjectLiteralExpression) {\n flags = this.parseInjectOptions(optionsArg);\n } else if (\n optionsArg.getKind() !== SyntaxKind.NullKeyword &&\n optionsArg.getKind() !== SyntaxKind.UndefinedKeyword\n ) {\n // Warn about invalid options (not null/undefined)\n if (this._options.verbose) {\n console.warn(\n `inject() called with invalid options type: ${optionsArg.getKindName()} - expected object literal`\n );\n }\n }\n }\n\n return {\n token,\n flags,\n source: 'inject',\n };\n } catch (error) {\n // Graceful error handling\n if (this._options.verbose) {\n const filePath = expression.getSourceFile().getFilePath();\n console.warn(\n `Warning: Failed to analyze inject() call in ${filePath}: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n return null;\n }\n }\n\n /**\n * Collect verbose statistics for decorator analysis\n * @param param Parameter declaration being analyzed\n * @param dependency Parsed dependency result\n * @param verboseStats Statistics object to update\n */\n private collectVerboseStats(\n param: ParameterDeclaration,\n dependency: ParsedDependency,\n verboseStats: VerboseStats\n ): void {\n const paramName = param.getName();\n\n // Log individual parameter analysis\n console.log(`Parameter: ${paramName}`);\n console.log(` Token: ${dependency.token}`);\n\n // Check if has flags\n const flagKeys = Object.keys(dependency.flags || {});\n if (flagKeys.length > 0) {\n verboseStats.parametersWithDecorators++;\n\n // Count individual decorator flags\n if (dependency.flags?.optional) verboseStats.decoratorCounts.optional++;\n if (dependency.flags?.self) verboseStats.decoratorCounts.self++;\n if (dependency.flags?.skipSelf) verboseStats.decoratorCounts.skipSelf++;\n if (dependency.flags?.host) verboseStats.decoratorCounts.host++;\n\n // Check for legacy decorators vs inject() patterns\n const decorators = param.getDecorators();\n let hasLegacyDecorators = false;\n let hasInjectPattern = false;\n\n // Re-analyze decorators to capture unknown ones for verbose stats\n this.analyzeParameterDecorators(param, true, verboseStats);\n\n for (const decorator of decorators) {\n const decoratorName = this.getDecoratorName(decorator);\n if (['Optional', 'Self', 'SkipSelf', 'Host'].includes(decoratorName)) {\n hasLegacyDecorators = true;\n console.log(` Legacy decorator: @${decoratorName}`);\n }\n }\n\n // Check for inject() pattern\n const initializer = param.getInitializer();\n if (initializer && initializer.getKind() === SyntaxKind.CallExpression) {\n const callExpr = initializer as CallExpression;\n const expression = callExpr.getExpression();\n if (expression.getKind() === SyntaxKind.Identifier) {\n const funcName = expression.getText();\n if (funcName === 'inject') {\n hasInjectPattern = true;\n verboseStats.injectPatternsUsed++;\n const flagsStr = JSON.stringify(dependency.flags);\n console.log(` inject() options: ${flagsStr}`);\n }\n }\n }\n\n if (hasLegacyDecorators) {\n verboseStats.legacyDecoratorsUsed++;\n\n // Check for precedence scenarios\n if (hasInjectPattern) {\n console.log(' Decorator Precedence Analysis');\n console.log(' Legacy decorators take precedence over inject() options');\n const appliedFlags = Object.keys(dependency.flags || {})\n .filter((key) => dependency.flags?.[key as keyof EdgeFlags] === true)\n .map((key) => `@${key.charAt(0).toUpperCase() + key.slice(1)}`)\n .join(', ');\n console.log(` Applied: ${appliedFlags}`);\n\n // Try to detect what inject() options were overridden\n const injectResult = this.analyzeInjectCall(initializer);\n if (injectResult && Object.keys(injectResult.flags).length > 0) {\n const overriddenFlags = JSON.stringify(injectResult.flags);\n console.log(` Overridden inject() options: ${overriddenFlags}`);\n }\n\n const finalFlags = JSON.stringify(dependency.flags);\n console.log(` Final flags: ${finalFlags}`);\n }\n }\n } else {\n verboseStats.parametersWithoutDecorators++;\n console.log(' No decorators detected');\n }\n }\n\n /**\n * Output comprehensive verbose analysis summary\n * @param dependencies All parsed dependencies\n * @param verboseStats Collected statistics\n * @param classDeclaration Class being analyzed\n */\n private outputVerboseAnalysis(\n dependencies: ParsedDependency[],\n verboseStats: VerboseStats,\n classDeclaration: ClassDeclaration\n ): void {\n if (!this._options.verbose) return;\n\n if (this._options.includeDecorators) {\n // Decorator Statistics\n console.log('=== Decorator Statistics ===');\n console.log(\n `Total decorators detected: ${verboseStats.decoratorCounts.optional + verboseStats.decoratorCounts.self + verboseStats.decoratorCounts.skipSelf + verboseStats.decoratorCounts.host}`\n );\n\n if (verboseStats.decoratorCounts.optional > 0) {\n console.log(`@Optional: ${verboseStats.decoratorCounts.optional}`);\n }\n if (verboseStats.decoratorCounts.self > 0) {\n console.log(`@Self: ${verboseStats.decoratorCounts.self}`);\n }\n if (verboseStats.decoratorCounts.skipSelf > 0) {\n console.log(`@SkipSelf: ${verboseStats.decoratorCounts.skipSelf}`);\n }\n if (verboseStats.decoratorCounts.host > 0) {\n console.log(`@Host: ${verboseStats.decoratorCounts.host}`);\n }\n\n console.log(`Parameters with decorators: ${verboseStats.parametersWithDecorators}`);\n console.log(`Parameters without decorators: ${verboseStats.parametersWithoutDecorators}`);\n\n // inject() Pattern Analysis\n if (verboseStats.injectPatternsUsed > 0) {\n console.log('inject() Pattern Analysis');\n\n // Analyze inject() patterns in dependencies\n for (const dep of dependencies) {\n if (dep.parameterName) {\n const constructors = classDeclaration.getConstructors();\n if (constructors.length > 0) {\n const param = constructors[0]\n .getParameters()\n .find((p) => p.getName() === dep.parameterName);\n if (param) {\n const initializer = param.getInitializer();\n if (initializer) {\n const injectResult = this.analyzeInjectCall(initializer);\n if (injectResult) {\n if (injectResult.token.startsWith('\"') && injectResult.token.endsWith('\"')) {\n console.log(`String token: ${injectResult.token}`);\n } else {\n console.log(`Service token: ${injectResult.token}`);\n }\n\n if (Object.keys(injectResult.flags).length > 0) {\n const flagsStr = JSON.stringify(injectResult.flags);\n console.log(`inject() options detected: ${flagsStr}`);\n } else {\n console.log('inject() with no options');\n }\n }\n }\n }\n }\n }\n }\n }\n\n // Skipped Decorators (if any were captured)\n if (verboseStats.skippedDecorators.length > 0) {\n console.log('Skipped Decorators');\n for (const skipped of verboseStats.skippedDecorators) {\n console.log(`${skipped.name}`);\n console.log(`Reason: ${skipped.reason}`);\n }\n console.log(`Total skipped: ${verboseStats.skippedDecorators.length}`);\n }\n\n // Performance Metrics\n console.log('Performance Metrics');\n console.log(`Decorator processing time: ${verboseStats.totalProcessingTime.toFixed(2)}ms`);\n console.log(`Total parameters analyzed: ${verboseStats.totalParameters}`);\n if (verboseStats.totalParameters > 0) {\n const avgTime = verboseStats.totalProcessingTime / verboseStats.totalParameters;\n console.log(`Average time per parameter: ${avgTime.toFixed(3)}ms`);\n }\n\n // Analysis Summary\n console.log('=== Analysis Summary ===');\n console.log(`Total dependencies: ${dependencies.length}`);\n console.log(`With decorator flags: ${verboseStats.parametersWithDecorators}`);\n console.log(`Without decorator flags: ${verboseStats.parametersWithoutDecorators}`);\n console.log(`Legacy decorators used: ${verboseStats.legacyDecoratorsUsed}`);\n console.log(`inject() patterns used: ${verboseStats.injectPatternsUsed}`);\n\n if (verboseStats.skippedDecorators.length > 0) {\n console.log(`Unknown decorators skipped: ${verboseStats.skippedDecorators.length}`);\n }\n\n // Flags distribution\n if (verboseStats.parametersWithDecorators > 0) {\n console.log('Flags distribution:');\n if (verboseStats.decoratorCounts.optional > 0) {\n console.log(`optional: ${verboseStats.decoratorCounts.optional}`);\n }\n if (verboseStats.decoratorCounts.self > 0) {\n console.log(`self: ${verboseStats.decoratorCounts.self}`);\n }\n if (verboseStats.decoratorCounts.skipSelf > 0) {\n console.log(`skipSelf: ${verboseStats.decoratorCounts.skipSelf}`);\n }\n if (verboseStats.decoratorCounts.host > 0) {\n console.log(`host: ${verboseStats.decoratorCounts.host}`);\n }\n }\n }\n }\n}\n","import { LogCategory, type Logger } from '../core/logger';\nimport type { Graph } from '../types';\n\n/**\n * JSON formatter for dependency graph output\n * Produces pretty-printed JSON with 2-space indentation\n */\nexport class JsonFormatter {\n /**\n * Logger instance for verbose output (optional)\n * @private\n */\n private readonly _logger?: Logger;\n\n /**\n * Create a new JSON formatter\n * @param logger Optional Logger instance for verbose mode\n */\n constructor(logger?: Logger) {\n this._logger = logger;\n }\n\n /**\n * Format a dependency graph as JSON\n * @param graph The dependency graph to format\n * @returns Pretty-printed JSON string\n */\n format(graph: Graph): string {\n this._logger?.time('json-format');\n this._logger?.info(LogCategory.PERFORMANCE, 'Generating JSON output', {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n });\n\n const result = JSON.stringify(graph, null, 2);\n\n const elapsed = this._logger?.timeEnd('json-format') ?? 0;\n this._logger?.info(LogCategory.PERFORMANCE, 'JSON output complete', {\n outputSize: result.length,\n elapsed,\n });\n\n return result;\n }\n}\n","import { LogCategory, type Logger } from '../core/logger';\nimport type { Graph } from '../types';\n\n/**\n * Mermaid formatter for dependency graph output\n * Produces flowchart LR syntax compatible with Mermaid Live Editor\n */\nexport class MermaidFormatter {\n /**\n * Logger instance for verbose output (optional)\n * @private\n */\n private readonly _logger?: Logger;\n\n /**\n * Create a new Mermaid formatter\n * @param logger Optional Logger instance for verbose mode\n */\n constructor(logger?: Logger) {\n this._logger = logger;\n }\n\n /**\n * Format a dependency graph as Mermaid flowchart\n * @param graph The dependency graph to format\n * @returns Mermaid flowchart string\n */\n format(graph: Graph): string {\n this._logger?.time('mermaid-format');\n this._logger?.info(LogCategory.PERFORMANCE, 'Generating Mermaid output', {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n });\n if (graph.nodes.length === 0) {\n const result = 'flowchart LR\\n %% Empty graph - no nodes to display';\n const elapsed = this._logger?.timeEnd('mermaid-format') ?? 0;\n this._logger?.info(LogCategory.PERFORMANCE, 'Mermaid output complete', {\n outputSize: result.length,\n elapsed,\n });\n return result;\n }\n\n const lines = ['flowchart LR'];\n\n // Add edges with proper formatting\n for (const edge of graph.edges) {\n const fromNode = this.sanitizeNodeName(edge.from);\n const toNode = this.sanitizeNodeName(edge.to);\n\n if (edge.isCircular) {\n lines.push(` ${fromNode} -.->|circular| ${toNode}`);\n } else {\n lines.push(` ${fromNode} --> ${toNode}`);\n }\n }\n\n // Add circular dependency comments if any\n if (graph.circularDependencies.length > 0) {\n lines.push('');\n lines.push(' %% Circular Dependencies Detected:');\n for (const cycle of graph.circularDependencies) {\n lines.push(` %% ${cycle.join(' -> ')} -> ${cycle[0]}`);\n }\n }\n\n const result = lines.join('\\n');\n\n const elapsed = this._logger?.timeEnd('mermaid-format') ?? 0;\n this._logger?.info(LogCategory.PERFORMANCE, 'Mermaid output complete', {\n outputSize: result.length,\n elapsed,\n });\n\n return result;\n }\n\n /**\n * Sanitize node names for Mermaid compatibility\n * Replaces special characters that break Mermaid syntax\n * @param nodeName The original node name\n * @returns Sanitized node name\n */\n private sanitizeNodeName(nodeName: string): string {\n // Replace special characters that break Mermaid syntax\n return nodeName.replace(/[.-]/g, '_').replace(/[^a-zA-Z0-9_]/g, '');\n }\n}\n","#!/usr/bin/env node\n/**\n * ng-di-graph CLI entry point\n * Supports Node.js (via tsx) execution\n */\nimport { Command } from 'commander';\nimport { CliError, ErrorHandler } from '../core/error-handler';\nimport { buildGraph } from '../core/graph-builder';\nimport { filterGraph } from '../core/graph-filter';\nimport { createLogger, LogCategory } from '../core/logger';\nimport { OutputHandler } from '../core/output-handler';\nimport { AngularParser } from '../core/parser';\nimport { JsonFormatter } from '../formatters/json-formatter';\nimport { MermaidFormatter } from '../formatters/mermaid-formatter';\nimport type { CliOptions } from '../types';\n\nconst MIN_NODE_MAJOR_VERSION = 20;\n\nfunction enforceMinimumNodeVersion(): void {\n const nodeVersion = process.versions.node;\n if (!nodeVersion) {\n return;\n }\n\n const [majorSegment] = nodeVersion.split('.');\n const major = Number(majorSegment);\n if (Number.isNaN(major) || major >= MIN_NODE_MAJOR_VERSION) {\n return;\n }\n\n const runtimeLabel = process.versions.bun\n ? `Bun (Node compatibility ${nodeVersion})`\n : `Node.js ${nodeVersion}`;\n\n console.error(\n [\n `ng-di-graph requires Node.js >= ${MIN_NODE_MAJOR_VERSION}.0.0.`,\n `Detected runtime: ${runtimeLabel}.`,\n 'Upgrade to Node.js 20.x LTS (match `.node-version` / mise config) before running ng-di-graph.',\n ].join('\\n')\n );\n process.exit(1);\n}\n\nenforceMinimumNodeVersion();\n\nconst program = new Command();\n\nprogram.name('ng-di-graph').description('Angular DI dependency graph CLI tool').version('0.1.0');\n\nprogram\n .option('-p, --project <path>', 'tsconfig.json path', './tsconfig.json')\n .option('-f, --format <format>', 'output format: json | mermaid', 'json')\n .option('-e, --entry <symbol...>', 'starting nodes for sub-graph')\n .option('-d, --direction <dir>', 'filtering direction: upstream|downstream|both', 'downstream')\n .option('--include-decorators', 'include Optional/Self/SkipSelf/Host flags', false)\n .option('--out <file>', 'output file (stdout if omitted)')\n .option('-v, --verbose', 'show detailed parsing information', false);\n\nprogram.action(async (options) => {\n try {\n // Validate direction option\n const validDirections = ['upstream', 'downstream', 'both'];\n if (options.direction && !validDirections.includes(options.direction)) {\n throw ErrorHandler.createError(\n `Invalid direction: ${options.direction}. Must be 'upstream', 'downstream', or 'both'`,\n 'INVALID_ARGUMENTS'\n );\n }\n\n // Validate format option\n const validFormats = ['json', 'mermaid'];\n if (options.format && !validFormats.includes(options.format)) {\n throw ErrorHandler.createError(\n `Invalid format: ${options.format}. Must be 'json' or 'mermaid'`,\n 'INVALID_ARGUMENTS'\n );\n }\n\n const cliOptions: CliOptions = {\n project: options.project,\n format: options.format as 'json' | 'mermaid',\n entry: options.entry,\n direction: options.direction as 'upstream' | 'downstream' | 'both',\n includeDecorators: options.includeDecorators,\n out: options.out,\n verbose: options.verbose,\n };\n\n // Create Logger when verbose mode is enabled\n const logger = createLogger(cliOptions.verbose);\n\n if (logger) {\n logger.time('total-execution');\n logger.info(LogCategory.FILE_PROCESSING, 'CLI execution started', {\n runtime: process.versions.bun ? 'Bun' : 'Node.js',\n version: process.versions.bun || process.versions.node,\n options: cliOptions,\n });\n }\n\n // Keep backward compatibility with console.log for user-facing output\n if (cliOptions.verbose) {\n console.log('🔧 CLI Options:', JSON.stringify(cliOptions, null, 2));\n console.log(\n `🚀 Running with ${process.versions.bun ? 'Bun' : 'Node.js'} ${process.versions.bun || process.versions.node}`\n );\n }\n\n // Initialize parser with logger\n const parser = new AngularParser(cliOptions, logger);\n\n if (cliOptions.verbose) {\n console.log('📂 Loading TypeScript project...');\n }\n\n // Load project\n parser.loadProject();\n\n if (cliOptions.verbose) {\n console.log('✅ Project loaded successfully');\n }\n\n // Parse Angular classes\n if (cliOptions.verbose) {\n console.log('🔍 Parsing Angular classes...');\n }\n\n const parsedClasses = await parser.parseClasses();\n\n if (cliOptions.verbose) {\n console.log(`✅ Found ${parsedClasses.length} decorated classes`);\n }\n\n // Build dependency graph with logger\n if (cliOptions.verbose) {\n console.log('🔗 Building dependency graph...');\n }\n\n let graph = buildGraph(parsedClasses, logger);\n\n if (cliOptions.verbose) {\n console.log(`✅ Graph built: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);\n if (graph.circularDependencies.length > 0) {\n console.log(`⚠️ Detected ${graph.circularDependencies.length} circular dependencies`);\n }\n }\n\n // Apply entry point filtering if specified\n if (cliOptions.entry && cliOptions.entry.length > 0) {\n if (cliOptions.verbose) {\n console.log(`🔍 Filtering graph by entry points: ${cliOptions.entry.join(', ')}`);\n }\n\n graph = filterGraph(graph, cliOptions);\n\n if (cliOptions.verbose) {\n console.log(`✅ Filtered graph: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);\n }\n }\n\n // Format output with logger\n let formatter: JsonFormatter | MermaidFormatter;\n if (cliOptions.format === 'mermaid') {\n formatter = new MermaidFormatter(logger);\n } else {\n formatter = new JsonFormatter(logger);\n }\n\n const formattedOutput = formatter.format(graph);\n\n // Write output\n const outputHandler = new OutputHandler();\n await outputHandler.writeOutput(formattedOutput, cliOptions.out);\n\n if (cliOptions.verbose && cliOptions.out) {\n console.log(`✅ Output written to: ${cliOptions.out}`);\n }\n\n // Display performance summary\n if (logger) {\n const totalTime = logger.timeEnd('total-execution');\n const stats = logger.getStats();\n\n console.error('\\n📊 Performance Summary:');\n console.error(` Total time: ${totalTime.toFixed(2)}ms`);\n console.error(` Peak memory: ${(stats.memoryUsage.peakUsage / 1024 / 1024).toFixed(2)}MB`);\n console.error(` Total logs: ${stats.totalLogs}`);\n }\n } catch (error) {\n // Handle CliError instances with structured error handling\n if (error instanceof CliError) {\n ErrorHandler.handleError(error, options.verbose);\n } else if (error instanceof Error) {\n // Convert generic Error to CliError\n const cliError = ErrorHandler.createError(error.message, 'INTERNAL_ERROR', undefined, {\n originalError: error.name,\n });\n ErrorHandler.handleError(cliError, options.verbose);\n } else {\n // Handle unknown error types\n const cliError = ErrorHandler.createError(\n 'An unexpected error occurred',\n 'INTERNAL_ERROR',\n undefined,\n { error: String(error) }\n );\n ErrorHandler.handleError(cliError, options.verbose);\n }\n }\n});\n\n// Enhanced unhandled rejection handling\nprocess.on('unhandledRejection', (reason, promise) => {\n const error = ErrorHandler.createError(\n `Unhandled promise rejection: ${reason}`,\n 'INTERNAL_ERROR',\n undefined,\n { promise: String(promise) }\n );\n ErrorHandler.handleError(error, false);\n});\n\n// Enhanced uncaught exception handling\nprocess.on('uncaughtException', (error) => {\n const cliError = ErrorHandler.createError(\n `Uncaught exception: ${error.message}`,\n 'INTERNAL_ERROR',\n undefined,\n { stack: error.stack }\n );\n ErrorHandler.handleError(cliError, true);\n});\n\n// Parse command line arguments\nconst argv = process.argv.length <= 2 ? [...process.argv, '--help'] : process.argv;\nprogram.parse(argv);\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Comprehensive error handling infrastructure for ng-di-graph CLI
3
+ * Implements FR-10 requirements from PRD Section 13
4
+ */
5
+ /**
6
+ * Exit codes for ng-di-graph CLI
7
+ * Implements PRD Section 13 error handling requirements
8
+ */
9
+ export declare enum ExitCodes {
10
+ SUCCESS = 0,// Successful execution
11
+ GENERAL_ERROR = 1,// Generic error (uncaught exception)
12
+ INVALID_ARGUMENTS = 2,// Invalid CLI arguments
13
+ TSCONFIG_ERROR = 3,// tsconfig.json not found or invalid
14
+ PARSING_ERROR = 4,// File parsing failure
15
+ TYPE_RESOLUTION_ERROR = 5,// Type resolution failure
16
+ MEMORY_ERROR = 6,// Memory limit exceeded
17
+ FILE_NOT_FOUND = 7,// Required file not found
18
+ PERMISSION_ERROR = 8
19
+ }
20
+ /**
21
+ * Error codes for structured error handling
22
+ * Each code maps to a specific error scenario
23
+ */
24
+ export type ErrorCode = 'TSCONFIG_NOT_FOUND' | 'TSCONFIG_INVALID' | 'PROJECT_LOAD_FAILED' | 'COMPILATION_ERROR' | 'FILE_PARSE_ERROR' | 'TYPE_RESOLUTION_ERROR' | 'MEMORY_LIMIT_EXCEEDED' | 'DEPENDENCY_NOT_FOUND' | 'ANONYMOUS_CLASS_SKIPPED' | 'INTERNAL_ERROR' | 'INVALID_ARGUMENTS' | 'FILE_NOT_FOUND' | 'PERMISSION_DENIED' | 'OUTPUT_WRITE_ERROR';
25
+ /**
26
+ * Custom error class for ng-di-graph CLI
27
+ * Provides structured error information with context
28
+ */
29
+ export declare class CliError extends Error {
30
+ readonly code: ErrorCode;
31
+ readonly filePath?: string | undefined;
32
+ readonly context?: Record<string, unknown> | undefined;
33
+ readonly name = "CliError";
34
+ constructor(message: string, code: ErrorCode, filePath?: string | undefined, context?: Record<string, unknown> | undefined);
35
+ /**
36
+ * Check if error is fatal (should exit immediately)
37
+ */
38
+ isFatal(): boolean;
39
+ /**
40
+ * Check if error is recoverable (can continue processing)
41
+ */
42
+ isRecoverable(): boolean;
43
+ }
44
+ /**
45
+ * ErrorHandler - Static utility class for error handling
46
+ * Provides centralized error formatting and exit code management
47
+ */
48
+ export declare class ErrorHandler {
49
+ /**
50
+ * Map error code to exit code
51
+ * @param error CliError instance or null
52
+ * @returns Exit code for process.exit()
53
+ */
54
+ static classifyExitCode(error: CliError | null): ExitCodes;
55
+ /**
56
+ * Format error for user-friendly display
57
+ * @param error CliError to format
58
+ * @param verbose Include stack traces and detailed info
59
+ * @returns Formatted error message string
60
+ */
61
+ static formatError(error: CliError, verbose?: boolean): string;
62
+ /**
63
+ * Get actionable recovery guidance for error
64
+ * @param error CliError to provide guidance for
65
+ * @returns Multi-line guidance string
66
+ */
67
+ static getRecoveryGuidance(error: CliError): string;
68
+ /**
69
+ * Handle error and exit process (never returns)
70
+ * @param error CliError to handle
71
+ * @param verbose Include verbose error information
72
+ */
73
+ static handleError(error: CliError, verbose?: boolean): never;
74
+ /**
75
+ * Factory method to create CliError
76
+ * @param message Error message
77
+ * @param code Error code
78
+ * @param filePath Optional file path
79
+ * @param context Optional context object
80
+ * @returns CliError instance
81
+ */
82
+ static createError(message: string, code: ErrorCode, filePath?: string, context?: Record<string, unknown>): CliError;
83
+ /**
84
+ * Output warning without exiting
85
+ * @param message Warning message
86
+ * @param filePath Optional file path
87
+ */
88
+ static warn(message: string, filePath?: string): void;
89
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Graph builder module for ng-di-graph CLI tool
3
+ * Transforms parsed classes into graph data structure
4
+ */
5
+ import type { Graph, ParsedClass } from '../types';
6
+ import { type Logger } from './logger';
7
+ /**
8
+ * Builds a dependency graph from parsed Angular classes
9
+ * @param parsedClasses Array of parsed classes with their dependencies
10
+ * @param logger Optional Logger instance for verbose mode logging
11
+ * @returns Graph containing nodes and edges representing the dependency relationships
12
+ */
13
+ export declare function buildGraph(parsedClasses: ParsedClass[], logger?: Logger): Graph;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Graph filtering module for ng-di-graph CLI tool
3
+ * Filters graphs based on entry points and direction
4
+ */
5
+ import type { CliOptions, Graph } from '../types';
6
+ /**
7
+ * Filters a graph based on entry points and traversal direction
8
+ * @param graph The graph to filter
9
+ * @param options CLI options containing entry points and direction
10
+ * @returns Filtered graph containing only nodes reachable from entry points
11
+ */
12
+ export declare function filterGraph(graph: Graph, options: CliOptions): Graph;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Logger Infrastructure for ng-di-graph
3
+ * Provides structured logging with categories, performance timing, and memory tracking
4
+ * Implements FR-12: Verbose mode support
5
+ *
6
+ * Usage:
7
+ * ```typescript
8
+ * const logger = createLogger(verbose);
9
+ * logger?.info(LogCategory.FILE_PROCESSING, 'Processing file', { filePath: '/test.ts' });
10
+ * logger?.time('operation');
11
+ * // ... operation ...
12
+ * const elapsed = logger?.timeEnd('operation');
13
+ * const stats = logger?.getStats();
14
+ * ```
15
+ */
16
+ /**
17
+ * Log category enumeration for structured logging
18
+ * Each category represents a distinct phase of ng-di-graph processing
19
+ */
20
+ export declare enum LogCategory {
21
+ FILE_PROCESSING = "file-processing",
22
+ AST_ANALYSIS = "ast-analysis",
23
+ TYPE_RESOLUTION = "type-resolution",
24
+ GRAPH_CONSTRUCTION = "graph-construction",
25
+ FILTERING = "filtering",
26
+ ERROR_RECOVERY = "error-recovery",
27
+ PERFORMANCE = "performance"
28
+ }
29
+ /**
30
+ * Optional context information attached to log messages
31
+ * Provides additional debugging context for verbose mode
32
+ */
33
+ export interface LogContext {
34
+ filePath?: string;
35
+ lineNumber?: number;
36
+ className?: string;
37
+ methodName?: string;
38
+ nodeId?: string;
39
+ timing?: number;
40
+ memoryUsage?: number;
41
+ [key: string]: unknown;
42
+ }
43
+ /**
44
+ * Logger interface for structured logging
45
+ * Supports log levels, performance timing, and statistics tracking
46
+ */
47
+ export interface Logger {
48
+ debug(category: LogCategory, message: string, context?: LogContext): void;
49
+ info(category: LogCategory, message: string, context?: LogContext): void;
50
+ warn(category: LogCategory, message: string, context?: LogContext): void;
51
+ error(category: LogCategory, message: string, context?: LogContext): void;
52
+ time(label: string): void;
53
+ timeEnd(label: string): number;
54
+ getStats(): LoggingStats;
55
+ }
56
+ /**
57
+ * Aggregated logging statistics
58
+ * Provides insights into verbose mode execution
59
+ */
60
+ export interface LoggingStats {
61
+ totalLogs: number;
62
+ categoryCounts: Record<LogCategory, number>;
63
+ performanceMetrics: {
64
+ totalTime: number;
65
+ fileProcessingTime: number;
66
+ graphBuildingTime: number;
67
+ outputGenerationTime: number;
68
+ };
69
+ memoryUsage: {
70
+ peakUsage: number;
71
+ currentUsage: number;
72
+ };
73
+ }
74
+ /**
75
+ * Internal timer tracking entry
76
+ * Stores performance timer state
77
+ */
78
+ export interface TimerEntry {
79
+ startTime: number;
80
+ category?: LogCategory;
81
+ }
82
+ /**
83
+ * Factory function for Logger creation
84
+ * Returns undefined when verbose is false (no-op pattern)
85
+ *
86
+ * @param verbose - Enable verbose logging
87
+ * @returns Logger instance or undefined
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const logger = createLogger(cliOptions.verbose);
92
+ * logger?.info(LogCategory.FILE_PROCESSING, 'Starting processing');
93
+ * ```
94
+ */
95
+ export declare function createLogger(verbose: boolean): Logger | undefined;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Handles output writing to stdout or files
3
+ * Supports directory creation and error handling
4
+ */
5
+ export declare class OutputHandler {
6
+ /**
7
+ * Write output content to stdout or file
8
+ * @param content The content to write
9
+ * @param filePath Optional file path. If not provided, writes to stdout
10
+ */
11
+ writeOutput(content: string, filePath?: string): Promise<void>;
12
+ }