-
Notifications
You must be signed in to change notification settings - Fork 235
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: general rename/clean up rules (#754)
- Loading branch information
1 parent
94d5af3
commit bbf7a32
Showing
74 changed files
with
5,063 additions
and
3,928 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { sprintf } from 'sprintf-js'; | ||
import { IRuleMetadata, RuleFailure } from 'tslint'; | ||
import { AbstractRule } from 'tslint/lib/rules'; | ||
import { createNodeArray, Decorator, isClassDeclaration, SourceFile } from 'typescript'; | ||
import { NgWalker } from './angular/ngWalker'; | ||
import { | ||
DecoratorKeys, | ||
Decorators, | ||
getDecoratorName, | ||
getNextToLastParentNode, | ||
isMetadataType, | ||
isNgDecorator, | ||
METADATA_TYPE_DECORATOR_MAPPER, | ||
MetadataTypes | ||
} from './util/utils'; | ||
|
||
interface FailureParameters { | ||
readonly className: string; | ||
readonly decoratorName: DecoratorKeys; | ||
readonly metadataType: MetadataTypes; | ||
} | ||
|
||
export const getFailureMessage = (failureParameters: FailureParameters): string => | ||
sprintf(Rule.FAILURE_STRING, failureParameters.decoratorName, failureParameters.className, failureParameters.metadataType); | ||
|
||
export class Rule extends AbstractRule { | ||
static readonly metadata: IRuleMetadata = { | ||
description: 'Ensures that classes use allowed decorator in its body.', | ||
options: null, | ||
optionsDescription: 'Not configurable.', | ||
rationale: `Some decorators can only be used in certain class types. For example, an @${Decorators.Input} should not be used in an @${ | ||
MetadataTypes.Injectable | ||
} class.`, | ||
ruleName: 'contextual-decorator', | ||
type: 'functionality', | ||
typescriptOnly: true | ||
}; | ||
|
||
static readonly FAILURE_STRING = 'The decorator "%s" is not allowed for class "%s" because it is decorated with "%s"'; | ||
|
||
apply(sourceFile: SourceFile): RuleFailure[] { | ||
return this.applyWithWalker(new ContextualDecoratorWalker(sourceFile, this.getOptions())); | ||
} | ||
} | ||
|
||
export class ContextualDecoratorWalker extends NgWalker { | ||
protected visitMethodDecorator(decorator: Decorator): void { | ||
this.validateDecorator(decorator); | ||
super.visitMethodDecorator(decorator); | ||
} | ||
|
||
protected visitPropertyDecorator(decorator: Decorator): void { | ||
this.validateDecorator(decorator); | ||
super.visitPropertyDecorator(decorator); | ||
} | ||
|
||
private validateDecorator(decorator: Decorator): void { | ||
const klass = getNextToLastParentNode(decorator); | ||
|
||
if (!isClassDeclaration(klass) || !klass.name) return; | ||
|
||
const metadataType = createNodeArray(klass.decorators) | ||
.map(x => x.expression.getText()) | ||
.map(x => x.replace(/[^a-zA-Z]/g, '')) | ||
.find(isMetadataType); | ||
|
||
if (!metadataType) return; | ||
|
||
const decoratorName = getDecoratorName(decorator); | ||
|
||
if (!decoratorName || !isNgDecorator(decoratorName)) return; | ||
|
||
const allowedDecorators = METADATA_TYPE_DECORATOR_MAPPER[metadataType]; | ||
|
||
if (!allowedDecorators || allowedDecorators.has(decoratorName)) return; | ||
|
||
const className = klass.name.getText(); | ||
const failure = getFailureMessage({ className, decoratorName, metadataType }); | ||
|
||
this.addFailureAtNode(decorator, failure); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { sprintf } from 'sprintf-js'; | ||
import { IRuleMetadata, RuleFailure } from 'tslint/lib'; | ||
import { AbstractRule } from 'tslint/lib/rules'; | ||
import { ClassDeclaration, Decorator, SourceFile } from 'typescript'; | ||
import { NgWalker } from './angular/ngWalker'; | ||
import { | ||
getClassName, | ||
getDecoratorName, | ||
isLifecycleMethod, | ||
isMetadataType, | ||
LifecycleMethodKeys, | ||
LifecycleMethods, | ||
METADATA_TYPE_LIFECYCLE_MAPPER, | ||
MetadataTypeKeys, | ||
MetadataTypes | ||
} from './util/utils'; | ||
|
||
interface FailureParameters { | ||
readonly className: string; | ||
readonly metadataType: MetadataTypeKeys; | ||
readonly methodName: LifecycleMethodKeys; | ||
} | ||
|
||
export const getFailureMessage = (failureParameters: FailureParameters): string => | ||
sprintf(Rule.FAILURE_STRING, failureParameters.methodName, failureParameters.className, failureParameters.metadataType); | ||
|
||
export class Rule extends AbstractRule { | ||
static readonly metadata: IRuleMetadata = { | ||
description: 'Ensures that classes use allowed lifecycle method in its body.', | ||
options: null, | ||
optionsDescription: 'Not configurable.', | ||
rationale: `Some lifecycle methods can only be used in certain class types. For example, ${ | ||
LifecycleMethods.ngOnInit | ||
}() method should not be used in an @${MetadataTypes.Injectable} class.`, | ||
ruleName: 'contextual-lifecycle', | ||
type: 'functionality', | ||
typescriptOnly: true | ||
}; | ||
|
||
static readonly FAILURE_STRING = 'The method "%s" is not allowed for class "%s" because it is decorated with "%s"'; | ||
|
||
apply(sourceFile: SourceFile): RuleFailure[] { | ||
return this.applyWithWalker(new ContextualLifecycleWalker(sourceFile, this.getOptions())); | ||
} | ||
} | ||
|
||
class ContextualLifecycleWalker extends NgWalker { | ||
protected visitNgInjectable(controller: ClassDeclaration, decorator: Decorator): void { | ||
this.validateDecorator(controller, decorator, METADATA_TYPE_LIFECYCLE_MAPPER.Injectable); | ||
super.visitNgInjectable(controller, decorator); | ||
} | ||
|
||
protected visitNgPipe(controller: ClassDeclaration, decorator: Decorator): void { | ||
this.validateDecorator(controller, decorator, METADATA_TYPE_LIFECYCLE_MAPPER.Pipe); | ||
super.visitNgPipe(controller, decorator); | ||
} | ||
|
||
private validateDecorator(controller: ClassDeclaration, decorator: Decorator, allowedMethods: ReadonlySet<LifecycleMethodKeys>): void { | ||
const className = getClassName(controller); | ||
|
||
if (!className) return; | ||
|
||
const metadataType = getDecoratorName(decorator); | ||
|
||
if (!metadataType || !isMetadataType(metadataType)) return; | ||
|
||
for (const member of controller.members) { | ||
const { name: memberName } = member; | ||
|
||
if (!memberName) continue; | ||
|
||
const methodName = memberName.getText(); | ||
|
||
if (!isLifecycleMethod(methodName) || allowedMethods.has(methodName)) continue; | ||
|
||
const failure = getFailureMessage({ className, metadataType, methodName }); | ||
|
||
this.addFailureAtNode(member, failure); | ||
} | ||
} | ||
} |
Oops, something went wrong.