The Nx Devkit provides utilities for creating custom generators, executors, and plugins to extend the Nx build system for different technologies and use cases.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
The Tree interface provides a virtual file system for generators and executors, enabling safe file manipulation with change tracking and batching capabilities.
The core interface for all file system operations in Nx generators and executors.
/**
* Virtual file system interface for generators and executors
* Tracks all changes for safe batching and rollback capabilities
*/
interface Tree {
/** Root directory of the workspace */
root: string;
/** Read file contents as buffer or string */
read(filePath: string): Buffer | null;
read(filePath: string, encoding: BufferEncoding): string | null;
/** Write content to a file (creates or updates) */
write(
filePath: string,
content: Buffer | string,
options?: TreeWriteOptions
): void;
/** Check if a file exists */
exists(filePath: string): boolean;
/** Delete a file */
delete(filePath: string): void;
/** Rename/move a file or directory */
rename(from: string, to: string): void;
/** Check if a path is a file (not a directory) */
isFile(filePath: string): boolean;
/** Get list of children of a directory */
children(dirPath: string): string[];
/** Get list of all changes made to the tree */
listChanges(): FileChange[];
/** Change file permissions */
changePermissions(filePath: string, mode: Mode): void;
}
interface TreeWriteOptions {
/** File permissions (e.g., '755' or 0o755) */
mode?: Mode;
}
type Mode = string | number;
interface FileChange {
/** Path to the changed file */
path: string;
/** Type of change made */
type: "CREATE" | "UPDATE" | "DELETE";
/** New content for the file (null for deletions) */
content: Buffer | null;
/** Optional file write options including permissions */
options?: TreeWriteOptions;
}Usage Examples:
import { Tree } from "@nx/devkit";
export default function myGenerator(tree: Tree) {
// Read existing file
const packageJson = tree.read("package.json", "utf-8");
// Create new file with permissions
tree.write("src/new-file.ts", `
export const greeting = "Hello, World!";
`, { mode: '644' });
// Create executable script
tree.write("scripts/build.sh", "#!/bin/bash\nnpm run build", { mode: '755' });
// Check if file exists before modifying
if (tree.exists("README.md")) {
const readme = tree.read("README.md", "utf-8");
tree.write("README.md", readme + "\n\n## New Section");
}
// Check if path is a file
if (tree.isFile("package.json")) {
console.log("package.json is a file");
}
// List directory contents
const srcFiles = tree.children("src");
console.log("Files in src:", srcFiles);
// Rename files
tree.rename("old-config.json", "new-config.json");
// Change file permissions
tree.changePermissions("scripts/deploy.sh", '755');
// View all changes
const changes = tree.listChanges();
console.log(`Made ${changes.length} changes`);
changes.forEach(change => {
console.log(`${change.type}: ${change.path}`);
if (change.options?.mode) {
console.log(` Mode: ${change.options.mode}`);
}
});
}Specialized utilities for reading, writing, and updating JSON files with proper parsing and formatting.
/**
* Read and parse a JSON file from the tree
* @param tree - File system tree
* @param path - Path to JSON file
* @returns Parsed JSON object
*/
function readJson<T = any>(tree: Tree, path: string): T;
/**
* Write an object to a JSON file with proper formatting
* @param tree - File system tree
* @param path - Path to JSON file
* @param value - Object to serialize
*/
function writeJson<T = any>(tree: Tree, path: string, value: T): void;
/**
* Update a JSON file using a callback function
* @param tree - File system tree
* @param path - Path to JSON file
* @param updater - Function to modify the JSON object
*/
function updateJson<T = any>(
tree: Tree,
path: string,
updater: (value: T) => T
): void;Usage Examples:
import { Tree, readJson, writeJson, updateJson } from "@nx/devkit";
export default function myGenerator(tree: Tree) {
// Read package.json
const packageJson = readJson(tree, "package.json");
// Create new JSON file
writeJson(tree, "nx.json", {
version: 2,
projects: {}
});
// Update existing JSON file
updateJson(tree, "package.json", (json) => ({
...json,
scripts: {
...json.scripts,
build: "nx build",
},
}));
}Utilities for finding files using glob patterns.
/**
* Synchronously find files matching glob patterns
* @param tree - File system tree
* @param patterns - Array of glob patterns
* @returns Array of matching file paths
*/
function glob(tree: Tree, patterns: string[]): string[];
/**
* Asynchronously find files matching glob patterns
* @param tree - File system tree
* @param patterns - Array of glob patterns
* @returns Promise resolving to array of matching file paths
*/
function globAsync(tree: Tree, patterns: string[]): Promise<string[]>;Usage Examples:
import { Tree, glob, globAsync } from "@nx/devkit";
export default async function myGenerator(tree: Tree) {
// Find all TypeScript files
const tsFiles = glob(tree, ["**/*.ts", "!**/*.spec.ts"]);
// Find test files asynchronously
const testFiles = await globAsync(tree, ["**/*.spec.ts", "**/*.test.ts"]);
console.log(`Found ${tsFiles.length} TypeScript files`);
console.log(`Found ${testFiles.length} test files`);
}Additional utilities for working with the file system through the Tree interface.
/**
* Visit all files in a directory that are not ignored by git
* @param tree - File system tree
* @param dirPath - Directory to visit (defaults to root)
* @param visitor - Function called for each file
*/
function visitNotIgnoredFiles(
tree: Tree,
dirPath: string = "",
visitor: (path: string) => void
): void;
/**
* Move files from one directory to another
* @param tree - File system tree
* @param oldDir - Source directory
* @param newDir - Target directory
*/
function moveFilesToNewDirectory(
tree: Tree,
oldDir: string,
newDir: string
): void;Usage Examples:
import { Tree, visitNotIgnoredFiles, moveFilesToNewDirectory } from "@nx/devkit";
export default function myGenerator(tree: Tree) {
// Process all non-ignored files
visitNotIgnoredFiles(tree, "src", (filePath) => {
if (filePath.endsWith(".ts")) {
console.log(`Processing TypeScript file: ${filePath}`);
}
});
// Move entire directory
moveFilesToNewDirectory(tree, "old-libs/mylib", "libs/mylib");
}Lower-level JSON parsing and serialization utilities for direct file system operations.
/**
* Parse JSON string with comment support
* @param input - JSON string to parse
* @param options - Parsing options
* @returns Parsed object
*/
function parseJson<T = any>(input: string, options?: JsonParseOptions): T;
/**
* Serialize object to JSON string with formatting
* @param input - Object to serialize
* @param options - Serialization options
* @returns Formatted JSON string
*/
function serializeJson<T = any>(
input: T,
options?: JsonSerializeOptions
): string;
/**
* Remove comments from JSON string
* @param text - JSON string with comments
* @returns Clean JSON string
*/
function stripJsonComments(text: string): string;
/**
* Read and parse JSON file from disk
* @param path - File path
* @returns Parsed JSON object
*/
function readJsonFile<T = any>(path: string): T;
/**
* Write object to JSON file on disk
* @param path - File path
* @param data - Object to write
*/
function writeJsonFile<T = any>(path: string, data: T): void;
interface JsonParseOptions {
/** Allow trailing commas */
allowTrailingComma?: boolean;
/** Preserve property order */
expectComments?: boolean;
}
interface JsonSerializeOptions {
/** Number of spaces for indentation */
spaces?: number;
/** Preserve property order */
replacer?: (key: string, value: any) => any;
}Install with Tessl CLI
npx tessl i tessl/npm-nx--devkit