Skip to content

Commit bd038a8

Browse files
committed
Merge pull request #27 from mgechev/component-class-suffix
add component/directive rule with update README
2 parents 2c09028 + 1a442bc commit bd038a8

5 files changed

+213
-2
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ Below you can find a recommended configuration which is based on the [Angular 2
4040
"call-forward-ref" :true,
4141
"life-cycle-hook": true,
4242
"pipe-transform-interface": true,
43-
"pipe-naming": [true, "kebab-case","sg"]
43+
"pipe-naming": [true, "kebab-case","sg"],
44+
"component-class-suffix":true,
45+
"directive-class-suffix":true
4446
}
4547
```
4648

@@ -69,7 +71,7 @@ Below you can find a recommended configuration which is based on the [Angular 2
6971
- [ ] Proper naming of modules (kebab-case followed by module type followed by extension for regular modules, module name plus extension name for facades).
7072
- [ ] Verify if used directive is declared in the current component or any parent component.
7173
- [ ] Verify that property or method used in the template exists in the current context.
72-
- [ ] Proper naming of directives and components (name plus `(Directive|Component)` suffix).
74+
- [x] Proper naming of directives and components (name plus `(Directive|Component)` suffix).
7375
- [ ] Locate component templates in the same directory.
7476
- [ ] Locate tests in the same directory (rise optional warning when no test file is found).
7577
- [ ] Rise warning on complex logic inside of the templates.

src/componentClassSuffixRule.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as Lint from 'tslint/lib/lint';
2+
import * as ts from 'typescript';
3+
import {sprintf} from 'sprintf-js';
4+
import SyntaxKind = require('./util/syntaxKind');
5+
import {Ng2Walker} from "./util/ng2Walker";
6+
7+
export class Rule extends Lint.Rules.AbstractRule {
8+
9+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
10+
return this.applyWithWalker(
11+
new ClassMetadataWalker(sourceFile,
12+
this.getOptions()));
13+
}
14+
15+
static FAILURE:string = "The name of the class %s should end with the suffix Component";
16+
17+
static validate(className:string):boolean {
18+
return /.*Component$/.test(className);
19+
}
20+
}
21+
22+
export class ClassMetadataWalker extends Ng2Walker {
23+
24+
visitNg2Component(controller:ts.ClassDeclaration, decorator:ts.Decorator) {
25+
let name = controller.name;
26+
let className:string = name.text;
27+
if (!Rule.validate(className)) {
28+
this.addFailure(
29+
this.createFailure(
30+
name.getStart(),
31+
name.getWidth(),
32+
sprintf.apply(this, [Rule.FAILURE, className])));
33+
}
34+
}
35+
}

src/directiveClassSuffixRule.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as Lint from 'tslint/lib/lint';
2+
import * as ts from 'typescript';
3+
import {sprintf} from 'sprintf-js';
4+
import SyntaxKind = require('./util/syntaxKind');
5+
import {Ng2Walker} from "./util/ng2Walker";
6+
7+
export class Rule extends Lint.Rules.AbstractRule {
8+
9+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
10+
return this.applyWithWalker(
11+
new ClassMetadataWalker(sourceFile,
12+
this.getOptions()));
13+
}
14+
15+
static FAILURE:string = "The name of the class %s should end with the suffix Directive";
16+
17+
static validate(className:string):boolean {
18+
return /.*Directive/.test(className);
19+
}
20+
}
21+
22+
export class ClassMetadataWalker extends Ng2Walker {
23+
24+
visitNg2Directive(controller:ts.ClassDeclaration, decorator:ts.Decorator) {
25+
let name = controller.name;
26+
let className:string = name.text;
27+
if (!Rule.validate(className)) {
28+
this.addFailure(
29+
this.createFailure(
30+
name.getStart(),
31+
name.getWidth(),
32+
sprintf.apply(this, [Rule.FAILURE, className])));
33+
}
34+
}
35+
36+
}

test/componentClassSuffixRule.spec.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {assertFailure, assertSuccess} from './testHelper';
2+
3+
describe('component-class-suffix', () => {
4+
describe('invalid component class suffix', () => {
5+
it('should fail when component class is with the wrong suffix', () => {
6+
let source = `
7+
@Component({
8+
selector: 'sg-foo-bar'
9+
})
10+
class Test {}`;
11+
assertFailure('component-class-suffix', source, {
12+
message: 'The name of the class Test should end with the suffix Component',
13+
startPosition: {
14+
line: 4,
15+
character: 20
16+
},
17+
endPosition: {
18+
line: 4,
19+
character: 24
20+
}
21+
});
22+
});
23+
});
24+
describe('valid component class name', () => {
25+
it('should succeed when the component class name ends with Component', () => {
26+
let source = `
27+
@Component({
28+
selector: 'sg-foo-bar'
29+
})
30+
class TestComponent {}`;
31+
assertSuccess('component-class-suffix', source);
32+
});
33+
});
34+
describe('valid directive class', () => {
35+
it('should succeed when is used @Directive decorator', () => {
36+
let source = `
37+
@Directive({
38+
selector: '[myHighlight]'
39+
})
40+
class TestDirective {}`;
41+
assertSuccess('component-class-suffix', source);
42+
});
43+
});
44+
describe('valid pipe class', () => {
45+
it('should succeed when is used @Pipe decorator', () => {
46+
let source = `
47+
@Pipe({
48+
selector: 'sg-test-pipe'
49+
})
50+
class TestPipe {}`;
51+
assertSuccess('component-class-suffix', source);
52+
});
53+
});
54+
describe('valid service class', () => {
55+
it('should succeed when is used @Injectable decorator', () => {
56+
let source = `
57+
@Injectable()
58+
class TestService {}`;
59+
assertSuccess('component-class-suffix', source);
60+
});
61+
});
62+
describe('valid empty class', () => {
63+
it('should succeed when the class is empty', () => {
64+
let source = `
65+
class TestEmpty {}`;
66+
assertSuccess('component-class-suffix', source);
67+
});
68+
});
69+
});

test/directiveClassSuffix.spec.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import {assertFailure, assertSuccess} from './testHelper';
2+
3+
describe('directive-class-suffix', () => {
4+
describe('invalid directive class suffix', () => {
5+
it('should fail when directive class is with the wrong suffix', () => {
6+
let source = `
7+
@Directive({
8+
selector: 'sgBarFoo'
9+
})
10+
class Test {}`;
11+
assertFailure('directive-class-suffix', source, {
12+
message: 'The name of the class Test should end with the suffix Directive',
13+
startPosition: {
14+
line: 4,
15+
character: 20
16+
},
17+
endPosition: {
18+
line: 4,
19+
character: 24
20+
}
21+
});
22+
});
23+
});
24+
describe('valid directive class name', () => {
25+
it('should succeed when the directive class name ends with Directive', () => {
26+
let source = `
27+
@Directive({
28+
selector: 'sgBarFoo'
29+
})
30+
class TestDirective {}`;
31+
assertSuccess('directive-class-suffix', source);
32+
});
33+
});
34+
describe('valid directive class', () => {
35+
it('should succeed when is used @Component decorator', () => {
36+
let source = `
37+
@Component({
38+
selector: 'sg-foo-bar'
39+
})
40+
class TestComponent {}`;
41+
assertSuccess('directive-class-suffix', source);
42+
});
43+
});
44+
describe('valid pipe class', () => {
45+
it('should succeed when is used @Pipe decorator', () => {
46+
let source = `
47+
@Pipe({
48+
selector: 'sg-test-pipe'
49+
})
50+
class TestPipe {}`;
51+
assertSuccess('directive-class-suffix', source);
52+
});
53+
});
54+
describe('valid service class', () => {
55+
it('should succeed when is used @Injectable decorator', () => {
56+
let source = `
57+
@Injectable()
58+
class TestService {}`;
59+
assertSuccess('directive-class-suffix', source);
60+
});
61+
});
62+
describe('valid empty class', () => {
63+
it('should succeed when the class is empty', () => {
64+
let source = `
65+
class TestEmpty {}`;
66+
assertSuccess('directive-class-suffix', source);
67+
});
68+
});
69+
});

0 commit comments

Comments
 (0)