Skip to content

Commit

Permalink
refactor: general rename/clean up rules (#754)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelss95 authored and mgechev committed Feb 24, 2019
1 parent 94d5af3 commit bbf7a32
Show file tree
Hide file tree
Showing 74 changed files with 5,063 additions and 3,928 deletions.
70 changes: 0 additions & 70 deletions src/bananaInBoxRule.ts

This file was deleted.

82 changes: 82 additions & 0 deletions src/contextualDecoratorRule.ts
@@ -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);
}
}
134 changes: 0 additions & 134 deletions src/contextualLifeCycleRule.ts

This file was deleted.

81 changes: 81 additions & 0 deletions src/contextualLifecycleRule.ts
@@ -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);
}
}
}

0 comments on commit bbf7a32

Please sign in to comment.