or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

file-system-actions.mdindex.mdnode-tools.mdoutput-sinks.mdrule-system.mdschematic-engine.mdtemplate-engine.mdtree-operations.mdworkflow.md
tile.json

tree-operations.mddocs/

Tree Operations

Core virtual file system operations for reading, writing, and manipulating files and directories in a staging environment. Trees represent the complete state of a file system without side effects until committed through a sink.

Capabilities

Tree Interface

The main interface for all file system operations within schematics.

/**
 * Virtual file system tree interface for staging file operations
 */
interface Tree {
  readonly root: DirEntry;
  readonly actions: Action[];
  
  // Structural operations
  branch(): Tree;
  merge(other: Tree, strategy?: MergeStrategy): void;
  
  // Read operations
  read(path: string): Buffer | null;
  readText(path: string): string;
  readJson(path: string): JsonValue;
  exists(path: string): boolean;
  get(path: string): FileEntry | null;
  getDir(path: string): DirEntry;
  visit(visitor: FileVisitor): void;
  
  // Write operations  
  create(path: string, content: Buffer | string): void;
  overwrite(path: string, content: Buffer | string): void;
  delete(path: string): void;
  rename(from: string, to: string): void;
  
  // Advanced operations
  beginUpdate(path: string): UpdateRecorder;
  commitUpdate(record: UpdateRecorder): void;
  apply(action: Action, strategy?: MergeStrategy): void;
}

Usage Examples:

import { Tree } from "@angular-devkit/schematics";

function myRule(tree: Tree): Tree {
  // Check if file exists
  if (tree.exists('/package.json')) {
    // Read and parse JSON
    const packageJson = tree.readJson('/package.json') as any;
    
    // Modify the content
    packageJson.scripts = {
      ...packageJson.scripts,
      'build': 'ng build'
    };
    
    // Write back to tree
    tree.overwrite('/package.json', JSON.stringify(packageJson, null, 2));
  } else {
    // Create new file
    tree.create('/package.json', JSON.stringify({
      name: 'my-app',
      version: '1.0.0'
    }, null, 2));
  }
  
  return tree;
}

Tree Constructor

Static methods for creating and manipulating trees.

interface TreeConstructor {
  /** Create an empty tree */
  empty(): Tree;
  /** Create a branch (copy) of an existing tree */
  branch(tree: Tree): Tree;
  /** Merge two trees using specified strategy */
  merge(tree: Tree, other: Tree, strategy?: MergeStrategy): Tree;
  /** Partition tree into two based on predicate */
  partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree, Tree];
  /** Optimize tree operations (identity function) */
  optimize(tree: Tree): Tree;
}

const Tree: TreeConstructor;

Tree Implementation Classes

Specialized tree implementations for different use cases.

/**
 * Tree implementation backed by a virtual file system host
 */
class HostTree implements Tree {
  constructor(host?: virtualFs.Host);
  readonly backend: virtualFs.ReadonlyHost;
}

/**
 * Tree that delegates operations to another tree
 */
abstract class DelegateTree implements Tree {
  constructor(tree: Tree);
  readonly delegate: Tree;
}

/**
 * Empty tree implementation for testing
 */
class EmptyTree implements Tree {
  static readonly instance: EmptyTree;
}

/**
 * Tree that filters files based on a predicate
 */
class FilterHostTree extends HostTree {
  constructor(tree: HostTree, filter: FilePredicate<boolean>);
}

/**
 * Tree scoped to a specific directory
 */
class ScopedTree extends DelegateTree {
  constructor(tree: Tree, scope: string);
}

/**
 * Null tree implementation (no operations)
 */
class NullTree implements Tree {
  // Minimal implementation for testing
}

Tree Utility Functions

/**
 * Check if an object is a Tree instance
 */
function isTree(maybeTree: any): maybeTree is Tree;

/**
 * Symbols used for tree identification
 */
const TreeSymbol: symbol;
const FileVisitorCancelToken: symbol;

Usage Examples:

import { Tree, HostTree, ScopedTree, FilterHostTree } from "@angular-devkit/schematics";

// Create empty tree
const emptyTree = Tree.empty();

// Branch existing tree
const branch = Tree.branch(tree);

// Merge trees with strategy
Tree.merge(tree, otherTree, MergeStrategy.AllowOverwriteConflict);

