From 8831a7aabbf1a3e9927c821741076f15b6f0aeff Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 29 Aug 2018 23:00:57 +0200 Subject: [PATCH] feat(ng-add): respect project default inlineStyle, inlineTemplate and spec option value (#12888) If developers create a new Angular CLI project, options like `--skipTests`, `--inlineTemplate` or `--inlineStyle` can be specified. These options will be stored in the workspace config file and will be respected if someone generates another component on the existing project. Since we technically also generate components, we should respect the default option value for those options if _not_ explicitly specified. Closes #11874 --- src/lib/schematics/address-form/index.ts | 6 +- src/lib/schematics/address-form/schema.json | 5 +- src/lib/schematics/dashboard/index.spec.ts | 62 ++++++++++++++-- src/lib/schematics/dashboard/index.ts | 6 +- src/lib/schematics/dashboard/schema.json | 5 +- src/lib/schematics/nav/index.spec.ts | 58 +++++++++++++-- src/lib/schematics/nav/index.ts | 6 +- src/lib/schematics/nav/schema.json | 5 +- src/lib/schematics/table/index.spec.ts | 71 +++++++++++++++---- src/lib/schematics/table/index.ts | 3 +- src/lib/schematics/table/schema.json | 5 +- src/lib/schematics/tree/index.ts | 6 +- src/lib/schematics/tree/schema.json | 5 +- src/lib/schematics/utils/build-component.ts | 13 ++-- src/lib/schematics/utils/default-style-ext.ts | 31 -------- src/lib/schematics/utils/schematic-options.ts | 45 ++++++++++++ 16 files changed, 233 insertions(+), 99 deletions(-) delete mode 100644 src/lib/schematics/utils/default-style-ext.ts create mode 100644 src/lib/schematics/utils/schematic-options.ts diff --git a/src/lib/schematics/address-form/index.ts b/src/lib/schematics/address-form/index.ts index 9d27dce05b5..06626425066 100644 --- a/src/lib/schematics/address-form/index.ts +++ b/src/lib/schematics/address-form/index.ts @@ -18,10 +18,8 @@ import {addModuleImportToModule, findModuleFromOptions} from '../utils/ast'; export default function(options: Schema): Rule { return chain([ buildComponent({...options}, { - template: options.inlineTemplate && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', - stylesheet: options.inlineStyle && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', + template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', + stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', }), options.skipImport ? noop() : addFormModulesToModule(options) ]); diff --git a/src/lib/schematics/address-form/schema.json b/src/lib/schematics/address-form/schema.json index c0e9f3b2b32..7aa223fad8f 100644 --- a/src/lib/schematics/address-form/schema.json +++ b/src/lib/schematics/address-form/schema.json @@ -28,13 +28,11 @@ "inlineStyle": { "description": "Specifies if the style will be in the ts file.", "type": "boolean", - "default": false, "alias": "s" }, "inlineTemplate": { "description": "Specifies if the template will be in the ts file.", "type": "boolean", - "default": false, "alias": "t" }, "viewEncapsulation": { @@ -62,8 +60,7 @@ }, "spec": { "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true + "description": "Specifies if a spec file is generated." }, "flat": { "type": "boolean", diff --git a/src/lib/schematics/dashboard/index.spec.ts b/src/lib/schematics/dashboard/index.spec.ts index 7fb9a1b4f4e..112dfb99f06 100644 --- a/src/lib/schematics/dashboard/index.spec.ts +++ b/src/lib/schematics/dashboard/index.spec.ts @@ -44,17 +44,65 @@ describe('material-dashboard-schematic', () => { `import { MatGridListModule, MatCardModule, MatMenuModule, MatIconModule, MatButtonModule } from '@angular/material';`); }); - it('should support passing the style extension option', () => { - const tree = runner.runSchematic( - 'dashboard', {styleext: 'scss', ...baseOptions}, createTestApp()); + describe('styleext option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'dashboard', {styleext: 'scss', ...baseOptions}, createTestApp()); - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('dashboard', baseOptions, createTestApp({style: 'less'})); + + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); + }); }); - it('should fallback to the default angular:component style extension', () => { - const tree = runner.runSchematic('dashboard', baseOptions, createTestApp({style: 'less'})); + describe('inlineStyle option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'dashboard', {inlineStyle: true, ...baseOptions}, createTestApp()); - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic( + 'dashboard', baseOptions, createTestApp({inlineStyle: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); }); + describe('inlineTemplate option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'dashboard', {inlineTemplate: true, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic( + 'dashboard', baseOptions, createTestApp({inlineTemplate: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + }); + + describe('spec option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'dashboard', {spec: false, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('dashboard', baseOptions, createTestApp({skipTests: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); + }); }); diff --git a/src/lib/schematics/dashboard/index.ts b/src/lib/schematics/dashboard/index.ts index ef02e6e3003..0c4366781df 100644 --- a/src/lib/schematics/dashboard/index.ts +++ b/src/lib/schematics/dashboard/index.ts @@ -18,10 +18,8 @@ import {Schema} from './schema'; export default function(options: Schema): Rule { return chain([ buildComponent({...options}, { - template: options.inlineTemplate && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', - stylesheet: options.inlineStyle && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', + template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', + stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', }), options.skipImport ? noop() : addNavModulesToModule(options) ]); diff --git a/src/lib/schematics/dashboard/schema.json b/src/lib/schematics/dashboard/schema.json index 28d3b0be9c0..0c833b046f1 100644 --- a/src/lib/schematics/dashboard/schema.json +++ b/src/lib/schematics/dashboard/schema.json @@ -28,13 +28,11 @@ "inlineStyle": { "description": "Specifies if the style will be in the ts file.", "type": "boolean", - "default": false, "alias": "s" }, "inlineTemplate": { "description": "Specifies if the template will be in the ts file.", "type": "boolean", - "default": false, "alias": "t" }, "viewEncapsulation": { @@ -62,8 +60,7 @@ }, "spec": { "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true + "description": "Specifies if a spec file is generated." }, "flat": { "type": "boolean", diff --git a/src/lib/schematics/nav/index.spec.ts b/src/lib/schematics/nav/index.spec.ts index bc2b2315544..9c93f7838ff 100644 --- a/src/lib/schematics/nav/index.spec.ts +++ b/src/lib/schematics/nav/index.spec.ts @@ -46,15 +46,61 @@ describe('material-nav-schematic', () => { `MatListModule } from '@angular/material';`); }); - it('should support passing the style extension option', () => { - const tree = runner.runSchematic('nav', {styleext: 'scss', ...baseOptions}, createTestApp()); + describe('styleext option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic('nav', {styleext: 'scss', ...baseOptions}, createTestApp()); - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('nav', baseOptions, createTestApp({style: 'less'})); + + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); + }); }); - it('should fallback to the default angular:component style extension', () => { - const tree = runner.runSchematic('nav', baseOptions, createTestApp({style: 'less'})); + describe('inlineStyle option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'nav', {inlineStyle: true, ...baseOptions}, createTestApp()); - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('nav', baseOptions, createTestApp({inlineStyle: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); + }); + + describe('inlineTemplate option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'nav', {inlineTemplate: true, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('nav', baseOptions, createTestApp({inlineTemplate: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + }); + + describe('spec option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic('nav', {spec: false, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('nav', baseOptions, createTestApp({skipTests: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); }); }); diff --git a/src/lib/schematics/nav/index.ts b/src/lib/schematics/nav/index.ts index 90b897d0cce..e25d949a0f9 100644 --- a/src/lib/schematics/nav/index.ts +++ b/src/lib/schematics/nav/index.ts @@ -18,10 +18,8 @@ import {addModuleImportToModule, findModuleFromOptions} from '../utils/ast'; export default function(options: Schema): Rule { return chain([ buildComponent({...options}, { - template: options.inlineTemplate && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', - stylesheet: options.inlineStyle && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', + template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', + stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', }), options.skipImport ? noop() : addNavModulesToModule(options) ]); diff --git a/src/lib/schematics/nav/schema.json b/src/lib/schematics/nav/schema.json index db3c7423a11..89afc4bf117 100644 --- a/src/lib/schematics/nav/schema.json +++ b/src/lib/schematics/nav/schema.json @@ -28,13 +28,11 @@ "inlineStyle": { "description": "Specifies if the style will be in the ts file.", "type": "boolean", - "default": false, "alias": "s" }, "inlineTemplate": { "description": "Specifies if the template will be in the ts file.", "type": "boolean", - "default": false, "alias": "t" }, "viewEncapsulation": { @@ -62,8 +60,7 @@ }, "spec": { "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true + "description": "Specifies if a spec file is generated." }, "flat": { "type": "boolean", diff --git a/src/lib/schematics/table/index.spec.ts b/src/lib/schematics/table/index.spec.ts index 317ccb25794..c7250b34f9a 100644 --- a/src/lib/schematics/table/index.spec.ts +++ b/src/lib/schematics/table/index.spec.ts @@ -41,18 +41,6 @@ describe('material-table-schematic', () => { expect(componentContent).toContain('FooDataSource'); }); - it('should support passing the style extension option', () => { - const tree = runner.runSchematic('table', {styleext: 'scss', ...baseOptions}, createTestApp()); - - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); - }); - - it('should fallback to the default angular:component style extension', () => { - const tree = runner.runSchematic('table', baseOptions, createTestApp({style: 'less'})); - - expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); - }); - it('should add table imports to module', () => { const tree = runner.runSchematic('table', baseOptions, createTestApp()); const moduleContent = getFileContent(tree, '/projects/material/src/app/app.module.ts'); @@ -65,4 +53,63 @@ describe('material-table-schematic', () => { `import { MatTableModule, MatPaginatorModule, MatSortModule } from '@angular/material';`); }); + describe('styleext option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'table', {styleext: 'scss', ...baseOptions}, createTestApp()); + + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.scss'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('table', baseOptions, createTestApp({style: 'less'})); + + expect(tree.files).toContain('/projects/material/src/app/foo/foo.component.less'); + }); + }); + + describe('inlineStyle option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'table', {inlineStyle: true, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('table', baseOptions, createTestApp({inlineStyle: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.css'); + }); + }); + + describe('inlineTemplate option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic( + 'table', {inlineTemplate: true, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic( + 'table', baseOptions, createTestApp({inlineTemplate: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.html'); + }); + }); + + describe('spec option', () => { + it('should respect the option value', () => { + const tree = runner.runSchematic('table', {spec: false, ...baseOptions}, createTestApp()); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); + + it('should fallback to the @schematics/angular:component option value', () => { + const tree = runner.runSchematic('table', baseOptions, createTestApp({skipTests: true})); + + expect(tree.files).not.toContain('/projects/material/src/app/foo/foo.component.spec.ts'); + }); + }); }); diff --git a/src/lib/schematics/table/index.ts b/src/lib/schematics/table/index.ts index ca29ac24a7b..90c7d302852 100644 --- a/src/lib/schematics/table/index.ts +++ b/src/lib/schematics/table/index.ts @@ -18,8 +18,7 @@ import {addModuleImportToModule, findModuleFromOptions} from '../utils/ast'; export default function(options: Schema): Rule { return chain([ buildComponent({...options}, { - template: options.inlineTemplate && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html' + template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html' }), options.skipImport ? noop() : addTableModulesToModule(options) ]); diff --git a/src/lib/schematics/table/schema.json b/src/lib/schematics/table/schema.json index ded294915e1..766e3e99714 100644 --- a/src/lib/schematics/table/schema.json +++ b/src/lib/schematics/table/schema.json @@ -28,13 +28,11 @@ "inlineStyle": { "description": "Specifies if the style will be in the ts file.", "type": "boolean", - "default": false, "alias": "s" }, "inlineTemplate": { "description": "Specifies if the template will be in the ts file.", "type": "boolean", - "default": false, "alias": "t" }, "viewEncapsulation": { @@ -62,8 +60,7 @@ }, "spec": { "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true + "description": "Specifies if a spec file is generated." }, "flat": { "type": "boolean", diff --git a/src/lib/schematics/tree/index.ts b/src/lib/schematics/tree/index.ts index b2cc2523ece..726a39c28e5 100644 --- a/src/lib/schematics/tree/index.ts +++ b/src/lib/schematics/tree/index.ts @@ -18,10 +18,8 @@ import {Schema} from './schema'; export default function(options: Schema): Rule { return chain([ buildComponent({...options}, { - template: options.inlineTemplate && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', - stylesheet: options.inlineStyle && - './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', + template: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html', + stylesheet: './__path__/__name@dasherize@if-flat__/__name@dasherize__.component.__styleext__', }), options.skipImport ? noop() : addTreeModulesToModule(options) ]); diff --git a/src/lib/schematics/tree/schema.json b/src/lib/schematics/tree/schema.json index a9e697b514c..9e1182e2ef2 100644 --- a/src/lib/schematics/tree/schema.json +++ b/src/lib/schematics/tree/schema.json @@ -28,13 +28,11 @@ "inlineStyle": { "description": "Specifies if the style will be in the ts file.", "type": "boolean", - "default": false, "alias": "s" }, "inlineTemplate": { "description": "Specifies if the template will be in the ts file.", "type": "boolean", - "default": false, "alias": "t" }, "viewEncapsulation": { @@ -62,8 +60,7 @@ }, "spec": { "type": "boolean", - "description": "Specifies if a spec file is generated.", - "default": true + "description": "Specifies if a spec file is generated." }, "flat": { "type": "boolean", diff --git a/src/lib/schematics/utils/build-component.ts b/src/lib/schematics/utils/build-component.ts index 5ada72e87e1..ad2ec2efe30 100644 --- a/src/lib/schematics/utils/build-component.ts +++ b/src/lib/schematics/utils/build-component.ts @@ -36,10 +36,10 @@ import {buildRelativePath, findModuleFromOptions} from '@schematics/angular/util import {parseName} from '@schematics/angular/utility/parse-name'; import {buildDefaultPath} from '@schematics/angular/utility/project'; import {validateHtmlSelector, validateName} from '@schematics/angular/utility/validation'; -import {resolve, dirname, join} from 'path'; import {readFileSync} from 'fs'; +import {dirname, join, resolve} from 'path'; import * as ts from 'typescript'; -import {determineDefaultStyleExt} from './default-style-ext'; +import {getDefaultComponentOptions} from './schematic-options'; function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile { const text = host.read(modulePath); @@ -163,14 +163,17 @@ export function buildComponent(options: ComponentOptions, return (host: Tree, context: FileSystemSchematicContext) => { const workspace = getWorkspace(host); const project = workspace.projects[options.project || workspace.defaultProject]; + const defaultComponentOptions = getDefaultComponentOptions(project); const schematicFilesUrl = './files'; const schematicFilesPath = resolve(dirname(context.schematic.description.path), schematicFilesUrl); - if (!options.styleext) { - options.styleext = determineDefaultStyleExt(project); - } + // Add the default component option values to the options if an option is not explicitly + // specified but a default component option is available. + Object.keys(options) + .filter(optionName => options[optionName] == null && defaultComponentOptions[optionName]) + .forEach(optionName => options[optionName] = defaultComponentOptions[optionName]); if (options.path === undefined) { options.path = buildDefaultPath(project); diff --git a/src/lib/schematics/utils/default-style-ext.ts b/src/lib/schematics/utils/default-style-ext.ts deleted file mode 100644 index 39ce0cd5251..00000000000 --- a/src/lib/schematics/utils/default-style-ext.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * 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 {WorkspaceProject} from '@schematics/angular/utility/config'; - -/** - * Style extension that will be used if no default style extension for the CLI project - * could be determined. - */ -const fallbackStyleExtension = 'css'; - -/** - * Determines the default style extension in the Angular CLI project by looking for the default - * component schematic options. This is necessary for now because when creating CLI projects, - * the CLI only makes the default `--style` option available for the `angular:component` schematic. - */ -export function determineDefaultStyleExt(project: WorkspaceProject): string | null { - if (project.schematics && - project.schematics['@schematics/angular:component'] && - project.schematics['@schematics/angular:component']['styleext']) { - - return project.schematics['@schematics/angular:component']['styleext']; - } - - return fallbackStyleExtension; -} diff --git a/src/lib/schematics/utils/schematic-options.ts b/src/lib/schematics/utils/schematic-options.ts new file mode 100644 index 00000000000..cb5ae44fedd --- /dev/null +++ b/src/lib/schematics/utils/schematic-options.ts @@ -0,0 +1,45 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * 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 {WorkspaceProject} from '@schematics/angular/utility/config'; + +/** + * Returns the default options for the `@schematics/angular:component` schematic which would + * have been specified at project initialization (ng new or ng init). + * + * This is necessary because the Angular CLI only exposes the default values for the "--style", + * "--inlineStyle", "--skipTests" and "--inlineTemplate" options to the "component" schematic. + */ +export function getDefaultComponentOptions(project: WorkspaceProject) { + // Note: Not all options which are available when running "ng new" will be stored in the + // workspace config. List of options which will be available in the configuration: + // angular/angular-cli/blob/master/packages/schematics/angular/application/index.ts#L109-L131 + return { + styleext: getDefaultComponentOption(project, 'styleext', 'css'), + inlineStyle: getDefaultComponentOption(project, 'inlineStyle', false), + inlineTemplate: getDefaultComponentOption(project, 'inlineTemplate', false), + spec: getDefaultComponentOption(project, 'spec', true), + }; +} + +/** + * Gets the default value for the specified option. The default options will be determined + * by looking at the stored schematic options for `@schematics/angular:component` in the + * CLI workspace configuration. + */ +function getDefaultComponentOption(project: WorkspaceProject, optionName: string, + fallbackValue: T): T | null { + if (project.schematics && + project.schematics['@schematics/angular:component'] && + project.schematics['@schematics/angular:component'][optionName] != null) { + + return project.schematics['@schematics/angular:component'][optionName]; + } + + return fallbackValue; +}