Skip to content

Commit

Permalink
feat(rule): add prefer-output-readonly rule (#564)
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelss95 authored and mgechev committed Apr 20, 2018
1 parent b9c899b commit 3d652d1
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { Rule as NoOutputRenameRule } from './noOutputRenameRule';
export { Rule as NoUnusedCssRule } from './noUnusedCssRule';
export { Rule as PipeImpureRule } from './pipeImpureRule';
export { Rule as PipeNamingRule } from './pipeNamingRule';
export { Rule as PreferOutputReadonlyRule } from './preferOutputReadonlyRule';
export { Rule as TemplateConditionalComplexityRule } from './templateConditionalComplexityRule';
export { Rule as TemplateCyclomaticComplexityRule } from './templateCyclomaticComplexityRule';
export { Rule as TemplatesNoNegatedAsync } from './templatesNoNegatedAsyncRule';
Expand Down
34 changes: 34 additions & 0 deletions src/preferOutputReadonlyRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as Lint from 'tslint';
import * as ts from 'typescript';
import { NgWalker } from './angular/ngWalker';

export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
description: 'Prefer to declare `@Output` as readonly since they are not supposed to be reassigned.',
options: null,
optionsDescription: 'Not configurable.',
rationale: '',
ruleName: 'prefer-output-readonly',
type: 'maintainability',
typescriptOnly: true,
};

static FAILURE_STRING = 'Prefer to declare `@Output` as readonly since they are not supposed to be reassigned';

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new OutputMetadataWalker(sourceFile, this.getOptions()));
}
}

export class OutputMetadataWalker extends NgWalker {
visitNgOutput(property: ts.PropertyDeclaration, output: ts.Decorator, args: string[]) {
if (property.modifiers && property.modifiers.some(m => m.kind === ts.SyntaxKind.ReadonlyKeyword)) {
return;
}

const className = (property.parent as ts.PropertyAccessExpression).name.getText();
const memberName = property.name.getText();
this.addFailureAtNode(property.name, Rule.FAILURE_STRING);
super.visitNgOutput(property, output, args);
}
}
32 changes: 32 additions & 0 deletions test/preferOutputReadonlyRule.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { assertAnnotated, assertSuccess } from './testHelper';

const ruleName = 'prefer-output-readonly';

describe(ruleName, () => {
describe('failure', () => {
it('should fail when an @Output is not readonly', () => {
const source = `
class Test {
@Output() testEmitter = new EventEmitter<string>();
~~~~~~~~~~~
}
`;
assertAnnotated({
ruleName,
message: 'Prefer to declare `@Output` as readonly since they are not supposed to be reassigned',
source
});
});
});

describe('success', () => {
it('should pass when an @Output is readonly', () => {
const source = `
class Test {
@Output() readonly testEmitter = new EventEmitter<string>();
}
`;
assertSuccess(ruleName, source);
});
});
});

0 comments on commit 3d652d1

Please sign in to comment.