or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

architect.mdbuild-context.mdbuilder-creation.mdindex.mdjob-system.mdnode-integration.mdprogress-reporting.mdtesting.md
tile.json

build-context.mddocs/

Build Context

Rich context interface providing builders access to workspace information, target scheduling, and progress reporting. The BuilderContext is passed as the second argument to all builder handler functions and provides essential utilities for interacting with the workspace and reporting progress.

Capabilities

Builder Context Interface

The main context interface passed to builder handlers.

/**
 * Context provided to builder handlers with workspace access and utilities
 */
interface BuilderContext {
  /**
   * Unique identifier for this build context, same as corresponding run ID
   */
  id: number;
  
  /**
   * Builder information and metadata
   */
  builder: BuilderInfo;
  
  /**
   * Logger instance for this builder
   */
  logger: logging.LoggerApi;
  
  /**
   * Absolute workspace root directory path
   */
  workspaceRoot: string;
  
  /**
   * Current directory where builder was invoked
   */
  currentDirectory: string;
  
  /**
   * Target information if builder was scheduled via target
   */
  target?: Target;
  
  /**
   * Schedule another target within the same workspace
   * @param target - Target to schedule
   * @param overrides - Options to override workspace configuration
   * @param scheduleOptions - Additional scheduling options
   * @returns Promise resolving to BuilderRun
   */
  scheduleTarget(
    target: Target,
    overrides?: json.JsonObject,
    scheduleOptions?: ScheduleOptions
  ): Promise<BuilderRun>;
  
  /**
   * Schedule a builder by name
   * @param builderName - Builder name in format "package:builder"
   * @param options - Options to pass to builder
   * @param scheduleOptions - Additional scheduling options
   * @returns Promise resolving to BuilderRun
   */
  scheduleBuilder(
    builderName: string,
    options?: json.JsonObject,
    scheduleOptions?: ScheduleOptions
  ): Promise<BuilderRun>;
  
  /**
   * Get options for a target from workspace configuration
   * @param target - Target to get options for
   * @returns Promise resolving to target options
   */
  getTargetOptions(target: Target): Promise<json.JsonObject>;
  
  /**
   * Get project metadata from workspace
   * @param projectName - Name of project or target with project
   * @returns Promise resolving to project metadata
   */
  getProjectMetadata(projectName: string): Promise<json.JsonObject>;
  getProjectMetadata(target: Target): Promise<json.JsonObject>;
  
  /**
   * Get builder name for a target
   * @param target - Target to resolve builder name for
   * @returns Promise resolving to builder name
   */
  getBuilderNameForTarget(target: Target): Promise<string>;
  
  /**
   * Validate options against a builder schema
   * @param options - Options to validate
   * @param builderName - Builder name to validate against
   * @returns Promise resolving to validated options
   */
  validateOptions<T extends json.JsonObject = json.JsonObject>(
    options: json.JsonObject,
    builderName: string
  ): Promise<T>;
  
  /**
   * Report that the builder is now running
   */
  reportRunning(): void;
  
  /**
   * Update the status message displayed to users
   * @param status - Status message to display
   */
  reportStatus(status: string): void;
  
  /**
   * Report progress with current and total values
   * @param current - Current progress value
   * @param total - Total progress value (optional)
   * @param status - Status message (optional)
   */
  reportProgress(current: number, total?: number, status?: string): void;
  
  /**
   * Add cleanup logic to run when context is being stopped
   * @param teardown - Function to run during cleanup
   */
  addTeardown(teardown: () => Promise<void> | void): void;
}

Usage Examples:

import { createBuilder } from "@angular-devkit/architect";

export default createBuilder((options, context) => {
  // Access workspace information
  console.log(`Workspace root: ${context.workspaceRoot}`);
  console.log(`Current directory: ${context.currentDirectory}`);
  console.log(`Builder: ${context.builder.builderName}`);
  
  // Use logger
  context.logger.info("Starting build process");
  context.logger.warn("This is a warning");
  
  // Report progress
  context.reportStatus("Initializing...");
  context.reportProgress(0, 100, "Starting");
  
  // Perform work...
  context.reportProgress(50, 100, "Halfway done");
  
  return { success: true };
});

Target Scheduling

Schedule other targets or builders from within a builder.

// Schedule dependent targets
export const complexBuilder = createBuilder(async (options, context) => {
  // Build dependencies first
  const libBuild = await context.scheduleTarget(
    { project: "my-lib", target: "build" }
  );
  
  const libResult = await libBuild.result;
  if (!libResult.success) {
    return { success: false, error: "Library build failed" };
  }
  
  // Now build the main project
  const appBuild = await context.scheduleTarget(
    { project: "my-app", target: "build" },
    { buildOptimizer: true } // override options
  );
  
  return appBuild.result;
});

