CLI compatibility layer for older Nx workspaces that serves as a bridge to core nx functionality
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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;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;
}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;
}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;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 = () => {};');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');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);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);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());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