Angular Schematics is a powerful scaffolding and code generation library that enables developers to create, transform, and manage filesystem changes in a purely descriptive way without side effects. It provides a comprehensive set of tools including Schematics (generators), Collections (metadata containers), Rules (transformation functions), Trees (staging areas for changes), and Actions (atomic operations), all designed to work together in a deterministic and reversible manner.
npm install @angular-devkit/schematicsimport {
Rule,
Source,
Tree,
SchematicContext,
chain,
apply,
mergeWith,
template,
move,
url,
empty,
noop
} from "@angular-devkit/schematics";For engine functionality:
import {
Engine,
Schematic,
Collection,
SchematicEngine
} from "@angular-devkit/schematics";For sinks:
import {
HostSink,
DryRunSink
} from "@angular-devkit/schematics";For testing:
import {
SchematicTestRunner,
UnitTestTree
} from "@angular-devkit/schematics/testing";For format validation:
import {
standardFormats,
htmlSelectorFormat,
pathFormat
} from "@angular-devkit/schematics";For workflow:
import {
BaseWorkflow,
Workflow,
WorkflowExecutionContext
} from "@angular-devkit/schematics";For Node.js tools:
import {
NodeModulesEngineHost,
NodeModulesTestEngineHost,
validateOptionsWithSchema
} from "@angular-devkit/schematics/tools";import {
Rule,
SchematicContext,
Tree,
apply,
url,
template,
move,
chain,
mergeWith
} from "@angular-devkit/schematics";
// Define a simple schematic rule
function mySchematic(options: any): Rule {
return (tree: Tree, context: SchematicContext) => {
// Create a new file
tree.create('/hello.txt', 'Hello World!');
return tree;
};
}
// Complex schematic with templates
function generateComponent(options: { name: string; path: string }): Rule {
return chain([
// Apply templates from URL source
mergeWith(
apply(url('./files'), [
template({
...options,
classify: (str: string) => str.charAt(0).toUpperCase() + str.slice(1),
dasherize: (str: string) => str.replace(/[A-Z]/g, '-$&').toLowerCase()
}),
move(options.path)
])
)
]);
}Angular Schematics is built around several core concepts:
Core virtual file system operations for reading, writing, and manipulating files and directories in a staging environment.
interface Tree {
// Structural operations
branch(): Tree;
merge(other: Tree, strategy?: MergeStrategy): void;
// Read operations
read(path: string): Buffer | null;
readText(path: string): string;
readJson(path: string): JsonValue;
exists(path: string): boolean;
get(path: string): FileEntry | null;
getDir(path: string): DirEntry;
// Write operations
create(path: string, content: Buffer | string): void;
overwrite(path: string, content: Buffer | string): void;
delete(path: string): void;
rename(from: string, to: string): void;
// Advanced operations
beginUpdate(path: string): UpdateRecorder;
commitUpdate(record: UpdateRecorder): void;
apply(action: Action, strategy?: MergeStrategy): void;
visit(visitor: FileVisitor): void;
}
const Tree: TreeConstructor;
interface TreeConstructor {
empty(): Tree;
branch(tree: Tree): Tree;
merge(tree: Tree, other: Tree, strategy?: MergeStrategy): Tree;
partition(tree: Tree, predicate: FilePredicate<boolean>): [Tree, Tree];
optimize(tree: Tree): Tree;
}Composable transformation functions that modify trees in a functional, predictable way. Rules are the building blocks of schematics.
type Rule = (tree: Tree, context: SchematicContext) =>
Tree | Observable<Tree> | Rule | Promise<void | Tree | Rule> | void;
type Source = (context: SchematicContext) => Tree | Observable<Tree>;
type RuleFactory<T> = (options: T) => Rule;
// Core rule functions
function chain(rules: Iterable<Rule> | AsyncIterable<Rule>): Rule;
function apply(source: Source, rules: Rule[]): Source;
function mergeWith(source: Source, strategy?: MergeStrategy): Rule;
function noop(): Rule;
function empty(): Source;
function source(tree: Tree): Source;Template processing system for generating files with dynamic content using popular template syntaxes and path transformations.
function template<T>(options: T): Rule;
function contentTemplate<T>(options: T): Rule;
function pathTemplate<T>(options: T): Rule;
function applyTemplates<T>(options: T): Rule;
function renameTemplateFiles(): Rule;
function applyContentTemplate<T>(options: T): FileOperator;
function applyPathTemplate<T>(data: T, options?: PathTemplateOptions): FileOperator;Execution engine for running schematics with metadata, collections, and workflow orchestration. Handles schematic discovery and execution.
interface Engine<CollectionMetadataT, SchematicMetadataT> {
readonly defaultMergeStrategy: MergeStrategy;
readonly workflow: Workflow | null;
createCollection(name: string, requester?: Collection<CollectionMetadataT, SchematicMetadataT>): Collection<CollectionMetadataT, SchematicMetadataT>;
createSchematic(name: string, collection: Collection<CollectionMetadataT, SchematicMetadataT>): Schematic<CollectionMetadataT, SchematicMetadataT>;
executePostTasks(): Observable<void>;
}
interface SchematicContext {
readonly debug: boolean;
readonly engine: Engine<any, any>;
readonly logger: logging.LoggerApi;
readonly schematic: Schematic<any, any>;
readonly strategy: MergeStrategy;
readonly interactive: boolean;
addTask<T>(task: TaskConfigurationGenerator<T>, dependencies?: Array<TaskId>): TaskId;
}
class SchematicEngine<CollectionT, SchematicT> implements Engine<CollectionT, SchematicT> {
constructor(host: EngineHost<CollectionT, SchematicT>, workflow?: Workflow);
}Output destination system for committing tree changes to various targets including filesystem and dry-run reporting.
interface Sink {
commit(tree: Tree): Observable<void>;
}
class HostSink extends SimpleSinkBase {
constructor(host: virtualFs.Host, force?: boolean);
}
class DryRunSink extends SimpleSinkBase {
readonly reporter: Observable<DryRunEvent>;
constructor(host: virtualFs.Host | string, force?: boolean);
}Low-level atomic operations for file system modifications that can be applied to trees and tracked for optimization.
interface Action {
readonly id: number;
readonly parent: number;
readonly path: Path;
}
interface CreateFileAction extends Action {
readonly kind: 'c';
readonly content: Buffer;
}
interface OverwriteFileAction extends Action {
readonly kind: 'o';
readonly content: Buffer;
}
interface RenameFileAction extends Action {
readonly kind: 'r';
readonly to: Path;
}
interface DeleteFileAction extends Action {
readonly kind: 'd';
}
class ActionList {
create(path: Path, content: Buffer): void;
overwrite(path: Path, content: Buffer): void;
rename(path: Path, to: Path): void;
delete(path: Path): void;
optimize(): void;
}High-level orchestration for schematic execution with lifecycle management, error handling, and context propagation.
interface Workflow {
readonly context: Readonly<WorkflowExecutionContext>;
execute(options: Partial<WorkflowExecutionContext> & RequiredWorkflowExecutionContext): Observable<void>;
}
abstract class BaseWorkflow implements Workflow {
readonly engine: Engine<{}, {}>;
readonly engineHost: EngineHost<{}, {}>;
readonly registry: schema.SchemaRegistry;
readonly reporter: Observable<DryRunEvent>;
readonly lifeCycle: Observable<LifeCycleEvent>;
}Node.js-specific utilities for working with npm packages, module resolution, and testing environments.
class NodeModulesEngineHost extends FileSystemEngineHostBase {
constructor(paths?: string[]);
}
class NodeModulesTestEngineHost extends NodeModulesEngineHost {
readonly tasks: TaskConfiguration[];
registerCollection(collectionName: string, collectionPath: string): void;
registerTaskExecutor<T>(factory: TaskExecutorFactory<T>): void;
}
function validateOptionsWithSchema(registry: schema.SchemaRegistry): OptionTransform<object, object>;Testing infrastructure for unit testing schematics with isolated tree environments and collection execution.
class SchematicTestRunner {
constructor(collectionName: string, collectionPath: string);
readonly engine: SchematicEngine<{}, {}>;
readonly logger: logging.Logger;
runSchematic<SchematicSchemaT>(
schematicName: string,
options?: SchematicSchemaT,
tree?: Tree
): Promise<UnitTestTree>;
runSchematicAsync<SchematicSchemaT>(
schematicName: string,
options?: SchematicSchemaT,
tree?: Tree
): Observable<UnitTestTree>;
callRule(rule: Rule, tree: Tree, parentContext?: Partial<SchematicContext>): Observable<Tree>;
}
class UnitTestTree extends DelegateTree {
readonly files: string[];
readContent(path: string): string;
}Schema format validators for validating string patterns in schematic options.
const standardFormats: SchemaFormat[];
const htmlSelectorFormat: SchemaFormat;
const pathFormat: SchemaFormat;
function formatValidator(
data: JsonValue,
dataSchema: JsonObject,
formats: SchemaFormat[]
): Promise<SchemaValidatorResult>;enum MergeStrategy {
Default = 0,
Error = 1,
AllowOverwriteConflict = 2,
AllowCreationConflict = 4,
AllowDeleteConflict = 8,
ContentOnly = 2,
Overwrite = 14
}
type FilePredicate<T> = (path: Path, entry?: Readonly<FileEntry> | null) => T;
type FileVisitor = FilePredicate<void>;
type FileOperator = (entry: FileEntry) => FileEntry | null;
interface FileEntry {
readonly path: Path;
readonly content: Buffer;
}
interface DirEntry {
readonly parent: DirEntry | null;
readonly path: Path;
readonly subdirs: PathFragment[];
readonly subfiles: PathFragment[];
dir(name: PathFragment): DirEntry;
file(name: PathFragment): FileEntry | null;
visit(visitor: FileVisitor): void;
}
interface UpdateRecorder {
insertLeft(index: number, content: Buffer | string): UpdateRecorder;
insertRight(index: number, content: Buffer | string): UpdateRecorder;
remove(index: number, length: number): UpdateRecorder;
}class SchematicsException extends Error {}
class FileDoesNotExistException extends SchematicsException {
constructor(path: string);
}
class FileAlreadyExistException extends SchematicsException {
constructor(path: string);
}
class ContentHasMutatedException extends SchematicsException {
constructor(path: string);
}
class MergeConflictException extends SchematicsException {
constructor(path: string);
}