From d2350c4e86e16c19b54a85230da5b908fb21888f Mon Sep 17 00:00:00 2001 From: Jason Jean Date: Fri, 1 Jun 2018 11:07:38 -0400 Subject: [PATCH] feat(schematics): fail gracefully and warn when project is missing a format npm script --- package.json | 2 +- packages/bazel/src/collection/app/index.ts | 97 +++++++------ packages/bazel/src/collection/app/schema.d.ts | 1 + packages/bazel/src/collection/app/schema.json | 5 + packages/bazel/src/collection/lib/index.ts | 15 +- packages/bazel/src/collection/lib/schema.d.ts | 1 + packages/bazel/src/collection/lib/schema.json | 77 +++++----- packages/bazel/src/collection/module/index.ts | 57 +++----- .../bazel/src/collection/module/schema.d.ts | 5 + .../bazel/src/collection/module/schema.json | 136 +++++++++--------- .../bazel/src/utils/rules/format-files.ts | 40 ++++++ packages/bazel/src/utils/tasks.ts | 25 ---- .../migrations/update-6-0-0/update-6-0-0.ts | 10 +- .../src/collection/application/index.ts | 15 +- .../src/collection/application/schema.d.ts | 1 + .../src/collection/application/schema.json | 5 + .../src/collection/downgrade-module/index.ts | 23 ++- .../collection/downgrade-module/schema.d.ts | 1 + .../collection/downgrade-module/schema.json | 15 +- .../src/collection/library/index.ts | 11 +- .../src/collection/library/library.spec.ts | 2 - .../src/collection/library/schema.d.ts | 1 + .../src/collection/library/schema.json | 11 +- .../schematics/src/collection/ngrx/index.ts | 28 ++-- .../src/collection/ngrx/schema.d.ts | 1 + .../src/collection/ngrx/schema.json | 27 ++-- .../src/collection/upgrade-module/index.ts | 23 ++- .../src/collection/upgrade-module/schema.d.ts | 1 + .../src/collection/upgrade-module/schema.json | 18 ++- .../collection/workspace-schematic/index.ts | 29 ++-- .../workspace-schematic/schema.d.ts | 1 + .../workspace-schematic/schema.json | 8 +- .../workspace-schematic.spec.ts | 2 - .../src/utils/rules/format-files.spec.ts | 66 +++++++++ .../src/utils/rules/format-files.ts | 42 ++++++ packages/schematics/src/utils/tasks.ts | 25 ---- 36 files changed, 481 insertions(+), 346 deletions(-) create mode 100644 packages/bazel/src/utils/rules/format-files.ts delete mode 100644 packages/bazel/src/utils/tasks.ts create mode 100644 packages/schematics/src/utils/rules/format-files.spec.ts create mode 100644 packages/schematics/src/utils/rules/format-files.ts delete mode 100644 packages/schematics/src/utils/tasks.ts diff --git a/package.json b/package.json index b6ed16bef2..66cc80036f 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "jest": { "modulePathIgnorePatterns": [ "tmp", - "files" + "collection/.*/files" ] } } diff --git a/packages/bazel/src/collection/app/index.ts b/packages/bazel/src/collection/app/index.ts index 60e5998015..befc184b94 100644 --- a/packages/bazel/src/collection/app/index.ts +++ b/packages/bazel/src/collection/app/index.ts @@ -24,7 +24,7 @@ import { addBootstrapToModule } from '@schematics/angular/utility/ast-utils'; import { insertImport } from '@schematics/angular/utility/route-utils'; import { addApp, readCliConfigFile } from '../../utils/fileutils'; import { offsetFromRoot } from '../../utils/common'; -import { wrapIntoFormat } from '../../utils/tasks'; +import { formatFiles } from '../../utils/rules/format-files'; interface NormalizedSchema extends Schema { fullName: string; @@ -236,57 +236,56 @@ ts_web_test( } export default function(schema: Schema): Rule { - return wrapIntoFormat(() => { - let npmScope = schema.npmScope; - if (!npmScope) { - npmScope = readCliConfigFile().project.npmScope; - } + let npmScope = schema.npmScope; + if (!npmScope) { + npmScope = readCliConfigFile().project.npmScope; + } - const options = normalizeOptions(schema); - const templateSource = apply(url('./files'), [ - template({ - utils: strings, - dot: '.', - tmpl: '', - offsetFromRoot: offsetFromRoot(options.fullPath), - ...(options as object), - npmScope - }) - ]); + const options = normalizeOptions(schema); + const templateSource = apply(url('./files'), [ + template({ + utils: strings, + dot: '.', + tmpl: '', + offsetFromRoot: offsetFromRoot(options.fullPath), + ...(options as object), + npmScope + }) + ]); - const selector = `${options.prefix}-root`; + const selector = `${options.prefix}-root`; - return chain([ - branchAndMerge(chain([mergeWith(templateSource)])), - externalSchematic('@schematics/angular', 'module', { - name: 'app', - commonModule: false, - flat: true, - routing: false, - sourceDir: options.fullPath, - spec: false - }), - externalSchematic('@schematics/angular', 'component', { - name: 'app', - selector: selector, - sourceDir: options.fullPath, - flat: true, - inlineStyle: options.inlineStyle, - inlineTemplate: options.inlineTemplate, - spec: !options.skipTests, - styleext: options.style, - viewEncapsulation: options.viewEncapsulation, - changeDetection: options.changeDetection - }), - updateComponentTemplate(options), - addBootstrap(options.fullPath), - addNxModule(options.fullPath), - addAppToAngularCliJson(options), - addBazelBuildFile(options.fullPath), - addAppToAngularCliJson(options), - options.routing ? addRouterRootConfiguration(options.fullPath) : noop() - ]); - }); + return chain([ + branchAndMerge(chain([mergeWith(templateSource)])), + externalSchematic('@schematics/angular', 'module', { + name: 'app', + commonModule: false, + flat: true, + routing: false, + sourceDir: options.fullPath, + spec: false + }), + externalSchematic('@schematics/angular', 'component', { + name: 'app', + selector: selector, + sourceDir: options.fullPath, + flat: true, + inlineStyle: options.inlineStyle, + inlineTemplate: options.inlineTemplate, + spec: !options.skipTests, + styleext: options.style, + viewEncapsulation: options.viewEncapsulation, + changeDetection: options.changeDetection + }), + updateComponentTemplate(options), + addBootstrap(options.fullPath), + addNxModule(options.fullPath), + addAppToAngularCliJson(options), + addBazelBuildFile(options.fullPath), + addAppToAngularCliJson(options), + options.routing ? addRouterRootConfiguration(options.fullPath) : noop(), + formatFiles(options) + ]); } function normalizeOptions(options: Schema): NormalizedSchema { diff --git a/packages/bazel/src/collection/app/schema.d.ts b/packages/bazel/src/collection/app/schema.d.ts index 347f5ed098..4cf167e3c3 100644 --- a/packages/bazel/src/collection/app/schema.d.ts +++ b/packages/bazel/src/collection/app/schema.d.ts @@ -1,5 +1,6 @@ export interface Schema { name: string; + skipFormat: boolean; npmScope?: string; directory?: string; sourceDir?: string; diff --git a/packages/bazel/src/collection/app/schema.json b/packages/bazel/src/collection/app/schema.json index af74788f56..4cc129f796 100644 --- a/packages/bazel/src/collection/app/schema.json +++ b/packages/bazel/src/collection/app/schema.json @@ -43,6 +43,11 @@ "type": "boolean", "default": false }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false + }, "skipTests": { "description": "Skip creating spec files.", "type": "boolean", diff --git a/packages/bazel/src/collection/lib/index.ts b/packages/bazel/src/collection/lib/index.ts index 6e08b9a1bf..095a072cb6 100644 --- a/packages/bazel/src/collection/lib/index.ts +++ b/packages/bazel/src/collection/lib/index.ts @@ -29,8 +29,8 @@ import { toFileName, toPropertyName } from '../../utils/name-utils'; -import { wrapIntoFormat } from '../../utils/tasks'; import { Schema } from './schema'; +import { formatFiles } from '../../utils/rules/format-files'; interface NormalizedSchema extends Schema { name: string; @@ -279,12 +279,11 @@ function validateLibSchema(schema) { } export default function(schema: Schema): Rule { - return wrapIntoFormat(() => { - const { templateSource, routingRules } = validateLibSchema(schema); + const { templateSource, routingRules } = validateLibSchema(schema); - return chain([ - branchAndMerge(chain([mergeWith(templateSource)])), - ...routingRules - ]); - }); + return chain([ + branchAndMerge(chain([mergeWith(templateSource)])), + ...routingRules, + formatFiles(schema) + ]); } diff --git a/packages/bazel/src/collection/lib/schema.d.ts b/packages/bazel/src/collection/lib/schema.d.ts index 0f62e3cbd1..8ada87bd68 100644 --- a/packages/bazel/src/collection/lib/schema.d.ts +++ b/packages/bazel/src/collection/lib/schema.d.ts @@ -1,5 +1,6 @@ export interface Schema { name: string; + skipFormat: boolean; directory?: string; sourceDir?: string; nomodule: boolean; diff --git a/packages/bazel/src/collection/lib/schema.json b/packages/bazel/src/collection/lib/schema.json index 4a121fffbc..888a832a59 100644 --- a/packages/bazel/src/collection/lib/schema.json +++ b/packages/bazel/src/collection/lib/schema.json @@ -1,38 +1,43 @@ { - "$schema": "http://json-schema.org/schema", - "id": "library", - "title": "Create a library", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Library name" - }, - "directory": { - "type": "string", - "description": "A directory where the app is placed" - }, - "nomodule": { - "type": "boolean", - "default": false, - "description": "Generate a simple TS library when set to true." - }, - "routing": { - "type": "boolean", - "default": false, - "description": "Add router configuration. See lazy for more information." - }, - "lazy": { - "type": "boolean", - "default": false, - "description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false." - }, - "parentModule": { - "type": "string", - "description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to." - } + "$schema": "http://json-schema.org/schema", + "id": "library", + "title": "Create a library", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Library name" }, - "required": [ - "name" - ] -} \ No newline at end of file + "directory": { + "type": "string", + "description": "A directory where the app is placed" + }, + "nomodule": { + "type": "boolean", + "default": false, + "description": "Generate a simple TS library when set to true." + }, + "routing": { + "type": "boolean", + "default": false, + "description": "Add router configuration. See lazy for more information." + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false + }, + "lazy": { + "type": "boolean", + "default": false, + "description": + "Add RouterModule.forChild when set to true, and a simple array of routes when set to false." + }, + "parentModule": { + "type": "string", + "description": + "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to." + } + }, + "required": ["name"] +} diff --git a/packages/bazel/src/collection/module/index.ts b/packages/bazel/src/collection/module/index.ts index 6816f4242b..13e4ea25d7 100644 --- a/packages/bazel/src/collection/module/index.ts +++ b/packages/bazel/src/collection/module/index.ts @@ -17,46 +17,27 @@ import { } from '@angular-devkit/schematics'; import { Schema } from './schema'; - -class FormatFiles implements TaskConfigurationGenerator { - toConfiguration(): TaskConfiguration { - return { - name: 'node-package', - options: { - command: 'run format', - quiet: true - } - }; - } -} - -function wrapIntoFormat(fn: Function): any { - return (host: Tree, context: SchematicContext) => { - context.addTask(new FormatFiles()); - return fn(context)(host, context); - }; -} +import { formatFiles } from '../../utils/rules/format-files'; export default function(schema: Schema): Rule { - return wrapIntoFormat(() => { - schema.path = schema.path ? normalize(schema.path) : schema.path; - const sourceDir = schema.sourceDir; - if (!sourceDir) { - throw new SchematicsException(`sourceDir option is required.`); - } + schema.path = schema.path ? normalize(schema.path) : schema.path; + const sourceDir = schema.sourceDir; + if (!sourceDir) { + throw new SchematicsException(`sourceDir option is required.`); + } - const templateSource = apply(url('./files'), [ - template({ - ...strings, - 'if-flat': (s: string) => (schema.flat ? '' : s), - ...schema - }), - move(sourceDir) - ]); + const templateSource = apply(url('./files'), [ + template({ + ...strings, + 'if-flat': (s: string) => (schema.flat ? '' : s), + ...schema + }), + move(sourceDir) + ]); - return chain([ - branchAndMerge(chain([mergeWith(templateSource)])), - externalSchematic('@schematics/angular', 'module', schema) - ]); - }); + return chain([ + branchAndMerge(chain([mergeWith(templateSource)])), + externalSchematic('@schematics/angular', 'module', schema), + formatFiles(schema) + ]); } diff --git a/packages/bazel/src/collection/module/schema.d.ts b/packages/bazel/src/collection/module/schema.d.ts index 5c4af2b278..9ccbe8958e 100644 --- a/packages/bazel/src/collection/module/schema.d.ts +++ b/packages/bazel/src/collection/module/schema.d.ts @@ -11,6 +11,11 @@ export interface Schema { * The name of the module. */ name: string; + + /** + * Skip formatting of files + */ + skipFormat: boolean; /** * The path to create the module. */ diff --git a/packages/bazel/src/collection/module/schema.json b/packages/bazel/src/collection/module/schema.json index ecd5c5143a..95a14252b8 100644 --- a/packages/bazel/src/collection/module/schema.json +++ b/packages/bazel/src/collection/module/schema.json @@ -1,70 +1,70 @@ { - "$schema": "http://json-schema.org/schema", - "id": "module", - "title": "NX Module Options Schema", - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "The name of the module." - }, - "path": { - "type": "string", - "format": "path", - "description": "The path to create the module.", - "default": "app", - "visible": false - }, - "sourceDir": { - "type": "string", - "format": "path", - "description": "The path of the source directory.", - "default": "src", - "visible": false - }, - "appRoot": { - "type": "string", - "format": "path", - "description": "The root of the application.", - "visible": false - }, - "routing": { - "type": "boolean", - "description": "Generates a routing module.", - "default": false - }, - "routingScope": { - "enum": [ - "Child", - "Root" - ], - "type": "string", - "description": "The scope for the generated routing.", - "default": "Child" - }, - "spec": { - "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true - }, - "flat": { - "type": "boolean", - "description": "Flag to indicate if a dir is created.", - "default": false - }, - "commonModule": { - "type": "boolean", - "description": "Flag to control whether the CommonModule is imported.", - "default": true, - "visible": false - }, - "module": { - "type": "string", - "description": "Allows specification of the declaring module.", - "alias": "m" - } + "$schema": "http://json-schema.org/schema", + "id": "module", + "title": "NX Module Options Schema", + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the module." }, - "required": [ - "name" - ] -} \ No newline at end of file + "path": { + "type": "string", + "format": "path", + "description": "The path to create the module.", + "default": "app", + "visible": false + }, + "sourceDir": { + "type": "string", + "format": "path", + "description": "The path of the source directory.", + "default": "src", + "visible": false + }, + "appRoot": { + "type": "string", + "format": "path", + "description": "The root of the application.", + "visible": false + }, + "routing": { + "type": "boolean", + "description": "Generates a routing module.", + "default": false + }, + "routingScope": { + "enum": ["Child", "Root"], + "type": "string", + "description": "The scope for the generated routing.", + "default": "Child" + }, + "spec": { + "type": "boolean", + "description": "Specifies if a spec file is generated.", + "default": true + }, + "flat": { + "type": "boolean", + "description": "Flag to indicate if a dir is created.", + "default": false + }, + "commonModule": { + "type": "boolean", + "description": "Flag to control whether the CommonModule is imported.", + "default": true, + "visible": false + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false + }, + "module": { + "type": "string", + "description": "Allows specification of the declaring module.", + "alias": "m" + } + }, + "required": ["name"] +} diff --git a/packages/bazel/src/utils/rules/format-files.ts b/packages/bazel/src/utils/rules/format-files.ts new file mode 100644 index 0000000000..eab7c27b45 --- /dev/null +++ b/packages/bazel/src/utils/rules/format-files.ts @@ -0,0 +1,40 @@ +import { readJsonInTree } from '../ast-utils'; +import { + TaskConfigurationGenerator, + TaskConfiguration, + Tree, + SchematicContext, + Rule, + noop +} from '@angular-devkit/schematics'; + +class FormatFiles implements TaskConfigurationGenerator { + toConfiguration(): TaskConfiguration { + return { + name: 'node-package', + options: { + packageName: 'run format -- --untracked', // workaround. we should define a custom task executor. + quiet: true + } + }; + } +} + +export function formatFiles(options: { skipFormat: boolean }): Rule { + if (options.skipFormat) { + return noop(); + } + return (host: Tree, context: SchematicContext) => { + const packageJson = readJsonInTree(host, 'package.json'); + if (packageJson.scripts && packageJson.scripts.format) { + context.addTask(new FormatFiles()); + } else { + context.logger.warn( + 'The "format" npm script is missing in your package.json' + ); + context.logger.warn( + 'Your files were not formated during this code generation' + ); + } + }; +} diff --git a/packages/bazel/src/utils/tasks.ts b/packages/bazel/src/utils/tasks.ts deleted file mode 100644 index 2a20e80898..0000000000 --- a/packages/bazel/src/utils/tasks.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - TaskConfigurationGenerator, - TaskConfiguration, - Tree, - SchematicContext -} from '@angular-devkit/schematics'; - -export class FormatFiles implements TaskConfigurationGenerator { - toConfiguration(): TaskConfiguration { - return { - name: 'node-package', - options: { - command: 'run format -- --untracked', - quiet: true - } - }; - } -} - -export function wrapIntoFormat(fn: Function): any { - return (host: Tree, context: SchematicContext) => { - context.addTask(new FormatFiles()); - return fn(context)(host, context); - }; -} diff --git a/packages/schematics/migrations/update-6-0-0/update-6-0-0.ts b/packages/schematics/migrations/update-6-0-0/update-6-0-0.ts index c74d705e80..dddf1694f8 100644 --- a/packages/schematics/migrations/update-6-0-0/update-6-0-0.ts +++ b/packages/schematics/migrations/update-6-0-0/update-6-0-0.ts @@ -8,11 +8,11 @@ import { stripIndents } from '@angular-devkit/core/src/utils/literals'; import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { readJsonInTree, updateJsonInTree } from '../../src/utils/ast-utils'; -import { FormatFiles } from '../../src/utils/tasks'; import { serializeJson, renameSync } from '../../src/utils/fileutils'; import { parseTarget, serializeTarget } from '../../src/utils/cli-config-utils'; import * as fs from 'fs'; -import { offsetFromRoot } from '@nrwl/schematics/src/utils/common'; +import { offsetFromRoot } from '../../src/utils/common'; +import { formatFiles } from '../../src/utils/rules/format-files'; function createKarma(host: Tree, project: any) { const offset = offsetFromRoot(project.root); @@ -648,9 +648,8 @@ const updateAngularJson = updateJsonInTree('angular.json', json => { return json; }); -function addTasks(host: Tree, context: SchematicContext) { +function addInstallTask(host: Tree, context: SchematicContext) { context.addTask(new NodePackageInstallTask()); - context.addTask(new FormatFiles()); } function checkCli6Upgraded(host: Tree) { @@ -688,6 +687,7 @@ export default function(): Rule { createAdditionalFiles, deleteUnneededFiles, patchLibIndexFiles, - addTasks + addInstallTask, + formatFiles() ]); } diff --git a/packages/schematics/src/collection/application/index.ts b/packages/schematics/src/collection/application/index.ts index d26360b880..6d02725a47 100644 --- a/packages/schematics/src/collection/application/index.ts +++ b/packages/schematics/src/collection/application/index.ts @@ -4,7 +4,8 @@ import { move, noop, Rule, - Tree + Tree, + SchematicContext } from '@angular-devkit/schematics'; import { Schema } from './schema'; import * as ts from 'typescript'; @@ -15,7 +16,6 @@ import { insert, updateJsonInTree } from '../../utils/ast-utils'; -import { wrapIntoFormat } from '../../utils/tasks'; import { toFileName } from '../../utils/name-utils'; import { offsetFromRoot } from '@nrwl/schematics/src/utils/common'; import { @@ -23,7 +23,7 @@ import { getWorkspacePath, replaceAppNameWithPath } from '@nrwl/schematics/src/utils/cli-config-utils'; -import { k } from '@angular/core/src/render3'; +import { formatFiles } from '../../utils/rules/format-files'; interface NormalizedSchema extends Schema { appProjectRoot: string; @@ -232,7 +232,7 @@ function updateE2eProject(options: NormalizedSchema): Rule { } export default function(schema: Schema): Rule { - return wrapIntoFormat((host: Tree) => { + return (host: Tree, context: SchematicContext) => { const options = normalizeOptions(host, schema); return chain([ externalSchematic('@schematics/angular', 'application', { @@ -248,9 +248,10 @@ export default function(schema: Schema): Rule { updateComponentTemplate(options), addNxModule(options), - options.routing ? addRouterRootConfiguration(options) : noop() - ]); - }); + options.routing ? addRouterRootConfiguration(options) : noop(), + formatFiles(options) + ])(host, context); + }; } function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { diff --git a/packages/schematics/src/collection/application/schema.d.ts b/packages/schematics/src/collection/application/schema.d.ts index 8dd1763cc0..17db0cc15b 100644 --- a/packages/schematics/src/collection/application/schema.d.ts +++ b/packages/schematics/src/collection/application/schema.d.ts @@ -1,5 +1,6 @@ export interface Schema { name: string; + skipFormat: boolean; inlineStyle?: boolean; inlineTemplate?: boolean; viewEncapsulation?: 'Emulated' | 'Native' | 'None'; diff --git a/packages/schematics/src/collection/application/schema.json b/packages/schematics/src/collection/application/schema.json index ec426d3e78..a2aff75a01 100644 --- a/packages/schematics/src/collection/application/schema.json +++ b/packages/schematics/src/collection/application/schema.json @@ -55,6 +55,11 @@ "default": false, "alias": "S" }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false + }, "skipPackageJson": { "type": "boolean", "default": false, diff --git a/packages/schematics/src/collection/downgrade-module/index.ts b/packages/schematics/src/collection/downgrade-module/index.ts index 8cfdef456e..f6e789a91c 100644 --- a/packages/schematics/src/collection/downgrade-module/index.ts +++ b/packages/schematics/src/collection/downgrade-module/index.ts @@ -8,7 +8,7 @@ import { } from '../../utils/ast-utils'; import { Schema } from './schema'; import { addUpgradeToPackageJson } from '../../utils/common'; -import { wrapIntoFormat } from '../../utils/tasks'; +import { formatFiles } from '../../utils/rules/format-files'; function updateMain(angularJsImport: string, options: Schema): Rule { return (host: Tree) => { @@ -89,16 +89,15 @@ function addEntryComponentsToModule(options: Schema): Rule { } export default function(options: Schema): Rule { - return wrapIntoFormat(() => { - const angularJsImport = options.angularJsImport - ? options.angularJsImport - : options.name; + const angularJsImport = options.angularJsImport + ? options.angularJsImport + : options.name; - return chain([ - updateMain(angularJsImport, options), - addEntryComponentsToModule(options), - rewriteBootstrapLogic(options), - options.skipPackageJson ? noop() : addUpgradeToPackageJson() - ]); - }); + return chain([ + updateMain(angularJsImport, options), + addEntryComponentsToModule(options), + rewriteBootstrapLogic(options), + options.skipPackageJson ? noop() : addUpgradeToPackageJson(), + formatFiles(options) + ]); } diff --git a/packages/schematics/src/collection/downgrade-module/schema.d.ts b/packages/schematics/src/collection/downgrade-module/schema.d.ts index ff5167442e..294838c268 100644 --- a/packages/schematics/src/collection/downgrade-module/schema.d.ts +++ b/packages/schematics/src/collection/downgrade-module/schema.d.ts @@ -1,6 +1,7 @@ export interface Schema { angularJsImport: string; name: string; + skipFormat: boolean; skipPackageJson: boolean; project: string; } diff --git a/packages/schematics/src/collection/downgrade-module/schema.json b/packages/schematics/src/collection/downgrade-module/schema.json index 7991dee52f..94e7d02649 100644 --- a/packages/schematics/src/collection/downgrade-module/schema.json +++ b/packages/schematics/src/collection/downgrade-module/schema.json @@ -18,15 +18,20 @@ }, "angularJsImport": { "type": "string", - "description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)." + "description": + "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)." + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false }, "skipPackageJson": { "type": "boolean", "default": false, - "description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)" + "description": + "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)" } }, - "required": [ - "project" - ] + "required": ["project"] } diff --git a/packages/schematics/src/collection/library/index.ts b/packages/schematics/src/collection/library/index.ts index 2c182ad5b8..eca1255aca 100644 --- a/packages/schematics/src/collection/library/index.ts +++ b/packages/schematics/src/collection/library/index.ts @@ -20,7 +20,6 @@ import { updateJsonInTree } from '../../utils/ast-utils'; import { offsetFromRoot } from '../../utils/common'; -import { wrapIntoFormat } from '../../utils/tasks'; import { toClassName, toFileName, @@ -32,6 +31,7 @@ import { replaceAppNameWithPath } from '@nrwl/schematics/src/utils/cli-config-utils'; import * as fs from 'fs'; +import { formatFiles } from '../../utils/rules/format-files'; interface NormalizedSchema extends Schema { name: string; @@ -337,7 +337,7 @@ function updateTsConfig(options: NormalizedSchema): Rule { } export default function(schema: Schema): Rule { - return wrapIntoFormat((host: Tree) => { + return (host: Tree, context: SchematicContext) => { const options = normalizeOptions(host, schema); if (!options.routing && options.lazy) { throw new Error(`routing must be set`); @@ -402,9 +402,10 @@ export default function(schema: Schema): Rule { : noop(), options.routing && !options.lazy && options.parentModule ? addChildren(options) - : noop() - ]); - }); + : noop(), + formatFiles(options) + ])(host, context); + }; } function normalizeOptions(host: Tree, options: Schema): NormalizedSchema { diff --git a/packages/schematics/src/collection/library/library.spec.ts b/packages/schematics/src/collection/library/library.spec.ts index 9824689766..d0c657d607 100644 --- a/packages/schematics/src/collection/library/library.spec.ts +++ b/packages/schematics/src/collection/library/library.spec.ts @@ -17,8 +17,6 @@ describe('lib', () => { beforeEach(() => { appTree = new VirtualTree(); appTree = createEmptyWorkspace(appTree); - - schematicRunner.logger.subscribe(s => console.log(s)); }); describe('not nested', () => { diff --git a/packages/schematics/src/collection/library/schema.d.ts b/packages/schematics/src/collection/library/schema.d.ts index 3c12b5e507..ea2d3e600f 100644 --- a/packages/schematics/src/collection/library/schema.d.ts +++ b/packages/schematics/src/collection/library/schema.d.ts @@ -1,5 +1,6 @@ export interface Schema { name: string; + skipFormat: boolean; directory?: string; sourceDir?: string; publishable: boolean; diff --git a/packages/schematics/src/collection/library/schema.json b/packages/schematics/src/collection/library/schema.json index 278d347e40..d6da253c12 100644 --- a/packages/schematics/src/collection/library/schema.json +++ b/packages/schematics/src/collection/library/schema.json @@ -27,6 +27,11 @@ "description": "The prefix to apply to generated selectors.", "alias": "p" }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false + }, "skipPackageJson": { "type": "boolean", "default": false, @@ -45,11 +50,13 @@ "lazy": { "type": "boolean", "default": false, - "description": "Add RouterModule.forChild when set to true, and a simple array of routes when set to false." + "description": + "Add RouterModule.forChild when set to true, and a simple array of routes when set to false." }, "parentModule": { "type": "string", - "description": "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to." + "description": + "Update the router configuration of the parent module using loadChildren or children, depending on what `lazy` is set to." }, "tags": { "type": "string", diff --git a/packages/schematics/src/collection/ngrx/index.ts b/packages/schematics/src/collection/ngrx/index.ts index e3b1a25680..6635dcd1ad 100644 --- a/packages/schematics/src/collection/ngrx/index.ts +++ b/packages/schematics/src/collection/ngrx/index.ts @@ -3,14 +3,14 @@ import { externalSchematic, move, Rule, - Tree + Tree, + SchematicContext } from '@angular-devkit/schematics'; import { Schema } from './schema'; import * as path from 'path'; import { names, toFileName } from '../../utils/name-utils'; -import { wrapIntoFormat } from '../../utils/tasks'; import { addImportsToModule, @@ -20,6 +20,7 @@ import { updateNgrxEffects, updateNgrxReducers } from './rules'; +import { formatFiles } from '../../utils/rules/format-files'; function effectsSpec(className: string, fileName: string) { return ` @@ -88,9 +89,9 @@ describe('${propertyName}Reducer', () => { * Rule to generate the Nx 'ngrx' Collection */ export default function generateNgrxCollection(_options: Schema): Rule { - return wrapIntoFormat((host: Tree) => { + return (host: Tree, context: SchematicContext) => { const options = normalizeOptions(_options); - const context: RequestContext = { + const requestContext: RequestContext = { featureName: options.name, moduleDir: path.dirname(options.module), options, @@ -100,16 +101,16 @@ export default function generateNgrxCollection(_options: Schema): Rule { const fileGeneration = options.onlyEmptyRoot ? [] : [ - generateNgrxFiles(context), - generateNxFiles(context), - updateNgrxActions(context), - updateNgrxReducers(context), - updateNgrxEffects(context) + generateNgrxFiles(requestContext), + generateNxFiles(requestContext), + updateNgrxActions(requestContext), + updateNgrxReducers(requestContext), + updateNgrxEffects(requestContext) ]; const moduleModification = options.onlyAddFiles ? [] - : [addImportsToModule(context)]; + : [addImportsToModule(requestContext)]; const packageJsonModification = options.skipPackageJson ? [] : [addNgRxToPackageJson()]; @@ -117,9 +118,10 @@ export default function generateNgrxCollection(_options: Schema): Rule { return chain([ ...fileGeneration, ...moduleModification, - ...packageJsonModification - ]); - }); + ...packageJsonModification, + formatFiles(options) + ])(host, context); + }; } // ******************************************************** diff --git a/packages/schematics/src/collection/ngrx/schema.d.ts b/packages/schematics/src/collection/ngrx/schema.d.ts index 2c5a2db262..c1e4df5528 100644 --- a/packages/schematics/src/collection/ngrx/schema.d.ts +++ b/packages/schematics/src/collection/ngrx/schema.d.ts @@ -2,6 +2,7 @@ export interface Schema { name: string; onlyEmptyRoot: boolean; root: boolean; + skipFormat: boolean; onlyAddFiles: boolean; module: string; skipPackageJson: boolean; diff --git a/packages/schematics/src/collection/ngrx/schema.json b/packages/schematics/src/collection/ngrx/schema.json index 775718766e..933a9150fe 100644 --- a/packages/schematics/src/collection/ngrx/schema.json +++ b/packages/schematics/src/collection/ngrx/schema.json @@ -14,35 +14,44 @@ }, "module": { "type": "string", - "description": "Path to ngModule; host directory will contain the new '+state' directory (e.g., src/libs/mylib/mylib.module.ts)." + "description": + "Path to ngModule; host directory will contain the new '+state' directory (e.g., src/libs/mylib/mylib.module.ts)." }, "onlyAddFiles": { "type": "boolean", "default": false, - "description": "Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)." + "description": + "Only add new NgRx files, without changing the module file (e.g., --onlyAddFiles)." }, "directory": { "type": "string", "default": "+state", - "description": "The directory name for the ngrx files: contains actions, effects, reducers. (e.g., +state)" + "description": + "The directory name for the ngrx files: contains actions, effects, reducers. (e.g., +state)" }, "root": { "type": "boolean", "default": false, - "description": "Add StoreModule.forRoot and EffectsModule.forRoot instead of forFeature (e.g., --root)." + "description": + "Add StoreModule.forRoot and EffectsModule.forRoot instead of forFeature (e.g., --root)." }, "onlyEmptyRoot": { "type": "boolean", "default": false, - "description": "Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)." + "description": + "Do not generate any files. Only generate StoreModule.forRoot and EffectsModule.forRoot (e.g., --onlyEmptyRoot)." + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false }, "skipPackageJson": { "type": "boolean", "default": false, - "description": "Do not add ngrx dependencies to package.json (e.g., --skipPackageJson)" + "description": + "Do not add ngrx dependencies to package.json (e.g., --skipPackageJson)" } }, - "required": [ - "module" - ] + "required": ["module"] } diff --git a/packages/schematics/src/collection/upgrade-module/index.ts b/packages/schematics/src/collection/upgrade-module/index.ts index d033125150..f12ed44904 100644 --- a/packages/schematics/src/collection/upgrade-module/index.ts +++ b/packages/schematics/src/collection/upgrade-module/index.ts @@ -28,7 +28,7 @@ import { import { insertImport } from '@schematics/angular/utility/route-utils'; import { Schema } from './schema'; import { addUpgradeToPackageJson } from '../../utils/common'; -import { wrapIntoFormat } from '../../utils/tasks'; +import { formatFiles } from '../../utils/rules/format-files'; function addImportsToModule(options: Schema): Rule { return (host: Tree) => { @@ -125,16 +125,15 @@ function createFiles(angularJsImport: string, options: Schema): Rule { } export default function(options: Schema): Rule { - return wrapIntoFormat(() => { - const angularJsImport = options.angularJsImport - ? options.angularJsImport - : options.name; + const angularJsImport = options.angularJsImport + ? options.angularJsImport + : options.name; - return chain([ - createFiles(angularJsImport, options), - addImportsToModule(options), - addNgDoBootstrapToModule(options), - options.skipPackageJson ? noop() : addUpgradeToPackageJson() - ]); - }); + return chain([ + createFiles(angularJsImport, options), + addImportsToModule(options), + addNgDoBootstrapToModule(options), + options.skipPackageJson ? noop() : addUpgradeToPackageJson(), + formatFiles(options) + ]); } diff --git a/packages/schematics/src/collection/upgrade-module/schema.d.ts b/packages/schematics/src/collection/upgrade-module/schema.d.ts index 748844d013..9684909799 100644 --- a/packages/schematics/src/collection/upgrade-module/schema.d.ts +++ b/packages/schematics/src/collection/upgrade-module/schema.d.ts @@ -3,6 +3,7 @@ export interface Schema { angularJsImport: string; angularJsCmpSelector: string; name: string; + skipFormat: boolean; skipPackageJson: boolean; router: boolean; } diff --git a/packages/schematics/src/collection/upgrade-module/schema.json b/packages/schematics/src/collection/upgrade-module/schema.json index 3feb177ac9..bd5aee567a 100644 --- a/packages/schematics/src/collection/upgrade-module/schema.json +++ b/packages/schematics/src/collection/upgrade-module/schema.json @@ -18,16 +18,24 @@ }, "angularJsImport": { "type": "string", - "description": "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)." + "description": + "Import expression of the AngularJS application (e.g., --angularJsImport=some_node_module/my_app)." }, "angularJsCmpSelector": { "type": "string", - "description": "The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)" + "description": + "The selector of an AngularJS component (e.g., --angularJsCmpSelector=myComponent)" + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false }, "skipPackageJson": { "type": "boolean", "default": false, - "description": "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)" + "description": + "Do not add @angular/upgrade to package.json (e.g., --skipPackageJson)" }, "router": { "type": "boolean", @@ -35,7 +43,5 @@ "description": "Sets up router synchronization (e.g., --router)" } }, - "required": [ - "project" - ] + "required": ["project"] } diff --git a/packages/schematics/src/collection/workspace-schematic/index.ts b/packages/schematics/src/collection/workspace-schematic/index.ts index f51f26c944..f5d70d24b3 100644 --- a/packages/schematics/src/collection/workspace-schematic/index.ts +++ b/packages/schematics/src/collection/workspace-schematic/index.ts @@ -9,22 +9,23 @@ import { move } from '@angular-devkit/schematics'; import { Schema } from './schema'; -import { wrapIntoFormat } from '@nrwl/schematics/src/utils/tasks'; -import { toFileName } from '@nrwl/schematics/src/utils/name-utils'; +import { toFileName } from '../../utils/name-utils'; +import { formatFiles } from '../../utils/rules/format-files'; export default function(schema: Schema): Rule { - return wrapIntoFormat(() => { - const options = normalizeOptions(schema); - const templateSource = apply(url('./files'), [ - template({ - dot: '.', - tmpl: '', - ...(options as any) - }), - move('tools/schematics') - ]); - return chain([branchAndMerge(chain([mergeWith(templateSource)]))]); - }); + const options = normalizeOptions(schema); + const templateSource = apply(url('./files'), [ + template({ + dot: '.', + tmpl: '', + ...(options as any) + }), + move('tools/schematics') + ]); + return chain([ + branchAndMerge(chain([mergeWith(templateSource)])), + formatFiles(options) + ]); } function normalizeOptions(options: Schema): Schema { diff --git a/packages/schematics/src/collection/workspace-schematic/schema.d.ts b/packages/schematics/src/collection/workspace-schematic/schema.d.ts index 995dbae301..13e049e4b1 100644 --- a/packages/schematics/src/collection/workspace-schematic/schema.d.ts +++ b/packages/schematics/src/collection/workspace-schematic/schema.d.ts @@ -1,3 +1,4 @@ export interface Schema { name: string; + skipFormat: boolean; } diff --git a/packages/schematics/src/collection/workspace-schematic/schema.json b/packages/schematics/src/collection/workspace-schematic/schema.json index ba49831b37..65326057fc 100644 --- a/packages/schematics/src/collection/workspace-schematic/schema.json +++ b/packages/schematics/src/collection/workspace-schematic/schema.json @@ -11,8 +11,12 @@ "$source": "argv", "index": 0 } + }, + "skipFormat": { + "description": "Skip formatting files", + "type": "boolean", + "default": false } }, - "required": [ - ] + "required": [] } diff --git a/packages/schematics/src/collection/workspace-schematic/workspace-schematic.spec.ts b/packages/schematics/src/collection/workspace-schematic/workspace-schematic.spec.ts index 4f1f434c96..bc7bb7bef3 100644 --- a/packages/schematics/src/collection/workspace-schematic/workspace-schematic.spec.ts +++ b/packages/schematics/src/collection/workspace-schematic/workspace-schematic.spec.ts @@ -14,8 +14,6 @@ describe('workspace-schematic', () => { beforeEach(() => { appTree = new VirtualTree(); appTree = createEmptyWorkspace(appTree); - - schematicRunner.logger.subscribe(s => console.log(s)); }); it('should generate files', () => { diff --git a/packages/schematics/src/utils/rules/format-files.spec.ts b/packages/schematics/src/utils/rules/format-files.spec.ts new file mode 100644 index 0000000000..f82f01968e --- /dev/null +++ b/packages/schematics/src/utils/rules/format-files.spec.ts @@ -0,0 +1,66 @@ +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import { Tree } from '@angular-devkit/schematics'; + +import * as path from 'path'; + +import { createEmptyWorkspace } from '../testing-utils'; +import { formatFiles } from './format-files'; +import { serializeJson } from '../fileutils'; + +describe('formatFiles', () => { + let tree: Tree; + let schematicRunner: SchematicTestRunner; + beforeEach(() => { + schematicRunner = new SchematicTestRunner( + '@nrwl/schematics', + path.join(__dirname, '../../collection.json') + ); + tree = createEmptyWorkspace(Tree.empty()); + tree.overwrite( + 'package.json', + serializeJson({ + scripts: { + format: 'prettier' + } + }) + ); + }); + + it('should format files', done => { + schematicRunner.callRule(formatFiles(), tree).subscribe(result => { + expect(schematicRunner.tasks.length).toBe(1); + expect(schematicRunner.tasks[0]).toEqual({ + name: 'node-package', + options: { + packageName: 'run format -- --untracked', + quiet: true + } + }); + done(); + }); + }); + + it('should not format files if there is no format npm script', done => { + tree.overwrite( + 'package.json', + serializeJson({ + scripts: {} + }) + ); + schematicRunner.callRule(formatFiles(), tree).subscribe(result => { + expect(schematicRunner.tasks.length).toBe(0); + //TODO: test that a warning is emitted. + done(); + }); + }); + + it('should not format files if skipFormat is passed', done => { + schematicRunner + .callRule(formatFiles({ skipFormat: true }), tree) + .subscribe(result => { + expect(schematicRunner.tasks.length).toBe(0); + //TODO: test that a warning is not emitted. + done(); + }); + }); +}); diff --git a/packages/schematics/src/utils/rules/format-files.ts b/packages/schematics/src/utils/rules/format-files.ts new file mode 100644 index 0000000000..6c295898a2 --- /dev/null +++ b/packages/schematics/src/utils/rules/format-files.ts @@ -0,0 +1,42 @@ +import { readJsonInTree } from '../ast-utils'; +import { + TaskConfigurationGenerator, + TaskConfiguration, + Tree, + SchematicContext, + Rule, + noop +} from '@angular-devkit/schematics'; +import { stripIndents } from '@angular-devkit/core/src/utils/literals'; + +class FormatFiles implements TaskConfigurationGenerator { + toConfiguration(): TaskConfiguration { + return { + name: 'node-package', + options: { + packageName: 'run format -- --untracked', // workaround. we should define a custom task executor. + quiet: true + } + }; + } +} + +export function formatFiles( + options: { skipFormat: boolean } = { skipFormat: false } +): Rule { + if (options.skipFormat) { + return noop(); + } + return (host: Tree, context: SchematicContext) => { + const packageJson = readJsonInTree(host, 'package.json'); + if (packageJson.scripts && packageJson.scripts.format) { + context.addTask(new FormatFiles()); + } else { + context.logger.warn(stripIndents` + Files were not formated during this code generation. + The "format" npm script is missing in your package.json. + Please either add a format script or pass --skip-format. + `); + } + }; +} diff --git a/packages/schematics/src/utils/tasks.ts b/packages/schematics/src/utils/tasks.ts deleted file mode 100644 index 8030993471..0000000000 --- a/packages/schematics/src/utils/tasks.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - TaskConfigurationGenerator, - TaskConfiguration, - Tree, - SchematicContext -} from '@angular-devkit/schematics'; - -export class FormatFiles implements TaskConfigurationGenerator { - toConfiguration(): TaskConfiguration { - return { - name: 'node-package', - options: { - packageName: 'run format -- --untracked', // workaround. we should define a custom task executor. - quiet: true - } - }; - } -} - -export function wrapIntoFormat(fn: Function): any { - return (host: Tree, context: SchematicContext) => { - context.addTask(new FormatFiles()); - return fn(host, context)(host, context); - }; -}