UNPKG

@angular-devkit/schematics

Version:
155 lines (154 loc) 8.98 kB
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import { logging } from '@angular-devkit/core'; import { Url } from 'node:url'; import { Observable } from 'rxjs'; import { FileEntry, MergeStrategy, Tree } from '../tree/interface'; import { Workflow } from '../workflow/interface'; export interface TaskConfiguration<T = {}> { name: string; dependencies?: Array<TaskId>; options?: T; } export interface TaskConfigurationGenerator<T = {}> { toConfiguration(): TaskConfiguration<T>; } export type TaskExecutor<T = {}> = (options: T | undefined, context: SchematicContext) => Promise<void> | Observable<void>; export interface TaskExecutorFactory<T> { readonly name: string; create(options?: T): Promise<TaskExecutor> | Observable<TaskExecutor>; } export interface TaskId { readonly id: number; } export interface TaskInfo { readonly id: number; readonly priority: number; readonly configuration: TaskConfiguration; readonly context: SchematicContext; } export interface ExecutionOptions { scope: string; interactive: boolean; } /** * The description (metadata) of a collection. This type contains every information the engine * needs to run. The CollectionMetadataT type parameter contains additional metadata that you * want to store while remaining type-safe. */ export type CollectionDescription<CollectionMetadataT extends object> = CollectionMetadataT & { readonly name: string; readonly extends?: string[]; }; /** * The description (metadata) of a schematic. This type contains every information the engine * needs to run. The SchematicMetadataT and CollectionMetadataT type parameters contain additional * metadata that you want to store while remaining type-safe. */ export type SchematicDescription<CollectionMetadataT extends object, SchematicMetadataT extends object> = SchematicMetadataT & { readonly collection: CollectionDescription<CollectionMetadataT>; readonly name: string; readonly private?: boolean; readonly hidden?: boolean; }; /** * The Host for the Engine. Specifically, the piece of the tooling responsible for resolving * collections and schematics descriptions. The SchematicMetadataT and CollectionMetadataT type * parameters contain additional metadata that you want to store while remaining type-safe. */ export interface EngineHost<CollectionMetadataT extends object, SchematicMetadataT extends object> { createCollectionDescription(name: string, requester?: CollectionDescription<CollectionMetadataT>): CollectionDescription<CollectionMetadataT>; listSchematicNames(collection: CollectionDescription<CollectionMetadataT>, includeHidden?: boolean): string[]; createSchematicDescription(name: string, collection: CollectionDescription<CollectionMetadataT>): SchematicDescription<CollectionMetadataT, SchematicMetadataT> | null; getSchematicRuleFactory<OptionT extends object>(schematic: SchematicDescription<CollectionMetadataT, SchematicMetadataT>, collection: CollectionDescription<CollectionMetadataT>): RuleFactory<OptionT>; createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source | null; transformOptions<OptionT extends object, ResultT extends object>(schematic: SchematicDescription<CollectionMetadataT, SchematicMetadataT>, options: OptionT, context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Observable<ResultT>; transformContext(context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT> | void; createTaskExecutor(name: string): Observable<TaskExecutor>; hasTaskExecutor(name: string): boolean; readonly defaultMergeStrategy?: MergeStrategy; } /** * The root Engine for creating and running schematics and collections. Everything related to * a schematic execution starts from this interface. * * CollectionMetadataT is, by default, a generic Collection metadata type. This is used throughout * the engine typings so that you can use a type that's merged into descriptions, while being * type-safe. * * SchematicMetadataT is a type that contains additional typing for the Schematic Description. */ export interface Engine<CollectionMetadataT extends object, SchematicMetadataT extends object> { createCollection(name: string, requester?: Collection<CollectionMetadataT, SchematicMetadataT>): Collection<CollectionMetadataT, SchematicMetadataT>; createContext(schematic: Schematic<CollectionMetadataT, SchematicMetadataT>, parent?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>, executionOptions?: Partial<ExecutionOptions>): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>; createSchematic(name: string, collection: Collection<CollectionMetadataT, SchematicMetadataT>): Schematic<CollectionMetadataT, SchematicMetadataT>; createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Source; transformOptions<OptionT extends object, ResultT extends object>(schematic: Schematic<CollectionMetadataT, SchematicMetadataT>, options: OptionT, context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>): Observable<ResultT>; executePostTasks(): Observable<void>; readonly defaultMergeStrategy: MergeStrategy; readonly workflow: Workflow | null; } /** * A Collection as created by the Engine. This should be used by the tool to create schematics, * or by rules to create other schematics as well. */ export interface Collection<CollectionMetadataT extends object, SchematicMetadataT extends object> { readonly description: CollectionDescription<CollectionMetadataT>; readonly baseDescriptions?: Array<CollectionDescription<CollectionMetadataT>>; createSchematic(name: string, allowPrivate?: boolean): Schematic<CollectionMetadataT, SchematicMetadataT>; listSchematicNames(includeHidden?: boolean): string[]; } /** * A Schematic as created by the Engine. This should be used by the tool to execute the main * schematics, or by rules to execute other schematics as well. */ export interface Schematic<CollectionMetadataT extends object, SchematicMetadataT extends object> { readonly description: SchematicDescription<CollectionMetadataT, SchematicMetadataT>; readonly collection: Collection<CollectionMetadataT, SchematicMetadataT>; call<OptionT extends object>(options: OptionT, host: Observable<Tree>, parentContext?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>, executionOptions?: Partial<ExecutionOptions>): Observable<Tree>; } /** * A SchematicContext. Contains information necessary for Schematics to execute some rules, for * example when using another schematics, as we need the engine and collection. */ export interface TypedSchematicContext<CollectionMetadataT extends object, SchematicMetadataT extends object> { readonly debug: boolean; readonly engine: Engine<CollectionMetadataT, SchematicMetadataT>; readonly logger: logging.LoggerApi; readonly schematic: Schematic<CollectionMetadataT, SchematicMetadataT>; readonly strategy: MergeStrategy; readonly interactive: boolean; addTask<T extends object>(task: TaskConfigurationGenerator<T>, dependencies?: Array<TaskId>): TaskId; } /** * This is used by the Schematics implementations in order to avoid needing to have typing from * the tooling. Schematics are not specific to a tool. */ export type SchematicContext = TypedSchematicContext<{}, {}>; /** * A rule factory, which is normally the way schematics are implemented. Returned by the tooling * after loading a schematic description. */ export type RuleFactory<T extends object> = (options: T) => Rule; /** * A FileOperator applies changes synchronously to a FileEntry. An async operator returns * asynchronously. We separate them so that the type system can catch early errors. */ export type FileOperator = (entry: FileEntry) => FileEntry | null; export type AsyncFileOperator = (tree: FileEntry) => Observable<FileEntry | null>; /** * A source is a function that generates a Tree from a specific context. A rule transforms a tree * into another tree from a specific context. In both cases, an Observable can be returned if * the source or the rule are asynchronous. Only the last Tree generated in the observable will * be used though. * * We obfuscate the context of Source and Rule because the schematic implementation should not * know which types is the schematic or collection metadata, as they are both tooling specific. */ export type Source = (context: SchematicContext) => Tree | Observable<Tree>; export type Rule = (tree: Tree, context: SchematicContext) => Tree | Observable<Tree> | Rule | Promise<void | Tree | Rule> | void;