Skip to content

Commit 8d816c8

Browse files
rafaelss95wKoza
authored andcommitted
refactor: general refactor/fixes (#797)
* refactor: make use of 'isNotNullOrUndefined' and add tslint rule to enforce file name casing * fix: 'getDecoratorName' not handling decorators without parenthesis * refactor(utils): add missing decorators and normalize enum/constants/interfaces/types names to Angular* * refactor: order tslint.json * fix: add missing decorators for 'NgModule' * fix: parseInvalidSource method not handling other chars * fix: remove unnecessary export * refactor: improve testHelper * fix: adjust file-name-casing * refactor: change decorator constant name
1 parent 6d283e6 commit 8d816c8

29 files changed

+471
-367
lines changed

src/angularWhitespaceRule.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ExpTypes } from './angular/expressionTypes';
66
import { NgWalker, NgWalkerConfig } from './angular/ngWalker';
77
import { BasicTemplateAstVisitor } from './angular/templates/basicTemplateAstVisitor';
88
import { RecursiveAngularExpressionVisitor } from './angular/templates/recursiveAngularExpressionVisitor';
9+
import { isNotNullOrUndefined } from './util/isNotNullOrUndefined';
910

1011
// Check if ES6 'y' flag is usable.
1112
const stickyFlagUsable = (() => {
@@ -173,7 +174,7 @@ class TemplateVisitorCtrl extends BasicTemplateAstVisitor {
173174
this.visitors
174175
.filter(v => options.indexOf(v.getCheckOption()) >= 0)
175176
.map(v => v.visitBoundText(text, this))
176-
.filter(Boolean)
177+
.filter(isNotNullOrUndefined)
177178
.forEach(f =>
178179
this.addFailureFromStartToEnd(f.getStartPosition().getPosition(), f.getEndPosition().getPosition(), f.getFailure(), f.getFix())
179180
);
@@ -185,7 +186,7 @@ class TemplateVisitorCtrl extends BasicTemplateAstVisitor {
185186
this.visitors
186187
.filter(v => options.indexOf(v.getCheckOption()) >= 0)
187188
.map(v => v.visitDirectiveProperty(prop, this))
188-
.filter(Boolean)
189+
.filter(isNotNullOrUndefined)
189190
.forEach(f =>
190191
this.addFailureFromStartToEnd(f.getStartPosition().getPosition(), f.getEndPosition().getPosition(), f.getFailure(), f.getFix())
191192
);
@@ -270,7 +271,7 @@ class ExpressionVisitorCtrl extends RecursiveAngularExpressionVisitor {
270271
.map(v => v.addParentAST(this.parentAST))
271272
.filter(v => options.indexOf(v.getCheckOption()) >= 0)
272273
.map(v => v.visitPipe(expr, this))
273-
.filter(Boolean)
274+
.filter(isNotNullOrUndefined)
274275
.forEach(f =>
275276
this.addFailureFromStartToEnd(f.getStartPosition().getPosition(), f.getEndPosition().getPosition(), f.getFailure(), f.getFix())
276277
);

src/componentSelectorRule.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
SelectorStyle,
1010
SelectorType
1111
} from './selectorPropertyBase';
12-
import { isNotNullOrUndefined } from './util/is-not-null-or-undefined';
12+
import { isNotNullOrUndefined } from './util/isNotNullOrUndefined';
1313

1414
const STYLE_GUIDE_PREFIX_LINK = 'https://angular.io/guide/styleguide#style-02-07';
1515
const STYLE_GUIDE_STYLE_LINK = 'https://angular.io/guide/styleguide#style-05-02';

src/contextualDecoratorRule.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,45 @@
11
import { sprintf } from 'sprintf-js';
22
import { IRuleMetadata, RuleFailure } from 'tslint';
33
import { AbstractRule } from 'tslint/lib/rules';
4+
import { dedent } from 'tslint/lib/utils';
45
import { createNodeArray, Decorator, isClassDeclaration, SourceFile } from 'typescript';
56
import { NgWalker } from './angular/ngWalker';
67
import {
7-
DecoratorKeys,
8-
Decorators,
8+
ANGULAR_CLASS_DECORATOR_MAPPER,
9+
AngularClassDecoratorKeys,
10+
AngularClassDecorators,
11+
AngularInnerClassDecoratorKeys,
12+
AngularInnerClassDecorators,
913
getDecoratorName,
1014
getNextToLastParentNode,
11-
isMetadataType,
12-
isNgDecorator,
13-
METADATA_TYPE_DECORATOR_MAPPER,
14-
MetadataTypes
15+
isAngularClassDecorator,
16+
isAngularInnerClassDecorator
1517
} from './util/utils';
1618

1719
interface FailureParameters {
1820
readonly className: string;
19-
readonly decoratorName: DecoratorKeys;
20-
readonly metadataType: MetadataTypes;
21+
readonly classDecoratorName: AngularClassDecoratorKeys;
22+
readonly innerClassDecoratorName: AngularInnerClassDecoratorKeys;
2123
}
2224

2325
export const getFailureMessage = (failureParameters: FailureParameters): string =>
24-
sprintf(Rule.FAILURE_STRING, failureParameters.decoratorName, failureParameters.className, failureParameters.metadataType);
26+
sprintf(
27+
Rule.FAILURE_STRING,
28+
failureParameters.innerClassDecoratorName,
29+
failureParameters.className,
30+
failureParameters.classDecoratorName
31+
);
2532

2633
export class Rule extends AbstractRule {
2734
static readonly metadata: IRuleMetadata = {
2835
description: 'Ensures that classes use allowed decorator in its body.',
2936
options: null,
3037
optionsDescription: 'Not configurable.',
31-
rationale: `Some decorators can only be used in certain class types. For example, an @${Decorators.Input} should not be used in an @${
32-
MetadataTypes.Injectable
33-
} class.`,
38+
rationale: dedent`
39+
Some decorators can only be used in certain class types.
40+
For example, an @${AngularInnerClassDecorators.Input} should not be used
41+
in an @${AngularClassDecorators.Injectable} class.
42+
`,
3443
ruleName: 'contextual-decorator',
3544
type: 'functionality',
3645
typescriptOnly: true
@@ -61,23 +70,23 @@ class Walker extends NgWalker {
6170

6271
if (!isClassDeclaration(klass) || !klass.name) return;
6372

64-
const metadataType = createNodeArray(klass.decorators)
73+
const classDecoratorName = createNodeArray(klass.decorators)
6574
.map(x => x.expression.getText())
6675
.map(x => x.replace(/[^a-zA-Z]/g, ''))
67-
.find(isMetadataType);
76+
.find(isAngularClassDecorator);
6877

69-
if (!metadataType) return;
78+
if (!classDecoratorName) return;
7079

71-
const decoratorName = getDecoratorName(decorator);
80+
const innerClassDecoratorName = getDecoratorName(decorator);
7281

73-
if (!decoratorName || !isNgDecorator(decoratorName)) return;
82+
if (!innerClassDecoratorName || !isAngularInnerClassDecorator(innerClassDecoratorName)) return;
7483

75-
const allowedDecorators = METADATA_TYPE_DECORATOR_MAPPER[metadataType];
84+
const allowedDecorators = ANGULAR_CLASS_DECORATOR_MAPPER.get(classDecoratorName);
7685

77-
if (!allowedDecorators || allowedDecorators.has(decoratorName)) return;
86+
if (!allowedDecorators || allowedDecorators.has(innerClassDecoratorName)) return;
7887

7988
const className = klass.name.getText();
80-
const failure = getFailureMessage({ className, decoratorName, metadataType });
89+
const failure = getFailureMessage({ classDecoratorName, className, innerClassDecoratorName });
8190

8291
this.addFailureAtNode(decorator, failure);
8392
}

src/contextualLifecycleRule.ts

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,41 @@
11
import { sprintf } from 'sprintf-js';
22
import { IRuleMetadata, RuleFailure } from 'tslint/lib';
33
import { AbstractRule } from 'tslint/lib/rules';
4+
import { dedent } from 'tslint/lib/utils';
45
import { SourceFile } from 'typescript';
56
import { InjectableMetadata, ModuleMetadata, PipeMetadata } from './angular';
67
import { NgWalker } from './angular/ngWalker';
78
import {
9+
ANGULAR_CLASS_DECORATOR_LIFECYCLE_METHOD_MAPPER,
10+
AngularClassDecoratorKeys,
11+
AngularClassDecorators,
12+
AngularLifecycleMethodKeys,
13+
AngularLifecycleMethods,
814
getClassName,
915
getDecoratorName,
10-
isLifecycleMethod,
11-
isMetadataType,
12-
LifecycleMethodKeys,
13-
LifecycleMethods,
14-
METADATA_TYPE_LIFECYCLE_MAPPER,
15-
MetadataTypeKeys,
16-
MetadataTypes
16+
isAngularClassDecorator,
17+
isAngularLifecycleMethod
1718
} from './util/utils';
1819

1920
interface FailureParameters {
2021
readonly className: string;
21-
readonly metadataType: MetadataTypeKeys;
22-
readonly methodName: LifecycleMethodKeys;
22+
readonly decoratorName: AngularClassDecoratorKeys;
23+
readonly methodName: AngularLifecycleMethodKeys;
2324
}
2425

2526
export const getFailureMessage = (failureParameters: FailureParameters): string =>
26-
sprintf(Rule.FAILURE_STRING, failureParameters.methodName, failureParameters.className, failureParameters.metadataType);
27+
sprintf(Rule.FAILURE_STRING, failureParameters.methodName, failureParameters.className, failureParameters.decoratorName);
2728

2829
export class Rule extends AbstractRule {
2930
static readonly metadata: IRuleMetadata = {
3031
description: 'Ensures that classes use allowed lifecycle method in its body.',
3132
options: null,
3233
optionsDescription: 'Not configurable.',
33-
rationale: `Some lifecycle methods can only be used in certain class types. For example, ${
34-
LifecycleMethods.ngOnInit
35-
}() method should not be used in an @${MetadataTypes.Injectable} class.`,
34+
rationale: dedent`
35+
Some lifecycle methods can only be used in certain class types.
36+
For example, ${AngularLifecycleMethods.ngOnInit}() method should not be used
37+
in an @${AngularClassDecorators.Injectable} class.
38+
`,
3639
ruleName: 'contextual-lifecycle',
3740
type: 'functionality',
3841
typescriptOnly: true
@@ -49,26 +52,38 @@ export class Rule extends AbstractRule {
4952

5053
class Walker extends NgWalker {
5154
protected visitNgInjectable(metadata: InjectableMetadata): void {
52-
this.validateDecorator(metadata, METADATA_TYPE_LIFECYCLE_MAPPER.Injectable);
55+
const injectableAllowedMethods = ANGULAR_CLASS_DECORATOR_LIFECYCLE_METHOD_MAPPER.get(AngularClassDecorators.Injectable);
56+
57+
if (!injectableAllowedMethods) return;
58+
59+
this.validateDecorator(metadata, injectableAllowedMethods);
5360
super.visitNgInjectable(metadata);
5461
}
5562

56-
protected visitNgPipe(metadata: PipeMetadata): void {
57-
this.validateDecorator(metadata, METADATA_TYPE_LIFECYCLE_MAPPER.Pipe);
58-
super.visitNgPipe(metadata);
59-
}
63+
protected visitNgModule(metadata: ModuleMetadata): void {
64+
const ngModuleAllowedMethods = ANGULAR_CLASS_DECORATOR_LIFECYCLE_METHOD_MAPPER.get(AngularClassDecorators.NgModule);
65+
66+
if (!ngModuleAllowedMethods) return;
6067

61-
protected visitNgModule(metadata: ModuleMetadata) {
62-
this.validateDecorator(metadata, METADATA_TYPE_LIFECYCLE_MAPPER.NgModule);
68+
this.validateDecorator(metadata, ngModuleAllowedMethods);
6369
super.visitNgModule(metadata);
6470
}
6571

66-
private validateDecorator(metadata: PipeMetadata, allowedMethods: ReadonlySet<LifecycleMethodKeys>): void {
72+
protected visitNgPipe(metadata: PipeMetadata): void {
73+
const pipeAllowedMethods = ANGULAR_CLASS_DECORATOR_LIFECYCLE_METHOD_MAPPER.get(AngularClassDecorators.Pipe);
74+
75+
if (!pipeAllowedMethods) return;
76+
77+
this.validateDecorator(metadata, pipeAllowedMethods);
78+
super.visitNgPipe(metadata);
79+
}
80+
81+
private validateDecorator(metadata: PipeMetadata, allowedMethods: ReadonlySet<AngularLifecycleMethodKeys>): void {
6782
const className = getClassName(metadata.controller)!;
6883

69-
const metadataType = getDecoratorName(metadata.decorator);
84+
const decoratorName = getDecoratorName(metadata.decorator);
7085

71-
if (!metadataType || !isMetadataType(metadataType)) return;
86+
if (!decoratorName || !isAngularClassDecorator(decoratorName)) return;
7287

7388
for (const member of metadata.controller.members) {
7489
const { name: memberName } = member;
@@ -77,9 +92,9 @@ class Walker extends NgWalker {
7792

7893
const methodName = memberName.getText();
7994

80-
if (!isLifecycleMethod(methodName) || allowedMethods.has(methodName)) continue;
95+
if (!isAngularLifecycleMethod(methodName) || allowedMethods.has(methodName)) continue;
8196

82-
const failure = getFailureMessage({ className, metadataType, methodName });
97+
const failure = getFailureMessage({ className, decoratorName, methodName });
8398

8499
this.addFailureAtNode(member, failure);
85100
}

src/directiveClassSuffixRule.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Lint from 'tslint';
33
import * as ts from 'typescript';
44
import { DirectiveMetadata } from './angular/metadata';
55
import { NgWalker } from './angular/ngWalker';
6-
import { getSymbolName } from './util/utils';
6+
import { getDeclaredInterfaceNames } from './util/utils';
77

88
const ValidatorSuffix = 'Validator';
99

@@ -40,25 +40,17 @@ export class Rule extends Lint.Rules.AbstractRule {
4040
}
4141

4242
class Walker extends NgWalker {
43-
protected visitNgDirective(metadata: DirectiveMetadata) {
43+
protected visitNgDirective(metadata: DirectiveMetadata): void {
4444
const name = metadata.controller.name!;
4545
const className = name.text;
4646
const options = this.getOptions();
4747
const suffixes: string[] = options.length ? options : ['Directive'];
48-
const { heritageClauses } = metadata.controller;
4948

50-
if (heritageClauses) {
51-
const i = heritageClauses.filter(h => h.token === ts.SyntaxKind.ImplementsKeyword);
49+
const declaredInterfaceNames = getDeclaredInterfaceNames(metadata.controller);
50+
const hasValidatorInterface = declaredInterfaceNames.some(interfaceName => interfaceName.endsWith(ValidatorSuffix));
5251

53-
if (
54-
i.length !== 0 &&
55-
i[0].types
56-
.map(getSymbolName)
57-
.filter(Boolean)
58-
.some(x => x.endsWith(ValidatorSuffix))
59-
) {
60-
suffixes.push(ValidatorSuffix);
61-
}
52+
if (hasValidatorInterface) {
53+
suffixes.push(ValidatorSuffix);
6254
}
6355

6456
if (!Rule.validate(className, suffixes)) {

src/directiveSelectorRule.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
SelectorStyle,
1010
SelectorType
1111
} from './selectorPropertyBase';
12-
import { isNotNullOrUndefined } from './util/is-not-null-or-undefined';
12+
import { isNotNullOrUndefined } from './util/isNotNullOrUndefined';
1313

1414
const STYLE_GUIDE_PREFIX_LINK = 'https://angular.io/guide/styleguide#style-02-08';
1515
const STYLE_GUIDE_STYLE_TYPE_LINK = 'https://angular.io/guide/styleguide#style-02-06';

src/noConflictingLifecycleRule.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,26 @@ import { AbstractRule } from 'tslint/lib/rules';
44
import { dedent } from 'tslint/lib/utils';
55
import { ClassDeclaration, forEachChild, isClassDeclaration, Node, SourceFile } from 'typescript';
66
import {
7-
getDeclaredLifecycleInterfaces,
8-
getDeclaredLifecycleMethods,
9-
LifecycleInterfaceKeys,
10-
LifecycleInterfaces,
11-
LifecycleMethodKeys,
12-
LifecycleMethods
7+
AngularLifecycleInterfaceKeys,
8+
AngularLifecycleInterfaces,
9+
AngularLifecycleMethodKeys,
10+
AngularLifecycleMethods,
11+
getDeclaredAngularLifecycleInterfaces,
12+
getDeclaredAngularLifecycleMethods
1313
} from './util/utils';
1414

1515
interface FailureParameters {
1616
readonly message: typeof Rule.FAILURE_STRING_INTERFACE_HOOK | typeof Rule.FAILURE_STRING_METHOD_HOOK;
1717
}
1818

19-
const LIFECYCLE_INTERFACES: ReadonlyArray<LifecycleInterfaceKeys> = [LifecycleInterfaces.DoCheck, LifecycleInterfaces.OnChanges];
20-
const LIFECYCLE_METHODS: ReadonlyArray<LifecycleMethodKeys> = [LifecycleMethods.ngDoCheck, LifecycleMethods.ngOnChanges];
19+
const LIFECYCLE_INTERFACES: ReadonlyArray<AngularLifecycleInterfaceKeys> = [
20+
AngularLifecycleInterfaces.DoCheck,
21+
AngularLifecycleInterfaces.OnChanges
22+
];
23+
const LIFECYCLE_METHODS: ReadonlyArray<AngularLifecycleMethodKeys> = [
24+
AngularLifecycleMethods.ngDoCheck,
25+
AngularLifecycleMethods.ngOnChanges
26+
];
2127

2228
export const getFailureMessage = (failureParameters: FailureParameters): string => sprintf(failureParameters.message);
2329

@@ -28,8 +34,8 @@ export class Rule extends AbstractRule {
2834
options: null,
2935
optionsDescription: 'Not configurable.',
3036
rationale: dedent`
31-
A directive typically should not use both ${LifecycleInterfaces.DoCheck} and ${LifecycleInterfaces.OnChanges} to respond
32-
to changes on the same input, as ${LifecycleMethods.ngOnChanges} will continue to be called when the
37+
A directive typically should not use both ${AngularLifecycleInterfaces.DoCheck} and ${AngularLifecycleInterfaces.OnChanges} to respond
38+
to changes on the same input, as ${AngularLifecycleMethods.ngOnChanges} will continue to be called when the
3339
default change detector detects changes.
3440
`,
3541
ruleName: 'no-conflicting-lifecycle',
@@ -38,10 +44,10 @@ export class Rule extends AbstractRule {
3844
};
3945

4046
static readonly FAILURE_STRING_INTERFACE_HOOK = dedent`
41-
Implementing ${LifecycleInterfaces.DoCheck} and ${LifecycleInterfaces.OnChanges} in a class is not recommended
47+
Implementing ${AngularLifecycleInterfaces.DoCheck} and ${AngularLifecycleInterfaces.OnChanges} in a class is not recommended
4248
`;
4349
static readonly FAILURE_STRING_METHOD_HOOK = dedent`
44-
Declaring ${LifecycleMethods.ngDoCheck} and ${LifecycleMethods.ngOnChanges} method in a class is not recommended
50+
Declaring ${AngularLifecycleMethods.ngDoCheck} and ${AngularLifecycleMethods.ngOnChanges} method in a class is not recommended
4551
`;
4652

4753
apply(sourceFile: SourceFile): RuleFailure[] {
@@ -55,9 +61,9 @@ const validateClassDeclaration = (context: WalkContext<void>, node: ClassDeclara
5561
};
5662

5763
const validateInterfaces = (context: WalkContext<void>, node: ClassDeclaration): void => {
58-
const declaredLifecycleInterfaces = getDeclaredLifecycleInterfaces(node);
59-
const hasConflictingLifecycle = LIFECYCLE_INTERFACES.every(
60-
lifecycleInterface => declaredLifecycleInterfaces.indexOf(lifecycleInterface) !== -1
64+
const declaredAngularLifecycleInterfaces = getDeclaredAngularLifecycleInterfaces(node);
65+
const hasConflictingLifecycle = LIFECYCLE_INTERFACES.every(lifecycleInterface =>
66+
declaredAngularLifecycleInterfaces.includes(lifecycleInterface)
6167
);
6268

6369
if (!hasConflictingLifecycle) return;
@@ -70,8 +76,8 @@ const validateInterfaces = (context: WalkContext<void>, node: ClassDeclaration):
7076
};
7177

7278
const validateMethods = (context: WalkContext<void>, node: ClassDeclaration): void => {
73-
const declaredLifecycleMethods = getDeclaredLifecycleMethods(node);
74-
const hasConflictingLifecycle = LIFECYCLE_METHODS.every(lifecycleMethod => declaredLifecycleMethods.indexOf(lifecycleMethod) !== -1);
79+
const declaredAngularLifecycleMethods = getDeclaredAngularLifecycleMethods(node);
80+
const hasConflictingLifecycle = LIFECYCLE_METHODS.every(lifecycleMethod => declaredAngularLifecycleMethods.includes(lifecycleMethod));
7581

7682
if (!hasConflictingLifecycle) return;
7783

0 commit comments

Comments
 (0)