CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-nrwl--tao

CLI compatibility layer for older Nx workspaces that serves as a bridge to core nx functionality

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

tree-api.mddocs/

Tree API

Virtual file system API for code generation and file system operations with change tracking. Provides a virtual abstraction over the file system that tracks changes and enables batch operations.

Capabilities

Tree Interface

Main virtual file system interface for reading, writing, and manipulating files.

/**
 * Virtual file system interface with change tracking
 */
export interface Tree {
  /** Workspace root directory path */
  root: string;
  
  /** Read file as Buffer */
  read(filePath: string): Buffer | null;
  /** Read file as string with encoding */
  read(filePath: string, encoding: BufferEncoding): string | null;
  
  /** Write content to file */
  write(filePath: string, content: Buffer | string, options?: TreeWriteOptions): void;
  /** Check if file exists */
  exists(filePath: string): boolean;
  /** Delete file */
  delete(filePath: string): void;
  /** Rename/move file */
  rename(from: string, to: string): void;
  /** Check if path is a file (not directory) */
  isFile(filePath: string): boolean;
  /** List child files/directories in a directory */
  children(dirPath: string): string[];
  /** Get list of all tracked changes */
  listChanges(): FileChange[];
  /** Change file permissions */
  changePermissions(filePath: string, mode: Mode): void;
}

/** File write options */
export interface TreeWriteOptions {
  /** File permissions mode */
  mode?: Mode;
}

/** File permission mode type */
export type Mode = number;

Change Tracking

Types for tracking and applying file system changes.

/**
 * Describes a file system change operation
 */
export interface FileChange {
  /** Path of the file being changed */
  path: string;
  /** Type of change operation */
  type: 'CREATE' | 'DELETE' | 'UPDATE';
  /** File content (null for deletions) */
  content: Buffer | null;
  /** Write options for the change */
  options?: TreeWriteOptions;
}

Tree Implementation

Concrete file system-backed implementation of the Tree interface.

/**
 * File system-backed implementation of Tree interface
 */
export class FsTree implements Tree {
  constructor(root: string, isVerbose?: boolean);
  
  root: string;
  read(filePath: string): Buffer | null;
  read(filePath: string, encoding: BufferEncoding): string | null;
  write(filePath: string, content: Buffer | string, options?: TreeWriteOptions): void;
  exists(filePath: string): boolean;
  delete(filePath: string): void;
  rename(from: string, to: string): void;
  isFile(filePath: string): boolean;
  children(dirPath: string): string[];
  listChanges(): FileChange[];
  changePermissions(filePath: string, mode: Mode): void;
}

Change Application

Functions for applying tracked changes to the actual file system.

/**
 * Applies tracked changes to the file system
 * @param root - Root directory to apply changes to
 * @param fileChanges - Array of changes to apply
 */
export function flushChanges(root: string, fileChanges: FileChange[]): void;

/**
 * Prints a summary of tracked changes
 * @param fileChanges - Array of changes to print
 * @param indent - Indentation string for formatting (defaults to empty string)
 */
export function printChanges(fileChanges: FileChange[], indent?: string): void;

Usage Examples

Basic File Operations

import { FsTree } from "@nrwl/tao/shared/tree";

// Create a tree for the current workspace
const tree = new FsTree(process.cwd());

// Read files
const packageJson = tree.read('package.json', 'utf-8');
const binaryFile = tree.read('logo.png'); // Returns Buffer

// Check file existence
if (tree.exists('src/app.ts')) {
  console.log('App file exists');
}

// Write files
tree.write('src/new-component.ts', `
export class NewComponent {
  constructor() {
    console.log('New component created');
  }
}
`);

// Create directories by writing files with paths
tree.write('src/utils/helper.ts', 'export const helper = () => {};');

Change Tracking and Application

import { FsTree, flushChanges, printChanges } from "@nrwl/tao/shared/tree";

const tree = new FsTree('/path/to/workspace');

// Make several changes
tree.write('new-file.ts', 'export const newFunction = () => {};');
tree.write('src/existing.ts', 'updated content');
tree.delete('old-file.ts');
tree.rename('temp.ts', 'renamed.ts');

// Review changes before applying
const changes = tree.listChanges();
console.log(`Found ${changes.length} changes:`);
printChanges(changes, '  ');

// Apply all changes to the file system
flushChanges(tree.root, changes);
console.log('All changes applied to file system');

File System Navigation

import { FsTree } from "@nrwl/tao/shared/tree";

const tree = new FsTree('/workspace');

// List directory contents
const srcFiles = tree.children('src');
console.log('Source files:', srcFiles);

// Check file types
srcFiles.forEach(file => {
  const fullPath = `src/${file}`;
  if (tree.isFile(fullPath)) {
    console.log(`${file} is a file`);
  } else {
    console.log(`${file} is a directory`);
  }
});

