CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nx

AI-first build platform designed for monorepo development that provides task orchestration, project graph generation, and CLI tools for managing complex software projects

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

devkit-tasks.mddocs/

Task System

Task execution, caching, and orchestration APIs for building advanced build tools and custom task runners.

Capabilities

Task Execution

Functions for running executors and tasks programmatically.

/**
 * Runs an executor programmatically
 * @param targetDescription - Target to execute (project:target:configuration)
 * @param overrides - Options to override target configuration
 * @param context - Execution context
 * @returns Async iterator yielding execution results
 */
function runExecutor<T = any>(
  targetDescription: Target,
  overrides: Partial<T>,
  context: ExecutorContext
): Promise<AsyncIterableIterator<{ success: boolean; [key: string]: any }>>;

/**
 * Parses a target string into its components
 * @param targetString - Target in format "project:target" or "project:target:configuration"
 * @returns Parsed target object
 */
function parseTargetString(targetString: string): Target;

/**
 * Converts target object to string representation
 * @param target - Target object
 * @returns Target string in format "project:target:configuration"
 */
function targetToTargetString(target: Target): string;

interface Target {
  project: string;
  target: string;
  configuration?: string;
}

interface ExecutorContext {
  /** Workspace root directory */
  root: string;
  /** Current working directory */
  cwd: string;
  /** Workspace configuration */
  workspace: WorkspaceJsonConfiguration;
  /** Whether verbose logging is enabled */
  isVerbose: boolean;
  /** Name of the project being executed */
  projectName?: string;
  /** Name of the target being executed */
  targetName?: string;
  /** Name of the configuration being used */
  configurationName?: string;
  /** Graph of all projects */
  projectGraph?: ProjectGraph;
  /** Task graph for current execution */
  taskGraph?: TaskGraph;
  /** Hash of the current task */
  hash?: string;
}

Task Graph Operations

Functions for working with task dependency graphs and task orchestration.

/**
 * Creates a task graph from target dependencies
 * @param projectGraph - Project dependency graph
 * @param targets - Array of targets to execute
 * @param nxJson - Nx configuration
 * @param overrides - Task overrides
 * @returns Task graph with dependencies
 */
function createTaskGraph(
  projectGraph: ProjectGraph,
  targets: Target[],
  nxJson: NxJsonConfiguration,
  overrides: { [target: string]: any }
): TaskGraph;

/**
 * Maps targets to projects for task graph creation
 * @param targets - Array of targets
 * @param projectGraph - Project dependency graph
 * @returns Map of project names to target arrays
 */
function groupTargetsByProject(
  targets: Target[],
  projectGraph: ProjectGraph
): Map<string, Target[]>;

interface TaskGraph {
  /** Map of task IDs to task definitions */
  tasks: Record<string, Task>;
  /** Map of task IDs to their dependency task IDs */
  dependencies: Record<string, string[]>;
  /** Array of root task IDs (tasks with no dependencies) */
  roots: string[];
}

interface Task {
  /** Unique task identifier */
  id: string;
  /** Target being executed */
  target: Target;
  /** Project root path */
  projectRoot: string;
  /** Override options for this task */
  overrides: any;
  /** Hash representing task inputs */
  hash?: string;
  /** Array of output paths for this task */
  outputs?: string[];
}

Task Hashing and Caching

APIs for computing task hashes and managing task caching.

/**
 * Task hasher for computing deterministic task hashes
 */
interface TaskHasher {
  /** Hashes a single task */
  hashTask(task: Task): Promise<string>;
  /** Hashes multiple tasks in batch */
  hashTasks(tasks: Task[]): Promise<Record<string, string>>;
}

/**
 * Creates a task hasher instance
 * @param projectGraph - Project dependency graph
 * @param nxJson - Nx configuration
 * @returns Task hasher instance
 */
function createTaskHasher(
  projectGraph: ProjectGraph,
  nxJson: NxJsonConfiguration
): TaskHasher;

/**
 * Hash computation utilities
 */
function hashArray(values: string[]): string;
function hashObject(obj: any): string;

interface FileData {
  file: string;
  hash: string;
}

