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

angular-cli-adapter.mddocs/

Angular CLI Adapter

Bridge functionality for running Angular schematics, migrations, and builders within Nx workspaces. Provides compatibility layer for Angular DevKit tools and enables seamless integration with Angular CLI workflows.

Capabilities

Target Execution

Schedule and execute Angular CLI targets (builders) within Nx workspaces.

/**
 * Schedules Angular CLI target execution
 * @param root - Workspace root directory
 * @param opts - Target execution options
 * @param verbose - Enable verbose logging
 * @returns Promise resolving to Observable of build results
 */
export function scheduleTarget(
  root: string,
  opts: {
    /** Project name to execute target for */
    project: string;
    /** Target name to execute */
    target: string;
    /** Configuration name (e.g., 'production', 'development') */
    configuration: string;
    /** Additional options to pass to the target */
    runOptions: any;
  },
  verbose: boolean
): Promise<Observable<BuilderOutput>>;

/** Build output interface from Angular DevKit */
export interface BuilderOutput {
  success: boolean;
  error?: string;
  target?: Target;
  outputPath?: string;
}

/** Target reference interface */
export interface Target {
  project: string;
  target: string;
  configuration?: string;
}

/** Observable interface for reactive programming */
export interface Observable<T> {
  subscribe(observer: {
    next?: (value: T) => void;
    error?: (error: any) => void;
    complete?: () => void;
  }): { unsubscribe(): void };
}

Schematic Generation

Run Angular schematics for code generation and project setup.

/**
 * Runs Angular schematic generation
 * @param root - Workspace root directory
 * @param opts - Generation options
 * @param verbose - Enable verbose logging
 * @returns Promise resolving to exit code (0 for success)
 */
export function generate(root: string, opts: GenerateOptions, verbose: boolean): Promise<number>;

/** Options for schematic generation */
export interface GenerateOptions {
  /** Collection name (e.g., '@angular/core', '@nx/angular') */
  collection: string;
  /** Schematic name to run */
  name: string;
  /** Options to pass to the schematic */
  options: Record<string, any>;
  /** Whether to perform a dry run */
  dryRun?: boolean;
  /** Whether to force overwrite existing files */
  force?: boolean;
}

Migration Execution

Execute package migrations for workspace updates and transformations.

/**
 * Executes migrations for package updates
 * @param root - Workspace root directory
 * @param packageName - Name of the package to migrate
 * @param migrationName - Name of the specific migration to run
 * @param isVerbose - Enable verbose logging
 * @returns Promise with migration results
 */
export function runMigration(
  root: string,
  packageName: string,
  migrationName: string,
  isVerbose: boolean
): Promise<{
  /** Log messages from the migration */
  loggingQueue: string[];
  /** Whether the migration made any changes */
  madeChanges: boolean;
}>;

Schematic Wrapping

Utilities for wrapping Angular DevKit schematics for use within Nx.

/**
 * Wraps Angular DevKit schematic for Nx usage
 * @param collectionName - Name of the schematic collection
 * @param generatorName - Name of the generator/schematic
 * @returns Wrapped schematic function compatible with Nx Tree API
 */
export function wrapAngularDevkitSchematic(
  collectionName: string,
  generatorName: string
): (host: Tree, generatorOptions: { [k: string]: any }) => Promise<void>;

Virtual File System Integration

Classes for integrating Angular DevKit with Nx file system operations.

/**
 * Virtual filesystem host for Angular DevKit integration
 */
export class NxScopedHost {
  constructor(root: string);
  
  /** Read file as string */
  readFile(path: string): Promise<string>;
  /** Write file content */
  writeFile(path: string, content: string): Promise<void>;
  /** Check if file exists */
  exists(path: string): Promise<boolean>;
  /** Delete file */
  delete(path: string): Promise<void>;
  /** Rename file */
  rename(from: string, to: string): Promise<void>;
  /** List directory contents */
  list(path: string): Promise<string[]>;
}

/**
 * Specialized host for wrapped schematics
 */
export class NxScopeHostUsedForWrappedSchematics extends NxScopedHost {
  constructor(root: string, host: Tree);
}

Logging and Utilities

Utilities for logging and converting data formats.

/**
 * Creates Angular DevKit logger
 * @param isVerbose - Enable verbose logging
 * @returns Logger instance compatible with Angular DevKit
 */
export function getLogger(isVerbose?: boolean): logging.Logger;

/**
 * Converts ArrayBuffer to string
 * @param buffer - ArrayBuffer to convert
 * @returns String representation of the buffer
 */
export function arrayBufferToString(buffer: any): string;

Testing Utilities

Functions for testing and mocking schematic operations.

/**
 * Override collection resolution for testing
 * @param collections - Map of collection names to paths
 */
export function overrideCollectionResolutionForTesting(collections: { [name: string]: string }): void;

/**
 * Mock schematics for testing
 * @param schematics - Map of schematic names to mock implementations
 */
