From a21d38a8fd9523d700da1a60d70387dbed3f94a3 Mon Sep 17 00:00:00 2001 From: mgechev Date: Thu, 11 Jul 2019 18:04:02 -0700 Subject: [PATCH] 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. --- .../schematics/tools/index.d.ts | 4 ++ .../angular/cli/models/schematic-command.ts | 38 +++++++++++++------ .../schematics/tools/description.ts | 3 ++ .../tools/workflow/node-workflow.ts | 8 ++-- .../tools/workflow/node-workflow_spec.ts | 1 + .../schematics_cli/bin/schematics.ts | 10 +++-- 6 files changed, 46 insertions(+), 18 deletions(-) diff --git a/etc/api/angular_devkit/schematics/tools/index.d.ts b/etc/api/angular_devkit/schematics/tools/index.d.ts index 9fc13dbca786..912d3bedcdd6 100644 --- a/etc/api/angular_devkit/schematics/tools/index.d.ts +++ b/etc/api/angular_devkit/schematics/tools/index.d.ts @@ -28,6 +28,7 @@ export declare type FileSystemCollection = Collection; export interface FileSystemCollectionDescription { + readonly name: string; readonly path: string; readonly schematics: { [name: string]: FileSystemSchematicDesc; @@ -92,9 +93,11 @@ export interface FileSystemSchematicDescription extends FileSystemSchematicJsonD export interface FileSystemSchematicJsonDescription { readonly aliases?: string[]; + readonly collection: FileSystemCollectionDescription; readonly description: string; readonly extends?: string; readonly factory: string; + readonly name: string; readonly schema?: string; } @@ -135,6 +138,7 @@ export declare class NodeWorkflow extends workflow.BaseWorkflow { dryRun?: boolean; root?: Path; packageManager?: string; + registry?: schema.CoreSchemaRegistry; }); } diff --git a/packages/angular/cli/models/schematic-command.ts b/packages/angular/cli/models/schematic-command.ts index c6d6fcb6a2a3..cac467501ec4 100644 --- a/packages/angular/cli/models/schematic-command.ts +++ b/packages/angular/cli/models/schematic-command.ts @@ -16,11 +16,17 @@ import { virtualFs, } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; -import { DryRunEvent, UnsuccessfulWorkflowExecution, workflow } from '@angular-devkit/schematics'; +import { + DryRunEvent, + UnsuccessfulWorkflowExecution, + formats, + workflow, +} from '@angular-devkit/schematics'; import { FileSystemCollection, FileSystemEngine, FileSystemSchematic, + FileSystemSchematicDescription, NodeWorkflow, validateOptionsWithSchema, } from '@angular-devkit/schematics/tools'; @@ -247,6 +253,7 @@ export abstract class SchematicCommand< dryRun, packageManager: getPackageManager(this.workspace.root), root: normalize(this.workspace.root), + registry: new schema.CoreSchemaRegistry(formats.standardFormats), }); workflow.engineHost.registerContextTransform(context => { // This is run by ALL schematics, so if someone uses `externalSchematics(...)` which @@ -262,15 +269,7 @@ export abstract class SchematicCommand< } }); - workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(workflow.registry)); - - if (options.defaults) { - workflow.registry.addPreTransform(schema.transforms.addUndefinedDefaults); - } else { - workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults); - } - - workflow.registry.addSmartDefaultProvider('projectName', () => { + const getProjectName = () => { if (this._workspace) { try { return ( @@ -292,7 +291,24 @@ export abstract class SchematicCommand< } return undefined; - }); + }; + + workflow.engineHost.registerOptionsTransform( + (schematic: FileSystemSchematicDescription, current: T) => ({ + ...getSchematicDefaults(schematic.collection.name, schematic.name, getProjectName()), + ...current, + }), + ); + + if (options.defaults) { + workflow.registry.addPreTransform(schema.transforms.addUndefinedDefaults); + } else { + workflow.registry.addPostTransform(schema.transforms.addUndefinedDefaults); + } + + workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(workflow.registry)); + + workflow.registry.addSmartDefaultProvider('projectName', getProjectName); if (options.interactive !== false && isTTY()) { workflow.registry.usePromptProvider((definitions: Array) => { diff --git a/packages/angular_devkit/schematics/tools/description.ts b/packages/angular_devkit/schematics/tools/description.ts index b95ecab8f813..52ee09d13f7c 100644 --- a/packages/angular_devkit/schematics/tools/description.ts +++ b/packages/angular_devkit/schematics/tools/description.ts @@ -19,6 +19,7 @@ import { export interface FileSystemCollectionDescription { + readonly name: string; readonly path: string; readonly version?: string; readonly schematics: { [name: string]: FileSystemSchematicDesc }; @@ -28,6 +29,8 @@ export interface FileSystemCollectionDescription { export interface FileSystemSchematicJsonDescription { readonly aliases?: string[]; readonly factory: string; + readonly name: string; + readonly collection: FileSystemCollectionDescription; readonly description: string; readonly schema?: string; readonly extends?: string; diff --git a/packages/angular_devkit/schematics/tools/workflow/node-workflow.ts b/packages/angular_devkit/schematics/tools/workflow/node-workflow.ts index 8649e9712108..9c55e7267995 100644 --- a/packages/angular_devkit/schematics/tools/workflow/node-workflow.ts +++ b/packages/angular_devkit/schematics/tools/workflow/node-workflow.ts @@ -5,14 +5,13 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import { Path, getSystemPath, virtualFs } from '@angular-devkit/core'; +import { Path, getSystemPath, schema, virtualFs } from '@angular-devkit/core'; import { workflow, } from '@angular-devkit/schematics'; // tslint:disable-line:no-implicit-dependencies import { BuiltinTaskExecutor } from '../../tasks/node'; import { FileSystemEngine } from '../description'; import { NodeModulesEngineHost } from '../node-module-engine-host'; -import { validateOptionsWithSchema } from '../schema-option-transform'; /** * A workflow specifically for Node tools. @@ -23,8 +22,9 @@ export class NodeWorkflow extends workflow.BaseWorkflow { options: { force?: boolean; dryRun?: boolean; - root?: Path, + root?: Path; packageManager?: string; + registry?: schema.CoreSchemaRegistry; }, ) { const engineHost = new NodeModulesEngineHost(); @@ -34,9 +34,9 @@ export class NodeWorkflow extends workflow.BaseWorkflow { force: options.force, dryRun: options.dryRun, + registry: options.registry }); - engineHost.registerOptionsTransform(validateOptionsWithSchema(this._registry)); engineHost.registerTaskExecutor( BuiltinTaskExecutor.NodePackage, { diff --git a/packages/angular_devkit/schematics/tools/workflow/node-workflow_spec.ts b/packages/angular_devkit/schematics/tools/workflow/node-workflow_spec.ts index df2af3c9d94c..22b25beb5786 100644 --- a/packages/angular_devkit/schematics/tools/workflow/node-workflow_spec.ts +++ b/packages/angular_devkit/schematics/tools/workflow/node-workflow_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ // tslint:disable:no-implicit-dependencies +import { schema } from '@angular-devkit/core'; import { NodeJsSyncHost } from '@angular-devkit/core/node'; import { NodeWorkflow } from '@angular-devkit/schematics/tools'; import * as path from 'path'; diff --git a/packages/angular_devkit/schematics_cli/bin/schematics.ts b/packages/angular_devkit/schematics_cli/bin/schematics.ts index 3ce98f7fa17f..255591e3728b 100644 --- a/packages/angular_devkit/schematics_cli/bin/schematics.ts +++ b/packages/angular_devkit/schematics_cli/bin/schematics.ts @@ -7,8 +7,8 @@ * found in the LICENSE file at https://angular.io/license */ -import 'symbol-observable'; // symbol polyfill must go first +import 'symbol-observable'; // tslint:disable-next-line:ordered-imports import-groups import { JsonObject, @@ -24,8 +24,9 @@ import { DryRunEvent, SchematicEngine, UnsuccessfulWorkflowExecution, + formats } from '@angular-devkit/schematics'; -import { NodeModulesEngineHost, NodeWorkflow } from '@angular-devkit/schematics/tools'; +import { NodeModulesEngineHost, NodeWorkflow, validateOptionsWithSchema } from '@angular-devkit/schematics/tools'; import * as inquirer from 'inquirer'; import * as minimist from 'minimist'; @@ -160,9 +161,12 @@ export async function main({ /** Create a Virtual FS Host scoped to where the process is being run. **/ const fsHost = new virtualFs.ScopedHost(new NodeJsSyncHost(), normalize(process.cwd())); + const registry = new schema.CoreSchemaRegistry(formats.standardFormats); /** Create the workflow that will be executed with this run. */ - const workflow = new NodeWorkflow(fsHost, { force, dryRun }); + const workflow = new NodeWorkflow(fsHost, { force, dryRun, registry }); + registry.addPostTransform(schema.transforms.addUndefinedDefaults); + workflow.engineHost.registerOptionsTransform(validateOptionsWithSchema(registry)); // Indicate to the user when nothing has been done. This is automatically set to off when there's // a new DryRunEvent.