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
Powerful code generation utilities for creating files from templates, formatting code, and managing project scaffolding with EJS templating support.
Generate files and directories from template folders with variable substitution and conditional logic.
/**
* Generates a folder of files based on provided templates
* Performs template substitutions using EJS and filename replacements
* @param tree - File system tree
* @param srcFolder - Source template folder (absolute path)
* @param target - Target directory relative to workspace root
* @param substitutions - Variables for template substitution
* @param options - Generation options including overwrite strategy
*/
function generateFiles(
tree: Tree,
srcFolder: string,
target: string,
substitutions: { [k: string]: any },
options?: GenerateFilesOptions
): void;
interface GenerateFilesOptions {
/** Strategy for handling existing files */
overwriteStrategy?: OverwriteStrategy;
}
enum OverwriteStrategy {
/** Replace existing files */
Overwrite = "overwrite",
/** Keep existing files unchanged */
KeepExisting = "keepExisting",
/** Throw error if file exists */
ThrowIfExisting = "throwIfExisting"
}Usage Examples:
import { Tree, generateFiles, OverwriteStrategy } from "@nx/devkit";
import * as path from "path";
export default function myGenerator(tree: Tree, options: { name: string; description?: string }) {
// Generate files from templates with substitutions
generateFiles(
tree,
path.join(__dirname, "files"), // Template source folder
`libs/${options.name}`, // Target directory
{
...options,
tmpl: "", // Remove __tmpl__ suffix from filenames
className: options.name.charAt(0).toUpperCase() + options.name.slice(1),
},
{
overwriteStrategy: OverwriteStrategy.ThrowIfExisting
}
);
}
// Template structure example:
// files/
// src/
// index.ts__tmpl__
// lib/
// __name__.ts__tmpl__
// README.md__tmpl__
// package.json__tmpl__
// Template file content example (index.ts__tmpl__):
/*
export * from './lib/<%= name %>';
// Generated with className: <%= className %>
// Description: <%= description || 'No description provided' %>
*/Format generated code using Prettier and other formatting tools.
/**
* Formats all created or updated files using Prettier
* Only formats if Prettier is detected in the workspace
* @param tree - File system tree
* @param options - Formatting options
*/
function formatFiles(
tree: Tree,
options?: {
sortRootTsconfigPaths?: boolean;
}
): Promise<void>;Usage Examples:
import { Tree, generateFiles, formatFiles } from "@nx/devkit";
export default async function myGenerator(tree: Tree, options: any) {
// Generate files
generateFiles(tree, templatePath, targetPath, substitutions);
// Format all generated files
await formatFiles(tree, {
sortRootTsconfigPaths: true // Sort paths in tsconfig.json
});
}Convert TypeScript files to JavaScript and update configurations accordingly.
/**
* Rename and transpile TypeScript files to JavaScript
* @param tree - File system tree
* @param options - Conversion options
*/
function toJS(tree: Tree, options?: ToJSOptions): void;
/**
* Update TypeScript configurations to allow JavaScript files
* @param tree - File system tree
* @param options - Configuration options
*/
function updateTsConfigsToJs(
tree: Tree,
options: { projectRoot: string }
): void;
interface ToJSOptions {
/** Convert all files in the tree */
all?: boolean;
/** Specific files to convert */
files?: string[];
}Usage Examples:
import { Tree, toJS, updateTsConfigsToJs } from "@nx/devkit";
export default function convertToJs(tree: Tree, options: { projectRoot: string }) {
// Convert TypeScript files to JavaScript
toJS(tree);
// Update tsconfig files to allow JS
updateTsConfigsToJs(tree, {
projectRoot: options.projectRoot
});
}Manage and sequence generator callbacks for post-generation tasks.
/**
* Run multiple generator callbacks in sequence
* @param tasks - Array of callback functions to execute
* @returns Combined callback that runs all tasks in order
*/
function runTasksInSerial(...tasks: GeneratorCallback[]): GeneratorCallback;
/**
* Callback function type returned by generators
* Used for post-generation tasks like installing packages
*/
type GeneratorCallback = () => void | Promise<void>;Usage Examples:
import {
Tree,
GeneratorCallback,
runTasksInSerial,
addDependenciesToPackageJson,
installPackagesTask
} from "@nx/devkit";
export default function myGenerator(tree: Tree, options: any): GeneratorCallback {
// Generate files...
// Create multiple tasks
const addDepsTask = addDependenciesToPackageJson(
tree,
{ react: "^18.0.0" },
{ "@types/react": "^18.0.0" }
);
const installTask = () => installPackagesTask(tree);
const customTask = () => {
console.log("Running custom post-generation task");
};
// Return combined task that runs in sequence
return runTasksInSerial(addDepsTask, installTask, customTask);
}Utilities for processing files and handling ignored files.
/**
* Visit all files 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 non-ignored file
*/
function visitNotIgnoredFiles(
tree: Tree,
dirPath?: string,
visitor: (path: string) => void
): void;Usage Examples:
import { Tree, visitNotIgnoredFiles } from "@nx/devkit";
export default function processFiles(tree: Tree) {
// Process all non-ignored TypeScript files
visitNotIgnoredFiles(tree, "src", (filePath) => {
if (filePath.endsWith(".ts") && !filePath.endsWith(".spec.ts")) {
const content = tree.read(filePath, "utf-8");
// Process the file content
const processedContent = content.replace(/old-pattern/g, "new-pattern");
tree.write(filePath, processedContent);
}
});
}/**
* Generator function signature
* Can return void, a callback, or a promise of either
*/
type Generator<T = any> = (
tree: Tree,
schema: T
) => void | GeneratorCallback | Promise<void | GeneratorCallback>;
/**
* Migration function signature for updating existing code
*/
type Migration<T = any> = (
tree: Tree,
schema: T
) => void | GeneratorCallback | Promise<void | GeneratorCallback>;
/**
* Schema definition for generators.json
*/
interface GeneratorsJson {
generators?: Record<string, GeneratorDescription>;
schematics?: Record<string, GeneratorDescription>; // Legacy support
}
interface GeneratorDescription {
/** Path to the generator implementation */
implementation?: string;
/** Path to the schema JSON file */
schema?: string;
/** Aliases for the generator */
aliases?: string[];
/** Whether generator is hidden from help */
hidden?: boolean;
/** Description of the generator */
description?: string;
}The generateFiles function supports EJS templating with these features:
Filename Substitution:
__name__ → replaced with substitutions.name__className__ → replaced with substitutions.className__tmpl__ → removed (use tmpl: "" in substitutions)Content Substitution:
<%= variable %> → outputs variable value<%- variable %> → outputs unescaped variable value<% if (condition) { %>...content...<% } %> → conditional content<% for (item of items) { %>...content...<% } %> → loopsAdvanced Usage Examples:
// Template file: __name__.service.ts__tmpl__
/*
<% if (type === 'api') { %>
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class <%= className %>Service {
constructor(private http: HttpClient) {}
<% for (method of methods) { %>
<%= method.name %>(<%- method.params %>): <%= method.returnType %> {
return this.http.<%= method.httpMethod %>('/<%= name %>/<%= method.endpoint %>');
}
<% } %>
}
<% } else { %>
export class <%= className %>Service {
// Simple service implementation
}
<% } %>
*/
// Generator usage:
generateFiles(tree, templatePath, targetPath, {
name: 'user',
className: 'User',
type: 'api',
methods: [
{
name: 'getUsers',
params: '',
returnType: 'Observable<User[]>',
httpMethod: 'get',
endpoint: 'list'
},
{
name: 'createUser',
params: 'user: User',
returnType: 'Observable<User>',
httpMethod: 'post',
endpoint: 'create'
}
],
tmpl: ''
});Converting Nx generators for use with other frameworks:
/**
* Convert an Nx Generator to Angular Devkit Schematic
* @param generator - Nx generator function
* @param skipWritingConfigInOldFormat - Skip legacy format
* @returns Angular DevKit schematic
*/
function convertNxGenerator<T = any>(
generator: Generator<T>,
skipWritingConfigInOldFormat?: boolean
): any;Usage Examples:
import { convertNxGenerator } from "@nx/devkit";
// Convert Nx generator to Angular schematic
const schematic = convertNxGenerator(myNxGenerator);
// Use in Angular CLI collection
export default schematic;Install with Tessl CLI
npx tessl i tessl/npm-nx--devkit