export function mockSchematicsForTesting(schematics: { 
  [name: string]: (host: Tree, generatorOptions: { [k: string]: any }) => Promise<void> 
}): void;

Usage Examples

Running Angular CLI Targets

import { scheduleTarget } from "@nrwl/tao/commands/ngcli-adapter";
import { workspaceRoot } from "@nrwl/tao/utils/app-root";

async function buildProject(projectName: string, configuration: string = 'production') {
  try {
    const buildResult$ = await scheduleTarget(
      workspaceRoot,
      {
        project: projectName,
        target: 'build',
        configuration: configuration,
        runOptions: {
          optimization: true,
          sourceMap: false
        }
      },
      true // verbose logging
    );

    // Subscribe to build results
    buildResult$.subscribe({
      next: (result) => {
        if (result.success) {
          console.log(`Build completed successfully`);
          if (result.outputPath) {
            console.log(`Output: ${result.outputPath}`);
          }
        } else {
          console.error(`Build failed: ${result.error}`);
        }
      },
      error: (error) => {
        console.error('Build error:', error);
      },
      complete: () => {
        console.log('Build process completed');
      }
    });
    
  } catch (error) {
    console.error('Failed to schedule build target:', error);
  }
}

// Usage
buildProject('my-app', 'production');

Running Schematics

import { generate, type GenerateOptions } from "@nrwl/tao/commands/ngcli-adapter";
import { workspaceRoot } from "@nrwl/tao/utils/app-root";
import { logger } from "@nrwl/tao/shared/logger";

async function generateComponent(name: string, project: string) {
  const options: GenerateOptions = {
    collection: '@angular/core',
    name: 'component',
    options: {
      name: name,
      project: project,
      style: 'scss',
      skipTests: false,
      changeDetection: 'OnPush'
    },
    dryRun: false,
    force: false
  };

  try {
    const exitCode = await generate(workspaceRoot, options, true);
    
    if (exitCode === 0) {
      logger.info(`Component '${name}' generated successfully`);
    } else {
      logger.error(`Component generation failed with exit code: ${exitCode}`);
    }
    
    return exitCode;
  } catch (error) {
    logger.error('Failed to generate component:', error);
    return 1;
  }
}

// Generate library
async function generateLibrary(name: string) {
  const options: GenerateOptions = {
    collection: '@nx/angular',
    name: 'library',
    options: {
      name: name,
      buildable: true,
      publishable: false,
      routing: false,
      lazy: false
    }
  };

  const exitCode = await generate(workspaceRoot, options, true);
  return exitCode === 0;
}

// Usage
generateComponent('user-profile', 'my-app');
generateLibrary('shared-utils');

Running Migrations

import { runMigration } from "@nrwl/tao/commands/ngcli-adapter";
import { workspaceRoot } from "@nrwl/tao/utils/app-root";
import { logger } from "@nrwl/tao/shared/logger";

async function migratePackage(packageName: string, migrationName: string) {
  try {
    logger.info(`Running migration: ${packageName}:${migrationName}`);
    
    const result = await runMigration(
      workspaceRoot,
      packageName,
      migrationName,
      true // verbose
    );

    // Log migration output
    result.loggingQueue.forEach(message => {
      console.log(message);
    });

    if (result.madeChanges) {
      logger.info('Migration completed with changes');
    } else {
      logger.info('Migration completed without changes');
    }

    return result;
  } catch (error) {
    logger.error(`Migration failed: ${error.message}`);
    throw error;
  }
}

// Example: Migrate Angular to newer version
async function migrateAngular() {
  try {
    // Run Angular update migration
    await migratePackage('@angular/core', 'migration-v15');
    
    // Run additional migrations if needed
    await migratePackage('@angular/cli', 'update-workspace-config');
    
    logger.info('Angular migration completed successfully');
  } catch (error) {
    logger.error('Angular migration failed:', error);
  }
}

migrateAngular();

Custom Schematic Wrapping

import { 
  wrapAngularDevkitSchematic,
  NxScopeHostUsedForWrappedSchematics 
} from "@nrwl/tao/commands/ngcli-adapter";
import { FsTree } from "@nrwl/tao/shared/tree";
import { workspaceRoot } from "@nrwl/tao/utils/app-root";

// Wrap an Angular schematic for use with Nx Tree API
const wrappedComponentSchematic = wrapAngularDevkitSchematic(
  '@angular/core',
  'component'
);

async function generateComponentWithNxTree(name: string, path: string) {
  const tree = new FsTree(workspaceRoot);
  
  try {
    // Use the wrapped schematic with Nx Tree
    await wrappedComponentSchematic(tree, {
      name: name,
      path: path,
      style: 'scss',
      skipTests: false
    });

    // Apply changes to file system
    const changes = tree.listChanges();
    console.log(`Generated ${changes.length} files for component '${name}'`);
    
    // You can review changes before applying
    changes.forEach(change => {
      console.log(`${change.type}: ${change.path}`);
    });

    // Apply changes
    flushChanges(tree.root, changes);
    
  } catch (error) {
    console.error('Failed to generate component:', error);
  }
}

