Skip to content

Commit f3a53bd

Browse files
rafaelss95wKoza
authored andcommitted
feat(rule): add special case to no-input-rename rule (#561)
1 parent 174ed46 commit f3a53bd

File tree

2 files changed

+119
-42
lines changed

2 files changed

+119
-42
lines changed

src/noInputRenameRule.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as Lint from 'tslint';
22
import * as ts from 'typescript';
33
import { sprintf } from 'sprintf-js';
4+
import { DirectiveMetadata } from './angular/metadata';
45
import { NgWalker } from './angular/ngWalker';
56

67
export class Rule extends Lint.Rules.AbstractRule {
@@ -15,29 +16,31 @@ export class Rule extends Lint.Rules.AbstractRule {
1516
typescriptOnly: true,
1617
};
1718

18-
static FAILURE_STRING: string = 'In the class "%s", the directive ' +
19-
'input property "%s" should not be renamed.' +
20-
'Please, consider the following use "@Input() %s: string"';
19+
static FAILURE_STRING: string = 'In the class "%s", the directive input property "%s" should not be renamed.';
2120

2221
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
23-
return this.applyWithWalker(
24-
new InputMetadataWalker(sourceFile,
25-
this.getOptions()));
22+
return this.applyWithWalker(new InputMetadataWalker(sourceFile, this.getOptions()));
2623
}
2724
}
2825

2926
export class InputMetadataWalker extends NgWalker {
27+
private directiveSelector: DirectiveMetadata['selector'][];
28+
29+
visitNgDirective(metadata: DirectiveMetadata): void {
30+
this.directiveSelector =
31+
(metadata.selector || '').replace(/[\[\]\s]/g, '').split(',');
32+
}
33+
3034
visitNgInput(property: ts.PropertyDeclaration, input: ts.Decorator, args: string[]) {
31-
let className = (<any>property).parent.name.text;
32-
let memberName = (<any>property.name).text;
33-
if (args.length !== 0 && memberName !== args[0]) {
34-
let failureConfig: string[] = [className, memberName, memberName];
35-
failureConfig.unshift(Rule.FAILURE_STRING);
36-
this.addFailure(
37-
this.createFailure(
38-
property.getStart(),
39-
property.getWidth(),
40-
sprintf.apply(this, failureConfig)));
35+
const className = (property.parent as any).name.text;
36+
const memberName = (property.name as any).text;
37+
38+
if (args.length === 0 ||
39+
(this.directiveSelector && this.directiveSelector.indexOf(memberName) !== -1)) {
40+
return;
4141
}
42+
43+
const failureConfig = [Rule.FAILURE_STRING, className, memberName];
44+
this.addFailureAtNode(property, sprintf.apply(this, failureConfig));
4245
}
4346
}

test/noInputRenameRule.spec.ts

Lines changed: 100 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,111 @@
11
import { assertSuccess, assertAnnotated } from './testHelper';
22

3-
describe('no-input-rename', () => {
4-
describe('invalid directive input property', () => {
5-
it('should fail, when a directive input property is renamed', () => {
6-
let source = `
7-
class ButtonComponent {
8-
@Input('labelAttribute') label: string;
9-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10-
}`;
11-
assertAnnotated({
12-
ruleName: 'no-input-rename',
13-
message: 'In the class "ButtonComponent", the directive input property "label" should not be renamed.' +
14-
'Please, consider the following use "@Input() label: string"',
15-
source
3+
const ruleName = 'no-input-rename';
4+
5+
const getMessage = (className: string, propertyName: string): string => {
6+
return `In the class "${className}", the directive input property "${propertyName}" should not be renamed.`;
7+
};
8+
9+
describe(ruleName, () => {
10+
describe('failure', () => {
11+
describe('Component', () => {
12+
it('should fail when a input property is renamed', () => {
13+
const source = `
14+
@Component
15+
class TestComponent {
16+
@Input('labelAttribute') label: string;
17+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18+
}
19+
`;
20+
21+
assertAnnotated({
22+
ruleName,
23+
message: getMessage('TestComponent', 'label'),
24+
source
25+
});
26+
});
27+
28+
it('should fail when input property is fake renamed', () => {
29+
const source = `
30+
@Component
31+
class TestComponent {
32+
@Input('label') label: string;
33+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34+
}
35+
`;
36+
37+
assertAnnotated({
38+
ruleName,
39+
message: getMessage('TestComponent', 'label'),
40+
source
41+
});
42+
});
43+
});
44+
45+
describe('Directive', () => {
46+
it('should fail when a input property is renamed', () => {
47+
const source = `
48+
@Directive
49+
class TestDirective {
50+
@Input('labelText') label: string;
51+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52+
}
53+
`;
54+
55+
assertAnnotated({
56+
ruleName,
57+
message: getMessage('TestDirective', 'label'),
58+
source
59+
});
60+
});
61+
62+
it(`should fail when input property is renamed and it's different from directive's selector`, () => {
63+
const source = `
64+
@Directive({
65+
selector: '[label], label2'
66+
})
67+
class TestDirective {
68+
@Input('label') labelText: string;
69+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70+
}
71+
`;
72+
73+
assertAnnotated({
74+
ruleName,
75+
message: getMessage('TestDirective', `labelText`),
76+
source
77+
});
1678
});
1779
});
1880
});
1981

20-
describe('valid directive input property', () => {
21-
it('should succeed, when a directive input property is properly used', () => {
22-
let source = `
23-
class ButtonComponent {
24-
@Input() label: string;
25-
}`;
26-
assertSuccess('no-input-rename', source);
82+
describe('success', () => {
83+
describe('Component', () => {
84+
it('should succeed when a input property is not renamed', () => {
85+
const source = `
86+
@Component
87+
class TestComponent {
88+
@Input() label: string;
89+
}
90+
`;
91+
92+
assertSuccess(ruleName, source);
93+
});
2794
});
2895

29-
it('should succeed, when a directive input property rename is the same as the name of the property', () => {
30-
let source = `
31-
class ButtonComponent {
32-
@Input('label') label: string;
33-
}`;
34-
assertSuccess('no-input-rename', source);
96+
describe('Directive', () => {
97+
it('should succeed when the directive name is also an input property', () => {
98+
const source = `
99+
@Directive({
100+
selector: '[label], label2'
101+
})
102+
class TestDirective {
103+
@Input('labelText') label: string;
104+
}
105+
`;
106+
107+
assertSuccess(ruleName, source);
108+
});
35109
});
36110
});
37111
});

0 commit comments

Comments
 (0)