ng-di-graph 0.4.0 → 0.4.1

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.
@@ -510,32 +510,37 @@ function buildGraph(parsedClasses, logger) {
510
510
  * @param adjacencyList The adjacency list for traversal
511
511
  * @param resultSet The set to collect traversal results
512
512
  * @param options CLI options for verbose output
513
+ * @param logger Optional logger for structured output
513
514
  */
514
- function validateAndTraverseEntryPoints(entryPoints, graph, adjacencyList, resultSet, options) {
515
+ function validateAndTraverseEntryPoints(entryPoints, graph, adjacencyList, resultSet, options, logger) {
515
516
  for (const entryPoint of entryPoints) if (graph.nodes.some((n) => n.id === entryPoint)) traverseFromEntry(entryPoint, adjacencyList, resultSet);
516
- else if (options.verbose) console.warn(`Entry point '${entryPoint}' not found in graph`);
517
+ else if (options.verbose && logger) {
518
+ const message = `Entry point '${entryPoint}' not found in graph`;
519
+ logger.warn(LogCategory.FILTERING, message, { entryPoint });
520
+ }
517
521
  }
518
522
  /**
519
523
  * Filters a graph based on entry points and traversal direction
520
524
  * @param graph The graph to filter
521
525
  * @param options CLI options containing entry points and direction
526
+ * @param logger Optional logger for structured output
522
527
  * @returns Filtered graph containing only nodes reachable from entry points
523
528
  */
524
- function filterGraph(graph, options) {
529
+ function filterGraph(graph, options, logger) {
525
530
  if (!options.entry || options.entry.length === 0) return graph;
526
531
  const includedNodeIds = /* @__PURE__ */ new Set();
527
532
  if (options.direction === "both") {
528
533
  const upstreamAdjacencyList = buildAdjacencyList(graph, "upstream");
529
534
  const upstreamNodes = /* @__PURE__ */ new Set();
530
- validateAndTraverseEntryPoints(options.entry, graph, upstreamAdjacencyList, upstreamNodes, options);
535
+ validateAndTraverseEntryPoints(options.entry, graph, upstreamAdjacencyList, upstreamNodes, options, logger);
531
536
  const downstreamAdjacencyList = buildAdjacencyList(graph, "downstream");
532
537
  const downstreamNodes = /* @__PURE__ */ new Set();
533
- validateAndTraverseEntryPoints(options.entry, graph, downstreamAdjacencyList, downstreamNodes, options);
538
+ validateAndTraverseEntryPoints(options.entry, graph, downstreamAdjacencyList, downstreamNodes, options, logger);
534
539
  const combinedNodes = new Set([...upstreamNodes, ...downstreamNodes]);
535
540
  for (const nodeId of combinedNodes) includedNodeIds.add(nodeId);
536
541
  } else {
537
542
  const adjacencyList = buildAdjacencyList(graph, options.direction);
538
- validateAndTraverseEntryPoints(options.entry, graph, adjacencyList, includedNodeIds, options);
543
+ validateAndTraverseEntryPoints(options.entry, graph, adjacencyList, includedNodeIds, options, logger);
539
544
  }
540
545
  const filteredNodes = graph.nodes.filter((node) => includedNodeIds.has(node.id));
541
546
  const filteredEdges = graph.edges.filter((edge) => includedNodeIds.has(edge.from) && includedNodeIds.has(edge.to));
@@ -554,9 +559,12 @@ function filterGraph(graph, options) {
554
559
  }
555
560
  return true;
556
561
  });
557
- if (options.verbose) {
558
- console.log(`Filtered graph: ${filteredNodes.length} nodes, ${filteredEdges.length} edges`);
559
- console.log(`Entry points: ${options.entry.join(", ")}`);
562
+ if (options.verbose && logger) {
563
+ logger.info(LogCategory.FILTERING, "Filtered graph summary", {
564
+ nodeCount: filteredNodes.length,
565
+ edgeCount: filteredEdges.length
566
+ });
567
+ logger.debug(LogCategory.FILTERING, "Entry points applied", { entryPoints: options.entry });
560
568
  }
561
569
  return {
562
570
  nodes: filteredNodes,
@@ -663,6 +671,21 @@ var AngularParser = class {
663
671
  totalCount: 0
664
672
  };
665
673
  }
674
+ verboseInfo(category, message, context) {
675
+ if (!this._options.verbose || !this._logger) return;
676
+ this._logger.info(category, message, context);
677
+ }
678
+ verboseDebug(category, message, context) {
679
+ if (!this._options.verbose || !this._logger) return;
680
+ this._logger.debug(category, message, context);
681
+ }
682
+ warn(category, message, context) {
683
+ if (this._logger) {
684
+ this._logger.warn(category, message, context);
685
+ return;
686
+ }
687
+ ErrorHandler.warn(message, context?.filePath);
688
+ }
666
689
  /**
667
690
  * Reset global warning deduplication state (useful for testing)
668
691
  */
@@ -698,8 +721,23 @@ var AngularParser = class {
698
721
  this._structuredWarnings.categories[category].push(warning);
699
722
  this._structuredWarnings.totalCount++;
700
723
  const location = warning.line ? `${warning.file}:${warning.line}:${warning.column}` : warning.file;
701
- console.warn(`[${warning.severity.toUpperCase()}] ${warning.message} (${location})`);
702
- if (warning.suggestion && this._options.verbose) console.warn(` Suggestion: ${warning.suggestion}`);
724
+ const formattedWarning = `[${warning.severity.toUpperCase()}] ${warning.message} (${location})`;
725
+ if (this._logger) {
726
+ this._logger.warn(LogCategory.ERROR_RECOVERY, formattedWarning, {
727
+ category,
728
+ filePath: warning.file,
729
+ lineNumber: warning.line,
730
+ suggestion: warning.suggestion
731
+ });
732
+ if (warning.suggestion && this._options.verbose) this._logger.info(LogCategory.ERROR_RECOVERY, warning.suggestion, {
733
+ category,
734
+ filePath: warning.file,
735
+ lineNumber: warning.line
736
+ });
737
+ } else {
738
+ ErrorHandler.warn(formattedWarning, warning.file);
739
+ if (warning.suggestion && this._options.verbose) ErrorHandler.warn(warning.suggestion, warning.file);
740
+ }
703
741
  GLOBAL_WARNING_KEYS.add(warnKey);
704
742
  }
705
743
  }
@@ -723,12 +761,6 @@ var AngularParser = class {
723
761
  }
724
762
  this._project = new ts_morph.Project({ tsConfigFilePath: tsConfigPath });
725
763
  if (!this._project) throw ErrorHandler.createError("Failed to load TypeScript project", "PROJECT_LOAD_FAILED", this._options.project);
726
- this._project.getSourceFiles();
727
- const diagnostics = this._project.getProgram().getConfigFileParsingDiagnostics();
728
- if (diagnostics.length > 0) {
729
- const message = diagnostics[0].getMessageText();
730
- throw ErrorHandler.createError(`TypeScript configuration error: ${message}`, "PROJECT_LOAD_FAILED", this._options.project, { diagnosticCount: diagnostics.length });
731
- }
732
764
  } catch (error) {
733
765
  if (error instanceof CliError) throw error;
734
766
  if (error instanceof Error) {
@@ -846,8 +878,11 @@ var AngularParser = class {
846
878
  matchedCount: matchedFiles.length
847
879
  });
848
880
  if (this._options.verbose) {
849
- console.log(`🎯 Filtering to specific file(s): ${matchedFiles.length}`);
850
- for (const file of matchedFiles) console.log(` - ${file.getFilePath()}`);
881
+ this._logger?.info(LogCategory.FILE_PROCESSING, `Filtering to specific file(s): ${matchedFiles.length}`, {
882
+ matchedCount: matchedFiles.length,
883
+ targetCount: targetPaths.length
884
+ });
885
+ for (const file of matchedFiles) this._logger?.debug(LogCategory.FILE_PROCESSING, "Including file", { filePath: file.getFilePath() });
851
886
  }
852
887
  return matchedFiles;
853
888
  }
@@ -866,14 +901,12 @@ var AngularParser = class {
866
901
  this._circularTypeRefs.clear();
867
902
  this._logger?.time("findDecoratedClasses");
868
903
  this._logger?.info(LogCategory.FILE_PROCESSING, "Starting file processing", { fileCount: sourceFiles.length });
869
- if (this._options.verbose) console.log(`Processing ${sourceFiles.length} source files`);
904
+ this._logger?.info(LogCategory.FILE_PROCESSING, "Processing source files", { fileCount: sourceFiles.length });
870
905
  for (const sourceFile of sourceFiles) {
871
906
  const filePath = sourceFile.getFilePath();
872
907
  try {
873
- if (this._options.verbose) console.log(`🔍 Parsing file: ${filePath}`);
874
- this._logger?.debug(LogCategory.FILE_PROCESSING, "Processing file", { filePath });
908
+ this._logger?.debug(LogCategory.FILE_PROCESSING, "Parsing file", { filePath });
875
909
  const classes = sourceFile.getClasses();
876
- if (this._options.verbose) console.log(`File: ${filePath}, Classes: ${classes.length}`);
877
910
  this._logger?.debug(LogCategory.AST_ANALYSIS, "Analyzing classes in file", {
878
911
  filePath,
879
912
  classCount: classes.length
@@ -882,7 +915,6 @@ var AngularParser = class {
882
915
  const parsedClass = this.parseClassDeclaration(classDeclaration);
883
916
  if (parsedClass) {
884
917
  decoratedClasses.push(parsedClass);
885
- if (this._options.verbose) console.log(`Found decorated class: ${parsedClass.name} (${parsedClass.kind})`);
886
918
  this._logger?.info(LogCategory.AST_ANALYSIS, "Found decorated class", {
887
919
  className: parsedClass.name,
888
920
  kind: parsedClass.kind,
@@ -904,7 +936,10 @@ var AngularParser = class {
904
936
  ErrorHandler.warn(`Failed to parse file (skipping): ${error instanceof Error ? error.message : "Unknown error"}`, filePath);
905
937
  }
906
938
  }
907
- if (this._options.verbose) console.log(`✅ Processed ${processedFiles} files, skipped ${skippedFiles} files`);
939
+ this._logger?.info(LogCategory.FILE_PROCESSING, "File processing summary", {
940
+ processedFiles,
941
+ skippedFiles
942
+ });
908
943
  const elapsed = this._logger?.timeEnd("findDecoratedClasses") || 0;
909
944
  this._logger?.info(LogCategory.PERFORMANCE, "File processing complete", {
910
945
  totalClasses: decoratedClasses.length,
@@ -923,17 +958,26 @@ var AngularParser = class {
923
958
  parseClassDeclaration(classDeclaration) {
924
959
  const className = classDeclaration.getName();
925
960
  if (!className) {
926
- console.warn("Warning: Skipping anonymous class - classes must be named for dependency injection analysis");
961
+ const message = "Skipping anonymous class - classes must be named for dependency injection analysis";
962
+ if (this._logger) this._logger.warn(LogCategory.AST_ANALYSIS, message);
963
+ else ErrorHandler.warn(message);
927
964
  return null;
928
965
  }
929
966
  const decorators = classDeclaration.getDecorators();
930
967
  if (this._options.verbose) {
931
968
  const decoratorNames = decorators.map((d) => this.getDecoratorName(d)).join(", ");
932
- console.log(`Class: ${className}, Decorators: ${decorators.length} [${decoratorNames}]`);
969
+ this._logger?.debug(LogCategory.AST_ANALYSIS, "Decorator metadata", {
970
+ className,
971
+ decoratorCount: decorators.length,
972
+ decoratorNames
973
+ });
933
974
  }
934
975
  const angularDecorator = this.findAngularDecorator(decorators);
935
976
  if (!angularDecorator) {
936
- if (this._options.verbose && decorators.length > 0) console.log(` No Angular decorator found for ${className}`);
977
+ if (this._options.verbose && decorators.length > 0) this._logger?.debug(LogCategory.AST_ANALYSIS, "No Angular decorator found", {
978
+ className,
979
+ decoratorCount: decorators.length
980
+ });
937
981
  return null;
938
982
  }
939
983
  return {
@@ -1015,14 +1059,14 @@ var AngularParser = class {
1015
1059
  if (parent && parent.getKind() === ts_morph.SyntaxKind.CallExpression) {
1016
1060
  const grandParent = parent.getParent();
1017
1061
  if (grandParent && grandParent.getKind() === ts_morph.SyntaxKind.CallExpression) {
1018
- console.warn("Warning: Skipping anonymous class - classes must be named for dependency injection analysis");
1019
- if (this._options.verbose) console.log(` Anonymous class found in ${sourceFile.getFilePath()}`);
1062
+ this.warn(LogCategory.AST_ANALYSIS, "Skipping anonymous class - classes must be named for dependency injection analysis", { filePath: sourceFile.getFilePath() });
1063
+ this.verboseDebug(LogCategory.AST_ANALYSIS, `Anonymous class found in ${sourceFile.getFilePath()}`);
1020
1064
  }
1021
1065
  }
1022
1066
  }
1023
1067
  });
1024
1068
  } catch (error) {
1025
- if (this._options.verbose) console.log(` Could not detect anonymous classes in ${sourceFile.getFilePath()}: ${error}`);
1069
+ this.verboseDebug(LogCategory.AST_ANALYSIS, `Could not detect anonymous classes in ${sourceFile.getFilePath()}: ${error}`);
1026
1070
  }
1027
1071
  }
1028
1072
  /**
@@ -1051,11 +1095,11 @@ var AngularParser = class {
1051
1095
  };
1052
1096
  const startTime = performance.now();
1053
1097
  if (this._options.verbose && this._options.includeDecorators) {
1054
- console.log("=== Decorator Analysis ===");
1098
+ this.verboseInfo(LogCategory.AST_ANALYSIS, "=== Decorator Analysis ===");
1055
1099
  const className = classDeclaration.getName() || "unknown";
1056
- console.log(`Analyzing decorators for class: ${className}`);
1100
+ this.verboseInfo(LogCategory.AST_ANALYSIS, `Analyzing decorators for class: ${className}`, { className });
1057
1101
  }
1058
- if (this._options.verbose && !this._options.includeDecorators) console.log("Decorator analysis disabled - --include-decorators flag not set");
1102
+ if (this._options.verbose && !this._options.includeDecorators) this.verboseInfo(LogCategory.AST_ANALYSIS, "Decorator analysis disabled - --include-decorators flag not set");
1059
1103
  const constructors = classDeclaration.getConstructors();
1060
1104
  if (constructors.length > 0) {
1061
1105
  const parameters = constructors[0].getParameters();
@@ -1109,7 +1153,7 @@ var AngularParser = class {
1109
1153
  * @returns Token string or null
1110
1154
  */
1111
1155
  handleGenericType(typeText, _filePath, _lineNumber, _columnNumber) {
1112
- if (this._options.verbose) console.log(`Processing generic type: ${typeText}`);
1156
+ this.verboseDebug(LogCategory.TYPE_RESOLUTION, `Processing generic type: ${typeText}`);
1113
1157
  return typeText;
1114
1158
  }
1115
1159
  /**
@@ -1196,7 +1240,10 @@ var AngularParser = class {
1196
1240
  */
1197
1241
  extractTypeTokenEnhanced(typeNode, filePath, lineNumber, columnNumber) {
1198
1242
  const typeText = typeNode.getText();
1199
- if (this._options.verbose) console.log(`Type resolution steps: Processing '${typeText}' at ${filePath}:${lineNumber}:${columnNumber}`);
1243
+ this.verboseInfo(LogCategory.TYPE_RESOLUTION, `Type resolution steps: Processing '${typeText}' at ${filePath}:${lineNumber}:${columnNumber}`, {
1244
+ filePath,
1245
+ lineNumber
1246
+ });
1200
1247
  if (this.isCircularTypeReference(typeText, typeNode)) {
1201
1248
  this.addStructuredWarning("circularReferences", {
1202
1249
  type: "circular_type_reference",
@@ -1260,7 +1307,10 @@ var AngularParser = class {
1260
1307
  * @returns Resolved token or null
1261
1308
  */
1262
1309
  resolveInferredTypeEnhanced(type, typeText, param, filePath, lineNumber, columnNumber) {
1263
- if (this._options.verbose) console.log(`Attempting to resolve inferred type: ${typeText}`);
1310
+ this.verboseInfo(LogCategory.TYPE_RESOLUTION, `Attempting to resolve inferred type: ${typeText}`, {
1311
+ filePath,
1312
+ lineNumber
1313
+ });
1264
1314
  const symbol = type.getSymbol?.();
1265
1315
  if (symbol) {
1266
1316
  const symbolName = symbol.getName();
@@ -1362,14 +1412,20 @@ var AngularParser = class {
1362
1412
  cacheKey = `${filePath}:${parameterName}:${typeText}`;
1363
1413
  if (this._typeResolutionCache.has(cacheKey)) {
1364
1414
  const cachedResult = this._typeResolutionCache.get(cacheKey);
1365
- if (this._options.verbose) console.log(`Cache hit for parameter '${parameterName}': ${typeText}`);
1415
+ this.verboseDebug(LogCategory.TYPE_RESOLUTION, `Cache hit for parameter '${parameterName}': ${typeText}`, {
1416
+ parameterName,
1417
+ cacheKey
1418
+ });
1366
1419
  return cachedResult ? {
1367
1420
  token: cachedResult,
1368
1421
  flags,
1369
1422
  parameterName
1370
1423
  } : null;
1371
1424
  }
1372
- if (this._options.verbose) console.log(`Cache miss for parameter '${parameterName}': ${typeText}`);
1425
+ this.verboseDebug(LogCategory.TYPE_RESOLUTION, `Cache miss for parameter '${parameterName}': ${typeText}`, {
1426
+ parameterName,
1427
+ cacheKey
1428
+ });
1373
1429
  const resolvedToken = this.resolveInferredTypeEnhanced(type, typeText, param, filePath, lineNumber, columnNumber);
1374
1430
  this._typeResolutionCache.set(cacheKey, resolvedToken);
1375
1431
  if (resolvedToken) return {
@@ -1463,7 +1519,10 @@ var AngularParser = class {
1463
1519
  if (this._options.verbose) {
1464
1520
  const className = classDeclaration.getName() || "unknown";
1465
1521
  const filePath = classDeclaration.getSourceFile().getFilePath();
1466
- console.warn(`Warning: Failed to extract inject() dependencies for class '${className}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1522
+ this.warn(LogCategory.ERROR_RECOVERY, `Failed to extract inject() dependencies for class '${className}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
1523
+ className,
1524
+ filePath
1525
+ });
1467
1526
  }
1468
1527
  }
1469
1528
  return dependencies;
@@ -1499,7 +1558,10 @@ var AngularParser = class {
1499
1558
  if (this._options.verbose) {
1500
1559
  const propertyName = property.getName() || "unknown";
1501
1560
  const filePath = property.getSourceFile().getFilePath();
1502
- console.warn(`Warning: Failed to parse inject() property '${propertyName}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1561
+ this.warn(LogCategory.ERROR_RECOVERY, `Failed to parse inject() property '${propertyName}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
1562
+ propertyName,
1563
+ filePath
1564
+ });
1503
1565
  }
1504
1566
  return null;
1505
1567
  }
@@ -1525,7 +1587,7 @@ var AngularParser = class {
1525
1587
  const propertyAssignment = prop;
1526
1588
  const name = propertyAssignment.getName();
1527
1589
  if (!supportedOptions.has(name)) {
1528
- if (this._options.verbose) console.warn(`Unknown inject() option: '${name}' - ignoring`);
1590
+ if (this._options.verbose) this.warn(LogCategory.AST_ANALYSIS, `Unknown inject() option: '${name}' - ignoring`);
1529
1591
  continue;
1530
1592
  }
1531
1593
  const initializer = propertyAssignment.getInitializer();
@@ -1545,7 +1607,7 @@ var AngularParser = class {
1545
1607
  }
1546
1608
  }
1547
1609
  } catch (error) {
1548
- if (this._options.verbose) console.warn(`Warning: Failed to parse inject() options: ${error instanceof Error ? error.message : String(error)}`);
1610
+ if (this._options.verbose) this.warn(LogCategory.ERROR_RECOVERY, `Failed to parse inject() options: ${error instanceof Error ? error.message : String(error)}`);
1549
1611
  }
1550
1612
  return flags;
1551
1613
  }
@@ -1590,14 +1652,21 @@ var AngularParser = class {
1590
1652
  name: decoratorName,
1591
1653
  reason: "Unknown or unsupported decorator"
1592
1654
  });
1593
- console.warn(`Unknown or unsupported decorator: @${decoratorName}() - This decorator is not recognized as an Angular DI decorator and will be ignored.`);
1655
+ const className = parameter.getFirstAncestorByKind(ts_morph.SyntaxKind.ClassDeclaration)?.getName() || void 0;
1656
+ this.warn(LogCategory.AST_ANALYSIS, `Unknown or unsupported decorator: @${decoratorName}() - This decorator is not recognized as an Angular DI decorator and will be ignored.`, {
1657
+ filePath: parameter.getSourceFile().getFilePath(),
1658
+ className
1659
+ });
1594
1660
  }
1595
1661
  }
1596
1662
  } catch (error) {
1597
1663
  if (this._options.verbose) {
1598
1664
  const paramName = parameter.getName();
1599
1665
  const filePath = parameter.getSourceFile().getFilePath();
1600
- console.warn(`Warning: Failed to extract decorators for parameter '${paramName}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1666
+ this.warn(LogCategory.ERROR_RECOVERY, `Failed to extract decorators for parameter '${paramName}' in ${filePath}: ${error instanceof Error ? error.message : String(error)}`, {
1667
+ paramName,
1668
+ filePath
1669
+ });
1601
1670
  }
1602
1671
  return {};
1603
1672
  }
@@ -1622,7 +1691,7 @@ var AngularParser = class {
1622
1691
  }
1623
1692
  return false;
1624
1693
  } catch (error) {
1625
- if (this._options.verbose) console.warn(`Warning: Could not verify inject() import in ${sourceFile.getFilePath()}: ${error instanceof Error ? error.message : String(error)}`);
1694
+ if (this._options.verbose) this.warn(LogCategory.ERROR_RECOVERY, `Could not verify inject() import in ${sourceFile.getFilePath()}: ${error instanceof Error ? error.message : String(error)}`, { filePath: sourceFile.getFilePath() });
1626
1695
  return true;
1627
1696
  }
1628
1697
  }
@@ -1636,15 +1705,19 @@ var AngularParser = class {
1636
1705
  if (!expression) return null;
1637
1706
  if (expression.getKind() !== ts_morph.SyntaxKind.CallExpression) return null;
1638
1707
  const callExpression = expression;
1708
+ const sourceFile = expression.getSourceFile();
1709
+ const warnVerbose = (message) => {
1710
+ if (this._options.verbose) this.warn(LogCategory.AST_ANALYSIS, message, { filePath: sourceFile.getFilePath() });
1711
+ };
1639
1712
  try {
1640
1713
  const callIdentifier = callExpression.getExpression();
1641
1714
  if (callIdentifier.getKind() !== ts_morph.SyntaxKind.Identifier) return null;
1642
1715
  if (callIdentifier.getText() !== "inject") return null;
1643
- const sourceFile = expression.getSourceFile();
1644
- if (!this.isAngularInjectImported(sourceFile)) return null;
1716
+ const sourceFile$1 = expression.getSourceFile();
1717
+ if (!this.isAngularInjectImported(sourceFile$1)) return null;
1645
1718
  const args = callExpression.getArguments();
1646
1719
  if (args.length === 0) {
1647
- if (this._options.verbose) console.warn("inject() called without token parameter - skipping");
1720
+ warnVerbose("inject() called without token parameter - skipping");
1648
1721
  return null;
1649
1722
  }
1650
1723
  const tokenArg = args[0];
@@ -1652,22 +1725,22 @@ var AngularParser = class {
1652
1725
  if (tokenArg.getKind() === ts_morph.SyntaxKind.StringLiteral) {
1653
1726
  token = tokenArg.getText().slice(1, -1);
1654
1727
  if (!token) {
1655
- if (this._options.verbose) console.warn("inject() called with empty string token - skipping");
1728
+ warnVerbose("inject() called with empty string token - skipping");
1656
1729
  return null;
1657
1730
  }
1658
1731
  } else if (tokenArg.getKind() === ts_morph.SyntaxKind.Identifier) {
1659
1732
  token = tokenArg.getText();
1660
1733
  if (token === "undefined" || token === "null") {
1661
- if (this._options.verbose) console.warn(`inject() called with ${token} token - skipping`);
1734
+ warnVerbose(`inject() called with ${token} token - skipping`);
1662
1735
  return null;
1663
1736
  }
1664
1737
  } else if (tokenArg.getKind() === ts_morph.SyntaxKind.NullKeyword) {
1665
- if (this._options.verbose) console.warn("inject() called with null token - skipping");
1738
+ warnVerbose("inject() called with null token - skipping");
1666
1739
  return null;
1667
1740
  } else {
1668
1741
  token = tokenArg.getText();
1669
1742
  if (!token) {
1670
- if (this._options.verbose) console.warn("inject() called with invalid token expression - skipping");
1743
+ warnVerbose("inject() called with invalid token expression - skipping");
1671
1744
  return null;
1672
1745
  }
1673
1746
  }
@@ -1675,9 +1748,7 @@ var AngularParser = class {
1675
1748
  if (args.length > 1 && this._options.includeDecorators) {
1676
1749
  const optionsArg = args[1];
1677
1750
  if (optionsArg.getKind() === ts_morph.SyntaxKind.ObjectLiteralExpression) flags = this.parseInjectOptions(optionsArg);
1678
- else if (optionsArg.getKind() !== ts_morph.SyntaxKind.NullKeyword && optionsArg.getKind() !== ts_morph.SyntaxKind.UndefinedKeyword) {
1679
- if (this._options.verbose) console.warn(`inject() called with invalid options type: ${optionsArg.getKindName()} - expected object literal`);
1680
- }
1751
+ else if (optionsArg.getKind() !== ts_morph.SyntaxKind.NullKeyword && optionsArg.getKind() !== ts_morph.SyntaxKind.UndefinedKeyword) warnVerbose(`inject() called with invalid options type: ${optionsArg.getKindName()} - expected object literal`);
1681
1752
  }
1682
1753
  return {
1683
1754
  token,
@@ -1685,10 +1756,7 @@ var AngularParser = class {
1685
1756
  source: "inject"
1686
1757
  };
1687
1758
  } catch (error) {
1688
- if (this._options.verbose) {
1689
- const filePath = expression.getSourceFile().getFilePath();
1690
- console.warn(`Warning: Failed to analyze inject() call in ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
1691
- }
1759
+ if (this._options.verbose) warnVerbose(`Failed to analyze inject() call in ${sourceFile.getFilePath()}: ${error instanceof Error ? error.message : String(error)}`);
1692
1760
  return null;
1693
1761
  }
1694
1762
  }
@@ -1700,8 +1768,11 @@ var AngularParser = class {
1700
1768
  */
1701
1769
  collectVerboseStats(param, dependency, verboseStats) {
1702
1770
  const paramName = param.getName();
1703
- console.log(`Parameter: ${paramName}`);
1704
- console.log(` Token: ${dependency.token}`);
1771
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "Parameter analysis", {
1772
+ paramName,
1773
+ token: dependency.token,
1774
+ flags: dependency.flags
1775
+ });
1705
1776
  if (Object.keys(dependency.flags || {}).length > 0) {
1706
1777
  verboseStats.parametersWithDecorators++;
1707
1778
  if (dependency.flags?.optional) verboseStats.decoratorCounts.optional++;
@@ -1721,7 +1792,10 @@ var AngularParser = class {
1721
1792
  "Host"
1722
1793
  ].includes(decoratorName)) {
1723
1794
  hasLegacyDecorators = true;
1724
- console.log(` Legacy decorator: @${decoratorName}`);
1795
+ this.verboseDebug(LogCategory.AST_ANALYSIS, `Legacy decorator detected`, {
1796
+ decoratorName,
1797
+ paramName
1798
+ });
1725
1799
  }
1726
1800
  }
1727
1801
  const initializer = param.getInitializer();
@@ -1732,29 +1806,44 @@ var AngularParser = class {
1732
1806
  hasInjectPattern = true;
1733
1807
  verboseStats.injectPatternsUsed++;
1734
1808
  const flagsStr = JSON.stringify(dependency.flags);
1735
- console.log(` inject() options: ${flagsStr}`);
1809
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "inject() options detected", {
1810
+ paramName,
1811
+ flags: flagsStr
1812
+ });
1736
1813
  }
1737
1814
  }
1738
1815
  }
1739
1816
  if (hasLegacyDecorators) {
1740
1817
  verboseStats.legacyDecoratorsUsed++;
1741
1818
  if (hasInjectPattern) {
1742
- console.log(" Decorator Precedence Analysis");
1743
- console.log(" Legacy decorators take precedence over inject() options");
1819
+ this.verboseInfo(LogCategory.AST_ANALYSIS, "Decorator precedence analysis", {
1820
+ paramName,
1821
+ legacyDecorators: true,
1822
+ injectPattern: true
1823
+ });
1744
1824
  const appliedFlags = Object.keys(dependency.flags || {}).filter((key) => dependency.flags?.[key] === true).map((key) => `@${key.charAt(0).toUpperCase() + key.slice(1)}`).join(", ");
1745
- console.log(` Applied: ${appliedFlags}`);
1825
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "Applied decorator flags", {
1826
+ paramName,
1827
+ appliedFlags
1828
+ });
1746
1829
  const injectResult = this.analyzeInjectCall(initializer);
1747
1830
  if (injectResult && Object.keys(injectResult.flags).length > 0) {
1748
1831
  const overriddenFlags = JSON.stringify(injectResult.flags);
1749
- console.log(` Overridden inject() options: ${overriddenFlags}`);
1832
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "Overridden inject() options", {
1833
+ paramName,
1834
+ overriddenFlags
1835
+ });
1750
1836
  }
1751
1837
  const finalFlags = JSON.stringify(dependency.flags);
1752
- console.log(` Final flags: ${finalFlags}`);
1838
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "Final decorator flags", {
1839
+ paramName,
1840
+ finalFlags
1841
+ });
1753
1842
  }
1754
1843
  }
1755
1844
  } else {
1756
1845
  verboseStats.parametersWithoutDecorators++;
1757
- console.log(" No decorators detected");
1846
+ this.verboseDebug(LogCategory.AST_ANALYSIS, "No decorators detected", { paramName });
1758
1847
  }
1759
1848
  }
1760
1849
  /**
@@ -1764,69 +1853,24 @@ var AngularParser = class {
1764
1853
  * @param classDeclaration Class being analyzed
1765
1854
  */
1766
1855
  outputVerboseAnalysis(dependencies, verboseStats, classDeclaration) {
1767
- if (!this._options.verbose) return;
1768
- if (this._options.includeDecorators) {
1769
- console.log("=== Decorator Statistics ===");
1770
- console.log(`Total decorators detected: ${verboseStats.decoratorCounts.optional + verboseStats.decoratorCounts.self + verboseStats.decoratorCounts.skipSelf + verboseStats.decoratorCounts.host}`);
1771
- if (verboseStats.decoratorCounts.optional > 0) console.log(`@Optional: ${verboseStats.decoratorCounts.optional}`);
1772
- if (verboseStats.decoratorCounts.self > 0) console.log(`@Self: ${verboseStats.decoratorCounts.self}`);
1773
- if (verboseStats.decoratorCounts.skipSelf > 0) console.log(`@SkipSelf: ${verboseStats.decoratorCounts.skipSelf}`);
1774
- if (verboseStats.decoratorCounts.host > 0) console.log(`@Host: ${verboseStats.decoratorCounts.host}`);
1775
- console.log(`Parameters with decorators: ${verboseStats.parametersWithDecorators}`);
1776
- console.log(`Parameters without decorators: ${verboseStats.parametersWithoutDecorators}`);
1777
- if (verboseStats.injectPatternsUsed > 0) {
1778
- console.log("inject() Pattern Analysis");
1779
- for (const dep of dependencies) if (dep.parameterName) {
1780
- const constructors = classDeclaration.getConstructors();
1781
- if (constructors.length > 0) {
1782
- const param = constructors[0].getParameters().find((p) => p.getName() === dep.parameterName);
1783
- if (param) {
1784
- const initializer = param.getInitializer();
1785
- if (initializer) {
1786
- const injectResult = this.analyzeInjectCall(initializer);
1787
- if (injectResult) {
1788
- if (injectResult.token.startsWith("\"") && injectResult.token.endsWith("\"")) console.log(`String token: ${injectResult.token}`);
1789
- else console.log(`Service token: ${injectResult.token}`);
1790
- if (Object.keys(injectResult.flags).length > 0) {
1791
- const flagsStr = JSON.stringify(injectResult.flags);
1792
- console.log(`inject() options detected: ${flagsStr}`);
1793
- } else console.log("inject() with no options");
1794
- }
1795
- }
1796
- }
1797
- }
1798
- }
1799
- }
1800
- if (verboseStats.skippedDecorators.length > 0) {
1801
- console.log("Skipped Decorators");
1802
- for (const skipped of verboseStats.skippedDecorators) {
1803
- console.log(`${skipped.name}`);
1804
- console.log(`Reason: ${skipped.reason}`);
1805
- }
1806
- console.log(`Total skipped: ${verboseStats.skippedDecorators.length}`);
1807
- }
1808
- console.log("Performance Metrics");
1809
- console.log(`Decorator processing time: ${verboseStats.totalProcessingTime.toFixed(2)}ms`);
1810
- console.log(`Total parameters analyzed: ${verboseStats.totalParameters}`);
1811
- if (verboseStats.totalParameters > 0) {
1812
- const avgTime = verboseStats.totalProcessingTime / verboseStats.totalParameters;
1813
- console.log(`Average time per parameter: ${avgTime.toFixed(3)}ms`);
1814
- }
1815
- console.log("=== Analysis Summary ===");
1816
- console.log(`Total dependencies: ${dependencies.length}`);
1817
- console.log(`With decorator flags: ${verboseStats.parametersWithDecorators}`);
1818
- console.log(`Without decorator flags: ${verboseStats.parametersWithoutDecorators}`);
1819
- console.log(`Legacy decorators used: ${verboseStats.legacyDecoratorsUsed}`);
1820
- console.log(`inject() patterns used: ${verboseStats.injectPatternsUsed}`);
1821
- if (verboseStats.skippedDecorators.length > 0) console.log(`Unknown decorators skipped: ${verboseStats.skippedDecorators.length}`);
1822
- if (verboseStats.parametersWithDecorators > 0) {
1823
- console.log("Flags distribution:");
1824
- if (verboseStats.decoratorCounts.optional > 0) console.log(`optional: ${verboseStats.decoratorCounts.optional}`);
1825
- if (verboseStats.decoratorCounts.self > 0) console.log(`self: ${verboseStats.decoratorCounts.self}`);
1826
- if (verboseStats.decoratorCounts.skipSelf > 0) console.log(`skipSelf: ${verboseStats.decoratorCounts.skipSelf}`);
1827
- if (verboseStats.decoratorCounts.host > 0) console.log(`host: ${verboseStats.decoratorCounts.host}`);
1828
- }
1829
- }
1856
+ if (!this._options.verbose || !this._logger) return;
1857
+ const className = classDeclaration.getName() || "unknown";
1858
+ this._logger.info(LogCategory.AST_ANALYSIS, "Decorator analysis summary", {
1859
+ className,
1860
+ decoratorCounts: verboseStats.decoratorCounts,
1861
+ parametersWithDecorators: verboseStats.parametersWithDecorators,
1862
+ parametersWithoutDecorators: verboseStats.parametersWithoutDecorators,
1863
+ injectPatternsUsed: verboseStats.injectPatternsUsed,
1864
+ skippedDecorators: verboseStats.skippedDecorators.length,
1865
+ legacyDecoratorsUsed: verboseStats.legacyDecoratorsUsed,
1866
+ totalProcessingTime: verboseStats.totalProcessingTime,
1867
+ totalParameters: verboseStats.totalParameters,
1868
+ dependencyCount: dependencies.length
1869
+ });
1870
+ if (verboseStats.skippedDecorators.length > 0) this._logger.debug(LogCategory.AST_ANALYSIS, "Skipped decorators", {
1871
+ skippedDecorators: verboseStats.skippedDecorators,
1872
+ className
1873
+ });
1830
1874
  }
1831
1875
  };
1832
1876
 
@@ -2015,7 +2059,7 @@ program.action(async (filePaths = [], options) => {
2015
2059
  }
2016
2060
  if (cliOptions.entry && cliOptions.entry.length > 0) {
2017
2061
  if (cliOptions.verbose) console.log(`🔍 Filtering graph by entry points: ${cliOptions.entry.join(", ")}`);
2018
- graph = filterGraph(graph, cliOptions);
2062
+ graph = filterGraph(graph, cliOptions, logger);
2019
2063
  if (cliOptions.verbose) console.log(`✅ Filtered graph: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);
2020
2064
  }
2021
2065
  let formatter;