// Schedule builder by name
export const builderSchedulingExample = createBuilder(async (options, context) => {
  // Run linting first
  const lintRun = await context.scheduleBuilder(
    "@angular-eslint/builder:lint",
    { lintFilePatterns: ["src/**/*.ts"] }
  );
  
  const lintResult = await lintRun.result;
  if (!lintResult.success) {
    context.logger.warn("Linting failed, continuing anyway");
  }
  
  // Continue with main build logic
  return { success: true };
});

Workspace Integration

Access workspace configuration and project metadata.

export const workspaceAwareBuilder = createBuilder(async (options, context) => {
  if (!context.target) {
    return { success: false, error: "Target required" };
  }
  
  // Get target options
  const targetOptions = await context.getTargetOptions(context.target);
  context.logger.info(`Target options: ${JSON.stringify(targetOptions)}`);
  
  // Get project metadata
  const projectMeta = await context.getProjectMetadata(context.target.project);
  const projectType = projectMeta.projectType;
  
  if (projectType === "application") {
    // Application-specific logic
    context.logger.info("Building application");
  } else if (projectType === "library") {
    // Library-specific logic
    context.logger.info("Building library");
  }
  
  // Get builder name for another target
  const testTarget = { ...context.target, target: "test" };
  try {
    const testBuilderName = await context.getBuilderNameForTarget(testTarget);
    context.logger.info(`Test builder: ${testBuilderName}`);
  } catch (error) {
    context.logger.info("No test target configured");
  }
  
  return { success: true };
});

Progress Reporting

Provide detailed progress updates during build execution.

export const progressReportingBuilder = createBuilder(async (options, context) => {
  const tasks = [
    "Analyzing dependencies",
    "Compiling TypeScript", 
    "Optimizing bundle",
    "Generating assets",
    "Writing output"
  ];
  
  context.reportRunning(); // Mark as running
  
  for (let i = 0; i < tasks.length; i++) {
    const task = tasks[i];
    context.reportStatus(task);
    context.reportProgress(i, tasks.length, task);
    
    // Simulate work
    await new Promise(resolve => setTimeout(resolve, 1000));
    
    context.logger.info(`Completed: ${task}`);
  }
  
  context.reportProgress(tasks.length, tasks.length, "Complete");
  
  return { success: true };
});

Option Validation

Validate options against builder schemas.

interface MyBuilderOptions {
  outputPath: string;
  optimization: boolean;
  target: "es5" | "es2015" | "es2017";
}

export const validatingBuilder = createBuilder(async (options, context) => {
  // Validate options against another builder's schema
  try {
    const validatedOptions = await context.validateOptions(
      { outputPath: "./dist", optimization: true },
      "@angular-devkit/build-angular:browser"
    );
    context.logger.info("Options are valid");
  } catch (error) {
    return { success: false, error: `Invalid options: ${error.message}` };
  }
  
  // Use typed options
  const typedOptions = options as MyBuilderOptions;
  context.logger.info(`Building to ${typedOptions.outputPath}`);
  
  return { success: true };
});

Cleanup and Teardown

Register cleanup logic that runs when the builder is stopped.

export const cleanupBuilder = createBuilder(async (options, context) => {
  const tempFiles: string[] = [];
  
  // Register cleanup
  context.addTeardown(async () => {
    context.logger.info("Cleaning up temporary files");
    for (const file of tempFiles) {
      await fs.unlink(file).catch(() => {}); // Ignore errors
    }
  });
  
  // Create temporary files during build
  const tempFile = `/tmp/build-${context.id}.tmp`;
  await fs.writeFile(tempFile, "temporary data");
  tempFiles.push(tempFile);
  
  // Register additional cleanup
  context.addTeardown(() => {
    context.logger.info("Final cleanup completed");
  });
  
  return { success: true };
});

Logger Integration

Use the provided logger for consistent output formatting.

export const loggingBuilder = createBuilder((options, context) => {
  const logger = context.logger;
  
  // Different log levels
  logger.debug("Debug information");
  logger.info("General information");  
  logger.warn("Warning message");
  logger.error("Error message");
  logger.fatal("Fatal error");
  
  // Create child loggers
  const childLogger = logger.createChild("sub-task");
  childLogger.info("Child logger message");
  
  // Log structured data
  logger.info("Build configuration", {
    outputPath: "./dist",
    optimization: true,
    sourceMap: false
  });
  
  return { success: true };
});