generateComponentWithNxTree('feature-component', 'src/app/features');

Testing with Mocked Schematics

import { 
  mockSchematicsForTesting,
  overrideCollectionResolutionForTesting 
} from "@nrwl/tao/commands/ngcli-adapter";
import { FsTree } from "@nrwl/tao/shared/tree";

// Set up testing environment
function setupSchematicTesting() {
  // Override collection paths for testing
  overrideCollectionResolutionForTesting({
    '@nx/angular': '/path/to/test/collections/nx-angular',
    '@angular/core': '/path/to/test/collections/angular-core'
  });

  // Mock specific schematics
  mockSchematicsForTesting({
    'component': async (tree, options) => {
      // Mock component generation
      const componentPath = `${options.path}/${options.name}.component.ts`;
      tree.write(componentPath, `
        import { Component } from '@angular/core';
        
        @Component({
          selector: 'app-${options.name}',
          template: '<div>Mock ${options.name} component</div>'
        })
        export class ${options.name}Component {}
      `);
    },
    
    'service': async (tree, options) => {
      // Mock service generation  
      const servicePath = `${options.path}/${options.name}.service.ts`;
      tree.write(servicePath, `
        import { Injectable } from '@angular/core';
        
        @Injectable({
          providedIn: 'root'
        })
        export class ${options.name}Service {}
      `);
    }
  });
}

// Use in tests
describe('Schematic Integration', () => {
  beforeEach(() => {
    setupSchematicTesting();
  });

  it('should generate component using mocked schematic', async () => {
    const tree = new FsTree('/test-workspace');
    
    // This will use the mocked schematic
    const wrappedSchematic = wrapAngularDevkitSchematic('@angular/core', 'component');
    await wrappedSchematic(tree, {
      name: 'test-component',
      path: 'src/app'
    });

    expect(tree.exists('src/app/test-component.component.ts')).toBe(true);
  });
});

Logging Integration

import { getLogger, arrayBufferToString } from "@nrwl/tao/commands/ngcli-adapter";
import { logger as nxLogger } from "@nrwl/tao/shared/logger";

// Create Angular DevKit logger that integrates with Nx logging
const angularLogger = getLogger(true); // verbose logging

// Use Angular logger for DevKit operations
angularLogger.info('Starting Angular DevKit operation');
angularLogger.warn('This is a warning from Angular DevKit');

// Convert binary data for logging
const binaryData = new ArrayBuffer(8);
const stringData = arrayBufferToString(binaryData);
nxLogger.debug('Binary data as string:', stringData);

// Custom logging bridge
function bridgeAngularToNxLogging(angularLogger: any, nxLogger: any) {
  const originalInfo = angularLogger.info;
  const originalWarn = angularLogger.warn;
  const originalError = angularLogger.error;

  angularLogger.info = (message: string) => {
    nxLogger.info(`[Angular] ${message}`);
    return originalInfo.call(angularLogger, message);
  };

  angularLogger.warn = (message: string) => {
    nxLogger.warn(`[Angular] ${message}`);
    return originalWarn.call(angularLogger, message);
  };

  angularLogger.error = (message: string) => {
    nxLogger.error(`[Angular] ${message}`);
    return originalError.call(angularLogger, message);
  };
}

bridgeAngularToNxLogging(angularLogger, nxLogger);

Integration with Workspace Management

The Angular CLI adapter works seamlessly with other @nrwl/tao APIs:

import { scheduleTarget } from "@nrwl/tao/commands/ngcli-adapter";
import { Workspaces } from "@nrwl/tao/shared/workspace";
import { logger } from "@nrwl/tao/shared/logger";
import { workspaceRoot } from "@nrwl/tao/utils/app-root";

async function buildAllApps() {
  const workspaces = new Workspaces(workspaceRoot);
  const projects = workspaces.readProjectsConfigurations();

  // Find all application projects
  const apps = Object.entries(projects.projects)
    .filter(([_, config]) => config.projectType === 'application')
    .map(([name]) => name);

  logger.info(`Found ${apps.length} applications to build`);

  // Build each app
  for (const appName of apps) {
    try {
      logger.info(`Building ${appName}...`);
      
      const buildResult$ = await scheduleTarget(
        workspaceRoot,
        {
          project: appName,
          target: 'build',
          configuration: 'production',
          runOptions: {}
        },
        false // not verbose for batch operation
      );

      await new Promise((resolve, reject) => {
        buildResult$.subscribe({
          next: (result) => {
            if (result.success) {
              logger.info(`✓ ${appName} built successfully`);
            } else {
              logger.error(`✗ ${appName} build failed: ${result.error}`);
            }
          },
          error: reject,
          complete: resolve
        });
      });
      
    } catch (error) {
      logger.error(`Failed to build ${appName}:`, error);
    }
  }
  
  logger.info('Batch build completed');
}

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