AI-first build platform designed for monorepo development that provides task orchestration, project graph generation, and CLI tools for managing complex software projects
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
File system utilities and virtual file tree operations for generators, code transformations, and automated workspace modifications.
High-level utilities for reading, writing, and updating JSON files with type safety.
/**
* Reads and parses a JSON file
* @param path - Absolute or workspace-relative path to JSON file
* @returns Parsed JSON object
*/
function readJsonFile<T = any>(path: string): T;
/**
* Writes an object to a JSON file with formatting
* @param path - Absolute or workspace-relative path to JSON file
* @param data - Object to serialize as JSON
*/
function writeJsonFile<T = any>(path: string, data: T): void;
/**
* Updates a JSON file using an updater function
* @param path - Absolute or workspace-relative path to JSON file
* @param updater - Function that receives current data and returns updated data
*/
function updateJsonFile<T = any>(path: string, updater: (data: T) => T): void;
/**
* Reads and parses a JSON file from virtual tree
* @param tree - Virtual file tree
* @param path - Path to JSON file
* @returns Parsed JSON object
*/
function readJson<T = any>(tree: Tree, path: string): T;
/**
* Writes an object to a JSON file in virtual tree
* @param tree - Virtual file tree
* @param path - Path to JSON file
* @param data - Object to serialize as JSON
*/
function writeJson<T = any>(tree: Tree, path: string, data: T): void;
/**
* Updates a JSON file in virtual tree using an updater function
* @param tree - Virtual file tree
* @param path - Path to JSON file
* @param updater - Function that receives current data and returns updated data
*/
function updateJson<T = any>(tree: Tree, path: string, updater: (data: T) => T): void;Virtual file system for generators that allows batch operations before writing to disk.
/**
* Virtual file system interface for generators
*/
interface Tree {
/** Read file contents as buffer */
read(filePath: string): Buffer | null;
/** Read file contents as string with optional encoding */
read(filePath: string, encoding: BufferEncoding): string | null;
/** Write file contents from buffer or string */
write(filePath: string, content: Buffer | string): void;
/** Check if file exists */
exists(filePath: string): boolean;
/** Delete file */
delete(filePath: string): void;
/** Rename file */
rename(from: string, to: string): void;
/** Check if path is a file */
isFile(filePath: string): boolean;
/** List children of directory */
children(dirPath: string): string[];
/** Get file change type */
listChanges(): FileChange[];
}
interface FileChange {
path: string;
type: 'CREATE' | 'DELETE' | 'UPDATE';
content?: Buffer | string;
}
/**
* Creates a virtual file tree from the current file system
* @param root - Root directory path (defaults to workspace root)
* @returns Virtual file tree
*/
function createFsTree(root?: string): FsTree;
/**
* File system-based tree implementation
*/
declare class FsTree implements Tree {
constructor(root: string, isVerbose: boolean);
read(filePath: string): Buffer | null;
read(filePath: string, encoding: BufferEncoding): string | null;
write(filePath: string, content: Buffer | string): 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[];
}Functions for applying changes from virtual trees to the actual file system.
/**
* Applies all changes from a virtual tree to the file system
* @param tree - Virtual file tree with pending changes
* @param verbose - Whether to log changes being applied
*/
function flushChanges(tree: Tree, verbose?: boolean): void;
/**
* Applies string changes to content
* @param content - Original string content
* @param changes - Array of string insertions and deletions
* @returns Modified string content
*/
function applyChangesToString(
content: string,
changes: (StringInsertion | StringDeletion)[]
): string;
interface StringInsertion {
type: 'insertion';
index: number;
text: string;
}
interface StringDeletion {
type: 'deletion';
start: number;
length: number;
}Utilities for working with file paths in a cross-platform manner.
/**
* Joins path fragments using the correct separator
* @param fragments - Path fragments to join
* @returns Joined path string
*/
function joinPathFragments(...fragments: string[]): string;
/**
* Normalizes a path by resolving . and .. segments
* @param path - Path to normalize
* @returns Normalized path string
*/
function normalizePath(path: string): string;
/**
* Gets the relative path from one path to another
* @param from - Source path
* @param to - Target path
* @returns Relative path string
*/
function getRelativePath(from: string, to: string): string;
/**
* Converts Windows-style paths to Unix-style paths
* @param path - Path to convert
* @returns Unix-style path
*/
function toUnixPath(path: string): string;Functions for processing file templates with variable substitution.
/**
* Generates files from templates with variable substitution
* @param tree - Virtual file tree
* @param templatePath - Path to template directory
* @param targetPath - Target directory for generated files
* @param substitutions - Variables for template substitution
*/
function generateFiles(
tree: Tree,
templatePath: string,
targetPath: string,
substitutions: any
): void;
/**
* Offset from template file names for substitution patterns
* @param name - Template file name
* @param substitutions - Variables for substitution
* @returns Processed file name
*/
function offsetFromRoot(name: string): string;
/**
* Names utility functions for template processing
*/
interface Names {
/** Convert to className format */
className: (name: string) => string;
/** Convert to propertyName format */
propertyName: (name: string) => string;
/** Convert to constantName format */
constantName: (name: string) => string;
/** Convert to fileName format */
fileName: (name: string) => string;
}
const names: Names;Utilities for traversing and filtering files in the workspace.
/**
* Visits files that are not ignored by .gitignore
* @param tree - Virtual file tree
* @param dirPath - Directory to start from (default: workspace root)
* @param visitor - Function called for each file
*/
function visitNotIgnoredFiles(
tree: Tree,
dirPath: string,
visitor: (filePath: string) => void
): void;
/**
* Gets all files matching a glob pattern
* @param tree - Virtual file tree
* @param pattern - Glob pattern to match
* @returns Array of matching file paths
*/
function getMatchingFiles(tree: Tree, pattern: string): string[];
/**
* Checks if a file should be ignored based on .gitignore rules
* @param filePath - Path to check
* @param root - Root directory for .gitignore lookup
* @returns True if file should be ignored
*/
function isIgnored(filePath: string, root?: string): boolean;import {
readJsonFile,
writeJsonFile,
updateJsonFile,
logger
} from "nx/src/devkit-exports";
// Read package.json
const packageJson = readJsonFile('package.json');
logger.info(`Project: ${packageJson.name} v${packageJson.version}`);
// Update package.json
updateJsonFile('package.json', (json) => ({
...json,
scripts: {
...json.scripts,
'custom-build': 'nx build --configuration=production'
}
}));
// Write new configuration file
writeJsonFile('nx-custom.json', {
customSetting: 'value',
options: {
enableFeature: true
}
});import {
Tree,
generateFiles,
formatFiles,
joinPathFragments,
names
} from "nx/src/devkit-exports";
interface ComponentGeneratorSchema {
name: string;
project: string;
directory?: string;
}
export default async function componentGenerator(
tree: Tree,
options: ComponentGeneratorSchema
) {
const projectRoot = readProjectConfiguration(tree, options.project).root;
const componentDir = options.directory
? joinPathFragments(projectRoot, 'src', options.directory)
: joinPathFragments(projectRoot, 'src');
// Generate component files from templates
generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
componentDir,
{
...options,
...names(options.name),
template: '' // Remove __template__ from file names
}
);
// Update barrel export
const indexPath = joinPathFragments(componentDir, 'index.ts');
const componentName = names(options.name).className;
if (tree.exists(indexPath)) {
const content = tree.read(indexPath, 'utf-8');
const updatedContent = `${content}\nexport * from './${names(options.name).fileName}';`;
tree.write(indexPath, updatedContent);
} else {
tree.write(indexPath, `export * from './${names(options.name).fileName}';`);
}
// Format generated files
await formatFiles(tree);
}import {
Tree,
visitNotIgnoredFiles,
applyChangesToString,
StringInsertion
} from "nx/src/devkit-exports";
function addImportToFiles(tree: Tree, importStatement: string) {
visitNotIgnoredFiles(tree, '', (filePath) => {
if (!filePath.endsWith('.ts') && !filePath.endsWith('.tsx')) {
return;
}
const content = tree.read(filePath, 'utf-8');
if (!content || content.includes(importStatement)) {
return;
}
// Add import at the top of the file
const changes: StringInsertion[] = [{
type: 'insertion',
index: 0,
text: `${importStatement}\n`
}];
const updatedContent = applyChangesToString(content, changes);
tree.write(filePath, updatedContent);
});
}
// Usage: Add import to all TypeScript files
addImportToFiles(tree, "import { logger } from 'nx/src/devkit-exports';");Template files use substitution patterns in file names and content:
files/
├── __name@fileName__.component.ts__template__
├── __name@fileName__.component.spec.ts__template__
└── __name@fileName__.module.ts__template__Template content example (__name@fileName__.component.ts__template__):
import { Component } from '@angular/core';
@Component({
selector: '<%= selector %>',
templateUrl: './<%= fileName %>.component.html',
styleUrls: ['./<%= fileName %>.component.css']
})
export class <%= className %>Component {
title = '<%= name %>';
}import { Tree, updateJsonFile, readProjectConfiguration } from "nx/src/devkit-exports";
function updateProjectTsConfig(tree: Tree, projectName: string, options: any) {
const projectConfig = readProjectConfiguration(tree, projectName);
const tsConfigPath = joinPathFragments(projectConfig.root, 'tsconfig.json');
if (tree.exists(tsConfigPath)) {
updateJsonFile(tree, tsConfigPath, (tsconfig) => ({
...tsconfig,
compilerOptions: {
...tsconfig.compilerOptions,
...options.compilerOptions
}
}));
}
}Install with Tessl CLI
npx tessl i tessl/npm-nx