CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nx--devkit

The Nx Devkit provides utilities for creating custom generators, executors, and plugins to extend the Nx build system for different technologies and use cases.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

project-graph.mddocs/

Project Graph Operations

Project dependency graph operations for analyzing relationships between projects, creating dependency graphs, and understanding workspace structure.

Capabilities

Project Graph Creation and Access

Create and access the project dependency graph for workspace analysis and task planning.

/**
 * Create project graph asynchronously with full analysis
 * @param opts - Options for graph creation
 * @returns Promise resolving to complete project graph
 */
function createProjectGraphAsync(
  opts?: CreateProjectGraphOptions
): Promise<ProjectGraph>;

/**
 * Read cached project graph (faster but may be stale)
 * @returns Cached project graph
 */
function readCachedProjectGraph(): ProjectGraph;

/**
 * Extract projects configuration from project graph
 * @param projectGraph - Project graph to extract from
 * @returns Projects configuration object
 */
function readProjectsConfigurationFromProjectGraph(
  projectGraph: ProjectGraph
): ProjectsConfigurations;

interface CreateProjectGraphOptions {
  /** Exit process on error */
  exitOnError?: boolean;
  /** Reset daemon */
  resetDaemon?: boolean;
  /** File changes to consider */
  fileChanges?: FileChange[];
  /** Base commit for change detection */
  base?: string;
  /** Head commit for change detection */
  head?: string;
}

Usage Examples:

import {
  createProjectGraphAsync,
  readCachedProjectGraph,
  readProjectsConfigurationFromProjectGraph
} from "@nx/devkit";

async function analyzeWorkspace() {
  // Create fresh project graph
  const projectGraph = await createProjectGraphAsync();
  
  // Or use cached version for better performance
  const cachedGraph = readCachedProjectGraph();
  
  // Extract projects configuration
  const projects = readProjectsConfigurationFromProjectGraph(projectGraph);
  
  console.log(`Workspace has ${Object.keys(projects.projects).length} projects`);
  
  // Analyze dependencies
  const appProjects = Object.entries(projects.projects)
    .filter(([_, config]) => config.projectType === "application")
    .map(([name]) => name);
    
  console.log("Applications:", appProjects);
}

Dependency Analysis

Analyze and manipulate project dependencies within the graph.

/**
 * Reverse the direction of dependencies in a graph
 * @param graph - Graph to reverse
 * @returns Graph with reversed dependencies
 */
function reverse<T>(graph: Graph<T>): Graph<T>;

/**
 * Validate a dependency relationship
 * @param dependency - Dependency to validate
 * @returns Whether dependency is valid
 */
function validateDependency(
  dependency: RawProjectGraphDependency
): boolean;

enum DependencyType {
  /** Direct code imports/requires */
  static = "static",
  /** Runtime/dynamic dependencies */
  dynamic = "dynamic", 
  /** Configuration-based dependencies */
  implicit = "implicit"
}

Usage Examples:

import {
  ProjectGraph,
  reverse,
  validateDependency,
  DependencyType
} from "@nx/devkit";

function analyzeDependencies(projectGraph: ProjectGraph) {
  // Get dependencies for a specific project
  const myAppDeps = projectGraph.dependencies["my-app"] || [];
  
  console.log(`my-app has ${myAppDeps.length} dependencies:`);
  myAppDeps.forEach(dep => {
    console.log(`- ${dep.target} (${dep.type})`);
  });
  
  // Find all projects that depend on a library
  const dependents: string[] = [];
  Object.entries(projectGraph.dependencies).forEach(([project, deps]) => {
    if (deps.some(dep => dep.target === "shared-lib")) {
      dependents.push(project);
    }
  });
  
  console.log(`Projects depending on shared-lib:`, dependents);
  
  // Reverse the graph to find dependents easily
  const reversedGraph = reverse(projectGraph);
  const sharedLibDependents = reversedGraph.dependencies["shared-lib"] || [];
  console.log("Reverse dependencies:", sharedLibDependents.map(d => d.target));
}