// Recursively list all TypeScript files
function listTsFiles(tree: FsTree, dir: string = ''): string[] {
  const tsFiles: string[] = [];
  
  try {
    const children = tree.children(dir);
    
    for (const child of children) {
      const childPath = dir ? `${dir}/${child}` : child;
      
      if (tree.isFile(childPath) && child.endsWith('.ts')) {
        tsFiles.push(childPath);
      } else if (!tree.isFile(childPath)) {
        tsFiles.push(...listTsFiles(tree, childPath));
      }
    }
  } catch (error) {
    // Directory might not exist or be accessible
  }
  
  return tsFiles;
}

const allTsFiles = listTsFiles(tree, 'src');
console.log('All TypeScript files:', allTsFiles);

Code Generation Patterns

import { FsTree, type TreeWriteOptions } from "@nrwl/tao/shared/tree";

function generateComponent(tree: FsTree, name: string, path: string) {
  const componentDir = `${path}/${name}`;
  
  // Generate component file
  tree.write(`${componentDir}/${name}.component.ts`, `
import { Component } from '@angular/core';

@Component({
  selector: 'app-${name}',
  templateUrl: './${name}.component.html',
  styleUrls: ['./${name}.component.css']
})
export class ${pascalCase(name)}Component {
  constructor() {}
}
`);

  // Generate template
  tree.write(`${componentDir}/${name}.component.html`, `
<div class="${name}-container">
  <h2>${name} works!</h2>
</div>
`);

  // Generate styles
  tree.write(`${componentDir}/${name}.component.css`, `
.${name}-container {
  padding: 1rem;
}
`);

  // Generate test file
  tree.write(`${componentDir}/${name}.component.spec.ts`, `
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ${pascalCase(name)}Component } from './${name}.component';

describe('${pascalCase(name)}Component', () => {
  let component: ${pascalCase(name)}Component;
  let fixture: ComponentFixture<${pascalCase(name)}Component>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [${pascalCase(name)}Component]
    });
    fixture = TestBed.createComponent(${pascalCase(name)}Component);
    component = fixture.componentInstance;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});
`);
}

function pascalCase(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

// Usage
const tree = new FsTree('/workspace');
generateComponent(tree, 'user-profile', 'src/app/components');

// Apply changes
const changes = tree.listChanges();
printChanges(changes);
flushChanges(tree.root, changes);

File Permissions and Options

import { FsTree, type TreeWriteOptions } from "@nrwl/tao/shared/tree";

const tree = new FsTree('/workspace');

// Write executable script
const scriptOptions: TreeWriteOptions = {
  mode: 0o755 // rwxr-xr-x
};

tree.write('scripts/build.sh', `#!/bin/bash
echo "Building project..."
npm run build
`, scriptOptions);

// Or change permissions after writing
tree.write('scripts/deploy.sh', '#!/bin/bash\necho "Deploying..."');
tree.changePermissions('scripts/deploy.sh', 0o755);

// Apply changes
flushChanges(tree.root, tree.listChanges());

Integration with Code Generation

The Tree API is commonly used with other @nrwl/tao APIs for comprehensive code generation:

import { FsTree, flushChanges } from "@nrwl/tao/shared/tree";
import { Workspaces } from "@nrwl/tao/shared/workspace";
import { logger } from "@nrwl/tao/shared/logger";

async function generateLibrary(workspaceRoot: string, libName: string) {
  const tree = new FsTree(workspaceRoot);
  const workspaces = new Workspaces(workspaceRoot);
  
  try {
    // Read existing workspace configuration
    const projects = workspaces.readProjectsConfigurations();
    
    // Generate library files
    const libPath = `libs/${libName}`;
    tree.write(`${libPath}/src/index.ts`, `export * from './lib/${libName}';`);
    tree.write(`${libPath}/src/lib/${libName}.ts`, `
export function ${libName}(): string {
  return '${libName}';
}
`);
    
    // Update workspace configuration
    const updatedProjects = {
      ...projects,
      projects: {
        ...projects.projects,
        [libName]: {
          root: libPath,
          sourceRoot: `${libPath}/src`,
          projectType: 'library' as const,
          targets: {
            build: {
              executor: '@nx/js:tsc',
              options: {
                outputPath: `dist/${libPath}`,
                main: `${libPath}/src/index.ts`,
                tsConfig: `${libPath}/tsconfig.lib.json`
              }
            }
          }
        }
      }
    };
    
    // Write updated configuration (this would typically be done through workspace APIs)
    tree.write('workspace.json', JSON.stringify(updatedProjects, null, 2));
    
    // Apply all changes
    const changes = tree.listChanges();
    logger.info(`Generating library '${libName}' with ${changes.length} file operations`);
    
    flushChanges(tree.root, changes);
    logger.info(`Library '${libName}' generated successfully`);
    
  } catch (error) {
    logger.error('Failed to generate library:', error);
    throw error;
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-nrwl--tao

docs

angular-cli-adapter.md

configuration.md

index.md

logging.md

package-manager.md

project-graph.md

tree-api.md

workspace-management.md

workspace-root.md

tile.json