-
Notifications
You must be signed in to change notification settings - Fork 234
/
useLifeCycleInterfaceRule.ts
82 lines (70 loc) · 2.72 KB
/
useLifeCycleInterfaceRule.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
import * as Lint from 'tslint';
import * as ts from 'typescript';
import {sprintf} from 'sprintf-js';
import SyntaxKind = require('./util/syntaxKind');
const getInterfaceName = (t: any) => {
if (t.expression && t.expression.name) {
return t.expression.name.text;
}
return t.expression.text;
};
export class Rule extends Lint.Rules.AbstractRule {
static FAILURE:string = 'Implement lifecycle hook interface %s for method %s in class %s ($$09-01$$)';
static HOOKS_PREFIX = 'ng';
static LIFE_CYCLE_HOOKS_NAMES:Array<any> = [
'OnChanges',
'OnInit',
'DoCheck',
'AfterContentInit',
'AfterContentChecked',
'AfterViewInit',
'AfterViewChecked',
'OnDestroy'
];
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
return this.applyWithWalker(
new ClassMetadataWalker(sourceFile,
this.getOptions()));
}
}
export class ClassMetadataWalker extends Lint.RuleWalker {
visitClassDeclaration(node:ts.ClassDeclaration) {
let syntaxKind = SyntaxKind.current();
let className = node.name.text;
let interfaces = this.extractInterfaces(node,syntaxKind);
let methods = node.members.filter(m=>m.kind === syntaxKind.MethodDeclaration);
this.validateMethods(methods,interfaces,className);
super.visitClassDeclaration(node);
}
private extractInterfaces(node:ts.ClassDeclaration,syntaxKind:SyntaxKind.SyntaxKind):string[]{
let interfaces:string[] = [];
if (node.heritageClauses) {
let interfacesClause = node.heritageClauses.filter(h=>h.token === syntaxKind.ImplementsKeyword);
if (interfacesClause.length !== 0) {
interfaces = interfacesClause[0].types.map(getInterfaceName);
}
}
return interfaces;
}
private validateMethods( methods:any[],interfaces:string[],className:string){
methods.forEach(m => {
let n = (<any>m.name).text;
if(n && this.isMethodValidHook(m,interfaces)){
let hookName = n.substr(2, n.lenght);
this.addFailure(
this.createFailure(
m.name.getStart(),
m.name.getWidth(),
sprintf.apply(this, [Rule.FAILURE, hookName,Rule.HOOKS_PREFIX + hookName, className])));
}
});
}
private isMethodValidHook(m:any,interfaces:string[]):boolean{
let n = (<any>m.name).text;
let isNg:boolean = n.substr(0, 2) === Rule.HOOKS_PREFIX;
let hookName = n.substr(2, n.lenght);
let isHook = Rule.LIFE_CYCLE_HOOKS_NAMES.indexOf(hookName) !== -1;
let isNotIn:boolean = interfaces.indexOf(hookName) === -1;
return isNg && isHook && isNotIn;
}
}