Skip to content

Commit 6e227f2

Browse files
committed
Merge pull request #10 from mgechev/ng2walker-refactor
Ng2walker refactor
2 parents 87fc3be + 07631fc commit 6e227f2

12 files changed

+199
-179
lines changed

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ Below you can find a recommended configuration which is based on the [Angular 2
1919
"output-parameter-decorator": true,
2020
"attribute-parameter-decorator": true,
2121
"input-property-directive": true,
22-
"output-property-directive": true
22+
"output-property-directive": true,
23+
"call-forward-ref":true
2324
}
2425
```
2526

@@ -39,7 +40,7 @@ Below you can find a recommended configuration which is based on the [Angular 2
3940
- [x] Do not rename outputs.
4041
- [ ] Externalize template above *n* lines of code.
4142
- [x] Do not use the `@Attribute` decorator.
42-
- [ ] Do not use `forwardRef`.
43+
- [x] Do not use `forwardRef`.
4344
- [ ] Rise a warning for impure pipes.
4445
- [ ] Do not declare global providers.
4546
- [ ] Follow convention for naming the routes.
+38-10
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,47 @@
11
import * as Lint from 'tslint/lib/lint';
22
import * as ts from 'typescript';
3-
import {ConstructorRule} from "./parameterConstructorBase";
4-
import {decoratorValidator} from "./util/decoratorValidator";
3+
import {sprintf} from 'sprintf-js';
54

6-
const FAILURE_STRING = 'In the constructor of class "%s", the parameter "%s" uses the @Attribute decorator, ' +
7-
'which is considered as a bad practice. Please, consider construction of type "@Input() %s: string"';
5+
export class Rule extends Lint.Rules.AbstractRule {
86

9-
const attributeCondition = (name)=>{
10-
return (name=='Attribute')
11-
};
7+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
8+
return this.applyWithWalker(
9+
new ConstructorMetadataWalker(sourceFile,
10+
this.getOptions()));
11+
}
12+
13+
static FAILURE_STRING:string = 'In the constructor of class "%s",' +
14+
' the parameter "%s" uses the @Attribute decorator, ' +
15+
'which is considered as a bad practice. Please,' +
16+
' consider construction of type "@Input() %s: string"';
1217

13-
export class Rule extends ConstructorRule {
18+
}
1419

15-
constructor(ruleName:string, value:any, disabledIntervals:Lint.IDisabledInterval[]) {
16-
super(ruleName, value, disabledIntervals,decoratorValidator(attributeCondition),FAILURE_STRING);
20+
export class ConstructorMetadataWalker extends Lint.RuleWalker {
21+
22+
visitConstructorDeclaration(node:ts.ConstructorDeclaration) {
23+
let parentName = (<ts.ClassDeclaration>node.parent).name.text;
24+
(node.parameters || []).forEach(this.validateParameter.bind(this, parentName));
25+
super.visitConstructorDeclaration(node);
1726
}
1827

28+
validateParameter(className:string, parameter) {
29+
let parameterName = (<ts.Identifier>parameter.name).text;
30+
if (parameter.decorators) {
31+
parameter.decorators.forEach((decorator)=> {
32+
let baseExpr = <any>decorator.expression || {};
33+
let expr = baseExpr.expression || {};
34+
let name = expr.text;
35+
if (name == 'Attribute') {
36+
let failureConfig:string[] = [className, parameterName, parameterName];
37+
failureConfig.unshift(Rule.FAILURE_STRING);
38+
this.addFailure(
39+
this.createFailure(
40+
parameter.getStart(),
41+
parameter.getWidth(),
42+
sprintf.apply(this, failureConfig)));
43+
}
44+
})
45+
}
46+
}
1947
}

src/callForwardRefRule.ts

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import * as Lint from 'tslint/lib/lint';
2+
import * as ts from 'typescript';
3+
import {sprintf} from 'sprintf-js';
4+
5+
export class Rule extends Lint.Rules.AbstractRule {
6+
7+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
8+
return this.applyWithWalker(
9+
new ExpressionCallMetadataWalker(sourceFile,
10+
this.getOptions()));
11+
}
12+
13+
static FAILURE_STRING:string = 'In the class "%s" you are calling forwardRef,' +
14+
' which is considered a bad practice ' +
15+
'and indicates either a cyclic dependency or ' +
16+
'inconsistency in the services declaration';
17+
}
18+
19+
20+
export class ExpressionCallMetadataWalker extends Lint.RuleWalker {
21+
22+
visitCallExpression(node:ts.CallExpression) {
23+
this.validateCallExpression(node);
24+
super.visitCallExpression(node);
25+
}
26+
27+
private validateCallExpression(callExpression) {
28+
if (callExpression.expression.text === 'forwardRef') {
29+
let currentNode:any = callExpression;
30+
while (currentNode.parent.parent) {
31+
currentNode = currentNode.parent;
32+
}
33+
let failureConfig:string[] = [currentNode.name.text];
34+
failureConfig.unshift(Rule.FAILURE_STRING);
35+
this.addFailure(
36+
this.createFailure(
37+
callExpression.getStart(),
38+
callExpression.getWidth(),
39+
sprintf.apply(this, failureConfig)));
40+
}
41+
}
42+
43+
}

src/inputPropertyDirectiveRule.ts

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
11
import * as Lint from 'tslint/lib/lint';
22
import * as ts from 'typescript';
3-
import {ClassParameterRule} from "./propertyClassBase";
4-
import {decoratorValidator} from './util/decoratorValidator';
3+
import {sprintf} from 'sprintf-js';
4+
import {Ng2Walker} from "./util/ng2Walker";
55

6-
const FAILURE_STRING = 'In the class "%s", the directive input property "%s" should not be renamed.' +
7-
'Please, consider the following use "@Input() %s: string"';
6+
export class Rule extends Lint.Rules.AbstractRule {
87

9-
const renameInputCondition = (name, arg, element)=> {
10-
let memberName = element.name.text;
11-
return (name === 'Input' && arg && memberName != arg.text);
12-
};
8+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
9+
return this.applyWithWalker(
10+
new InputMetadataWalker(sourceFile,
11+
this.getOptions()));
12+
}
1313

14-
export class Rule extends ClassParameterRule {
14+
static FAILURE_STRING:string = 'In the class "%s", the directive ' +
15+
'input property "%s" should not be renamed.' +
16+
'Please, consider the following use "@Input() %s: string"';
17+
}
1518

16-
constructor(ruleName:string, value:any, disabledIntervals:Lint.IDisabledInterval[]) {
17-
super(ruleName, value, disabledIntervals, decoratorValidator(renameInputCondition), FAILURE_STRING);
18-
}
1919

20+
export class InputMetadataWalker extends Ng2Walker {
21+
22+
visitNg2Input(property:ts.PropertyDeclaration, input:ts.Decorator, args:string[]) {
23+
let className = (<any>property).parent.name.text;
24+
let memberName = (<any>property.name).text;
25+
if (args.length != 0 && memberName != args[0]) {
26+
let failureConfig:string[] = [className, memberName, memberName];
27+
failureConfig.unshift(Rule.FAILURE_STRING);
28+
this.addFailure(
29+
this.createFailure(
30+
property.getStart(),
31+
property.getWidth(),
32+
sprintf.apply(this, failureConfig)));
33+
}
34+
}
2035
}

src/outputPropertyDirectiveRule.ts

+27-12
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,35 @@
11
import * as Lint from 'tslint/lib/lint';
22
import * as ts from 'typescript';
3-
import {ClassParameterRule} from "./propertyClassBase";
4-
import {decoratorValidator} from './util/decoratorValidator';
3+
import {sprintf} from 'sprintf-js';
4+
import {Ng2Walker} from "./util/ng2Walker";
55

6-
const FAILURE_STRING = 'In the class "%s", the directive output property "%s" should not be renamed.' +
7-
'Please, consider the following use "@Output() %s = new EventEmitter();"';
6+
export class Rule extends Lint.Rules.AbstractRule {
87

9-
const renameOutputCondition = (name, arg, element)=> {
10-
let memberName = element.name.text;
11-
return (name === 'Output' && arg && memberName !== arg.text);
12-
};
8+
public apply(sourceFile:ts.SourceFile):Lint.RuleFailure[] {
9+
return this.applyWithWalker(
10+
new OutputMetadataWalker(sourceFile,
11+
this.getOptions()));
12+
}
1313

14-
export class Rule extends ClassParameterRule {
14+
static FAILURE_STRING:string = 'In the class "%s", the directive output ' +
15+
'property "%s" should not be renamed.' +
16+
'Please, consider the following use "@Output() %s = new EventEmitter();"';
17+
}
1518

16-
constructor(ruleName:string, value:any, disabledIntervals:Lint.IDisabledInterval[]) {
17-
super(ruleName, value, disabledIntervals, decoratorValidator(renameOutputCondition), FAILURE_STRING);
18-
}
1919

20+
export class OutputMetadataWalker extends Ng2Walker {
21+
22+
visitNg2Output(property:ts.PropertyDeclaration, output:ts.Decorator, args:string[]) {
23+
let className = (<any>property).parent.name.text;
24+
let memberName = (<any>property.name).text;
25+
if (args.length != 0 && memberName != args[0]) {
26+
let failureConfig:string[] = [className, memberName, memberName];
27+
failureConfig.unshift(Rule.FAILURE_STRING);
28+
this.addFailure(
29+
this.createFailure(
30+
property.getStart(),
31+
property.getWidth(),
32+
sprintf.apply(this, failureConfig)));
33+
}
34+
}
2035
}

src/parameterConstructorBase.ts

-60
This file was deleted.

src/propertyClassBase.ts

-58
This file was deleted.

src/util/decoratorValidator.ts

-18
This file was deleted.

test/attributeParameterDecoratorRule.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ describe('attribute-parameter-decorator', () => {
66
let source = `
77
class ButtonComponent {
88
label: string;
9-
constructor(public @Attribute('label') label) {
9+
constructor(@Attribute('label') label) {
1010
this.label = label;
1111
}
1212
}`;
@@ -15,11 +15,11 @@ describe('attribute-parameter-decorator', () => {
1515
'which is considered as a bad practice. Please, consider construction of type "@Input() label: string"',
1616
startPosition: {
1717
line: 3,
18-
character: 35
18+
character: 28
1919
},
2020
endPosition: {
2121
line: 3,
22-
character: 60
22+
character: 53
2323
}
2424
});
2525
});

0 commit comments

Comments
 (0)