Skip to content

Commit e67470b

Browse files
committed
fix(@angular/cli): use correct schematic defaults considering workspace
Fix #14986 This PR includes some refactoring to simplify the interaction of the `NodeWorkflow` and the `BaseWorkflow` with the registry. We were registering redundant `addPostTransform`s. Some of them in the constructor of the `BaseWorkflow`, which did not allow us to intercept `addUndefinedDefaults`. Additionally, we were setting the `validateOptionsWithSchema` transform multiple times unnecessarily. An issue left to fix is support for the `--project` option in schematic commands. Currently, `getProjectName` does not know about this option, since `createWorkflow` does not know how to parse the command line arguments. The parsing logic is implemented partially by the concrete implementation of the `SchematicCommand` template method.
1 parent 560ba2a commit e67470b

File tree

10 files changed

+70
-20
lines changed

10 files changed

+70
-20
lines changed

etc/api/angular_devkit/schematics/tools/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export declare type FileSystemCollection = Collection<FileSystemCollectionDescri
2828
export declare type FileSystemCollectionDesc = CollectionDescription<FileSystemCollectionDescription>;
2929

3030
export interface FileSystemCollectionDescription {
31+
readonly name: string;
3132
readonly path: string;
3233
readonly schematics: {
3334
[name: string]: FileSystemSchematicDesc;
@@ -92,9 +93,11 @@ export interface FileSystemSchematicDescription extends FileSystemSchematicJsonD
9293

9394
export interface FileSystemSchematicJsonDescription {
9495
readonly aliases?: string[];
96+
readonly collection: FileSystemCollectionDescription;
9597
readonly description: string;
9698
readonly extends?: string;
9799
readonly factory: string;
100+
readonly name: string;
98101
readonly schema?: string;
99102
}
100103

@@ -135,6 +138,7 @@ export declare class NodeWorkflow extends workflow.BaseWorkflow {
135138
dryRun?: boolean;
136139
root?: Path;
137140
packageManager?: string;
141+
registry?: schema.CoreSchemaRegistry;
138142
});
139143
}
140144

packages/angular/cli/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ ts_library(
3131
"//packages/angular_devkit/architect:node",
3232
"//packages/angular_devkit/core",
3333
"//packages/angular_devkit/core:node",
34+
"//packages/angular_devkit/schematics:formats",
3435
"//packages/angular_devkit/schematics",
3536
"//packages/angular_devkit/schematics:tools",
3637
# @typings: es2017.object

packages/angular/cli/models/schematic-command.ts

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import {
1717
} from '@angular-devkit/core';
1818
import { NodeJsSyncHost } from '@angular-devkit/core/node';
1919
import { DryRunEvent, UnsuccessfulWorkflowExecution, workflow } from '@angular-devkit/schematics';
20+
import { standardFormats } from '@angular-devkit/schematics/src/formats';
2021
import {
2122
FileSystemCollection,
2223
FileSystemEngine,
2324
FileSystemSchematic,
25+
FileSystemSchematicDescription,
2426
NodeWorkflow,
2527
validateOptionsWithSchema,
2628
} from '@angular-devkit/schematics/tools';
@@ -247,6 +249,7 @@ export abstract class SchematicCommand<
247249
dryRun,
248250
packageManager: getPackageManager(this.workspace.root),
249251
root: normalize(this.workspace.root),
252+
registry: new schema.CoreSchemaRegistry(standardFormats)
250253
});
251254
workflow.engineHost.registerContextTransform(context => {
252255
// This is run by ALL schematics, so if someone uses `externalSchematics(...)` which
@@ -262,15 +265,7 @@ export abstract class SchematicCommand<
262265
}
263266
});
264267

265-
workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(workflow.registry));
266-
267-
if (options.defaults) {
268-
workflow.registry.addPreTransform(schema.transforms.addUndefinedDefaults);
269-
} else {
270-
workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults);
271-
}
272-
273-
workflow.registry.addSmartDefaultProvider('projectName', () => {
268+
const getProjectName = () => {
274269
if (this._workspace) {
275270
try {
276271
return (
@@ -292,7 +287,24 @@ export abstract class SchematicCommand<
292287
}
293288

294289
return undefined;
295-
});
290+
};
291+
292+
workflow.engineHost.registerOptionsTransform(
293+
<T extends {}>(schematic: FileSystemSchematicDescription, current: T) => ({
294+
...getSchematicDefaults(schematic.collection.name, schematic.name, getProjectName()),
295+
...current,
296+
})
297+
);
298+
299+
if (options.defaults) {
300+
workflow.registry.addPreTransform(schema.transforms.addUndefinedDefaults);
301+
} else {
302+
workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults);
303+
}
304+
305+
workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(workflow.registry));
306+
307+
workflow.registry.addSmartDefaultProvider('projectName', getProjectName);
296308

