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
General utility functions for string manipulation, path operations, naming conventions, workspace layout management, and common development tasks.
Generate various naming conventions from a base name for consistent file and variable naming.
/**
* Generate different naming conventions from a base name
* @param name - Base name to generate variations from
* @returns Object with different naming conventions
*/
function names(name: string): {
/** Original name */
name: string;
/** PascalCase class name */
className: string;
/** camelCase property name */
propertyName: string;
/** CONSTANT_CASE constant name */
constantName: string;
/** kebab-case file name */
fileName: string;
};Usage Examples:
import { names } from "@nx/devkit";
function createComponent(componentName: string) {
const nameVariations = names(componentName);
console.log(nameVariations);
// Input: "user-profile"
// Output: {
// name: "user-profile",
// className: "UserProfile",
// propertyName: "userProfile",
// constantName: "USER_PROFILE",
// fileName: "user-profile"
// }
// Use in templates
const templateVars = {
...nameVariations,
// Template will use: <%= className %>, <%= propertyName %>, etc.
};
}Utilities for working with file paths, calculating relative paths, and handling cross-platform path operations.
/**
* Calculate relative path from project directory to workspace root
* @param fullPathToDir - Full path to directory
* @returns Relative path string (e.g., "../.." or "../../..")
*/
function offsetFromRoot(fullPathToDir: string): string;
/**
* Join path fragments in a cross-platform way
* @param fragments - Path segments to join
* @returns Joined path string
*/
function joinPathFragments(...fragments: string[]): string;
/**
* Normalize path separators for current platform
* @param osSpecificPath - Path with OS-specific separators
* @returns Normalized path string
*/
function normalizePath(osSpecificPath: string): string;
/**
* Workspace root directory path
*/
const workspaceRoot: string;Usage Examples:
import {
offsetFromRoot,
joinPathFragments,
normalizePath,
workspaceRoot
} from "@nx/devkit";
function generateProjectFiles(projectRoot: string) {
// Calculate relative path to workspace root
const rootOffset = offsetFromRoot(projectRoot);
console.log(`From ${projectRoot} to root: ${rootOffset}`);
// "libs/my-lib" → "../../"
// "apps/my-app/src" → "../../../"
// Join path fragments safely
const configPath = joinPathFragments(projectRoot, "src", "config", "settings.json");
console.log(`Config path: ${configPath}`);
// "libs/my-lib/src/config/settings.json"
// Normalize paths from different sources
const windowsPath = "libs\\my-lib\\src\\index.ts";
const normalizedPath = normalizePath(windowsPath);
console.log(`Normalized: ${normalizedPath}`);
// "libs/my-lib/src/index.ts"
// Use workspace root for absolute paths
const absoluteProjectPath = joinPathFragments(workspaceRoot, projectRoot);
console.log(`Absolute project path: ${absoluteProjectPath}`);
}Advanced string manipulation utilities for applying changes and transformations.
/**
* Apply a series of changes to a string
* @param text - Original text
* @param changes - Array of changes to apply
* @returns Modified text with all changes applied
*/
function applyChangesToString(
text: string,
changes: StringChange[]
): string;
/**
* Remove leading indentation from template strings
* @param strings - Template string parts
* @param values - Template values
* @returns String with consistent indentation removed
*/
function stripIndents(strings: TemplateStringsArray, ...values: any[]): string;
/**
* String change operation
*/
type StringChange = StringInsertion | StringDeletion;
/**
* Insert text at specific position
*/
interface StringInsertion {
type: ChangeType.Insert;
index: number;
text: string;
}
/**
* Delete text from specific range
*/
interface StringDeletion {
type: ChangeType.Delete;
start: number;
length: number;
}
enum ChangeType {
Insert = "insert",
Delete = "delete"
}Usage Examples:
import {
applyChangesToString,
stripIndents,
ChangeType,
StringChange
} from "@nx/devkit";
function modifySourceCode() {
const originalCode = `
function hello() {
console.log("Hello");
}
`;
// Define changes to apply
const changes: StringChange[] = [
{
type: ChangeType.Insert,
index: originalCode.indexOf('"Hello"'),
text: 'name: string, '
},
{
type: ChangeType.Insert,
index: originalCode.indexOf('"Hello"') + 7,
text: ', ${name}'
}
];
// Apply changes
const modifiedCode = applyChangesToString(originalCode, changes);
console.log(modifiedCode);
// Use stripIndents for clean template strings
const template = stripIndents`
export class ${className} {
constructor(private name: string) {}
greet(): string {
return \`Hello, \${this.name}!\`;
}
}
`;
console.log(template); // No leading indentation
}Low-level JSON parsing and serialization utilities for direct file system operations.
/**
* Parse JSON string with comment support and error handling
* @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;
/** Expect comments in JSON */
expectComments?: boolean;
/** Disable comments */
disallowComments?: boolean;
}
interface JsonSerializeOptions {
/** Number of spaces for indentation */
spaces?: number;
/** Replacer function for custom serialization */
replacer?: (key: string, value: any) => any;
}Usage Examples:
import {
parseJson,
serializeJson,
stripJsonComments,
readJsonFile,
writeJsonFile
} from "@nx/devkit";
function processJsonFiles() {
// Parse JSON with comments
const jsonWithComments = `{
// Project configuration
"name": "my-project",
/* Dependencies */
"dependencies": {
"react": "^18.0.0", // Latest React
}
}`;
const parsed = parseJson(jsonWithComments, { expectComments: true });
console.log(parsed);
// Remove comments from JSON string
const cleanJson = stripJsonComments(jsonWithComments);
console.log("Clean JSON:", cleanJson);
// Serialize with formatting
const formatted = serializeJson(parsed, { spaces: 2 });
console.log("Formatted:", formatted);
// Read/write JSON files directly
const packageJson = readJsonFile("package.json");
packageJson.scripts = { ...packageJson.scripts, build: "nx build" };
writeJsonFile("package.json", packageJson);
}Manage workspace directory structure and understand project organization patterns.
/**
* Get workspace layout configuration including apps and libs directories
* @param tree - File system tree
* @returns Workspace layout configuration
*/
function getWorkspaceLayout(tree: Tree): {
appsDir: string;
libsDir: string;
standaloneAsDefault: boolean;
};
/**
* Extract layout directory information from a path
* @param directory - Directory path to analyze
* @returns Layout directory components
*/
function extractLayoutDirectory(directory?: string): {
layoutDirectory: string | null;
projectDirectory?: string;
};
/**
* Default workspace layout configuration
*/
const workspaceLayout: {
appsDir: string;
libsDir: string;
};Usage Examples:
import {
Tree,
getWorkspaceLayout,
extractLayoutDirectory,
workspaceLayout
} from "@nx/devkit";
function organizeProject(tree: Tree, options: {
name: string;
directory?: string;
projectType: "application" | "library"
}) {
// Get current workspace layout
const layout = getWorkspaceLayout(tree);
console.log(`Apps directory: ${layout.appsDir}`); // "apps"
console.log(`Libs directory: ${layout.libsDir}`); // "libs"
// Parse directory option
const { layoutDirectory, projectDirectory } = extractLayoutDirectory(options.directory);
// Input: "ui/components" → { layoutDirectory: "ui", projectDirectory: "components" }
// Determine final project path
const baseDir = options.projectType === "application"
? layout.appsDir
: layout.libsDir;
let projectPath: string;
if (layoutDirectory && projectDirectory) {
projectPath = `${baseDir}/${layoutDirectory}/${projectDirectory}`;
} else if (options.directory) {
projectPath = `${baseDir}/${options.directory}`;
} else {
projectPath = `${baseDir}/${options.name}`;
}
console.log(`Final project path: ${projectPath}`);
// Use default layout for fallback
if (!layout.appsDir) {
console.log(`Using default apps directory: ${workspaceLayout.appsDir}`);
}
}Structured logging and formatted output utilities for generators and executors.
/**
* Structured logger with different log levels
*/
const logger: {
/** Debug level logging (hidden by default) */
debug(message: string, ...args: any[]): void;
/** Informational logging */
info(message: string, ...args: any[]): void;
/** Warning level logging */
warn(message: string, ...args: any[]): void;
/** Error level logging */
error(message: string, ...args: any[]): void;
/** Fatal error logging */
fatal(message: string, ...args: any[]): void;
/** Log without formatting */
log(message: string, ...args: any[]): void;
};
/**
* Formatted output utilities for console display
*/
const output: {
/** Write formatted output */
write(str: string): void;
/** Write line with formatting */
writeLine(str: string): void;
/** Add vertical whitespace */
addVerticalSeparator(): void;
/** Add horizontal separator */
addHorizontalSeparator(): void;
/** Display success message */
success(message: string): void;
/** Display error message */
error(message: string): void;
/** Display warning message */
warn(message: string): void;
/** Display note/info message */
note(message: string): void;
};Usage Examples:
import { logger, output } from "@nx/devkit";
export default function myGenerator(tree: Tree, options: any) {
// Use structured logging
logger.info(`Generating ${options.name} project...`);
logger.debug(`Options: ${JSON.stringify(options)}`);
try {
// Generate files...
logger.info("Files generated successfully");
// Use formatted output
output.success(`✓ Created ${options.name} project`);
output.addVerticalSeparator();
output.note("Next steps:");
output.writeLine(" 1. Run nx build " + options.name);
output.writeLine(" 2. Run nx test " + options.name);
} catch (error) {
logger.error("Generation failed:", error.message);
output.error(`✗ Failed to create ${options.name} project`);
output.warn("Check the logs for more details");
throw error;
}
}Utilities for working with Nx's caching system and performance optimization.
/**
* Cache directory path
*/
const cacheDir: string;
/**
* Check if Nx daemon is enabled and running
* @returns Whether daemon is enabled
*/
function isDaemonEnabled(): boolean;
/**
* Hash an array of values for cache key generation
* @param values - Array of values to hash
* @returns Hash string
*/
function hashArray(values: any[]): string;Usage Examples:
import { cacheDir, isDaemonEnabled, hashArray } from "@nx/devkit";
import { existsSync, readFileSync } from "fs";
import { join } from "path";
function checkCacheStatus() {
// Check cache directory
console.log(`Cache directory: ${cacheDir}`);
if (existsSync(cacheDir)) {
console.log("Cache directory exists");
// Check cache contents
const lockFilePath = join(cacheDir, "d", "daemon.lock");
if (existsSync(lockFilePath)) {
console.log("Daemon lock file found");
}
}
// Check if daemon is running
const daemonEnabled = isDaemonEnabled();
console.log(`Daemon enabled: ${daemonEnabled}`);
if (daemonEnabled) {
console.log("Using Nx daemon for better performance");
} else {
console.log("Running without daemon (slower but more reliable)");
}
// Generate cache keys using hash function
const cacheInputs = ["src/**/*.ts", "package.json", process.env.NODE_ENV];
const cacheKey = hashArray(cacheInputs);
console.log(`Cache key: ${cacheKey}`);
}Convert between Nx and other framework conventions.
/**
* Convert Nx generator to Angular DevKit schematic
* @param generator - Nx generator function
* @param skipWritingConfigInOldFormat - Skip legacy config format
* @returns Angular DevKit schematic
*/
function convertNxGenerator<T = any>(
generator: Generator<T>,
skipWritingConfigInOldFormat?: boolean
): any;
/**
* Convert Nx executor to Angular DevKit builder
* @param executor - Nx executor function
* @returns Angular DevKit builder
*/
function convertNxExecutor(executor: Executor): any;Usage Examples:
import { convertNxGenerator, convertNxExecutor, Generator, Executor } from "@nx/devkit";
// Convert Nx generator for use in Angular CLI
const myGenerator: Generator = (tree, options) => {
// Nx generator implementation
};
const angularSchematic = convertNxGenerator(myGenerator);
// Convert Nx executor for use in Angular CLI
const myExecutor: Executor = (options, context) => {
// Nx executor implementation
return Promise.resolve({ success: true });
};
const angularBuilder = convertNxExecutor(myExecutor);
// Export for Angular CLI collections
export { angularSchematic as mySchematic, angularBuilder as myBuilder };Install with Tessl CLI
npx tessl i tessl/npm-nx--devkit