ng-di-graph 0.6.0 โ 0.7.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.
- package/README.md +2 -0
- package/dist/cli/index.cjs +154 -45
- package/dist/cli/index.cjs.map +1 -1
- package/dist/core/parser.d.ts +9 -0
- package/dist/types/index.d.ts +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ ng-di-graph --project ./tsconfig.json --format json
|
|
|
49
49
|
- ๐จ **Entry Point Filtering** - Generate sub-graphs from specific starting nodes
|
|
50
50
|
- ๐ **Bidirectional Analysis** - Explore upstream dependencies, downstream consumers, or both
|
|
51
51
|
- ๐ **Circular Detection** - Automatically detect and report circular dependencies
|
|
52
|
+
- ๐งน **Angular Core Filtering** - Hide `@angular/core` nodes by default (`--include-angular-core` to show)
|
|
52
53
|
|
|
53
54
|
## Common commands
|
|
54
55
|
- Full project, text output (default):
|
|
@@ -79,6 +80,7 @@ Option | Default | Description
|
|
|
79
80
|
`-e, --entry <symbol...>` | none | Start the graph from one or more symbols.
|
|
80
81
|
`-d, --direction <dir>` | `downstream` | `downstream`, `upstream`, or `both` relative to entries.
|
|
81
82
|
`--include-decorators` | `false` | Add `@Optional`, `@Self`, `@SkipSelf`, `@Host` flags to edges.
|
|
83
|
+
`--include-angular-core` | `false` | Include nodes that originate from `@angular/core` imports.
|
|
82
84
|
`--out <file>` | stdout | Write output to a file.
|
|
83
85
|
`-v, --verbose` | `false` | Show detailed parsing and resolution logs.
|
|
84
86
|
`-h, --help` | `false` | Display CLI help.
|
package/dist/cli/index.cjs
CHANGED
|
@@ -451,15 +451,20 @@ function buildGraph(parsedClasses, logger) {
|
|
|
451
451
|
let unknownNodeCount = 0;
|
|
452
452
|
for (const parsedClass of parsedClasses) for (const dependency of parsedClass.dependencies) {
|
|
453
453
|
if (!nodeMap.has(dependency.token)) {
|
|
454
|
-
|
|
454
|
+
const unknownNode = {
|
|
455
455
|
id: dependency.token,
|
|
456
456
|
kind: "unknown"
|
|
457
|
-
}
|
|
457
|
+
};
|
|
458
|
+
if (dependency.origin) unknownNode.origin = dependency.origin;
|
|
459
|
+
nodeMap.set(dependency.token, unknownNode);
|
|
458
460
|
unknownNodeCount++;
|
|
459
461
|
logger?.warn(LogCategory.GRAPH_CONSTRUCTION, `Created unknown node: ${dependency.token}`, {
|
|
460
462
|
nodeId: dependency.token,
|
|
461
463
|
referencedBy: parsedClass.name
|
|
462
464
|
});
|
|
465
|
+
} else {
|
|
466
|
+
const existingNode = nodeMap.get(dependency.token);
|
|
467
|
+
if (existingNode && existingNode.kind === "unknown" && !existingNode.origin && dependency.origin) existingNode.origin = dependency.origin;
|
|
463
468
|
}
|
|
464
469
|
const edge = {
|
|
465
470
|
from: parsedClass.name,
|
|
@@ -531,24 +536,57 @@ function validateAndTraverseEntryPoints(entryPoints, graph, adjacencyList, resul
|
|
|
531
536
|
* @returns Filtered graph containing only nodes reachable from entry points
|
|
532
537
|
*/
|
|
533
538
|
function filterGraph(graph, options, logger) {
|
|
534
|
-
|
|
539
|
+
const baseGraph = filterGraphByOrigin(graph, options.includeAngularCore ?? false);
|
|
540
|
+
if (!options.entry || options.entry.length === 0) {
|
|
541
|
+
const includedNodeIds$1 = new Set(baseGraph.nodes.map((node) => node.id));
|
|
542
|
+
return {
|
|
543
|
+
nodes: baseGraph.nodes,
|
|
544
|
+
edges: baseGraph.edges,
|
|
545
|
+
circularDependencies: filterCircularDependencies(baseGraph, includedNodeIds$1)
|
|
546
|
+
};
|
|
547
|
+
}
|
|
535
548
|
const includedNodeIds = /* @__PURE__ */ new Set();
|
|
536
549
|
if (options.direction === "both") {
|
|
537
|
-
const upstreamAdjacencyList = buildAdjacencyList(
|
|
550
|
+
const upstreamAdjacencyList = buildAdjacencyList(baseGraph, "upstream");
|
|
538
551
|
const upstreamNodes = /* @__PURE__ */ new Set();
|
|
539
|
-
validateAndTraverseEntryPoints(options.entry,
|
|
540
|
-
const downstreamAdjacencyList = buildAdjacencyList(
|
|
552
|
+
validateAndTraverseEntryPoints(options.entry, baseGraph, upstreamAdjacencyList, upstreamNodes, options, logger);
|
|
553
|
+
const downstreamAdjacencyList = buildAdjacencyList(baseGraph, "downstream");
|
|
541
554
|
const downstreamNodes = /* @__PURE__ */ new Set();
|
|
542
|
-
validateAndTraverseEntryPoints(options.entry,
|
|
555
|
+
validateAndTraverseEntryPoints(options.entry, baseGraph, downstreamAdjacencyList, downstreamNodes, options, logger);
|
|
543
556
|
const combinedNodes = new Set([...upstreamNodes, ...downstreamNodes]);
|
|
544
557
|
for (const nodeId of combinedNodes) includedNodeIds.add(nodeId);
|
|
545
558
|
} else {
|
|
546
|
-
const adjacencyList = buildAdjacencyList(
|
|
547
|
-
validateAndTraverseEntryPoints(options.entry,
|
|
559
|
+
const adjacencyList = buildAdjacencyList(baseGraph, options.direction);
|
|
560
|
+
validateAndTraverseEntryPoints(options.entry, baseGraph, adjacencyList, includedNodeIds, options, logger);
|
|
548
561
|
}
|
|
549
|
-
const filteredNodes =
|
|
550
|
-
const filteredEdges =
|
|
551
|
-
const filteredCircularDeps =
|
|
562
|
+
const filteredNodes = baseGraph.nodes.filter((node) => includedNodeIds.has(node.id));
|
|
563
|
+
const filteredEdges = baseGraph.edges.filter((edge) => includedNodeIds.has(edge.from) && includedNodeIds.has(edge.to));
|
|
564
|
+
const filteredCircularDeps = filterCircularDependencies(baseGraph, includedNodeIds);
|
|
565
|
+
if (options.verbose && logger) {
|
|
566
|
+
logger.info(LogCategory.FILTERING, "Filtered graph summary", {
|
|
567
|
+
nodeCount: filteredNodes.length,
|
|
568
|
+
edgeCount: filteredEdges.length
|
|
569
|
+
});
|
|
570
|
+
logger.debug(LogCategory.FILTERING, "Entry points applied", { entryPoints: options.entry });
|
|
571
|
+
}
|
|
572
|
+
return {
|
|
573
|
+
nodes: filteredNodes,
|
|
574
|
+
edges: filteredEdges,
|
|
575
|
+
circularDependencies: filteredCircularDeps
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function filterGraphByOrigin(graph, includeAngularCore) {
|
|
579
|
+
if (includeAngularCore) return graph;
|
|
580
|
+
const filteredNodes = graph.nodes.filter((node) => node.origin !== "angular-core");
|
|
581
|
+
const includedNodeIds = new Set(filteredNodes.map((node) => node.id));
|
|
582
|
+
return {
|
|
583
|
+
nodes: filteredNodes,
|
|
584
|
+
edges: graph.edges.filter((edge) => includedNodeIds.has(edge.from) && includedNodeIds.has(edge.to)),
|
|
585
|
+
circularDependencies: graph.circularDependencies
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function filterCircularDependencies(graph, includedNodeIds) {
|
|
589
|
+
return graph.circularDependencies.filter((cycle) => {
|
|
552
590
|
if (cycle.length < 2) return false;
|
|
553
591
|
const isSelfLoop = cycle.length === 2 && cycle[0] === cycle[1];
|
|
554
592
|
const isProperCycleWithClosing = cycle.length >= 3 && cycle[0] === cycle[cycle.length - 1];
|
|
@@ -563,18 +601,6 @@ function filterGraph(graph, options, logger) {
|
|
|
563
601
|
}
|
|
564
602
|
return true;
|
|
565
603
|
});
|
|
566
|
-
if (options.verbose && logger) {
|
|
567
|
-
logger.info(LogCategory.FILTERING, "Filtered graph summary", {
|
|
568
|
-
nodeCount: filteredNodes.length,
|
|
569
|
-
edgeCount: filteredEdges.length
|
|
570
|
-
});
|
|
571
|
-
logger.debug(LogCategory.FILTERING, "Entry points applied", { entryPoints: options.entry });
|
|
572
|
-
}
|
|
573
|
-
return {
|
|
574
|
-
nodes: filteredNodes,
|
|
575
|
-
edges: filteredEdges,
|
|
576
|
-
circularDependencies: filteredCircularDeps
|
|
577
|
-
};
|
|
578
604
|
}
|
|
579
605
|
/**
|
|
580
606
|
* Traverses the graph from a starting node using DFS
|
|
@@ -650,6 +676,7 @@ var OutputHandler = class {
|
|
|
650
676
|
* Implements FR-03: Constructor token resolution
|
|
651
677
|
*/
|
|
652
678
|
const GLOBAL_WARNING_KEYS = /* @__PURE__ */ new Set();
|
|
679
|
+
const ANGULAR_CORE_MODULE = "@angular/core";
|
|
653
680
|
const formatTsDiagnostics = (diagnostics) => diagnostics.map((diagnostic) => {
|
|
654
681
|
const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
655
682
|
if (diagnostic.file && diagnostic.start !== void 0) {
|
|
@@ -664,6 +691,8 @@ var AngularParser = class {
|
|
|
664
691
|
this._logger = _logger;
|
|
665
692
|
this._typeResolutionCache = /* @__PURE__ */ new Map();
|
|
666
693
|
this._circularTypeRefs = /* @__PURE__ */ new Set();
|
|
694
|
+
this._angularCoreImportCache = /* @__PURE__ */ new Map();
|
|
695
|
+
this._angularCoreAliasMatchers = [];
|
|
667
696
|
this._processingStats = {
|
|
668
697
|
processedFileCount: 0,
|
|
669
698
|
skippedFileCount: 0
|
|
@@ -773,6 +802,8 @@ var AngularParser = class {
|
|
|
773
802
|
const message = formatTsDiagnostics(parsedConfig.errors);
|
|
774
803
|
throw ErrorHandler.createError(`TypeScript configuration error: ${message}`, "PROJECT_LOAD_FAILED", this._options.project, { diagnosticCount: parsedConfig.errors.length });
|
|
775
804
|
}
|
|
805
|
+
this.cacheAngularCoreAliases(configFile.config);
|
|
806
|
+
this._angularCoreImportCache.clear();
|
|
776
807
|
this._project = new ts_morph.Project({ tsConfigFilePath: tsConfigPath });
|
|
777
808
|
if (!this._project) throw ErrorHandler.createError("Failed to load TypeScript project", "PROJECT_LOAD_FAILED", this._options.project);
|
|
778
809
|
} catch (error) {
|
|
@@ -1061,15 +1092,79 @@ var AngularParser = class {
|
|
|
1061
1092
|
*/
|
|
1062
1093
|
resolveDecoratorAlias(sourceFile, decoratorName) {
|
|
1063
1094
|
const importDeclarations = sourceFile.getImportDeclarations();
|
|
1064
|
-
for (const importDecl of importDeclarations)
|
|
1095
|
+
for (const importDecl of importDeclarations) {
|
|
1096
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1097
|
+
if (this.isAngularCoreModuleSpecifier(moduleSpecifier)) {
|
|
1098
|
+
const namedImports = importDecl.getNamedImports();
|
|
1099
|
+
for (const namedImport of namedImports) {
|
|
1100
|
+
const alias = namedImport.getAliasNode();
|
|
1101
|
+
if (alias && alias.getText() === decoratorName) return namedImport.getName();
|
|
1102
|
+
if (!alias && namedImport.getName() === decoratorName) return decoratorName;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
return null;
|
|
1107
|
+
}
|
|
1108
|
+
cacheAngularCoreAliases(tsConfig) {
|
|
1109
|
+
this._angularCoreAliasMatchers = [];
|
|
1110
|
+
if (!tsConfig || typeof tsConfig !== "object") return;
|
|
1111
|
+
const configRecord = tsConfig;
|
|
1112
|
+
const paths = (typeof configRecord.compilerOptions === "object" && configRecord.compilerOptions ? configRecord.compilerOptions : void 0)?.paths;
|
|
1113
|
+
if (!paths) return;
|
|
1114
|
+
for (const [alias, targets] of Object.entries(paths)) {
|
|
1115
|
+
if (!Array.isArray(targets)) continue;
|
|
1116
|
+
if (!targets.some((target) => this.isAngularCorePathTarget(target))) continue;
|
|
1117
|
+
this._angularCoreAliasMatchers.push(this.buildAliasMatcher(alias));
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
buildAliasMatcher(alias) {
|
|
1121
|
+
const pattern = alias.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*");
|
|
1122
|
+
return /* @__PURE__ */ new RegExp(`^${pattern}$`);
|
|
1123
|
+
}
|
|
1124
|
+
isAngularCorePathTarget(target) {
|
|
1125
|
+
const normalized = target.replace(/\\/g, "/");
|
|
1126
|
+
return normalized.includes(`${ANGULAR_CORE_MODULE}/`) || normalized.includes(ANGULAR_CORE_MODULE);
|
|
1127
|
+
}
|
|
1128
|
+
isAngularCoreModuleSpecifier(moduleSpecifier) {
|
|
1129
|
+
if (moduleSpecifier === ANGULAR_CORE_MODULE || moduleSpecifier.startsWith(`${ANGULAR_CORE_MODULE}/`)) return true;
|
|
1130
|
+
return this._angularCoreAliasMatchers.some((matcher) => matcher.test(moduleSpecifier));
|
|
1131
|
+
}
|
|
1132
|
+
getAngularCoreImportMap(sourceFile) {
|
|
1133
|
+
const cacheKey = sourceFile.getFilePath();
|
|
1134
|
+
const cached = this._angularCoreImportCache.get(cacheKey);
|
|
1135
|
+
if (cached) return cached;
|
|
1136
|
+
const named = /* @__PURE__ */ new Set();
|
|
1137
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
1138
|
+
const importDeclarations = sourceFile.getImportDeclarations();
|
|
1139
|
+
for (const importDecl of importDeclarations) {
|
|
1140
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1141
|
+
if (!this.isAngularCoreModuleSpecifier(moduleSpecifier)) continue;
|
|
1065
1142
|
const namedImports = importDecl.getNamedImports();
|
|
1066
1143
|
for (const namedImport of namedImports) {
|
|
1067
1144
|
const alias = namedImport.getAliasNode();
|
|
1068
|
-
|
|
1069
|
-
if (!alias && namedImport.getName() === decoratorName) return decoratorName;
|
|
1145
|
+
named.add(alias ? alias.getText() : namedImport.getName());
|
|
1070
1146
|
}
|
|
1147
|
+
const namespaceImport = importDecl.getNamespaceImport();
|
|
1148
|
+
if (namespaceImport) namespaces.add(namespaceImport.getText());
|
|
1071
1149
|
}
|
|
1072
|
-
|
|
1150
|
+
const map = {
|
|
1151
|
+
named,
|
|
1152
|
+
namespaces
|
|
1153
|
+
};
|
|
1154
|
+
this._angularCoreImportCache.set(cacheKey, map);
|
|
1155
|
+
return map;
|
|
1156
|
+
}
|
|
1157
|
+
resolveDependencyOrigin(token, sourceFile) {
|
|
1158
|
+
const importMap = this.getAngularCoreImportMap(sourceFile);
|
|
1159
|
+
const normalizedToken = this.normalizeTokenForOrigin(token);
|
|
1160
|
+
if (normalizedToken.includes(".")) {
|
|
1161
|
+
const namespace = normalizedToken.split(".")[0];
|
|
1162
|
+
if (importMap.namespaces.has(namespace)) return "angular-core";
|
|
1163
|
+
}
|
|
1164
|
+
if (importMap.named.has(normalizedToken)) return "angular-core";
|
|
1165
|
+
}
|
|
1166
|
+
normalizeTokenForOrigin(token) {
|
|
1167
|
+
return token.split("<")[0].replace(/\[\]$/, "");
|
|
1073
1168
|
}
|
|
1074
1169
|
/**
|
|
1075
1170
|
* Determine NodeKind from Angular decorator
|
|
@@ -1408,7 +1503,8 @@ var AngularParser = class {
|
|
|
1408
1503
|
*/
|
|
1409
1504
|
parseConstructorParameter(param) {
|
|
1410
1505
|
const parameterName = param.getName();
|
|
1411
|
-
const
|
|
1506
|
+
const sourceFile = param.getSourceFile();
|
|
1507
|
+
const filePath = sourceFile.getFilePath();
|
|
1412
1508
|
const lineNumber = param.getStartLineNumber();
|
|
1413
1509
|
const columnNumber = param.getStart() - param.getStartLinePos();
|
|
1414
1510
|
const startTime = performance.now();
|
|
@@ -1421,7 +1517,8 @@ var AngularParser = class {
|
|
|
1421
1517
|
if (token) return {
|
|
1422
1518
|
token,
|
|
1423
1519
|
flags,
|
|
1424
|
-
parameterName
|
|
1520
|
+
parameterName,
|
|
1521
|
+
origin: this.resolveDependencyOrigin(token, sourceFile)
|
|
1425
1522
|
};
|
|
1426
1523
|
}
|
|
1427
1524
|
const initializer = param.getInitializer();
|
|
@@ -1432,7 +1529,8 @@ var AngularParser = class {
|
|
|
1432
1529
|
return {
|
|
1433
1530
|
token: injectResult.token,
|
|
1434
1531
|
flags: finalFlags,
|
|
1435
|
-
parameterName
|
|
1532
|
+
parameterName,
|
|
1533
|
+
origin: this.resolveDependencyOrigin(injectResult.token, sourceFile)
|
|
1436
1534
|
};
|
|
1437
1535
|
}
|
|
1438
1536
|
}
|
|
@@ -1442,7 +1540,8 @@ var AngularParser = class {
|
|
|
1442
1540
|
if (token) return {
|
|
1443
1541
|
token,
|
|
1444
1542
|
flags,
|
|
1445
|
-
parameterName
|
|
1543
|
+
parameterName,
|
|
1544
|
+
origin: this.resolveDependencyOrigin(token, sourceFile)
|
|
1446
1545
|
};
|
|
1447
1546
|
}
|
|
1448
1547
|
const type = param.getType();
|
|
@@ -1469,7 +1568,8 @@ var AngularParser = class {
|
|
|
1469
1568
|
if (resolvedToken) return {
|
|
1470
1569
|
token: resolvedToken,
|
|
1471
1570
|
flags,
|
|
1472
|
-
parameterName
|
|
1571
|
+
parameterName,
|
|
1572
|
+
origin: this.resolveDependencyOrigin(resolvedToken, sourceFile)
|
|
1473
1573
|
};
|
|
1474
1574
|
return null;
|
|
1475
1575
|
} finally {
|
|
@@ -1590,7 +1690,8 @@ var AngularParser = class {
|
|
|
1590
1690
|
return {
|
|
1591
1691
|
token,
|
|
1592
1692
|
flags,
|
|
1593
|
-
parameterName: propertyName
|
|
1693
|
+
parameterName: propertyName,
|
|
1694
|
+
origin: this.resolveDependencyOrigin(token, property.getSourceFile())
|
|
1594
1695
|
};
|
|
1595
1696
|
} catch (error) {
|
|
1596
1697
|
if (this._options.verbose) {
|
|
@@ -1719,12 +1820,15 @@ var AngularParser = class {
|
|
|
1719
1820
|
isAngularInjectImported(sourceFile) {
|
|
1720
1821
|
try {
|
|
1721
1822
|
const importDeclarations = sourceFile.getImportDeclarations();
|
|
1722
|
-
for (const importDecl of importDeclarations)
|
|
1723
|
-
const
|
|
1724
|
-
|
|
1725
|
-
const
|
|
1726
|
-
const
|
|
1727
|
-
|
|
1823
|
+
for (const importDecl of importDeclarations) {
|
|
1824
|
+
const moduleSpecifier = importDecl.getModuleSpecifierValue();
|
|
1825
|
+
if (this.isAngularCoreModuleSpecifier(moduleSpecifier)) {
|
|
1826
|
+
const namedImports = importDecl.getNamedImports();
|
|
1827
|
+
for (const namedImport of namedImports) {
|
|
1828
|
+
const importName = namedImport.getName();
|
|
1829
|
+
const alias = namedImport.getAliasNode();
|
|
1830
|
+
if (importName === "inject" || alias && alias.getText() === "inject") return true;
|
|
1831
|
+
}
|
|
1728
1832
|
}
|
|
1729
1833
|
}
|
|
1730
1834
|
return false;
|
|
@@ -1937,7 +2041,11 @@ var JsonFormatter = class {
|
|
|
1937
2041
|
nodeCount: graph.nodes.length,
|
|
1938
2042
|
edgeCount: graph.edges.length
|
|
1939
2043
|
});
|
|
1940
|
-
const
|
|
2044
|
+
const sanitizedGraph = {
|
|
2045
|
+
...graph,
|
|
2046
|
+
nodes: graph.nodes.map(({ origin, ...node }) => node)
|
|
2047
|
+
};
|
|
2048
|
+
const result = JSON.stringify(sanitizedGraph, null, 2);
|
|
1941
2049
|
const elapsed = this._logger?.timeEnd("json-format") ?? 0;
|
|
1942
2050
|
this._logger?.info(LogCategory.PERFORMANCE, "JSON output complete", {
|
|
1943
2051
|
outputSize: result.length,
|
|
@@ -2279,7 +2387,7 @@ function enforceMinimumNodeVersion() {
|
|
|
2279
2387
|
enforceMinimumNodeVersion();
|
|
2280
2388
|
const program = new commander.Command();
|
|
2281
2389
|
program.name("ng-di-graph").description("Angular DI dependency graph CLI tool").version("0.1.0");
|
|
2282
|
-
program.argument("[filePaths...]", "TypeScript files to analyze (alias for --files)").option("-p, --project <path>", "tsconfig.json path (auto-discovered if omitted)").option("--files <paths...>", "specific file paths to analyze (similar to eslint targets)").option("-f, --format <format>", "output format: text | json | mermaid", "text").option("-e, --entry <symbol...>", "starting nodes for sub-graph").option("-d, --direction <dir>", "filtering direction: upstream|downstream|both", "downstream").option("--include-decorators", "include Optional/Self/SkipSelf/Host flags", false).option("--out <file>", "output file (stdout if omitted)").option("-v, --verbose", "show detailed parsing information", false);
|
|
2390
|
+
program.argument("[filePaths...]", "TypeScript files to analyze (alias for --files)").option("-p, --project <path>", "tsconfig.json path (auto-discovered if omitted)").option("--files <paths...>", "specific file paths to analyze (similar to eslint targets)").option("-f, --format <format>", "output format: text | json | mermaid", "text").option("-e, --entry <symbol...>", "starting nodes for sub-graph").option("-d, --direction <dir>", "filtering direction: upstream|downstream|both", "downstream").option("--include-decorators", "include Optional/Self/SkipSelf/Host flags", false).option("--include-angular-core", "include @angular/core nodes", false).option("--out <file>", "output file (stdout if omitted)").option("-v, --verbose", "show detailed parsing information", false);
|
|
2283
2391
|
program.action(async (filePaths = [], options) => {
|
|
2284
2392
|
try {
|
|
2285
2393
|
const mergedFiles = mergeFileTargets(filePaths, options.files);
|
|
@@ -2309,6 +2417,7 @@ program.action(async (filePaths = [], options) => {
|
|
|
2309
2417
|
entry: options.entry,
|
|
2310
2418
|
direction: options.direction,
|
|
2311
2419
|
includeDecorators: options.includeDecorators,
|
|
2420
|
+
includeAngularCore: options.includeAngularCore,
|
|
2312
2421
|
out: options.out,
|
|
2313
2422
|
verbose: options.verbose
|
|
2314
2423
|
};
|
|
@@ -2337,10 +2446,10 @@ program.action(async (filePaths = [], options) => {
|
|
|
2337
2446
|
console.log(`โ
Graph built: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);
|
|
2338
2447
|
if (graph.circularDependencies.length > 0) console.log(`โ ๏ธ Detected ${graph.circularDependencies.length} circular dependencies`);
|
|
2339
2448
|
}
|
|
2340
|
-
if (cliOptions.entry && cliOptions.entry.length > 0) {
|
|
2341
|
-
if (cliOptions.verbose) console.log(`๐ Filtering graph by entry points: ${cliOptions.entry.join(", ")}`);
|
|
2449
|
+
if (cliOptions.entry && cliOptions.entry.length > 0 || !cliOptions.includeAngularCore) {
|
|
2450
|
+
if (cliOptions.verbose && cliOptions.entry && cliOptions.entry.length > 0) console.log(`๐ Filtering graph by entry points: ${cliOptions.entry.join(", ")}`);
|
|
2342
2451
|
graph = filterGraph(graph, cliOptions, logger);
|
|
2343
|
-
if (cliOptions.verbose) console.log(`โ
Filtered graph: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);
|
|
2452
|
+
if (cliOptions.verbose && cliOptions.entry && cliOptions.entry.length > 0) console.log(`โ
Filtered graph: ${graph.nodes.length} nodes, ${graph.edges.length} edges`);
|
|
2344
2453
|
}
|
|
2345
2454
|
let formatter;
|
|
2346
2455
|
if (cliOptions.format === "mermaid") formatter = new MermaidFormatter(logger);
|