297309
if (options.interactive !== false && isTTY()) {
298310
workflow.registry.usePromptProvider((definitions: Array<schema.PromptDefinition>) => {

packages/angular_devkit/schematics/BUILD

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,36 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "npm_package")
1313

1414
licenses(["notice"]) # MIT License
1515

16+
# @angular-devkit/schematics/src/formats
17+
18+
ts_library(
19+
name = "formats",
20+
srcs = glob(
21+
include = ["src/formats/**/*.ts"],
22+
exclude = [
23+
"src/formats/**/*_spec.ts",
24+
"src/formats/**/*_benchmark.ts",
25+
],
26+
),
27+
module_name = "@angular-devkit/schematics/src/formats",
28+
module_root = "src/formats/index.d.ts",
29+
deps = [
30+
"//packages/angular_devkit/core",
31+
"@npm//@types/node",
32+
"@npm//rxjs",
33+
"@npm//tslint",
34+
"@npm//typescript",
35+
],
36+
)
37+
1638
# @angular-devkit/schematics
1739

1840
ts_library(
1941
name = "schematics",
2042
srcs = glob(
2143
include = ["src/**/*.ts"],
2244
exclude = [
45+
"src/formats/**/*.ts",
2346
"src/**/*_spec.ts",
2447
"src/**/*_spec_large.ts",
2548
"src/**/*_benchmark.ts",
@@ -28,6 +51,7 @@ ts_library(
2851
module_name = "@angular-devkit/schematics",
2952
module_root = "src/index.d.ts",
3053
deps = [
54+
":formats",
3155
"//packages/angular_devkit/core",
3256
"//packages/angular_devkit/core:node", # TODO: get rid of this for 6.0
3357
"@npm//@types/node",
@@ -48,6 +72,7 @@ ts_library(
4872
tsconfig = "//:tsconfig-test.json",
4973
# @external_end
5074
deps = [
75+
"formats",
5176
":schematics",
5277
":testing",
5378
"//packages/angular_devkit/core",

packages/angular_devkit/schematics/src/workflow/base.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { EMPTY, Observable, Subject, concat, from, of, throwError } from 'rxjs';
1010
import { concatMap, defaultIfEmpty, ignoreElements, last, map, tap } from 'rxjs/operators';
1111
import { Engine, EngineHost, SchematicEngine } from '../engine';
1212
import { UnsuccessfulWorkflowExecution } from '../exception/exception';
13-
import { standardFormats } from '../formats';
13+
import { standardFormats } from '../formats/index';
1414
import { DryRunEvent, DryRunSink } from '../sink/dryrun';
1515
import { HostSink } from '../sink/host';
1616
import { Sink } from '../sink/sink';
@@ -63,7 +63,6 @@ export abstract class BaseWorkflow implements Workflow {
6363
constructor(options: BaseWorkflowOptions) {
6464
this._host = options.host;
6565
this._engineHost = options.engineHost;
66-
6766
if (options.registry) {
6867
this._registry = options.registry;
6968
} else {

packages/angular_devkit/schematics/tools/description.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919

2020

2121
export interface FileSystemCollectionDescription {
22+
readonly name: string;
2223
readonly path: string;
2324
readonly version?: string;
2425
readonly schematics: { [name: string]: FileSystemSchematicDesc };
@@ -28,6 +29,8 @@ export interface FileSystemCollectionDescription {
2829
export interface FileSystemSchematicJsonDescription {
2930
readonly aliases?: string[];
3031
readonly factory: string;
32+
readonly name: string;
33+
readonly collection: FileSystemCollectionDescription;
3134
readonly description: string;
3235
readonly schema?: string;
3336
readonly extends?: string;

packages/angular_devkit/schematics/tools/workflow/node-workflow.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import { Path, getSystemPath, virtualFs } from '@angular-devkit/core';
8+
import { Path, getSystemPath, schema, virtualFs } from '@angular-devkit/core';
99
import {
1010
workflow,
1111
} from '@angular-devkit/schematics'; // tslint:disable-line:no-implicit-dependencies
1212
import { BuiltinTaskExecutor } from '../../tasks/node';
1313
import { FileSystemEngine } from '../description';
1414
import { NodeModulesEngineHost } from '../node-module-engine-host';
15-
import { validateOptionsWithSchema } from '../schema-option-transform';
1615

1716
/**
1817
* A workflow specifically for Node tools.
@@ -24,7 +23,8 @@ export class NodeWorkflow extends workflow.BaseWorkflow {
2423
force?: boolean;
2524
dryRun?: boolean;
2625
root?: Path,
27-
packageManager?: string;
26+
packageManager?: string,
27+
registry?: schema.CoreSchemaRegistry
2828
},
2929
) {
3030
const engineHost = new NodeModulesEngineHost();
@@ -34,9 +34,9 @@ export class NodeWorkflow extends workflow.BaseWorkflow {
3434

3535
force: options.force,
3636
dryRun: options.dryRun,
37+
registry: options.registry
3738
});
3839

39-
engineHost.registerOptionsTransform(validateOptionsWithSchema(this._registry));
4040
engineHost.registerTaskExecutor(
4141
BuiltinTaskExecutor.NodePackage,
4242
{

packages/angular_devkit/schematics/tools/workflow/node-workflow_spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88
// tslint:disable:no-implicit-dependencies
9+
import { schema } from '@angular-devkit/core';
910
import { NodeJsSyncHost } from '@angular-devkit/core/node';
1011
import { NodeWorkflow } from '@angular-devkit/schematics/tools';
1112
import * as path from 'path';
@@ -14,7 +15,7 @@ import * as path from 'path';
1415
describe('NodeWorkflow', () => {
1516
// TODO: this test seems to either not work on windows or on linux.
1617
xit('works', done => {
17-
const workflow = new NodeWorkflow(new NodeJsSyncHost(), { dryRun: true });
18+
const workflow = new NodeWorkflow(new NodeJsSyncHost(), { dryRun: true, registry: new schema.CoreSchemaRegistry() });
1819
const collection = path.join(__dirname, '../../../../schematics/angular/package.json');
1920

2021
workflow.execute({

packages/angular_devkit/schematics_cli/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ ts_library(
2626
"//packages/angular_devkit/core",
2727
"//packages/angular_devkit/core:node",
2828
"//packages/angular_devkit/schematics",
29+
"//packages/angular_devkit/schematics:formats",
2930
"//packages/angular_devkit/schematics:tasks",
3031
"//packages/angular_devkit/schematics:tools",
3132
"@npm//@types/inquirer",

packages/angular_devkit/schematics_cli/bin/schematics.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
* found in the LICENSE file at https://angular.io/license
88
*/
99

10-
import 'symbol-observable';
1110
// symbol polyfill must go first
1211
// tslint:disable-next-line:ordered-imports import-groups
1312
import {
@@ -25,9 +24,11 @@ import {
2524
SchematicEngine,
2625
UnsuccessfulWorkflowExecution,
2726
} from '@angular-devkit/schematics';
28-
import { NodeModulesEngineHost, NodeWorkflow } from '@angular-devkit/schematics/tools';
27+
import { standardFormats } from '@angular-devkit/schematics/src/formats';
28+
import { NodeModulesEngineHost, NodeWorkflow, validateOptionsWithSchema } from '@angular-devkit/schematics/tools';
2929
import * as inquirer from 'inquirer';
3030
import * as minimist from 'minimist';
31+
import 'symbol-observable';
3132

3233

3334
/**
@@ -160,9 +161,12 @@ export async function main({
160161

161162
/** Create a Virtual FS Host scoped to where the process is being run. **/
162163
const fsHost = new virtualFs.ScopedHost(new NodeJsSyncHost(), normalize(process.cwd()));
164+
const registry = new schema.CoreSchemaRegistry(standardFormats);
163165

164166
/** Create the workflow that will be executed with this run. */
165-
const workflow = new NodeWorkflow(fsHost, { force, dryRun });
167+
const workflow = new NodeWorkflow(fsHost, { force, dryRun, registry });
168+
registry.addPostTransform(schema.transforms.addUndefinedDefaults);
169+
workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(registry));
166170

167171
// Indicate to the user when nothing has been done. This is automatically set to off when there's
168172
// a new DryRunEvent.

0 commit comments

Comments
 (0)