11import { sprintf } from 'sprintf-js' ;
2- import { IRuleMetadata , RuleFailure } from 'tslint' ;
2+ import { IRuleMetadata , RuleFailure , WalkContext } from 'tslint' ;
33import { AbstractRule } from 'tslint/lib/rules' ;
44import { dedent } from 'tslint/lib/utils' ;
5- import { createNodeArray , Decorator , isClassDeclaration , SourceFile } from 'typescript' ;
6- import { NgWalker } from './angular/ngWalker' ;
5+ import {
6+ AccessorDeclaration ,
7+ createNodeArray ,
8+ Decorator ,
9+ forEachChild ,
10+ isAccessor ,
11+ isMethodDeclaration ,
12+ isParameterPropertyDeclaration ,
13+ isPropertyDeclaration ,
14+ MethodDeclaration ,
15+ Node ,
16+ ParameterPropertyDeclaration ,
17+ PropertyDeclaration ,
18+ SourceFile
19+ } from 'typescript' ;
20+ import { isNotNullOrUndefined } from './util/isNotNullOrUndefined' ;
721import {
822 ANGULAR_CLASS_DECORATOR_MAPPER ,
923 AngularClassDecoratorKeys ,
1024 AngularClassDecorators ,
11- AngularInnerClassDecoratorKeys ,
1225 AngularInnerClassDecorators ,
1326 getDecoratorName ,
1427 getNextToLastParentNode ,
@@ -17,77 +30,83 @@ import {
1730} from './util/utils' ;
1831
1932interface FailureParameters {
20- readonly className : string ;
2133 readonly classDecoratorName : AngularClassDecoratorKeys ;
22- readonly innerClassDecoratorName : AngularInnerClassDecoratorKeys ;
2334}
2435
25- export const getFailureMessage = ( failureParameters : FailureParameters ) : string =>
26- sprintf (
27- Rule . FAILURE_STRING ,
28- failureParameters . innerClassDecoratorName ,
29- failureParameters . className ,
30- failureParameters . classDecoratorName
31- ) ;
36+ type DeclarationLike = AccessorDeclaration | MethodDeclaration | ParameterPropertyDeclaration | PropertyDeclaration ;
37+
38+ export const getFailureMessage = ( failureParameters : FailureParameters ) : string => {
39+ return sprintf ( Rule . FAILURE_STRING , failureParameters . classDecoratorName ) ;
40+ } ;
3241
3342export class Rule extends AbstractRule {
3443 static readonly metadata : IRuleMetadata = {
35- description : 'Ensures that classes use allowed decorator in its body.' ,
44+ description : 'Ensures that classes use contextual decorators in its body.' ,
3645 options : null ,
3746 optionsDescription : 'Not configurable.' ,
3847 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 .
48+ Some decorators should only be used in certain class types. For example,
49+ the decorator @${ AngularInnerClassDecorators . Input } () should
50+ not be used in a class decorated with @${ AngularClassDecorators . Injectable } () .
4251 ` ,
4352 ruleName : 'contextual-decorator' ,
4453 type : 'functionality' ,
4554 typescriptOnly : true
4655 } ;
4756
48- static readonly FAILURE_STRING = 'The decorator "%s" is not allowed for class "%s" because it is decorated with "%s "' ;
57+ static readonly FAILURE_STRING = 'Decorator out of context for "@%s() "' ;
4958
5059 apply ( sourceFile : SourceFile ) : RuleFailure [ ] {
51- const walker = new Walker ( sourceFile , this . getOptions ( ) ) ;
52-
53- return this . applyWithWalker ( walker ) ;
60+ return this . applyWithFunction ( sourceFile , walk ) ;
5461 }
5562}
5663
57- class Walker extends NgWalker {
58- protected visitMethodDecorator ( decorator : Decorator ) : void {
59- this . validateDecorator ( decorator ) ;
60- super . visitMethodDecorator ( decorator ) ;
61- }
64+ const callbackHandler = ( walkContext : WalkContext , node : Node ) : void => {
65+ if ( isDeclarationLike ( node ) ) validateDeclaration ( walkContext , node ) ;
66+ } ;
6267
63- protected visitPropertyDecorator ( decorator : Decorator ) : void {
64- this . validateDecorator ( decorator ) ;
65- super . visitPropertyDecorator ( decorator ) ;
66- }
68+ const getClassDecoratorName = ( klass : Node ) : AngularClassDecoratorKeys | undefined => {
69+ return createNodeArray ( klass . decorators )
70+ . map ( getDecoratorName )
71+ . filter ( isNotNullOrUndefined )
72+ . find ( isAngularClassDecorator ) ;
73+ } ;
6774
68- private validateDecorator ( decorator : Decorator ) : void {
69- const klass = getNextToLastParentNode ( decorator ) ;
75+ const isDeclarationLike = ( node : Node ) : node is DeclarationLike => {
76+ return isAccessor ( node ) || isMethodDeclaration ( node ) || isParameterPropertyDeclaration ( node ) || isPropertyDeclaration ( node ) ;
77+ } ;
7078
71- if ( ! isClassDeclaration ( klass ) || ! klass . name ) return ;
79+ const validateDeclaration = ( walkContext : WalkContext , node : DeclarationLike ) : void => {
80+ const klass = getNextToLastParentNode ( node ) ;
81+ const classDecoratorName = getClassDecoratorName ( klass ) ;
7282
73- const classDecoratorName = createNodeArray ( klass . decorators )
74- . map ( x => x . expression . getText ( ) )
75- . map ( x => x . replace ( / [ ^ a - z A - Z ] / g, '' ) )
76- . find ( isAngularClassDecorator ) ;
83+ if ( ! classDecoratorName ) return ;
7784
78- if ( ! classDecoratorName ) return ;
85+ createNodeArray ( node . decorators ) . forEach ( decorator => validateDecorator ( walkContext , decorator , classDecoratorName ) ) ;
86+ } ;
7987
80- const innerClassDecoratorName = getDecoratorName ( decorator ) ;
88+ const validateDecorator = ( walkContext : WalkContext , node : Decorator , classDecoratorName : AngularClassDecoratorKeys ) : void => {
89+ const decoratorName = getDecoratorName ( node ) ;
8190
82- if ( ! innerClassDecoratorName || ! isAngularInnerClassDecorator ( innerClassDecoratorName ) ) return ;
91+ if ( ! decoratorName || ! isAngularInnerClassDecorator ( decoratorName ) ) return ;
8392
84- const allowedDecorators = ANGULAR_CLASS_DECORATOR_MAPPER . get ( classDecoratorName ) ;
93+ const allowedDecorators = ANGULAR_CLASS_DECORATOR_MAPPER . get ( classDecoratorName ) ;
8594
86- if ( ! allowedDecorators || allowedDecorators . has ( innerClassDecoratorName ) ) return ;
95+ if ( ! allowedDecorators || allowedDecorators . has ( decoratorName ) ) return ;
8796
88- const className = klass . name . getText ( ) ;
89- const failure = getFailureMessage ( { classDecoratorName, className, innerClassDecoratorName } ) ;
97+ const failure = getFailureMessage ( { classDecoratorName } ) ;
9098
91- this . addFailureAtNode ( decorator , failure ) ;
92- }
93- }
99+ walkContext . addFailureAtNode ( node , failure ) ;
100+ } ;
101+
102+ const walk = ( walkContext : WalkContext ) : void => {
103+ const { sourceFile } = walkContext ;
104+
105+ const callback = ( node : Node ) : void => {
106+ callbackHandler ( walkContext , node ) ;
107+
108+ forEachChild ( node , callback ) ;
109+ } ;
110+
111+ forEachChild ( sourceFile , callback ) ;
112+ } ;
0 commit comments