/**
 * Computes hash for workspace files
 * @param workspaceRoot - Workspace root directory
 * @param globs - Array of glob patterns to include
 * @returns Promise resolving to file data with hashes
 */
function hashFiles(workspaceRoot: string, globs: string[]): Promise<FileData[]>;

Task Runner Integration

Types and utilities for integrating with Nx task runners.

/**
 * Task runner interface for custom task execution
 */
interface TasksRunner {
  invoke(
    tasks: Task[],
    options: any,
    context?: TasksRunnerContext
  ): Promise<{ [taskId: string]: TaskResult }>;
}

interface TasksRunnerContext {
  target?: string;
  initiatingProject?: string;
  projectGraph: ProjectGraph;
  nxJson: NxJsonConfiguration;
  hasher: TaskHasher;
}

interface TaskResult {
  success: boolean;
  code?: number;
  terminalOutput?: string;
  startTime?: number;
  endTime?: number;
}

/**
 * Default task runner options
 */
interface DefaultTasksRunnerOptions {
  parallel?: number;
  maxParallel?: number;
  cacheableOperations?: string[];
  runtimeCacheInputs?: string[];
  cacheDirectory?: string;
  remoteCache?: RemoteCacheOptions;
}

interface RemoteCacheOptions {
  enabled?: boolean;
  url?: string;
  timeout?: number;
}

Executor Development

Types and utilities for creating custom executors.

/**
 * Executor function signature
 */
type Executor<T = any> = (
  options: T,
  context: ExecutorContext
) => Promise<{ success: boolean; [key: string]: any }> | 
   AsyncIterableIterator<{ success: boolean; [key: string]: any }>;

/**
 * Promise-based executor signature  
 */
type PromiseExecutor<T = any> = (
  options: T,
  context: ExecutorContext
) => Promise<{ success: boolean; [key: string]: any }>;

/**
 * Creates an executor that converts async iterators to promises
 * @param executor - Async iterator executor
 * @returns Promise-based executor
 */
function convertToPromiseExecutor<T>(
  executor: Executor<T>
): PromiseExecutor<T>;

/**
 * Schema validation for executor options
 */
interface ExecutorSchema {
  version: number;
  title: string;
  description?: string;
  type: 'object';
  properties: Record<string, any>;
  additionalProperties?: boolean;
  required?: string[];
}

Usage Examples

Running Tasks Programmatically

import { 
  runExecutor, 
  parseTargetString,
  ExecutorContext,
  logger 
} from "nx/src/devkit-exports";

async function buildProject(projectName: string) {
  const target = parseTargetString(`${projectName}:build`);
  
  const context: ExecutorContext = {
    root: process.cwd(),
    cwd: process.cwd(),
    workspace: readWorkspaceConfiguration(),
    isVerbose: false,
    projectName: target.project,
    targetName: target.target
  };
  
  try {
    const results = await runExecutor(target, {}, context);
    
    for await (const result of results) {
      if (result.success) {
        logger.info(`✅ ${projectName}:build completed successfully`);
      } else {
        logger.error(`❌ ${projectName}:build failed`);
        return false;
      }
    }
    
    return true;
  } catch (error) {
    logger.error(`Failed to run ${projectName}:build: ${error.message}`);
    return false;
  }
}

Custom Task Runner

import { 
  Task, 
  TasksRunner, 
  TasksRunnerContext,
  TaskResult,
  logger 
} from "nx/src/devkit-exports";

class CustomTasksRunner implements TasksRunner {
  async invoke(
    tasks: Task[],
    options: any,
    context?: TasksRunnerContext
  ): Promise<{ [taskId: string]: TaskResult }> {
    const results: { [taskId: string]: TaskResult } = {};
    
    logger.info(`Executing ${tasks.length} tasks`);
    
    for (const task of tasks) {
      const startTime = Date.now();
      
      try {
        // Execute task using runExecutor
        const target = task.target;
        const executorContext: ExecutorContext = {
          root: context.projectGraph.nodes[target.project].data.root,
          cwd: process.cwd(),
          workspace: {} as any, // Would normally load workspace config
          isVerbose: false,
          projectName: target.project,
          targetName: target.target,
          configurationName: target.configuration,
          projectGraph: context.projectGraph,
          hash: task.hash
        };
        
        const executorResults = await runExecutor(target, task.overrides, executorContext);
        let success = true;
        
        for await (const result of executorResults) {
          success = success && result.success;
        }
        
        results[task.id] = {
          success,
          startTime,
          endTime: Date.now()
        };
        
      } catch (error) {
        results[task.id] = {
          success: false,
          startTime,
          endTime: Date.now(),
          terminalOutput: error.message
        };
      }
    }
    
    return results;
  }
}

