Skip to content

Commit 6dbcd78

Browse files
chrste90Brocco
authored andcommitted
feat(@schematics/angular): Add directive and component selector rules
Fixes: angular#570
1 parent 6b479dc commit 6dbcd78

File tree

8 files changed

+104
-2
lines changed

8 files changed

+104
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"extends": "<%= relativeTsLintPath %>/tslint.json",
3+
"rules": {
4+
"directive-selector": [
5+
true,
6+
"attribute",
7+
"<%= prefix %>",
8+
"camelCase"
9+
],
10+
"component-selector": [
11+
true,
12+
"element",
13+
"<%= prefix %>",
14+
"kebab-case"
15+
]
16+
}
17+
}

packages/schematics/angular/application/index.ts

+22-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,8 @@ export default function (options: ApplicationOptions): Rule {
270270
throw new SchematicsException(`Invalid options, "name" is required.`);
271271
}
272272
validateProjectName(options.name);
273-
const appRootSelector = `${options.prefix || 'app'}-root`;
273+
const prefix = options.prefix || 'app';
274+
const appRootSelector = `${prefix}-root`;
274275
const componentOptions = {
275276
inlineStyle: options.inlineStyle,
276277
inlineTemplate: options.inlineTemplate,
@@ -285,6 +286,7 @@ export default function (options: ApplicationOptions): Rule {
285286
let sourceRoot = `${appDir}/src`;
286287
let sourceDir = `${sourceRoot}/app`;
287288
let relativeTsConfigPath = appDir.split('/').map(x => '..').join('/');
289+
let relativeTsLintPath = appDir.split('/').map(x => '..').join('/');
288290
const rootInSrc = options.projectRoot !== undefined;
289291
if (options.projectRoot !== undefined) {
290292
newProjectRoot = options.projectRoot;
@@ -295,7 +297,12 @@ export default function (options: ApplicationOptions): Rule {
295297
if (relativeTsConfigPath === '') {
296298
relativeTsConfigPath = '.';
297299
}
300+
relativeTsLintPath = relative(normalize('/' + sourceRoot), normalize('/'));
301+
if (relativeTsLintPath === '') {
302+
relativeTsLintPath = '.';
303+
}
298304
}
305+
const tsLintRoot = appDir;
299306

300307
const e2eOptions: E2eOptions = {
301308
name: `${options.name}-e2e`,
@@ -330,6 +337,20 @@ export default function (options: ApplicationOptions): Rule {
330337
}),
331338
move(appDir),
332339
])),
340+
mergeWith(
341+
apply(url('./files/lint'), [
342+
template({
343+
utils: strings,
344+
...options,
345+
tsLintRoot,
346+
relativeTsLintPath,
347+
prefix,
348+
}),
349+
// TODO: Moving should work but is bugged right now.
350+
// The __tsLintRoot__ is being used meanwhile.
351+
// Otherwise the tslint.json file could be inside of the root folder and
352+
// this block and the lint folder could be removed.
353+
])),
333354
schematic('module', {
334355
name: 'app',
335356
commonModule: false,

packages/schematics/angular/application/index_spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ describe('Application Schematic', () => {
4747
expect(files.indexOf('/projects/foo/karma.conf.js')).toBeGreaterThanOrEqual(0);
4848
expect(files.indexOf('/projects/foo/tsconfig.app.json')).toBeGreaterThanOrEqual(0);
4949
expect(files.indexOf('/projects/foo/tsconfig.spec.json')).toBeGreaterThanOrEqual(0);
50+
expect(files.indexOf('/projects/foo/tslint.json')).toBeGreaterThanOrEqual(0);
5051
expect(files.indexOf('/projects/foo/src/environments/environment.ts')).toBeGreaterThanOrEqual(0);
5152
expect(files.indexOf('/projects/foo/src/environments/environment.prod.ts')).toBeGreaterThanOrEqual(0);
5253
expect(files.indexOf('/projects/foo/src/favicon.ico')).toBeGreaterThanOrEqual(0);
@@ -109,6 +110,15 @@ describe('Application Schematic', () => {
109110
expect(specTsConfig.files).toEqual(['src/test.ts', 'src/polyfills.ts']);
110111
});
111112

113+
it('should set the right path and prefix in the tslint file', () => {
114+
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
115+
const path = '/projects/foo/tslint.json';
116+
const content = JSON.parse(tree.readContent(path));
117+
expect(content.extends).toMatch('../../tslint.json');
118+
expect(content.rules['directive-selector'][2]).toMatch('app');
119+
expect(content.rules['component-selector'][2]).toMatch('app');
120+
});
121+
112122
describe(`update package.json`, () => {
113123
it(`should add build-angular to devDependencies`, () => {
114124
const tree = schematicRunner.runSchematic('application', defaultOptions, workspaceTree);
@@ -157,6 +167,7 @@ describe('Application Schematic', () => {
157167
expect(files.indexOf('/src/karma.conf.js')).toBeGreaterThanOrEqual(0);
158168
expect(files.indexOf('/src/tsconfig.app.json')).toBeGreaterThanOrEqual(0);
159169
expect(files.indexOf('/src/tsconfig.spec.json')).toBeGreaterThanOrEqual(0);
170+
expect(files.indexOf('/src/tslint.json')).toBeGreaterThanOrEqual(0);
160171
expect(files.indexOf('/src/environments/environment.ts')).toBeGreaterThanOrEqual(0);
161172
expect(files.indexOf('/src/environments/environment.prod.ts')).toBeGreaterThanOrEqual(0);
162173
expect(files.indexOf('/src/favicon.ico')).toBeGreaterThanOrEqual(0);
@@ -196,5 +207,15 @@ describe('Application Schematic', () => {
196207
expect(specTsConfig.extends).toEqual('../tsconfig.json');
197208
expect(specTsConfig.files).toEqual(['test.ts', 'polyfills.ts']);
198209
});
210+
211+
it('should set the relative path and prefix in the tslint file', () => {
212+
const options = { ...defaultOptions, projectRoot: '' };
213+
214+
const tree = schematicRunner.runSchematic('application', options, workspaceTree);
215+
const content = JSON.parse(tree.readContent('/src/tslint.json'));
216+
expect(content.extends).toMatch('../tslint.json');
217+
expect(content.rules['directive-selector'][2]).toMatch('app');
218+
expect(content.rules['component-selector'][2]).toMatch('app');
219+
});
199220
});
200221
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"extends": "<%= relativeTsLintPath %>/tslint.json",
3+
"rules": {
4+
"directive-selector": [
5+
true,
6+
"attribute",
7+
"<%= prefix %>",
8+
"camelCase"
9+
],
10+
"component-selector": [
11+
true,
12+
"element",
13+
"<%= prefix %>",
14+
"kebab-case"
15+
]
16+
}
17+
}

packages/schematics/angular/library/index.ts

+5
Original file line numberDiff line numberDiff line change
@@ -172,17 +172,21 @@ export default function (options: LibraryOptions): Rule {
172172
throw new SchematicsException(`Invalid options, "name" is required.`);
173173
}
174174
const name = options.name;
175+
const prefix = options.prefix || 'lib';
175176

176177
const workspace = getWorkspace(host);
177178
const newProjectRoot = workspace.newProjectRoot;
178179
const projectRoot = `${newProjectRoot}/${options.name}`;
179180
const sourceDir = `${projectRoot}/src/lib`;
181+
const relativeTsLintPath = projectRoot.split('/').map(x => '..').join('/');
180182

181183
const templateSource = apply(url('./files'), [
182184
template({
183185
...strings,
184186
...options,
185187
projectRoot,
188+
relativeTsLintPath,
189+
prefix,
186190
}),
187191
// TODO: Moving inside `branchAndMerge` should work but is bugged right now.
188192
// The __projectRoot__ is being used meanwhile.
@@ -203,6 +207,7 @@ export default function (options: LibraryOptions): Rule {
203207
}),
204208
schematic('component', {
205209
name: name,
210+
selector: `${prefix}-${name}`,
206211
inlineStyle: true,
207212
inlineTemplate: true,
208213
flat: true,

packages/schematics/angular/library/index_spec.ts

+10
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ describe('Library Schematic', () => {
4444
expect(files.indexOf('/projects/foo/karma.conf.js')).toBeGreaterThanOrEqual(0);
4545
expect(files.indexOf('/projects/foo/ng-package.json')).toBeGreaterThanOrEqual(0);
4646
expect(files.indexOf('/projects/foo/package.json')).toBeGreaterThanOrEqual(0);
47+
expect(files.indexOf('/projects/foo/tslint.json')).toBeGreaterThanOrEqual(0);
4748
expect(files.indexOf('/projects/foo/src/test.ts')).toBeGreaterThanOrEqual(0);
4849
expect(files.indexOf('/projects/foo/src/my_index.ts')).toBeGreaterThanOrEqual(0);
4950
expect(files.indexOf('/projects/foo/src/lib/foo.module.ts')).toBeGreaterThanOrEqual(0);
@@ -88,6 +89,15 @@ describe('Library Schematic', () => {
8889
expect(fileContent).toContain('exports: [FooComponent]');
8990
});
9091

92+
it('should set the right path and prefix in the tslint file', () => {
93+
const tree = schematicRunner.runSchematic('library', defaultOptions, workspaceTree);
94+
const path = '/projects/foo/tslint.json';
95+
const content = JSON.parse(tree.readContent(path));
96+
expect(content.extends).toMatch('../../tslint.json');
97+
expect(content.rules['directive-selector'][2]).toMatch('lib');
98+
expect(content.rules['component-selector'][2]).toMatch('lib');
99+
});
100+
91101
describe(`update package.json`, () => {
92102
it(`should add ng-packagr to devDependencies`, () => {
93103
const tree = schematicRunner.runSchematic('library', defaultOptions, workspaceTree);

packages/schematics/angular/library/schema.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ export interface Schema {
1515
* The path to create the interface.
1616
*/
1717
entryFile: string;
18+
/**
19+
* The prefix to apply to generated selectors.
20+
*/
21+
prefix?: string;
1822
/**
1923
* Do not add dependencies to package.json (e.g., --skipPackageJson)
2024
*/

packages/schematics/angular/library/schema.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
"description": "The path to create the library's public API file.",
1919
"default": "public_api"
2020
},
21+
"prefix": {
22+
"type": "string",
23+
"format": "html-selector",
24+
"description": "The prefix to apply to generated selectors.",
25+
"default": "lib",
26+
"alias": "p"
27+
},
2128
"skipPackageJson": {
2229
"type": "boolean",
2330
"default": false,
@@ -30,4 +37,4 @@
3037
}
3138
},
3239
"required": []
33-
}
40+
}

0 commit comments

Comments
 (0)