The Nx Devkit provides utilities for creating custom generators, executors, and plugins to extend the Nx build system for different technologies and use cases.
npx @tessl/cli install tessl/npm-nx--devkit@21.4.0The Nx Devkit is a comprehensive toolkit for customizing and extending the Nx build system. It provides utilities for creating custom generators that scaffold new code and configurations, building executors that define how tasks are run, and managing workspace configurations programmatically. The devkit includes functions for file system operations, Abstract Syntax Tree (AST) manipulation, workspace and project configuration management, and integration with Nx's task scheduling and caching systems.
npm install @nx/devkitimport {
Tree,
generateFiles,
formatFiles,
addProjectConfiguration,
readProjectConfiguration,
updateProjectConfiguration,
getProjects,
runExecutor,
logger
} from "@nx/devkit";For CommonJS:
const {
generateFiles,
formatFiles,
addProjectConfiguration,
readProjectConfiguration,
updateProjectConfiguration,
getProjects,
runExecutor,
logger
} = require("@nx/devkit");import {
Tree,
generateFiles,
formatFiles,
addProjectConfiguration,
names,
offsetFromRoot,
} from "@nx/devkit";
import * as path from "path";
// Example generator that creates a new library
export default async function (tree: Tree, options: { name: string; directory?: string }) {
const { name, className, fileName } = names(options.name);
const projectDirectory = options.directory ? `libs/${options.directory}` : `libs/${fileName}`;
// Add project configuration
addProjectConfiguration(tree, name, {
root: projectDirectory,
projectType: "library",
sourceRoot: `${projectDirectory}/src`,
targets: {
build: {
executor: "@nx/js:tsc",
outputs: [`{workspaceRoot}/dist/${projectDirectory}`],
options: {
main: `${projectDirectory}/src/index.ts`,
tsConfig: `${projectDirectory}/tsconfig.lib.json`,
},
},
},
});
// Generate files from templates
generateFiles(
tree,
path.join(__dirname, "files"),
projectDirectory,
{
...options,
...names(options.name),
offsetFromRoot: offsetFromRoot(projectDirectory),
tmpl: "",
}
);
// Format all generated files
await formatFiles(tree);
}The Nx Devkit is built around several key architectural concepts:
Tree interface for change tracking and batchingCore file system operations using the Tree interface for generators and executors. Provides change tracking, batching, and safe file manipulation.
interface Tree {
root: string;
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;
listChanges(): FileChange[];
}
interface FileChange {
path: string;
type: "CREATE" | "UPDATE" | "DELETE";
content: Buffer | null;
}Comprehensive workspace and project configuration management for reading, creating, updating, and organizing Nx workspaces and projects.
function addProjectConfiguration(
tree: Tree,
projectName: string,
projectConfiguration: ProjectConfiguration
): void;
function readProjectConfiguration(
tree: Tree,
projectName: string
): ProjectConfiguration;
function updateProjectConfiguration(
tree: Tree,
projectName: string,
projectConfiguration: ProjectConfiguration
): void;
function getProjects(tree: Tree): Map<string, ProjectConfiguration>;Powerful code generation utilities for creating files from templates, formatting code, and managing project scaffolding with EJS templating support.
function generateFiles(
tree: Tree,
srcFolder: string,
target: string,
substitutions: { [k: string]: any },
options?: GenerateFilesOptions
): void;
function formatFiles(
tree: Tree,
options?: { sortRootTsconfigPaths?: boolean }
): Promise<void>;
type Generator<T = any> = (
tree: Tree,
schema: T
) => void | GeneratorCallback | Promise<void | GeneratorCallback>;
type GeneratorCallback = () => void | Promise<void>;Task execution system for running executors, parsing targets, and integrating with Nx's task graph for intelligent build orchestration.
function runExecutor<T = any>(
targetDescription: Target,
options: T,
context: ExecutorContext
): Promise<AsyncIterableIterator<any>>;
function parseTargetString(
targetString: string,
executorContext: ExecutorContext
): Target;
interface Target {
project: string;
target: string;
configuration?: string;
}
interface ExecutorContext {
root: string;
cwd: string;
workspace: WorkspaceJsonConfiguration;
isVerbose: boolean;
projectName?: string;
targetName?: string;
configurationName?: string;
}Package management utilities for adding dependencies, managing package.json files, and ensuring required packages are available.
function addDependenciesToPackageJson(
tree: Tree,
dependencies: Record<string, string>,
devDependencies: Record<string, string>,
packageJsonPath?: string,
keepExistingVersions?: boolean
): GeneratorCallback;
function ensurePackage<T = any>(pkg: string, version: string): T;
function installPackagesTask(
tree: Tree,
alwaysRun?: boolean,
cwd?: string,
packageManager?: PackageManager
): void;Project dependency graph operations for analyzing relationships between projects, creating dependency graphs, and understanding workspace structure.
function createProjectGraphAsync(
opts?: CreateProjectGraphOptions
): Promise<ProjectGraph>;
function readCachedProjectGraph(): ProjectGraph;
interface ProjectGraph {
nodes: Record<string, ProjectGraphProjectNode>;
dependencies: Record<string, ProjectGraphDependency[]>;
externalNodes?: Record<string, ProjectGraphExternalNode>;
}
interface ProjectGraphProjectNode {
name: string;
type: ProjectType;
data: ProjectConfiguration & { files?: ProjectFileMap };
}General utility functions for string manipulation, path operations, naming conventions, and workspace layout management.
function names(name: string): {
name: string;
className: string;
propertyName: string;
constantName: string;
fileName: string;
};
function offsetFromRoot(fullPathToDir: string): string;
function joinPathFragments(...fragments: string[]): string;
function normalizePath(osSpecificPath: string): string;
function getWorkspaceLayout(tree: Tree): {
appsDir: string;
libsDir: string;
standaloneAsDefault: boolean;
};
function stripIndents(strings: TemplateStringsArray, ...values: any[]): string;
const workspaceRoot: string;
const logger: {
debug(message: string, ...args: any[]): void;
info(message: string, ...args: any[]): void;
warn(message: string, ...args: any[]): void;
error(message: string, ...args: any[]): void;
fatal(message: string, ...args: any[]): void;
log(message: string, ...args: any[]): void;
};
const output: {
write(str: string): void;
writeLine(str: string): void;
addVerticalSeparator(): void;
addHorizontalSeparator(): void;
success(message: string): void;
error(message: string): void;
warn(message: string): void;
note(message: string): void;
};Plugin system for extending Nx with custom project discovery, dependency creation, and build integration capabilities.
interface NxPluginV2<TOptions = any> {
name: string;
createNodesV2?: CreateNodesV2<TOptions>;
createDependencies?: CreateDependencies;
createMetadata?: CreateMetadata;
}
interface CreateNodesV2<T = any> {
(
configFiles: string[],
options: T | undefined,
context: CreateNodesContextV2
): Promise<CreateNodesResultV2>;
}
interface CreateNodesResultV2 {
projects?: Record<string, CreateNodesResult>;
}Advanced utilities for task hashing, caching, and performance optimization.
type Hash = string;
interface TaskHasher {
hashTask(task: Task): Promise<Hash>;
hashTasks(tasks: Task[]): Promise<Hash[]>;
}
interface Hasher {
hashFile(path: string): string;
hashFiles(files: string[]): string;
hashArray(values: any[]): string;
}
function hashArray(values: any[]): string;
function defaultTasksRunner<T = any>(
tasks: Task[],
options: DefaultTasksRunnerOptions,
context: TasksRunnerContext
): Observable<TaskResult>;
interface DefaultTasksRunnerOptions {
parallel?: number;
maxParallel?: number;
cacheableOperations?: string[];
cacheDirectory?: string;
skipNxCache?: boolean;
captureStderr?: boolean;
passWithNoTests?: boolean;
remoteCache?: RemoteCache;
}
interface RemoteCache {
retrieve: (hash: string, cacheDirectory: string) => Promise<boolean>;
store: (hash: string, cacheDirectory: string) => Promise<boolean>;
}
interface TasksRunnerContext {
root: string;
workspace: WorkspaceJsonConfiguration;
isVerbose: boolean;
projectGraph: ProjectGraph;
target?: string;
}
interface Observable<T> {
subscribe(observer: (value: T) => void): void;
}
const cacheDir: string;
function isDaemonEnabled(): boolean;interface ProjectConfiguration {
name?: string;
root: string;
sourceRoot?: string;
projectType?: ProjectType;
targets?: Record<string, TargetConfiguration>;
tags?: string[];
implicitDependencies?: string[];
generators?: Record<string, any>;
namedInputs?: Record<string, (string | InputDefinition)[]>;
}
interface TargetConfiguration<T = any> {
executor?: string;
options?: T;
configurations?: Record<string, Partial<T>>;
defaultConfiguration?: string;
dependsOn?: TargetDependencyConfig[];
inputs?: (InputDefinition | string)[];
outputs?: string[];
}
type ProjectType = "application" | "library";
enum OverwriteStrategy {
Overwrite = "overwrite",
KeepExisting = "keepExisting",
ThrowIfExisting = "throwIfExisting"
}
type PackageManager = "npm" | "yarn" | "pnpm";interface Task {
id: string;
target: Target;
projectRoot?: string;
overrides: Record<string, any>;
hash?: string;
cache?: boolean;
}
interface TaskResult {
success: boolean;
task: Task;
startTime?: number;
endTime?: number;
terminalOutput?: string;
[key: string]: any;
}
interface TaskResults {
[taskId: string]: TaskResult;
}
interface TaskGraph {
tasks: Record<string, Task>;
dependencies: Record<string, string[]>;
roots: string[];
}interface NxJsonConfiguration<T = "*" | string[]> {
implicitDependencies?: ImplicitDependencyEntry<T>;
affected?: NxAffectedConfig;
workspaceLayout?: {
appsDir?: string;
libsDir?: string;
};
tasksRunnerOptions?: {
[tasksRunnerName: string]: {
runner?: string;
options?: any;
};
};
targetDefaults?: TargetDefaults;
namedInputs?: { [inputName: string]: (string | InputDefinition)[] };
generators?: Record<string, Record<string, any>>;
cli?: {
packageManager?: PackageManager;
defaultCollection?: string;
};
plugins?: PluginConfiguration[];
defaultBase?: string;
}
interface WorkspaceJsonConfiguration extends ProjectsConfigurations {
version: number;
}
interface ProjectsConfigurations {
version: number;
projects: Record<string, ProjectConfiguration>;
}