File Mapping

Create and work with project file mappings for change detection and analysis.

/**
 * Create project file map from project graph
 * @param graph - Project graph to create mapping from
 * @returns Map of projects to their files
 */
function createProjectFileMapUsingProjectGraph(
  graph: ProjectGraph
): ProjectFileMap;

/**
 * Get outputs for a specific target and configuration
 * @param task - Task to get outputs for
 * @param node - Project graph node
 * @returns Array of output paths
 */
function getOutputsForTargetAndConfiguration(
  task: Task,
  node: ProjectGraphProjectNode
): string[];

Usage Examples:

import {
  createProjectFileMapUsingProjectGraph,
  getOutputsForTargetAndConfiguration,
  ProjectGraph,
  Task
} from "@nx/devkit";

function analyzeProjectFiles(projectGraph: ProjectGraph) {
  // Create file mapping
  const fileMap = createProjectFileMapUsingProjectGraph(projectGraph);
  
  // Analyze files for each project
  Object.entries(fileMap).forEach(([project, files]) => {
    const tsFiles = files.filter(f => f.file.endsWith('.ts'));
    console.log(`${project} has ${tsFiles.length} TypeScript files`);
  });
  
  // Get outputs for a task
  const buildTask: Task = {
    id: "my-app:build",
    target: { project: "my-app", target: "build" },
    projectRoot: "apps/my-app",
    overrides: {}
  };
  
  const projectNode = projectGraph.nodes["my-app"];
  const outputs = getOutputsForTargetAndConfiguration(buildTask, projectNode);
  console.log("Build outputs:", outputs);
}

Project Graph Types

Core Graph Structure

/**
 * Complete project dependency graph
 */
interface ProjectGraph {
  /** Map of project names to project nodes */
  nodes: Record<string, ProjectGraphProjectNode>;
  /** Map of project names to their dependencies */
  dependencies: Record<string, ProjectGraphDependency[]>;
  /** External dependencies (npm packages, etc.) */
  externalNodes?: Record<string, ProjectGraphExternalNode>;
  /** Version of the graph format */
  version?: string;
}

/**
 * Project node in the dependency graph
 */
interface ProjectGraphProjectNode {
  /** Project name */
  name: string;
  /** Project type (application or library) */
  type: ProjectType;
  /** Project configuration and file data */
  data: ProjectConfiguration & { 
    files?: ProjectFileMap;
    root?: string;
    sourceRoot?: string;
  };
}

/**
 * External dependency node (npm packages, etc.)
 */
interface ProjectGraphExternalNode {
  /** Node type identifier */
  type: "npm";
  /** Package name */
  name: string;
  /** Package version */
  version: string;
}

/**
 * Dependency relationship between projects
 */
interface ProjectGraphDependency {
  /** Source project */
  source: string;
  /** Target project or external node */
  target: string;
  /** Type of dependency */
  type: DependencyType;
}

Dependency Types

/**
 * Raw dependency data from analysis
 */
interface RawProjectGraphDependency {
  /** Source file path */
  sourceFile: string;
  /** Target identifier */
  target: string;
  /** Type of dependency */
  type: DependencyType;
}

/**
 * Static code dependency (imports/requires)
 */
interface StaticDependency extends RawProjectGraphDependency {
  type: DependencyType.static;
}

/**
 * Dynamic runtime dependency
 */
interface DynamicDependency extends RawProjectGraphDependency {
  type: DependencyType.dynamic;
}

/**
 * Implicit configuration-based dependency
 */
interface ImplicitDependency extends RawProjectGraphDependency {
  type: DependencyType.implicit;
}

File and Task Types

/**
 * Map of projects to their files
 */
type ProjectFileMap = Record<string, FileData[]>;

/**
 * Map of all files in workspace
 */
type FileMap = Record<string, FileData>;

/**
 * Information about a file
 */
interface FileData {
  /** File path relative to workspace root */
  file: string;
  /** File hash for change detection */
  hash: string;
  /** Dependencies found in this file */
  deps?: string[];
}