// Partition tree based on file type
const [jsFiles, otherFiles] = Tree.partition(tree, (path) => 
  path.endsWith('.js') || path.endsWith('.ts')
);

File and Directory Entries

Interfaces representing files and directories within the tree.

/**
 * Represents a file within the tree
 */
interface FileEntry {
  readonly path: Path;
  readonly content: Buffer;
}

/**
 * Represents a directory within the tree
 */
interface DirEntry {
  readonly parent: DirEntry | null;
  readonly path: Path;
  readonly subdirs: PathFragment[];
  readonly subfiles: PathFragment[];
  
  /** Get subdirectory by name */
  dir(name: PathFragment): DirEntry;
  /** Get file by name, returns null if not found */
  file(name: PathFragment): FileEntry | null;
  /** Visit all files in directory tree */
  visit(visitor: FileVisitor): void;
}

Update Recorder

Interface for making granular modifications to existing files.

/**
 * Interface for recording file modifications with precise control
 */
interface UpdateRecorder {
  /** Insert content to the left of the specified index */
  insertLeft(index: number, content: Buffer | string): UpdateRecorder;
  /** Insert content to the right of the specified index */
  insertRight(index: number, content: Buffer | string): UpdateRecorder;
  /** Remove content from specified index with given length */
  remove(index: number, length: number): UpdateRecorder;
}

Usage Examples:

import { Tree, UpdateRecorder } from "@angular-devkit/schematics";

function updateFile(tree: Tree): Tree {
  const filePath = '/src/app/app.component.ts';
  
  if (tree.exists(filePath)) {
    const content = tree.readText(filePath);
    const recorder = tree.beginUpdate(filePath);
    
    // Find position to insert import
    const importIndex = content.indexOf('import');
    if (importIndex !== -1) {
      recorder.insertLeft(importIndex, 'import { Injectable } from "@angular/core";\n');
    }
    
    // Find and replace specific text
    const classIndex = content.indexOf('export class');
    if (classIndex !== -1) {
      recorder.insertLeft(classIndex, '@Injectable()\n');
    }
    
    // Commit the update
    tree.commitUpdate(recorder);
  }
  
  return tree;
}

Merge Strategies

Enumeration controlling how tree conflicts are resolved during merge operations.

/**
 * Strategies for resolving conflicts during tree merge operations
 */
enum MergeStrategy {
  /** Default strategy - throw error on conflicts */
  Default = 0,
  /** Always throw error on any conflict */
  Error = 1,
  /** Allow overwriting existing files */
  AllowOverwriteConflict = 2,
  /** Allow creating files that already exist */
  AllowCreationConflict = 4,
  /** Allow deleting files that don't exist */
  AllowDeleteConflict = 8,
  /** Allow content-only conflicts */
  ContentOnly = AllowOverwriteConflict,
  /** Allow all conflict types */
  Overwrite = AllowOverwriteConflict | AllowCreationConflict | AllowDeleteConflict
}

Tree Implementation Classes

Concrete implementations of the Tree interface for different use cases.

/**
 * Main tree implementation backed by virtual file system host
 */
class HostTree implements Tree {
  constructor(host?: virtualFs.Host);
}

/**
 * Tree implementation that delegates all operations to another tree
 */
class DelegateTree implements Tree {
  constructor(other: Tree);
}

/**
 * Tree that limits operations to a specific path scope
 */
class ScopedTree extends DelegateTree {
  constructor(tree: Tree, root: Path);
}

/**
 * Host tree that filters files based on predicate
 */
class FilterHostTree extends HostTree {
  constructor(tree: Tree, filter: FilePredicate<boolean>);
}

Static Tree Functions

Utility functions for creating and manipulating trees.

/** Create an empty host tree */
function empty(): HostTree;

/** Create a branch (copy) of the given tree */
function branch(tree: Tree): Tree;

/** Merge two trees using the specified strategy */
function merge(tree: Tree, other: Tree, strategy?: MergeStrategy): Tree;

/** Partition tree into two based on predicate function */
function partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree, Tree];

Type Definitions

type FilePredicate<T> = (path: Path, entry?: Readonly<FileEntry> | null) => T;
type FileVisitor = FilePredicate<void>;
type FileOperator = (entry: FileEntry) => FileEntry | null;

type Path = string & { __PRIVATE_DEVKIT_PATH: void };
type PathFragment = string & { __PRIVATE_DEVKIT_PATH_FRAGMENT: void };