Skip to content

Commit cdbc198

Browse files
clydinvikerman
authored andcommitted
feat(@angular-devkit/schematics): allow schematic rules to control interactivity
1 parent 489f0e9 commit cdbc198

File tree

7 files changed

+59
-20
lines changed

7 files changed

+59
-20
lines changed

packages/angular_devkit/schematics/src/engine/engine.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
CollectionDescription,
1919
Engine,
2020
EngineHost,
21+
ExecutionOptions,
2122
Schematic,
2223
SchematicContext,
2324
SchematicDescription,
@@ -215,12 +216,20 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
215216
createContext(
216217
schematic: Schematic<CollectionT, SchematicT>,
217218
parent?: Partial<TypedSchematicContext<CollectionT, SchematicT>>,
219+
executionOptions?: Partial<ExecutionOptions>,
218220
): TypedSchematicContext<CollectionT, SchematicT> {
219221
// Check for inconsistencies.
220222
if (parent && parent.engine && parent.engine !== this) {
221223
throw new SchematicEngineConflictingException();
222224
}
223225

226+
let interactive = true;
227+
if (executionOptions && executionOptions.interactive != undefined) {
228+
interactive = executionOptions.interactive;
229+
} else if (parent && parent.interactive != undefined) {
230+
interactive = parent.interactive;
231+
}
232+
224233
let context: TypedSchematicContext<CollectionT, SchematicT> = {
225234
debug: parent && parent.debug || false,
226235
engine: this,
@@ -229,6 +238,7 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
229238
schematic,
230239
strategy: (parent && parent.strategy !== undefined)
231240
? parent.strategy : this.defaultMergeStrategy,
241+
interactive,
232242
addTask,
233243
};
234244

@@ -325,8 +335,9 @@ export class SchematicEngine<CollectionT extends object, SchematicT extends obje
325335
transformOptions<OptionT extends object, ResultT extends object>(
326336
schematic: Schematic<CollectionT, SchematicT>,
327337
options: OptionT,
338+
context?: TypedSchematicContext<CollectionT, SchematicT>,
328339
): Observable<ResultT> {
329-
return this._host.transformOptions<OptionT, ResultT>(schematic.description, options);
340+
return this._host.transformOptions<OptionT, ResultT>(schematic.description, options, context);
330341
}
331342

332343
createSourceFromUrl(url: Url, context: TypedSchematicContext<CollectionT, SchematicT>): Source {

packages/angular_devkit/schematics/src/engine/interface.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ export interface TaskInfo {
4141
readonly context: SchematicContext;
4242
}
4343

44+
export interface ExecutionOptions {
45+
interactive: boolean;
46+
}
47+
4448
/**
4549
* The description (metadata) of a collection. This type contains every information the engine
4650
* needs to run. The CollectionMetadataT type parameter contains additional metadata that you
@@ -92,6 +96,7 @@ export interface EngineHost<CollectionMetadataT extends object, SchematicMetadat
9296
transformOptions<OptionT extends object, ResultT extends object>(
9397
schematic: SchematicDescription<CollectionMetadataT, SchematicMetadataT>,
9498
options: OptionT,
99+
context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
95100
): Observable<ResultT>;
96101
transformContext(
97102
context: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
@@ -118,6 +123,7 @@ export interface Engine<CollectionMetadataT extends object, SchematicMetadataT e
118123
createContext(
119124
schematic: Schematic<CollectionMetadataT, SchematicMetadataT>,
120125
parent?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>,
126+
executionOptions?: Partial<ExecutionOptions>,
121127
): TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>;
122128
createSchematic(
123129
name: string,
@@ -130,6 +136,7 @@ export interface Engine<CollectionMetadataT extends object, SchematicMetadataT e
130136
transformOptions<OptionT extends object, ResultT extends object>(
131137
schematic: Schematic<CollectionMetadataT, SchematicMetadataT>,
132138
options: OptionT,
139+
context?: TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>,
133140
): Observable<ResultT>;
134141
executePostTasks(): Observable<void>;
135142

@@ -166,6 +173,7 @@ export interface Schematic<CollectionMetadataT extends object, SchematicMetadata
166173
options: OptionT,
167174
host: Observable<Tree>,
168175
parentContext?: Partial<TypedSchematicContext<CollectionMetadataT, SchematicMetadataT>>,
176+
executionOptions?: Partial<ExecutionOptions>,
169177
): Observable<Tree>;
170178
}
171179

@@ -181,6 +189,7 @@ export interface TypedSchematicContext<CollectionMetadataT extends object,
181189
readonly logger: logging.LoggerApi;
182190
readonly schematic: Schematic<CollectionMetadataT, SchematicMetadataT>;
183191
readonly strategy: MergeStrategy;
192+
readonly interactive: boolean;
184193
addTask<T>(task: TaskConfigurationGenerator<T>, dependencies?: Array<TaskId>): TaskId;
185194
}
186195

packages/angular_devkit/schematics/src/engine/schematic.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { Tree } from '../tree/interface';
1313
import {
1414
Collection,
1515
Engine,
16+
ExecutionOptions,
1617
RuleFactory,
1718
Schematic,
1819
SchematicDescription,
@@ -46,13 +47,14 @@ export class SchematicImpl<CollectionT extends object, SchematicT extends object
4647
options: OptionT,
4748
host: Observable<Tree>,
4849
parentContext?: Partial<TypedSchematicContext<CollectionT, SchematicT>>,
50+
executionOptions?: Partial<ExecutionOptions>,
4951
): Observable<Tree> {
50-
const context = this._engine.createContext(this, parentContext);
52+
const context = this._engine.createContext(this, parentContext, executionOptions);
5153

5254
return host
5355
.pipe(
5456
first(),
55-
concatMap(tree => this._engine.transformOptions(this, options).pipe(
57+
concatMap(tree => this._engine.transformOptions(this, options, context).pipe(
5658
map(o => [tree, o]),
5759
)),
5860
concatMap(([tree, transformedOptions]: [Tree, OptionT]) => {

packages/angular_devkit/schematics/src/rules/schematic.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88
import { of as observableOf } from 'rxjs';
99
import { last, map } from 'rxjs/operators';
10-
import { Rule, SchematicContext } from '../engine/interface';
10+
import { ExecutionOptions, Rule, SchematicContext } from '../engine/interface';
1111
import { MergeStrategy, Tree } from '../tree/interface';
1212
import { branch } from '../tree/static';
1313

@@ -19,14 +19,17 @@ import { branch } from '../tree/static';
1919
* @param schematicName The name of the schematic to run.
2020
* @param options The options to pass as input to the RuleFactory.
2121
*/
22-
export function externalSchematic<OptionT extends object>(collectionName: string,
23-
schematicName: string,
24-
options: OptionT): Rule {
22+
export function externalSchematic<OptionT extends object>(
23+
collectionName: string,
24+
schematicName: string,
25+
options: OptionT,
26+
executionOptions?: Partial<ExecutionOptions>,
27+
): Rule {
2528
return (input: Tree, context: SchematicContext) => {
2629
const collection = context.engine.createCollection(collectionName);
2730
const schematic = collection.createSchematic(schematicName);
2831

29-
return schematic.call(options, observableOf(branch(input)), context);
32+
return schematic.call(options, observableOf(branch(input)), context, executionOptions);
3033
};
3134
}
3235

@@ -37,12 +40,16 @@ export function externalSchematic<OptionT extends object>(collectionName: string
3740
* @param schematicName The name of the schematic to run.
3841
* @param options The options to pass as input to the RuleFactory.
3942
*/
40-
export function schematic<OptionT extends object>(schematicName: string, options: OptionT): Rule {
43+
export function schematic<OptionT extends object>(
44+
schematicName: string,
45+
options: OptionT,
46+
executionOptions?: Partial<ExecutionOptions>,
47+
): Rule {
4148
return (input: Tree, context: SchematicContext) => {
4249
const collection = context.schematic.collection;
4350
const schematic = collection.createSchematic(schematicName, true);
4451

45-
return schematic.call(options, observableOf(branch(input)), context).pipe(
52+
return schematic.call(options, observableOf(branch(input)), context, executionOptions).pipe(
4653
last(),
4754
map(x => {
4855
// We allow overwrite conflict here because they're the only merge conflict we particularly

packages/angular_devkit/schematics/tools/fallback-engine-host.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ export type FallbackSchematicDescription = {
3131
};
3232
export type FallbackContext =
3333
TypedSchematicContext<FallbackCollectionDescription, FallbackSchematicDescription>;
34-
export declare type OptionTransform<T extends object, R extends object> = (
35-
schematic: SchematicDescription<FallbackCollectionDescription, FallbackSchematicDescription>,
36-
options: T,
37-
) => Observable<R>;
3834

3935

4036
/**
@@ -93,9 +89,12 @@ export class FallbackEngineHost implements EngineHost<{}, {}> {
9389
transformOptions<OptionT extends object, ResultT extends object>(
9490
schematic: SchematicDescription<FallbackCollectionDescription, FallbackSchematicDescription>,
9591
options: OptionT,
92+
context?: FallbackContext,
9693
): Observable<ResultT> {
9794
return (observableOf(options)
98-
.pipe(...this._hosts.map(host => mergeMap(opt => host.transformOptions(schematic, opt))))
95+
.pipe(...this._hosts
96+
.map(host => mergeMap(opt => host.transformOptions(schematic, opt, context))),
97+
)
9998
) as {} as Observable<ResultT>;
10099
}
101100

packages/angular_devkit/schematics/tools/file-system-engine-host-base.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ import { readJsonFile } from './file-system-utility';
3939

4040

4141
export declare type OptionTransform<T extends object, R extends object>
42-
= (schematic: FileSystemSchematicDescription, options: T) => Observable<R>;
42+
= (
43+
schematic: FileSystemSchematicDescription,
44+
options: T,
45+
context?: FileSystemSchematicContext,
46+
) => Observable<R>;
4347

4448

4549
export class CollectionCannotBeResolvedException extends BaseException {
@@ -269,11 +273,12 @@ export abstract class FileSystemEngineHostBase implements
269273
transformOptions<OptionT extends object, ResultT extends object>(
270274
schematic: FileSystemSchematicDesc,
271275
options: OptionT,
276+
context?: FileSystemSchematicContext,
272277
): Observable<ResultT> {
273278
return (observableOf(options)
274279
.pipe(
275280
...this._transforms.map(tFn => mergeMap(opt => {
276-
const newOptions = tFn(schematic, opt);
281+
const newOptions = tFn(schematic, opt, context);
277282
if (isObservable(newOptions)) {
278283
return newOptions;
279284
} else {

packages/angular_devkit/schematics/tools/schema-option-transform.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import { deepCopy, schema } from '@angular-devkit/core';
99
import { Observable, of as observableOf } from 'rxjs';
1010
import { first, map, mergeMap } from 'rxjs/operators';
11-
import { FileSystemSchematicDescription } from './description';
11+
import { FileSystemSchematicContext, FileSystemSchematicDescription } from './description';
1212

1313
export class InvalidInputOptions<T = {}> extends schema.SchemaValidationException {
1414
constructor(options: T, errors: schema.SchemaValidatorError[]) {
@@ -21,16 +21,22 @@ export class InvalidInputOptions<T = {}> extends schema.SchemaValidationExceptio
2121

2222
// This can only be used in NodeJS.
2323
export function validateOptionsWithSchema(registry: schema.SchemaRegistry) {
24-
return <T extends {}>(schematic: FileSystemSchematicDescription, options: T): Observable<T> => {
24+
return <T extends {}>(
25+
schematic: FileSystemSchematicDescription,
26+
options: T,
27+
context?: FileSystemSchematicContext,
28+
): Observable<T> => {
2529
// Prevent a schematic from changing the options object by making a copy of it.
2630
options = deepCopy(options);
2731

32+
const withPrompts = context ? context.interactive : true;
33+
2834
if (schematic.schema && schematic.schemaJson) {
2935
// Make a deep copy of options.
3036
return registry
3137
.compile(schematic.schemaJson)
3238
.pipe(
33-
mergeMap(validator => validator(options)),
39+
mergeMap(validator => validator(options, { withPrompts })),
3440
first(),
3541
map(result => {
3642
if (!result.success) {

0 commit comments

Comments
 (0)