77 */
88
99// tslint:disable:no-global-tslint-disable no-any
10- import { JsonObject , experimental } from '@angular-devkit/core' ;
11- import { normalize , strings , tags , terminal , virtualFs } from '@angular-devkit/core' ;
10+ import {
11+ JsonObject ,
12+ experimental ,
13+ logging ,
14+ normalize ,
15+ schema ,
16+ strings ,
17+ tags ,
18+ terminal ,
19+ virtualFs ,
20+ } from '@angular-devkit/core' ;
1221import { NodeJsSyncHost } from '@angular-devkit/core/node' ;
13- import { DryRunEvent , UnsuccessfulWorkflowExecution } from '@angular-devkit/schematics' ;
14- import { NodeWorkflow } from '@angular-devkit/schematics/tools' ;
22+ import {
23+ Collection ,
24+ DryRunEvent ,
25+ Engine ,
26+ Schematic ,
27+ SchematicEngine ,
28+ UnsuccessfulWorkflowExecution ,
29+ formats ,
30+ workflow ,
31+ } from '@angular-devkit/schematics' ;
32+ import {
33+ FileSystemCollectionDesc ,
34+ FileSystemEngineHostBase ,
35+ FileSystemSchematicDesc ,
36+ NodeModulesEngineHost ,
37+ NodeWorkflow ,
38+ validateOptionsWithSchema ,
39+ } from '@angular-devkit/schematics/tools' ;
1540import { take } from 'rxjs/operators' ;
1641import { WorkspaceLoader } from '../models/workspace-loader' ;
17- import { getDefaultSchematicCollection , getPackageManager } from '../utilities/config' ;
18- import { getSchematicDefaults } from '../utilities/config' ;
19- import { getCollection , getSchematic } from '../utilities/schematics' ;
20- import { ArgumentStrategy , Command , Option } from './command' ;
42+ import {
43+ getDefaultSchematicCollection ,
44+ getPackageManager ,
45+ getSchematicDefaults ,
46+ } from '../utilities/config' ;
47+ import { ArgumentStrategy , Command , CommandContext , Option } from './command' ;
2148
2249export interface CoreSchematicOptions {
2350 dryRun : boolean ;
@@ -44,15 +71,35 @@ export interface GetOptionsResult {
4471 arguments : Option [ ] ;
4572}
4673
74+ export class UnknownCollectionError extends Error {
75+ constructor ( collectionName : string ) {
76+ super ( `Invalid collection (${ collectionName } ).` ) ;
77+ }
78+ }
79+
4780export abstract class SchematicCommand extends Command {
4881 readonly options : Option [ ] = [ ] ;
4982 readonly allowPrivateSchematics : boolean = false ;
5083 private _host = new NodeJsSyncHost ( ) ;
5184 private _workspace : experimental . workspace . Workspace ;
5285 private _deAliasedName : string ;
5386 private _originalOptions : Option [ ] ;
87+ private _engineHost : FileSystemEngineHostBase ;
88+ private _engine : Engine < FileSystemCollectionDesc , FileSystemSchematicDesc > ;
89+ private _workFlow : workflow . BaseWorkflow ;
5490 argStrategy = ArgumentStrategy . Nothing ;
5591
92+ constructor (
93+ context : CommandContext , logger : logging . Logger ,
94+ engineHost : FileSystemEngineHostBase = new NodeModulesEngineHost ( ) ) {
95+ super ( context , logger ) ;
96+ this . _engineHost = engineHost ;
97+ this . _engine = new SchematicEngine ( this . _engineHost ) ;
98+ const registry = new schema . CoreSchemaRegistry ( formats . standardFormats ) ;
99+ this . _engineHost . registerOptionsTransform (
100+ validateOptionsWithSchema ( registry ) ) ;
101+ }
102+
56103 protected readonly coreOptions : Option [ ] = [
57104 {
58105 name : 'dryRun' ,
@@ -75,6 +122,31 @@ export abstract class SchematicCommand extends Command {
75122 this . _loadWorkspace ( ) ;
76123 }
77124
125+ protected getEngineHost ( ) {
126+ return this . _engineHost ;
127+ }
128+ protected getEngine ( ) :
129+ Engine < FileSystemCollectionDesc , FileSystemSchematicDesc > {
130+ return this . _engine ;
131+ }
132+
133+ protected getCollection ( collectionName : string ) : Collection < any , any > {
134+ const engine = this . getEngine ( ) ;
135+ const collection = engine . createCollection ( collectionName ) ;
136+
137+ if ( collection === null ) {
138+ throw new UnknownCollectionError ( collectionName ) ;
139+ }
140+
141+ return collection ;
142+ }
143+
144+ protected getSchematic (
145+ collection : Collection < any , any > , schematicName : string ,
146+ allowPrivate ?: boolean ) : Schematic < any , any > {
147+ return collection . createSchematic ( schematicName , allowPrivate ) ;
148+ }
149+
78150 protected setPathOptions ( options : any , workingDir : string ) : any {
79151 if ( workingDir === '' ) {
80152 return { } ;
@@ -91,22 +163,40 @@ export abstract class SchematicCommand extends Command {
91163 } , { } ) ;
92164 }
93165
166+ /*
167+ * Runtime hook to allow specifying customized workflow
168+ */
169+ protected getWorkFlow ( options : RunSchematicOptions ) : workflow . BaseWorkflow {
170+ const { force, dryRun} = options ;
171+ const fsHost = new virtualFs . ScopedHost (
172+ new NodeJsSyncHost ( ) , normalize ( this . project . root ) ) ;
173+
174+ return new NodeWorkflow (
175+ fsHost as any ,
176+ {
177+ force,
178+ dryRun,
179+ packageManager : getPackageManager ( ) ,
180+ root : this . project . root ,
181+ } ,
182+ ) ;
183+ }
184+
185+ private _getWorkFlow ( options : RunSchematicOptions ) : workflow . BaseWorkflow {
186+ if ( ! this . _workFlow ) {
187+ this . _workFlow = this . getWorkFlow ( options ) ;
188+ }
189+
190+ return this . _workFlow ;
191+ }
192+
94193 protected runSchematic ( options : RunSchematicOptions ) {
95- const { collectionName, schematicName, debug, force , dryRun } = options ;
194+ const { collectionName, schematicName, debug, dryRun} = options ;
96195 let schematicOptions = this . removeCoreOptions ( options . schematicOptions ) ;
97196 let nothingDone = true ;
98197 let loggingQueue : string [ ] = [ ] ;
99198 let error = false ;
100- const fsHost = new virtualFs . ScopedHost ( new NodeJsSyncHost ( ) , normalize ( this . project . root ) ) ;
101- const workflow = new NodeWorkflow (
102- fsHost as any ,
103- {
104- force,
105- dryRun,
106- packageManager : getPackageManager ( ) ,
107- root : this . project . root ,
108- } ,
109- ) ;
199+ const workflow = this . _getWorkFlow ( options ) ;
110200
111201 const workingDir = process . cwd ( ) . replace ( this . project . root , '' ) . replace ( / \\ / g, '/' ) ;
112202 const pathOptions = this . setPathOptions ( schematicOptions , workingDir ) ;
@@ -249,9 +339,10 @@ export abstract class SchematicCommand extends Command {
249339
250340 const collectionName = options . collectionName || getDefaultSchematicCollection ( ) ;
251341
252- const collection = getCollection ( collectionName ) ;
342+ const collection = this . getCollection ( collectionName ) ;
253343
254- const schematic = getSchematic ( collection , options . schematicName , this . allowPrivateSchematics ) ;
344+ const schematic = this . getSchematic ( collection , options . schematicName ,
345+ this . allowPrivateSchematics ) ;
255346 this . _deAliasedName = schematic . description . name ;
256347
257348 if ( ! schematic . description . schemaJson ) {
0 commit comments