Skip to content

feat(@schematics/angular): Allow generation of suffix-free files #16891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/angular/cli/lib/config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@
"description": "The prefix to apply to generated selectors.",
"alias": "p"
},
"noSuffix": {
"type": "boolean",
"default": false,
"description": "When true, removes suffix from the generated files."
},
"selector": {
"type": "string",
"format": "html-selector",
Expand Down Expand Up @@ -314,6 +319,11 @@
"format": "html-selector",
"description": "The prefix to apply to generated selectors."
},
"noSuffix": {
"type": "boolean",
"default": false,
"description": "When true, remove suffix from the generated files."
},
"root": {
"type": "string",
"description": "Root of the project files."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@
"type": "string",
"description": "The prefix to apply to generated selectors."
},
"noSuffix": {
"type": "boolean",
"default": false,
"description": "When true, removes suffix from the generated files."
},
"cli": {
"$ref": "#/definitions/tool",
"default": {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export interface WorkspaceProject {
* The prefix to apply to generated selectors.
*/
prefix: string;
/**
* The Suffix remove from generated files.
*/
noSuffix?: boolean;
/**
* Tool options.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ describe('Workspace', () => {
sourceRoot: 'projects/app/src',
projectType: 'application',
prefix: 'app',
noSuffix: false,
cli: {},
i18n: {
sourceLocale: 'en-US',
Expand Down
1 change: 1 addition & 0 deletions packages/angular_devkit/core/src/workspace/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface ProjectDefinition {

root: string;
prefix?: string;
noSuffix?: boolean;
sourceRoot?: string;
}

Expand Down
1 change: 1 addition & 0 deletions packages/angular_devkit/schematics/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './rules/random';
export * from './rules/schematic';
export * from './rules/template';
export * from './rules/url';
export * from './rules/rename';
export * from './tree/delegate';
export * from './tree/empty';
export * from './tree/host-tree';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { <%= classify(name) %><%= classify(type) %> } from './<%= dasherize(name) %>.<%= dasherize(type) %>';
import { <%= classify(name) %><%= !noSuffix ? classify(type) : '' %> } from './<%= dasherize(name) %><%= !noSuffix ? '.' + dasherize(type) : '' %>';

describe('<%= classify(name) %><%= classify(type) %>', () => {
let component: <%= classify(name) %><%= classify(type) %>;
let fixture: ComponentFixture<<%= classify(name) %><%= classify(type) %>>;
describe('<%= classify(name) %><%= !noSuffix ? classify(type) : '' %>', () => {
let component: <%= classify(name) %><%= !noSuffix ? classify(type) : '' %>;
let fixture: ComponentFixture<<%= classify(name) %><%= !noSuffix ? classify(type) : '' %>>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ <%= classify(name) %><%= classify(type) %> ]
declarations: [ <%= classify(name) %><%= !noSuffix ? classify(type) : '' %> ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(<%= classify(name) %><%= classify(type) %>);
fixture = TestBed.createComponent(<%= classify(name) %><%= !noSuffix ? classify(type) : '' %>);
component = fixture.componentInstance;
fixture.detectChanges();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import { Component, OnInit<% if(!!viewEncapsulation) { %>, ViewEncapsulation<% }
<p>
<%= dasherize(name) %> works!
</p>
`,<% } else { %>
templateUrl: './<%= dasherize(name) %>.<%= dasherize(type) %>.html',<% } if(inlineStyle) { %>
`,<% } else if(noSuffix) { %>
templateUrl: './<%= dasherize(name) %>.html',<% } else { %>
templateUrl: './<%= dasherize(name) %>.<%= dasherize(type) %>.html',<%} if(inlineStyle) { %>
styles: [<% if(displayBlock){ %>
`
:host {
display: block;
}
`<% } %>
],<% } else { %>
],<% } else if(noSuffix) { %>
styleUrls: ['./<%= dasherize(name) %>.<%= style %>']<% } else { %>
styleUrls: ['./<%= dasherize(name) %>.<%= dasherize(type) %>.<%= style %>']<% } %><% if(!!viewEncapsulation) { %>,
encapsulation: ViewEncapsulation.<%= viewEncapsulation %><% } if (changeDetection !== 'Default') { %>,
changeDetection: ChangeDetectionStrategy.<%= changeDetection %><% } %>
})
export class <%= classify(name) %><%= classify(type) %> implements OnInit {
export class <%= classify(name) %><%= !noSuffix ? classify(type) : '' %> implements OnInit {

constructor() { }

Expand Down
10 changes: 7 additions & 3 deletions packages/schematics/angular/component/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
mergeWith,
move,
noop,
rename,
url,
} from '@angular-devkit/schematics';
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
Expand Down Expand Up @@ -57,10 +58,10 @@ function addDeclarationToNgModule(options: ComponentOptions): Rule {
const componentPath = `/${options.path}/`
+ (options.flat ? '' : strings.dasherize(options.name) + '/')
+ strings.dasherize(options.name)
+ '.'
+ strings.dasherize(options.type);
+ (!options.noSuffix ? '.' : '')
+ (!options.noSuffix ? strings.dasherize(options.type) : '');
const relativePath = buildRelativePath(modulePath, componentPath);
const classifiedName = strings.classify(options.name) + strings.classify(options.type);
const classifiedName = strings.classify(options.name) + (!options.noSuffix ? strings.classify(options.type) : '');
const declarationChanges = addDeclarationToModule(source,
modulePath,
classifiedName,
Expand Down Expand Up @@ -131,6 +132,7 @@ export default function (options: ComponentOptions): Rule {
return async (host: Tree) => {
const workspace = await getWorkspace(host);
const project = workspace.projects.get(options.project as string);
const regType = new RegExp('.' + options.type + '.', 'i');

if (options.path === undefined && project) {
options.path = buildDefaultPath(project);
Expand All @@ -142,6 +144,7 @@ export default function (options: ComponentOptions): Rule {
options.name = parsedPath.name;
options.path = parsedPath.path;
options.selector = options.selector || buildSelector(options, project && project.prefix || '');
options.noSuffix = options.noSuffix || (project && project.noSuffix ? project.noSuffix : false);

validateName(options.name);
validateHtmlSelector(options.selector);
Expand All @@ -155,6 +158,7 @@ export default function (options: ComponentOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options,
}),
options.noSuffix ? rename(name => !!name.match(regType), (name) => name.replace(regType, '.')) : noop(),
move(parsedPath.path),
]);

Expand Down
18 changes: 18 additions & 0 deletions packages/schematics/angular/component/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('Component Schematic', () => {
type: 'Component',
skipTests: false,
module: undefined,
noSuffix: false,
export: false,
project: 'bar',
};
Expand Down Expand Up @@ -72,6 +73,23 @@ describe('Component Schematic', () => {
expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooComponent\r?\n/m);
});

it('should create a component without suffix', async () => {
const options = { ...defaultOptions, noSuffix: true, name: 'hello' };
const tree = await schematicRunner.runSchematicAsync('component', options, appTree).toPromise();
const files = tree.files;
expect(files).toEqual(
jasmine.arrayContaining([
'/projects/bar/src/app/hello/hello.css',
'/projects/bar/src/app/hello/hello.html',
'/projects/bar/src/app/hello/hello.spec.ts',
'/projects/bar/src/app/hello/hello.ts',
]),
);
const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
expect(moduleContent).toMatch(/import.*Hello.*from '.\/hello\/hello'/);
expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+Hello\r?\n/m);
});

it('should set change detection to OnPush', async () => {
const options = { ...defaultOptions, changeDetection: 'OnPush' };

Expand Down
5 changes: 5 additions & 0 deletions packages/schematics/angular/component/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@
"default": false,
"description": "When true, applies lint fixes after generating the component.",
"x-user-analytics": 15
},
"noSuffix": {
"type": "boolean",
"default": false,
"description": "When true, removes suffix from the generated component."
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { <%= classify(name) %>Directive } from './<%= dasherize(name) %>.directive';
import { <%= classify(name) %><%= !noSuffix ? 'Directive' : '' %> } from './<%= dasherize(name) %><%= !noSuffix ? '.directive' : '' %>';

describe('<%= classify(name) %>Directive', () => {
describe('<%= classify(name) %><%= !noSuffix ? 'Directive' : '' %>', () => {
it('should create an instance', () => {
const directive = new <%= classify(name) %>Directive();
const directive = new <%= classify(name) %><%= !noSuffix ? 'Directive' : '' %>();
expect(directive).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Directive } from '@angular/core';
@Directive({
selector: '[<%= selector %>]'
})
export class <%= classify(name) %>Directive {
export class <%= classify(name) %><%= !noSuffix ? 'Directive' : '' %> {

constructor() { }

Expand Down
9 changes: 7 additions & 2 deletions packages/schematics/angular/directive/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
mergeWith,
move,
noop,
rename,
url,
} from '@angular-devkit/schematics';
import * as ts from '../third_party/github.com/Microsoft/TypeScript/lib/typescript';
Expand Down Expand Up @@ -47,9 +48,9 @@ function addDeclarationToNgModule(options: DirectiveOptions): Rule {
const directivePath = `/${options.path}/`
+ (options.flat ? '' : strings.dasherize(options.name) + '/')
+ strings.dasherize(options.name)
+ '.directive';
+ (!options.noSuffix ? '.directive' : '');
const relativePath = buildRelativePath(modulePath, directivePath);
const classifiedName = strings.classify(`${options.name}Directive`);
const classifiedName = strings.classify(options.name) + (!options.noSuffix ? 'Directive' : '');
const declarationChanges = addDeclarationToModule(source,
modulePath,
classifiedName,
Expand Down Expand Up @@ -104,6 +105,8 @@ export default function (options: DirectiveOptions): Rule {
return async (host: Tree) => {
const workspace = await getWorkspace(host);
const project = workspace.projects.get(options.project as string);
const regType = new RegExp('.directive.');

if (!project) {
throw new SchematicsException(`Invalid project name (${options.project})`);
}
Expand All @@ -118,6 +121,7 @@ export default function (options: DirectiveOptions): Rule {
options.name = parsedPath.name;
options.path = parsedPath.path;
options.selector = options.selector || buildSelector(options, project.prefix || '');
options.noSuffix = options.noSuffix || (project && project.noSuffix ? project.noSuffix : false);

validateHtmlSelector(options.selector);

Expand All @@ -128,6 +132,7 @@ export default function (options: DirectiveOptions): Rule {
'if-flat': (s: string) => options.flat ? '' : s,
...options,
}),
options.noSuffix ? rename(name => !!name.match(regType), (name) => name.replace(regType, '.')) : noop(),
move(parsedPath.path),
]);

Expand Down
14 changes: 14 additions & 0 deletions packages/schematics/angular/directive/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ describe('Directive Schematic', () => {
module: undefined,
export: false,
prefix: 'app',
noSuffix: false,
flat: true,
project: 'bar',
};
Expand Down Expand Up @@ -58,6 +59,19 @@ describe('Directive Schematic', () => {
expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+FooDirective\r?\n/m);
});

it('should create a directive without suffix', async () => {
const options = { ...defaultOptions, name: 'hello', noSuffix: true };

const tree = await schematicRunner.runSchematicAsync('directive', options, appTree)
.toPromise();
const files = tree.files;
expect(files).toContain('/projects/bar/src/app/hello.spec.ts');
expect(files).toContain('/projects/bar/src/app/hello.ts');
const moduleContent = tree.readContent('/projects/bar/src/app/app.module.ts');
expect(moduleContent).toMatch(/import.*Hello.*from '.\/hello'/);
expect(moduleContent).toMatch(/declarations:\s*\[[^\]]+?,\r?\n\s+Hello\r?\n/m);
});

it('should create respect the flat flag', async () => {
const options = { ...defaultOptions, flat: false };

Expand Down
5 changes: 5 additions & 0 deletions packages/schematics/angular/directive/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@
}
]
},
"noSuffix": {
"type": "boolean",
"description": "When true, removes suffix from the generated directive.",
"default": false
},
"skipTests": {
"type": "boolean",
"description": "When true, does not create \"spec.ts\" test files for the new class.",
Expand Down
3 changes: 3 additions & 0 deletions packages/schematics/angular/enum/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
mergeWith,
move,
noop,
rename,
url,
} from '@angular-devkit/schematics';
import { applyLintFix } from '../utility/lint-fix';
Expand All @@ -29,6 +30,7 @@ export default function (options: EnumOptions): Rule {
options.path = await createDefaultPath(host, options.project as string);
}

const regType = new RegExp('.enum.');
const parsedPath = parseName(options.path, options.name);
options.name = parsedPath.name;
options.path = parsedPath.path;
Expand All @@ -38,6 +40,7 @@ export default function (options: EnumOptions): Rule {
...strings,
...options,
}),
options.noSuffix ? rename(name => !!name.match(regType), (name) => name.replace(regType, '.')) : noop(),
move(parsedPath.path),
]);

Expand Down
9 changes: 9 additions & 0 deletions packages/schematics/angular/enum/index_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('Enum Schematic', () => {
const defaultOptions: EnumOptions = {
name: 'foo',
project: 'bar',
noSuffix: false,
};

const workspaceOptions: WorkspaceOptions = {
Expand Down Expand Up @@ -49,6 +50,14 @@ describe('Enum Schematic', () => {
expect(files).toContain('/projects/bar/src/app/foo.enum.ts');
});

it('should create an enumeration without suffix', async () => {
const options = {...defaultOptions, noSuffix: true, name: 'hero'};
const tree = await schematicRunner.runSchematicAsync('enum', options, appTree)
.toPromise();
const files = tree.files;
expect(files).toContain('/projects/bar/src/app/hero.ts');
});

it('should create an enumeration', async () => {
const tree = await schematicRunner.runSchematicAsync('enum', defaultOptions, appTree)
.toPromise();
Expand Down
5 changes: 5 additions & 0 deletions packages/schematics/angular/enum/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
"default": false,
"description": "When true, applies lint fixes after generating the enum.",
"x-user-analytics": 15
},
"noSuffix": {
"type": "boolean",
"default": false,
"description": "When true, removes suffix from the generated enum."
}
},
"required": [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { TestBed } from '@angular/core/testing';

import { <%= classify(name) %>Guard } from './<%= dasherize(name) %>.guard';
import { <%= classify(name) %><%= !noSuffix ? 'Guard' : '' %> } from './<%= dasherize(name) %><%= !noSuffix ? '.guard' : '' %>';

describe('<%= classify(name) %>Guard', () => {
let guard: <%= classify(name) %>Guard;
describe('<%= classify(name) %><%= !noSuffix ? 'Guard' : '' %>', () => {
let guard: <%= classify(name) %><%= !noSuffix ? 'Guard' : '' %>;

beforeEach(() => {
TestBed.configureTestingModule({});
guard = TestBed.inject(<%= classify(name) %>Guard);
guard = TestBed.inject(<%= classify(name) %><%= !noSuffix ? 'Guard' : '' %>);
});

it('should be created', () => {
Expand Down
Loading