/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/angular_devkit/schematics/src/workflow/base.ts
214 строк
6 KB
Alan Agius
refactor: modernize array and string last element access using `.at(-1)`
20 ноя 2025, 18:00
Не верифицирован
20 ноя 2025, 18:00
2671945
Код
Авторство
О чём код?
/** * @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, schema, virtualFs } from '@angular-devkit/core'; import { EMPTY, Observable, Subject, concat, concatMap, defaultIfEmpty, from, ignoreElements, last, of, tap, throwError, } from 'rxjs'; import { Engine, EngineHost, SchematicEngine } from '../engine'; import { UnsuccessfulWorkflowExecution } from '../exception/exception'; import { standardFormats } from '../formats'; import { DryRunEvent, DryRunSink } from '../sink/dryrun'; import { HostSink } from '../sink/host'; import { Sink } from '../sink/sink'; import { HostTree } from '../tree/host-tree'; import { Tree } from '../tree/interface'; import { LifeCycleEvent, RequiredWorkflowExecutionContext, Workflow, WorkflowExecutionContext, } from './interface'; export interface BaseWorkflowOptions { host: virtualFs.Host; engineHost: EngineHost<{}, {}>; registry?: schema.CoreSchemaRegistry; force?: boolean; dryRun?: boolean; } /** * Base class for workflows. Even without abstract methods, this class should not be used without * surrounding some initialization for the registry and host. This class only adds life cycle and * dryrun/force support. You need to provide any registry and task executors that you need to * support. * See {@see NodeWorkflow} implementation for how to make a specialized subclass of this. * TODO: add default set of CoreSchemaRegistry transforms. Once the job refactor is done, use that * as the support for tasks. * * @public */ export abstract class BaseWorkflow implements Workflow { protected _engine: Engine<{}, {}>; protected _engineHost: EngineHost<{}, {}>; protected _registry: schema.CoreSchemaRegistry; protected _host: virtualFs.Host; protected _reporter: Subject<DryRunEvent> = new Subject(); protected _lifeCycle: Subject<LifeCycleEvent> = new Subject(); protected _context: WorkflowExecutionContext[]; protected _force: boolean; protected _dryRun: boolean; constructor(options: BaseWorkflowOptions) { this._host = options.host; this._engineHost = options.engineHost; if (options.registry) { this._registry = options.registry; } else { this._registry = new schema.CoreSchemaRegistry(standardFormats); this._registry.addPostTransform(schema.transforms.addUndefinedDefaults); } this._engine = new SchematicEngine(this._engineHost, this); this._context = []; this._force = options.force || false; this._dryRun = options.dryRun || false; } get context(): Readonly<WorkflowExecutionContext> { const maybeContext = this._context.at(-1); if (!maybeContext) { throw new Error('Cannot get context when workflow is not executing...'); } return maybeContext; } get engine(): Engine<{}, {}> { return this._engine; } get engineHost(): EngineHost<{}, {}> { return this._engineHost; } get registry(): schema.SchemaRegistry { return this._registry; } get reporter(): Observable<DryRunEvent> { return this._reporter.asObservable(); } get lifeCycle(): Observable<LifeCycleEvent> { return this._lifeCycle.asObservable(); } protected _createSinks(): Sink[] { let error = false; const dryRunSink = new DryRunSink(this._host, this._force); const dryRunSubscriber = dryRunSink.reporter.subscribe((event) => { this._reporter.next(event); error = error || event.kind == 'error'; }); // We need two sinks if we want to output what will happen, and actually do the work. return [ dryRunSink, // Add a custom sink that clean ourselves and throws an error if an error happened. { commit() { dryRunSubscriber.unsubscribe(); if (error) { return throwError(new UnsuccessfulWorkflowExecution()); } return of(); }, }, // Only add a HostSink if this is not a dryRun. ...(!this._dryRun ? [new HostSink(this._host, this._force)] : []), ]; } execute( options: Partial<WorkflowExecutionContext> & RequiredWorkflowExecutionContext, ): Observable<void> { const parentContext = this._context.at(-1); if (!parentContext) { this._lifeCycle.next({ kind: 'start' }); } /** Create the collection and the schematic. */ const collection = this._engine.createCollection(options.collection); // Only allow private schematics if called from the same collection. const allowPrivate = options.allowPrivate || (parentContext && parentContext.collection === options.collection); const schematic = collection.createSchematic(options.schematic, allowPrivate); const sinks = this._createSinks(); this._lifeCycle.next({ kind: 'workflow-start' }); const context = { ...options, debug: options.debug || false, logger: options.logger || (parentContext && parentContext.logger) || new logging.NullLogger(), parentContext, }; this._context.push(context); return schematic .call(options.options, of(new HostTree(this._host)), { logger: context.logger }) .pipe( concatMap((tree: Tree) => { // Process all sinks. return concat( from(sinks).pipe( concatMap((sink) => sink.commit(tree)), ignoreElements(), ), of(tree), ); }), concatMap(() => { if (this._dryRun) { return EMPTY; } this._lifeCycle.next({ kind: 'post-tasks-start' }); return this._engine .executePostTasks() .pipe( tap({ complete: () => this._lifeCycle.next({ kind: 'post-tasks-end' }) }), defaultIfEmpty(undefined), last(), ); }), tap({ complete: () => { this._lifeCycle.next({ kind: 'workflow-end' }); this._context.pop(); if (this._context.length == 0) { this._lifeCycle.next({ kind: 'end' }); } }, }), ); } }