0
# Project Graph Operations
1
2
Project dependency graph operations for analyzing relationships between projects, creating dependency graphs, and understanding workspace structure.
3
4
## Capabilities
5
6
### Project Graph Creation and Access
7
8
Create and access the project dependency graph for workspace analysis and task planning.
9
10
```typescript { .api }
11
/**
12
* Create project graph asynchronously with full analysis
13
* @param opts - Options for graph creation
14
* @returns Promise resolving to complete project graph
15
*/
16
function createProjectGraphAsync(
17
opts?: CreateProjectGraphOptions
18
): Promise<ProjectGraph>;
19
20
/**
21
* Read cached project graph (faster but may be stale)
22
* @returns Cached project graph
23
*/
24
function readCachedProjectGraph(): ProjectGraph;
25
26
/**
27
* Extract projects configuration from project graph
28
* @param projectGraph - Project graph to extract from
29
* @returns Projects configuration object
30
*/
31
function readProjectsConfigurationFromProjectGraph(
32
projectGraph: ProjectGraph
33
): ProjectsConfigurations;
34
35
interface CreateProjectGraphOptions {
36
/** Exit process on error */
37
exitOnError?: boolean;
38
/** Reset daemon */
39
resetDaemon?: boolean;
40
/** File changes to consider */
41
fileChanges?: FileChange[];
42
/** Base commit for change detection */
43
base?: string;
44
/** Head commit for change detection */
45
head?: string;
46
}
47
```
48
49
**Usage Examples:**
50
51
```typescript
52
import {
53
createProjectGraphAsync,
54
readCachedProjectGraph,
55
readProjectsConfigurationFromProjectGraph
56
} from "@nx/devkit";
57
58
async function analyzeWorkspace() {
59
// Create fresh project graph
60
const projectGraph = await createProjectGraphAsync();
61
62
// Or use cached version for better performance
63
const cachedGraph = readCachedProjectGraph();
64
65
// Extract projects configuration
66
const projects = readProjectsConfigurationFromProjectGraph(projectGraph);
67
68
console.log(`Workspace has ${Object.keys(projects.projects).length} projects`);
69
70
// Analyze dependencies
71
const appProjects = Object.entries(projects.projects)
72
.filter(([_, config]) => config.projectType === "application")
73
.map(([name]) => name);
74
75
console.log("Applications:", appProjects);
76
}
77
```
78
79
### Dependency Analysis
80
81
Analyze and manipulate project dependencies within the graph.
82
83
```typescript { .api }
84
/**
85
* Reverse the direction of dependencies in a graph
86
* @param graph - Graph to reverse
87
* @returns Graph with reversed dependencies
88
*/
89
function reverse<T>(graph: Graph<T>): Graph<T>;
90
91
/**
92
* Validate a dependency relationship
93
* @param dependency - Dependency to validate
94
* @returns Whether dependency is valid
95
*/
96
function validateDependency(
97
dependency: RawProjectGraphDependency
98
): boolean;
99
100
enum DependencyType {
101
/** Direct code imports/requires */
102
static = "static",
103
/** Runtime/dynamic dependencies */
104
dynamic = "dynamic",
105
/** Configuration-based dependencies */
106
implicit = "implicit"
107
}
108
```
109
110
**Usage Examples:**
111
112
```typescript
113
import {
114
ProjectGraph,
115
reverse,
116
validateDependency,
117
DependencyType
118
} from "@nx/devkit";
119
120
function analyzeDependencies(projectGraph: ProjectGraph) {
121
// Get dependencies for a specific project
122
const myAppDeps = projectGraph.dependencies["my-app"] || [];
123
124
console.log(`my-app has ${myAppDeps.length} dependencies:`);
125
myAppDeps.forEach(dep => {
126
console.log(`- ${dep.target} (${dep.type})`);
127
});
128
129
// Find all projects that depend on a library
130
const dependents: string[] = [];
131
Object.entries(projectGraph.dependencies).forEach(([project, deps]) => {
132
if (deps.some(dep => dep.target === "shared-lib")) {
133
dependents.push(project);
134
}
135
});
136
137
console.log(`Projects depending on shared-lib:`, dependents);
138
139
// Reverse the graph to find dependents easily
140
const reversedGraph = reverse(projectGraph);
141
const sharedLibDependents = reversedGraph.dependencies["shared-lib"] || [];
142
console.log("Reverse dependencies:", sharedLibDependents.map(d => d.target));
143
}
144
```
145
146
### File Mapping
147
148
Create and work with project file mappings for change detection and analysis.
149
150
```typescript { .api }
151
/**
152
* Create project file map from project graph
153
* @param graph - Project graph to create mapping from
154
* @returns Map of projects to their files
155
*/
156
function createProjectFileMapUsingProjectGraph(
157
graph: ProjectGraph
158
): ProjectFileMap;
159
160
/**
161
* Get outputs for a specific target and configuration
162
* @param task - Task to get outputs for
163
* @param node - Project graph node
164
* @returns Array of output paths
165
*/
166
function getOutputsForTargetAndConfiguration(
167
task: Task,
168
node: ProjectGraphProjectNode
169
): string[];
170
```
171
172
**Usage Examples:**
173
174
```typescript
175
import {
176
createProjectFileMapUsingProjectGraph,
177
getOutputsForTargetAndConfiguration,
178
ProjectGraph,
179
Task
180
} from "@nx/devkit";
181
182
function analyzeProjectFiles(projectGraph: ProjectGraph) {
183
// Create file mapping
184
const fileMap = createProjectFileMapUsingProjectGraph(projectGraph);
185
186
// Analyze files for each project
187
Object.entries(fileMap).forEach(([project, files]) => {
188
const tsFiles = files.filter(f => f.file.endsWith('.ts'));
189
console.log(`${project} has ${tsFiles.length} TypeScript files`);
190
});
191
192
// Get outputs for a task
193
const buildTask: Task = {
194
id: "my-app:build",
195
target: { project: "my-app", target: "build" },
196
projectRoot: "apps/my-app",
197
overrides: {}
198
};
199
200
const projectNode = projectGraph.nodes["my-app"];
201
const outputs = getOutputsForTargetAndConfiguration(buildTask, projectNode);
202
console.log("Build outputs:", outputs);
203
}
204
```
205
206
## Project Graph Types
207
208
### Core Graph Structure
209
210
```typescript { .api }
211
/**
212
* Complete project dependency graph
213
*/
214
interface ProjectGraph {
215
/** Map of project names to project nodes */
216
nodes: Record<string, ProjectGraphProjectNode>;
217
/** Map of project names to their dependencies */
218
dependencies: Record<string, ProjectGraphDependency[]>;
219
/** External dependencies (npm packages, etc.) */
220
externalNodes?: Record<string, ProjectGraphExternalNode>;
221
/** Version of the graph format */
222
version?: string;
223
}
224
225
/**
226
* Project node in the dependency graph
227
*/
228
interface ProjectGraphProjectNode {
229
/** Project name */
230
name: string;
231
/** Project type (application or library) */
232
type: ProjectType;
233
/** Project configuration and file data */
234
data: ProjectConfiguration & {
235
files?: ProjectFileMap;
236
root?: string;
237
sourceRoot?: string;
238
};
239
}
240
241
/**
242
* External dependency node (npm packages, etc.)
243
*/
244
interface ProjectGraphExternalNode {
245
/** Node type identifier */
246
type: "npm";
247
/** Package name */
248
name: string;
249
/** Package version */
250
version: string;
251
}
252
253
/**
254
* Dependency relationship between projects
255
*/
256
interface ProjectGraphDependency {
257
/** Source project */
258
source: string;
259
/** Target project or external node */
260
target: string;
261
/** Type of dependency */
262
type: DependencyType;
263
}
264
```
265
266
### Dependency Types
267
268
```typescript { .api }
269
/**
270
* Raw dependency data from analysis
271
*/
272
interface RawProjectGraphDependency {
273
/** Source file path */
274
sourceFile: string;
275
/** Target identifier */
276
target: string;
277
/** Type of dependency */
278
type: DependencyType;
279
}
280
281
/**
282
* Static code dependency (imports/requires)
283
*/
284
interface StaticDependency extends RawProjectGraphDependency {
285
type: DependencyType.static;
286
}
287
288
/**
289
* Dynamic runtime dependency
290
*/
291
interface DynamicDependency extends RawProjectGraphDependency {
292
type: DependencyType.dynamic;
293
}
294
295
/**
296
* Implicit configuration-based dependency
297
*/
298
interface ImplicitDependency extends RawProjectGraphDependency {
299
type: DependencyType.implicit;
300
}
301
```
302
303
### File and Task Types
304
305
```typescript { .api }
306
/**
307
* Map of projects to their files
308
*/
309
type ProjectFileMap = Record<string, FileData[]>;
310
311
/**
312
* Map of all files in workspace
313
*/
314
type FileMap = Record<string, FileData>;
315
316
/**
317
* Information about a file
318
*/
319
interface FileData {
320
/** File path relative to workspace root */
321
file: string;
322
/** File hash for change detection */
323
hash: string;
324
/** Dependencies found in this file */
325
deps?: string[];
326
}
327
328
/**
329
* Task to be executed
330
*/
331
interface Task {
332
/** Unique task identifier */
333
id: string;
334
/** Target specification */
335
target: Target;
336
/** Project root directory */
337
projectRoot?: string;
338
/** Task-specific overrides */
339
overrides: Record<string, any>;
340
/** Hash of task inputs */
341
hash?: string;
342
/** Cache configuration */
343
cache?: boolean;
344
}
345
346
/**
347
* Graph of tasks and their dependencies
348
*/
349
interface TaskGraph {
350
/** All tasks keyed by task ID */
351
tasks: Record<string, Task>;
352
/** Task dependencies */
353
dependencies: Record<string, string[]>;
354
/** Root tasks (no dependencies) */
355
roots: string[];
356
}
357
```
358
359
### Graph JSON Export
360
361
```typescript { .api }
362
/**
363
* JSON representation of project graph for export
364
*/
365
interface GraphJson {
366
/** Graph structure */
367
graph: {
368
nodes: Record<string, {
369
name: string;
370
type: string;
371
data: any;
372
}>;
373
dependencies: Record<string, Array<{
374
source: string;
375
target: string;
376
type: string;
377
}>>;
378
};
379
/** Affected projects */
380
affected?: string[];
381
/** Focus project */
382
focus?: string;
383
/** Include npm dependencies */
384
includeNpmDependencies?: boolean;
385
}
386
```
387
388
## Advanced Graph Operations
389
390
### Custom Graph Analysis
391
392
```typescript
393
import { ProjectGraph, DependencyType } from "@nx/devkit";
394
395
function findCircularDependencies(graph: ProjectGraph): string[][] {
396
const visited = new Set<string>();
397
const recursionStack = new Set<string>();
398
const cycles: string[][] = [];
399
400
function dfs(project: string, path: string[]): void {
401
if (recursionStack.has(project)) {
402
// Found cycle
403
const cycleStart = path.indexOf(project);
404
cycles.push(path.slice(cycleStart).concat(project));
405
return;
406
}
407
408
if (visited.has(project)) return;
409
410
visited.add(project);
411
recursionStack.add(project);
412
413
const dependencies = graph.dependencies[project] || [];
414
for (const dep of dependencies) {
415
if (dep.type === DependencyType.static) {
416
dfs(dep.target, [...path, project]);
417
}
418
}
419
420
recursionStack.delete(project);
421
}
422
423
Object.keys(graph.nodes).forEach(project => {
424
if (!visited.has(project)) {
425
dfs(project, []);
426
}
427
});
428
429
return cycles;
430
}
431
```
432
433
### Dependency Impact Analysis
434
435
```typescript
436
function analyzeImpact(
437
graph: ProjectGraph,
438
changedProject: string
439
): { affected: string[]; impactRadius: number } {
440
const affected = new Set<string>();
441
const queue = [changedProject];
442
let maxDepth = 0;
443
444
while (queue.length > 0) {
445
const current = queue.shift()!;
446
const depth = affected.size;
447
maxDepth = Math.max(maxDepth, depth);
448
449
// Find all projects that depend on current project
450
Object.entries(graph.dependencies).forEach(([project, deps]) => {
451
if (deps.some(dep => dep.target === current) && !affected.has(project)) {
452
affected.add(project);
453
queue.push(project);
454
}
455
});
456
}
457
458
return {
459
affected: Array.from(affected),
460
impactRadius: maxDepth
461
};
462
}
463
```
464
465
### Graph Visualization Data
466
467
```typescript
468
function prepareGraphForVisualization(graph: ProjectGraph): GraphJson {
469
return {
470
graph: {
471
nodes: Object.fromEntries(
472
Object.entries(graph.nodes).map(([name, node]) => [
473
name,
474
{
475
name: node.name,
476
type: node.type,
477
data: {
478
root: node.data.root,
479
sourceRoot: node.data.sourceRoot,
480
targets: Object.keys(node.data.targets || {}),
481
tags: node.data.tags || []
482
}
483
}
484
])
485
),
486
dependencies: Object.fromEntries(
487
Object.entries(graph.dependencies).map(([project, deps]) => [
488
project,
489
deps.map(dep => ({
490
source: project,
491
target: dep.target,
492
type: dep.type
493
}))
494
])
495
)
496
}
497
};
498
}
499
```