or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

executors.mdgenerators.mdindex.mdpackage-management.mdplugin-development.mdproject-graph.mdtree-operations.mdutilities.mdworkspace-configuration.md

project-graph.mddocs/

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

```