Last active
January 5, 2020 22:56
-
-
Save sandipchitale/52b5f4696f5cc83ded3dc5ce475750c2 to your computer and use it in GitHub Desktop.
TSLInt rule that ensures that a @component has constructor, ngOnInit and ngOnDestroy in correct order.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * TSLInt rule that ensures that a @Component has constructor, ngOnInit and ngOnDestroy in correct order. | |
| */ | |
| import { | |
| SourceFile, ClassDeclaration, MethodDeclaration, | |
| createNodeArray, | |
| isCallExpression, | |
| isIdentifier, | |
| Decorator, | |
| SyntaxKind | |
| } from 'typescript'; | |
| import { IRuleMetadata, RuleFailure, Rules, IOptions } from 'tslint/lib'; | |
| import { NgWalker } from 'codelyzer/angular/ngWalker'; | |
| export class Rule extends Rules.AbstractRule { | |
| static readonly metadata: IRuleMetadata = { | |
| ruleName: 'component-lifecycle-method-order', | |
| type: 'maintainability', | |
| description: `Ensures that component method order is first constructor(), ngInit(), ..., last ngDestroy().`, | |
| options: null, | |
| optionsDescription: 'Not configurable', | |
| rationale: `InfoArchive Coding Convention`, | |
| typescriptOnly: true, | |
| }; | |
| static readonly FAILURE_STRING = 'Component method order should be first constructor(), ngInit(), ..., last ngDestroy().'; | |
| apply(sourceFile: SourceFile): RuleFailure[] { | |
| return this.applyWithWalker(new ComponentWalker(sourceFile, this.getOptions())); | |
| } | |
| } | |
| class ComponentWalker extends NgWalker { | |
| map = { | |
| }; | |
| constructor(sourceFile: SourceFile, | |
| _originalOptions: IOptions) { | |
| super(sourceFile, _originalOptions); | |
| } | |
| visitClassDeclaration(classDeclaration: ClassDeclaration): void { | |
| createNodeArray(classDeclaration.decorators).forEach((decorator) => { | |
| // Make sure the class has @Component decorator | |
| if ('Component' === this.getDecoratorName(decorator)) { | |
| // Go thru members and record the order of constructor and methods | |
| const members = createNodeArray(classDeclaration.members); | |
| const order = []; | |
| for (let i = 0; i < members.length; i++) { | |
| if (members[i].kind === SyntaxKind.Constructor) { | |
| order.push('constructor'); | |
| } else if (members[i].kind === SyntaxKind.MethodDeclaration) { | |
| if (members[i].name) { | |
| order.push(members[i].name.getText()); | |
| } | |
| } | |
| } | |
| // Get the ordinal of constructor, ngOnInit and ngOnDestroy | |
| const cIndex = order.indexOf('constructor'); | |
| const niIndex = order.indexOf('ngOnInit'); | |
| const ndIndex = order.indexOf('ngOnDestroy'); | |
| if ((cIndex === -1 || cIndex === 0) && | |
| (niIndex === -1 || (cIndex === -1 && niIndex === 0) || (cIndex === 0 && niIndex === 1)) && | |
| (ndIndex === -1 || ndIndex === order.length - 1)) { | |
| // Order is correct | |
| } else { | |
| this.addFailureFromStartToEnd( | |
| classDeclaration.name.getStart(), | |
| classDeclaration.name.getEnd(), | |
| Rule.FAILURE_STRING); | |
| } | |
| } | |
| }); | |
| super.visitClassDeclaration(classDeclaration); | |
| } | |
| private getDecoratorName(decorator: Decorator): string | undefined { | |
| return isCallExpression(decorator.expression) && isIdentifier(decorator.expression.expression) | |
| ? decorator.expression.expression.text | |
| : undefined; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment