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
This commit is contained in:
Paul Gschwendtner
2018-08-29 23:00:57 +02:00
committed by Jeremy Elbourn
parent e4ca3cfaad
commit 8831a7aabb
16 changed files with 233 additions and 99 deletions
+2 -4
View File
@@ -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)
]);
+1 -4
View File
@@ -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",
+55 -7
View File
@@ -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');
});
});
});
+2 -4
View File
@@ -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)
]);
+1 -4
View File
@@ -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",
+52 -6
View File
@@ -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');
});
});
});
+2 -4
View File
@@ -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)
]);
+1 -4
View File
@@ -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",
+59 -12
View File
@@ -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');
});
});
});
+1 -2
View File
@@ -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)
]);
+1 -4
View File
@@ -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",
+2 -4
View File
@@ -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)
]);
+1 -4
View File
@@ -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",
+8 -5
View File
@@ -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);
@@ -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;
}
@@ -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<T>(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;
}