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
Built-in code generation and task execution tools, plus comprehensive APIs for creating custom generators and executors.
Functions and utilities for creating custom code generators.
/**
* Formats all files in the tree using configured formatters
* @param tree - Virtual file tree
* @returns Promise that resolves when formatting is complete
*/
function formatFiles(tree: Tree): Promise<void>;
/**
* 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;
/**
* Adds a callback to install packages after generation
* @param tree - Virtual file tree
* @param options - Package installation options
* @returns Generator callback function
*/
function installPackagesTask(tree: Tree, options?: InstallPackagesTaskOptions): GeneratorCallback;
/**
* Adds dependencies to package.json
* @param tree - Virtual file tree
* @param dependencies - Map of package names to versions
* @param options - Installation options
* @returns Generator callback function
*/
function addDependenciesToPackageJson(
tree: Tree,
dependencies: Record<string, string>,
devDependencies?: Record<string, string>,
options?: InstallPackagesTaskOptions
): GeneratorCallback;
/**
* Removes dependencies from package.json
* @param tree - Virtual file tree
* @param dependencies - Array of package names to remove
* @param devDependencies - Array of dev dependency names to remove
* @returns Generator callback function
*/
function removeDependenciesFromPackageJson(
tree: Tree,
dependencies: string[],
devDependencies?: string[]
): GeneratorCallback;
interface InstallPackagesTaskOptions {
packageManager?: 'npm' | 'yarn' | 'pnpm';
cwd?: string;
}
/**
* Generator callback function type
*/
type GeneratorCallback = () => void | Promise<void>;
/**
* Generator function signature
*/
type Generator<T = any> = (
tree: Tree,
schema: T
) => void | GeneratorCallback | Promise<void | GeneratorCallback>;Helper functions for common generator tasks.
/**
* Converts strings to various naming conventions
*/
interface Names {
/** Converts to PascalCase for class names */
className: (name: string) => string;
/** Converts to camelCase for property names */
propertyName: (name: string) => string;
/** Converts to CONSTANT_CASE */
constantName: (name: string) => string;
/** Converts to kebab-case for file names */
fileName: (name: string) => string;
}
/**
* Naming utility functions
* @param name - Input string to convert
* @returns Object with various naming convention functions
*/
function names(name: string): Names;
/**
* Strips indentation from template strings
* @param strings - Template string parts
* @param values - Template interpolation values
* @returns String with normalized indentation
*/
function stripIndents(strings: TemplateStringsArray, ...values: any[]): string;
/**
* Gets project configuration with error handling
* @param tree - Virtual file tree
* @param projectName - Name of the project
* @returns Project configuration
*/
function readProjectConfiguration(tree: Tree, projectName: string): ProjectConfiguration;
/**
* Updates project configuration
* @param tree - Virtual file tree
* @param projectName - Name of the project
* @param config - Updated project configuration
*/
function updateProjectConfiguration(
tree: Tree,
projectName: string,
config: ProjectConfiguration
): void;Nx includes built-in generators for common tasks.
/**
* Connect workspace to Nx Cloud generator
* Configures workspace for remote caching and distributed execution
*/
interface ConnectToNxCloudGeneratorOptions {
/** Skip interactive prompts */
hideFormatLogs?: boolean;
/** Installation method */
installationSource?: string;
}
/**
* Generator function for connecting to Nx Cloud
* @param tree - Virtual file tree
* @param options - Generator options
* @returns Generator callback for package installation
*/
declare function connectToNxCloudGenerator(
tree: Tree,
options: ConnectToNxCloudGeneratorOptions
): GeneratorCallback;Functions and types for creating custom executors.
/**
* Executor function signature for promise-based executors
*/
type PromiseExecutor<T = any> = (
options: T,
context: ExecutorContext
) => Promise<{ success: boolean; [key: string]: any }>;
/**
* Executor function signature for async iterator executors
*/
type Executor<T = any> = (
options: T,
context: ExecutorContext
) => Promise<{ success: boolean; [key: string]: any }> |
AsyncIterableIterator<{ success: boolean; [key: string]: any }>;
/**
* Converts async iterator executor to promise-based executor
* @param executor - Async iterator executor
* @returns Promise-based executor
*/
function convertToPromiseExecutor<T>(
executor: Executor<T>
): PromiseExecutor<T>;
interface ExecutorContext {
/** Workspace root directory */
root: string;
/** Current working directory */
cwd: string;
/** Workspace configuration */
workspace: WorkspaceJsonConfiguration;
/** Whether verbose logging is enabled */
isVerbose: boolean;
/** Name of the project being executed */
projectName?: string;
/** Name of the target being executed */
targetName?: string;
/** Name of the configuration being used */
configurationName?: string;
/** Task graph for current execution */
taskGraph?: TaskGraph;
/** Hash of the current task */
hash?: string;
}Nx includes several built-in executors for common tasks.
/**
* No-operation executor for testing
* Does nothing and always succeeds
*/
interface NoopExecutorOptions {}
/**
* Run arbitrary shell commands executor
*/
interface RunCommandsExecutorOptions {
/** Command to execute */
command?: string;
/** Multiple commands to execute */
commands?: Array<{
command: string;
/** Command description */
description?: string;
/** Override working directory */
cwd?: string;
/** Environment variables */
env?: Record<string, string>;
/** Prefix for command output */
prefix?: string;
/** Background execution */
background?: boolean;
}>;
/** Working directory for commands */
cwd?: string;
/** Environment variables */
env?: Record<string, string>;
/** Run commands in parallel */
parallel?: boolean;
/** Maximum parallel processes */
maxParallel?: number;
/** Prefix for output */
prefix?: string;
/** Color output */
color?: boolean;
/** Read commands from package.json scripts */
readyWhen?: string;
}
/**
* Run package.json scripts executor
*/
interface RunScriptExecutorOptions {
/** Script name from package.json */
script: string;
/** Additional arguments to pass to script */
args?: string[];
/** Working directory */
cwd?: string;
/** Environment variables */
env?: Record<string, string>;
}import {
Tree,
formatFiles,
generateFiles,
installPackagesTask,
addDependenciesToPackageJson,
readProjectConfiguration,
updateProjectConfiguration,
names,
joinPathFragments,
logger
} from "nx/src/devkit-exports";
interface ComponentGeneratorSchema {
name: string;
project: string;
directory?: string;
skipTests?: boolean;
export?: boolean;
}
export default async function componentGenerator(
tree: Tree,
options: ComponentGeneratorSchema
) {
logger.info(`Generating component: ${options.name}`);
// Read project configuration
const projectConfig = readProjectConfiguration(tree, options.project);
const projectRoot = projectConfig.root;
const sourceRoot = projectConfig.sourceRoot || `${projectRoot}/src`;
// Determine component directory
const componentDir = options.directory
? joinPathFragments(sourceRoot, options.directory)
: joinPathFragments(sourceRoot, 'components');
// Generate component files from templates
const substitutions = {
...options,
...names(options.name),
template: '', // Remove __template__ from file names
skipTests: options.skipTests
};
generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
componentDir,
substitutions
);
// Add component to barrel export
if (options.export !== false) {
const indexPath = joinPathFragments(sourceRoot, 'index.ts');
const componentName = names(options.name).className;
const relativePath = `./${names(options.name).fileName}`;
if (tree.exists(indexPath)) {
const content = tree.read(indexPath, 'utf-8') || '';
const exportStatement = `export * from '${relativePath}';`;
if (!content.includes(exportStatement)) {
tree.write(indexPath, `${content}\n${exportStatement}`);
}
} else {
tree.write(indexPath, `export * from '${relativePath}';`);
}
}
// Add dependencies if needed
const installTask = addDependenciesToPackageJson(
tree,
{}, // dependencies
{
'@types/react': '^18.0.0'
} // devDependencies
);
// Format all generated files
await formatFiles(tree);
return installTask;
}
// Schema definition (schema.json)
export const schema = {
$schema: 'http://json-schema.org/schema',
type: 'object',
properties: {
name: {
type: 'string',
description: 'The name of the component.'
},
project: {
type: 'string',
description: 'The name of the project.',
$default: {
$source: 'projectName'
}
},
directory: {
type: 'string',
description: 'The directory at which to create the component file, relative to the project source root.'
},
skipTests: {
type: 'boolean',
description: 'Skip creating test files.',
default: false
},
export: {
type: 'boolean',
description: 'Add the component to the nearest export barrel.',
default: true
}
},
required: ['name', 'project'],
additionalProperties: false
};Template directory structure:
files/
├── __name@fileName__.component.ts__template__
├── __name@fileName__.component.spec.ts__template__ (if !skipTests)
└── __name@fileName__.stories.ts__template__Template content (__name@fileName__.component.ts__template__):
import React from 'react';
export interface <%= className %>Props {
title?: string;
}
export function <%= className %>(props: <%= className %>Props) {
return (
<div>
<h1>{props.title || '<%= name %>'}</h1>
</div>
);
}
export default <%= className %>;import { ExecutorContext, logger } from "nx/src/devkit-exports";
import { spawn } from 'child_process';
import { promisify } from 'util';
interface DockerBuildExecutorOptions {
dockerfile?: string;
context?: string;
tag: string;
buildArgs?: Record<string, string>;
target?: string;
push?: boolean;
registry?: string;
}
export default async function dockerBuildExecutor(
options: DockerBuildExecutorOptions,
context: ExecutorContext
): Promise<{ success: boolean }> {
logger.info(`Building Docker image for ${context.projectName}`);
const projectRoot = context.workspace.projects[context.projectName].root;
const dockerfile = options.dockerfile || 'Dockerfile';
const buildContext = options.context || projectRoot;
// Build Docker command
const dockerArgs = [
'build',
'-f', dockerfile,
'-t', options.tag
];
// Add build args
if (options.buildArgs) {
for (const [key, value] of Object.entries(options.buildArgs)) {
dockerArgs.push('--build-arg', `${key}=${value}`);
}
}
// Add target if specified
if (options.target) {
dockerArgs.push('--target', options.target);
}
// Add build context
dockerArgs.push(buildContext);
try {
// Execute docker build
await executeCommand('docker', dockerArgs, context.root);
logger.info(`✅ Docker image built successfully: ${options.tag}`);
// Push if requested
if (options.push) {
const pushTag = options.registry
? `${options.registry}/${options.tag}`
: options.tag;
await executeCommand('docker', ['push', pushTag], context.root);
logger.info(`✅ Docker image pushed successfully: ${pushTag}`);
}
return { success: true };
} catch (error) {
logger.error(`❌ Docker build failed: ${error.message}`);
return { success: false };
}
}
function executeCommand(command: string, args: string[], cwd: string): Promise<void> {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
cwd,
stdio: 'inherit'
});
child.on('close', (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`Command failed with exit code ${code}`));
}
});
child.on('error', reject);
});
}
// Executor schema (schema.json)
export const schema = {
$schema: 'http://json-schema.org/schema',
type: 'object',
properties: {
dockerfile: {
type: 'string',
description: 'Path to the Dockerfile',
default: 'Dockerfile'
},
context: {
type: 'string',
description: 'Build context directory'
},
tag: {
type: 'string',
description: 'Image tag'
},
buildArgs: {
type: 'object',
description: 'Build arguments',
additionalProperties: { type: 'string' }
},
target: {
type: 'string',
description: 'Build target'
},
push: {
type: 'boolean',
description: 'Push image after build',
default: false
},
registry: {
type: 'string',
description: 'Docker registry URL'
}
},
required: ['tag'],
additionalProperties: false
};import { runExecutor, ExecutorContext } from "nx/src/devkit-exports";
// Using run-commands executor
async function runCustomCommands(projectName: string) {
const context: ExecutorContext = {
root: process.cwd(),
cwd: process.cwd(),
workspace: readWorkspaceConfiguration(),
isVerbose: false,
projectName
};
const options = {
commands: [
{
command: 'echo "Starting build process..."',
description: 'Build start message'
},
{
command: 'npm run build',
description: 'Build application',
cwd: 'apps/my-app'
},
{
command: 'npm run test',
description: 'Run tests',
background: false
}
],
parallel: false,
color: true
};
const results = await runExecutor(
{ project: projectName, target: 'run-commands' },
options,
context
);
for await (const result of results) {
if (!result.success) {
throw new Error('Commands execution failed');
}
}
}import {
Tree,
formatFiles,
generateFiles,
readWorkspaceConfiguration,
updateWorkspaceConfiguration,
addProjectConfiguration,
names,
joinPathFragments
} from "nx/src/devkit-exports";
interface LibraryGeneratorSchema {
name: string;
directory?: string;
tags?: string;
buildable?: boolean;
publishable?: boolean;
}
export default async function libraryGenerator(
tree: Tree,
options: LibraryGeneratorSchema
) {
const normalizedOptions = normalizeOptions(tree, options);
// Add project configuration
addProjectConfiguration(tree, normalizedOptions.projectName, {
root: normalizedOptions.projectRoot,
sourceRoot: `${normalizedOptions.projectRoot}/src`,
projectType: 'library',
targets: {
build: {
executor: '@nx/js:tsc',
outputs: [`{options.outputPath}`],
options: {
outputPath: `dist/${normalizedOptions.projectRoot}`,
main: `${normalizedOptions.projectRoot}/src/index.ts`,
tsConfig: `${normalizedOptions.projectRoot}/tsconfig.lib.json`
}
},
test: {
executor: '@nx/jest:jest',
outputs: [`{workspaceRoot}/coverage/${normalizedOptions.projectRoot}`],
options: {
jestConfig: `${normalizedOptions.projectRoot}/jest.config.ts`
}
}
},
tags: normalizedOptions.parsedTags
});
// Generate library files
generateFiles(
tree,
joinPathFragments(__dirname, 'files'),
normalizedOptions.projectRoot,
{
...normalizedOptions,
template: ''
}
);
await formatFiles(tree);
}
function normalizeOptions(tree: Tree, options: LibraryGeneratorSchema) {
const name = names(options.name).fileName;
const projectDirectory = options.directory
? `${names(options.directory).fileName}/${name}`
: name;
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
const projectRoot = `libs/${projectDirectory}`;
const parsedTags = options.tags
? options.tags.split(',').map((s) => s.trim())
: [];
return {
...options,
projectName,
projectRoot,
projectDirectory,
parsedTags
};
}Install with Tessl CLI
npx tessl i tessl/npm-nx