/**
 * Task to be executed
 */
interface Task {
  /** Unique task identifier */
  id: string;
  /** Target specification */
  target: Target;
  /** Project root directory */
  projectRoot?: string;
  /** Task-specific overrides */
  overrides: Record<string, any>;
  /** Hash of task inputs */
  hash?: string;
  /** Cache configuration */
  cache?: boolean;
}

/**
 * Graph of tasks and their dependencies
 */
interface TaskGraph {
  /** All tasks keyed by task ID */
  tasks: Record<string, Task>;
  /** Task dependencies */
  dependencies: Record<string, string[]>;
  /** Root tasks (no dependencies) */
  roots: string[];
}

Graph JSON Export

/**
 * JSON representation of project graph for export
 */
interface GraphJson {
  /** Graph structure */
  graph: {
    nodes: Record<string, {
      name: string;
      type: string;
      data: any;
    }>;
    dependencies: Record<string, Array<{
      source: string;
      target: string;
      type: string;
    }>>;
  };
  /** Affected projects */
  affected?: string[];
  /** Focus project */
  focus?: string;
  /** Include npm dependencies */
  includeNpmDependencies?: boolean;
}

Advanced Graph Operations

Custom Graph Analysis

import { ProjectGraph, DependencyType } from "@nx/devkit";

function findCircularDependencies(graph: ProjectGraph): string[][] {
  const visited = new Set<string>();
  const recursionStack = new Set<string>();
  const cycles: string[][] = [];
  
  function dfs(project: string, path: string[]): void {
    if (recursionStack.has(project)) {
      // Found cycle
      const cycleStart = path.indexOf(project);
      cycles.push(path.slice(cycleStart).concat(project));
      return;
    }
    
    if (visited.has(project)) return;
    
    visited.add(project);
    recursionStack.add(project);
    
    const dependencies = graph.dependencies[project] || [];
    for (const dep of dependencies) {
      if (dep.type === DependencyType.static) {
        dfs(dep.target, [...path, project]);
      }
    }
    
    recursionStack.delete(project);
  }
  
  Object.keys(graph.nodes).forEach(project => {
    if (!visited.has(project)) {
      dfs(project, []);
    }
  });
  
  return cycles;
}

Dependency Impact Analysis

function analyzeImpact(
  graph: ProjectGraph,
  changedProject: string
): { affected: string[]; impactRadius: number } {
  const affected = new Set<string>();
  const queue = [changedProject];
  let maxDepth = 0;
  
  while (queue.length > 0) {
    const current = queue.shift()!;
    const depth = affected.size;
    maxDepth = Math.max(maxDepth, depth);
    
    // Find all projects that depend on current project
    Object.entries(graph.dependencies).forEach(([project, deps]) => {
      if (deps.some(dep => dep.target === current) && !affected.has(project)) {
        affected.add(project);
        queue.push(project);
      }
    });
  }
  
  return {
    affected: Array.from(affected),
    impactRadius: maxDepth
  };
}

Graph Visualization Data

function prepareGraphForVisualization(graph: ProjectGraph): GraphJson {
  return {
    graph: {
      nodes: Object.fromEntries(
        Object.entries(graph.nodes).map(([name, node]) => [
          name,
          {
            name: node.name,
            type: node.type,
            data: {
              root: node.data.root,
              sourceRoot: node.data.sourceRoot,
              targets: Object.keys(node.data.targets || {}),
              tags: node.data.tags || []
            }
          }
        ])
      ),
      dependencies: Object.fromEntries(
        Object.entries(graph.dependencies).map(([project, deps]) => [
          project,
          deps.map(dep => ({
            source: project,
            target: dep.target,
            type: dep.type
          }))
        ])
      )
    }
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-nx--devkit

docs

executors.md

generators.md

index.md

package-management.md

plugin-development.md

project-graph.md

tree-operations.md

utilities.md

workspace-configuration.md

tile.json