export default CustomTasksRunner;

Task Graph Analysis

import { 
  createTaskGraph,
  parseTargetString,
  createProjectGraphAsync,
  readNxJson,
  logger 
} from "nx/src/devkit-exports";

async function analyzeTaskDependencies(targetStrings: string[]) {
  const projectGraph = await createProjectGraphAsync();
  const nxJson = readNxJson();
  
  // Parse target strings
  const targets = targetStrings.map(parseTargetString);
  
  // Create task graph
  const taskGraph = createTaskGraph(projectGraph, targets, nxJson, {});
  
  logger.info(`Task Graph Analysis:`);
  logger.info(`  Total tasks: ${Object.keys(taskGraph.tasks).length}`);
  logger.info(`  Root tasks: ${taskGraph.roots.length}`);
  
  // Find task dependencies
  for (const [taskId, task] of Object.entries(taskGraph.tasks)) {
    const dependencies = taskGraph.dependencies[taskId] || [];
    logger.info(`  ${task.target.project}:${task.target.target} depends on: ${dependencies.length} tasks`);
  }
  
  return taskGraph;
}

Custom Executor Implementation

import { ExecutorContext, logger } from "nx/src/devkit-exports";

interface CustomBuildOptions {
  command: string;
  args?: string[];
  cwd?: string;
  env?: Record<string, string>;
}

export default async function customBuildExecutor(
  options: CustomBuildOptions,
  context: ExecutorContext
): Promise<{ success: boolean }> {
  logger.info(`Running custom build for ${context.projectName}`);
  
  const { spawn } = await import('child_process');
  
  return new Promise((resolve) => {
    const child = spawn(options.command, options.args || [], {
      cwd: options.cwd || context.root,
      env: { ...process.env, ...options.env },
      stdio: 'inherit'
    });
    
    child.on('close', (code) => {
      const success = code === 0;
      
      if (success) {
        logger.info(`✅ Custom build completed successfully`);
      } else {
        logger.error(`❌ Custom build failed with code ${code}`);
      }
      
      resolve({ success });
    });
    
    child.on('error', (error) => {
      logger.error(`Failed to start custom build: ${error.message}`);
      resolve({ success: false });
    });
  });
}

// Executor schema (schema.json)
export const schema: ExecutorSchema = {
  version: 1,
  title: 'Custom Build Executor',
  description: 'Runs a custom build command',
  type: 'object',
  properties: {
    command: {
      type: 'string',
      description: 'Command to execute'
    },
    args: {
      type: 'array',
      description: 'Command arguments',
      items: { type: 'string' }
    },
    cwd: {
      type: 'string',
      description: 'Working directory'
    },
    env: {
      type: 'object',
      description: 'Environment variables'
    }
  },
  required: ['command']
};

Task Caching

import { 
  createTaskHasher,
  createProjectGraphAsync,
  readNxJson,
  Task,
  logger 
} from "nx/src/devkit-exports";

async function computeTaskHashes(tasks: Task[]) {
  const projectGraph = await createProjectGraphAsync();
  const nxJson = readNxJson();
  
  const hasher = createTaskHasher(projectGraph, nxJson);
  
  logger.info('Computing task hashes...');
  
  const hashes = await hasher.hashTasks(tasks);
  
  for (const [taskId, hash] of Object.entries(hashes)) {
    const task = tasks.find(t => t.id === taskId);
    logger.info(`${task.target.project}:${task.target.target} -> ${hash}`);
  }
  
  return hashes;
}

Install with Tessl CLI

npx tessl i tessl/npm-nx

docs

cli.md

devkit-core.md

devkit-files.md

devkit-tasks.md

generators-executors.md

index.md

plugins.md

tile.json