Skip to content

Commit

Permalink
feat(no-subject-value): Add BehaviorSubject rule.
Browse files Browse the repository at this point in the history
  • Loading branch information
cartant committed Feb 8, 2018
1 parent fbc0d9c commit 9c8f966
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 4 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ The package includes the following rules (none of which are enabled by default):
| `rxjs-no-ignored-subscribe` | Disallows the calling of subscribe without specifying arguments. | None |
| `rxjs-no-operator` | Disallows importation from the `operator` directory. Useful if you prefer ['lettable' operators](https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md) - which are located in the `operators` directory. | None |
| `rxjs-no-patched` | Disallows the calling of patched methods. Methods must be imported and called explicitly - not via `Observable` or `Observable.prototype`. | See below |
| `rxjs-no-subject-unsubscribe` | Disallows the calling of `unsubscribe` directly upon `Subject` instances. For an explanation of why this can be a problem, see [this](https://stackoverflow.com/a/45112125/6680611) Stack Overflow answer. | None |
| `rxjs-no-subject-unsubscribe` | Disallows calling the `unsubscribe` method of a `Subject` instance. For an explanation of why this can be a problem, see [this](https://stackoverflow.com/a/45112125/6680611) Stack Overflow answer. | None |
| `rxjs-no-subject-value` | Disallows accessing the `value` property of a `BehaviorSubject` instance. | None |
| `rxjs-no-tap` | An alias for `rxjs-no-do`. | None |
| `rxjs-no-unused-add` | Disallows the importation of patched observables or operators that are not used in the module. | None |
| `rxjs-no-wholesale` | Disallows the wholesale importation of `rxjs` or `rxjs/Rx`. | None |
Expand Down
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ The package includes the following rules (none of which are enabled by default):
| `rxjs-no-ignored-subscribe` | Disallows the calling of subscribe without specifying arguments. | None |
| `rxjs-no-operator` | Disallows importation from the `operator` directory. Useful if you prefer ['lettable' operators](https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md) - which are located in the `operators` directory. | None |
| `rxjs-no-patched` | Disallows the calling of patched methods. Methods must be imported and called explicitly - not via `Observable` or `Observable.prototype`. | See below |
| `rxjs-no-subject-unsubscribe` | Disallows the calling of `unsubscribe` directly upon `Subject` instances. For an explanation of why this can be a problem, see [this](https://stackoverflow.com/a/45112125/6680611) Stack Overflow answer. | None |
| `rxjs-no-subject-unsubscribe` | Disallows calling the `unsubscribe` method of a `Subject` instance. For an explanation of why this can be a problem, see [this](https://stackoverflow.com/a/45112125/6680611) Stack Overflow answer. | None |
| `rxjs-no-subject-value` | Disallows accessing the `value` property of a `BehaviorSubject` instance. | None |
| `rxjs-no-tap` | An alias for `rxjs-no-do`. | None |
| `rxjs-no-unused-add` | Disallows the importation of patched observables or operators that are not used in the module. | None |
| `rxjs-no-wholesale` | Disallows the wholesale importation of `rxjs` or `rxjs/Rx`. | None |
Expand Down
5 changes: 5 additions & 0 deletions fixtures/behavior-subject-value/fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { BehaviorSubject } from "rxjs/BehaviorSubject";

const subject = new BehaviorSubject<number>(1);
console.log(subject.value);
console.log(subject.getValue());
13 changes: 13 additions & 0 deletions fixtures/behavior-subject-value/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"baseUrl": ".",
"lib": ["es2015"],
"noEmit": true,
"paths": {
"rxjs": ["../node_modules/rxjs"]
},
"skipLibCheck": true,
"target": "es5"
},
"include": ["fixture.ts"]
}
8 changes: 8 additions & 0 deletions fixtures/behavior-subject-value/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"defaultSeverity": "error",
"jsRules": {},
"rules": {
"rxjs-no-subject-value": { "severity": "error" }
},
"rulesDirectory": "../../build/rules"
}
11 changes: 11 additions & 0 deletions source/fixtures-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,17 @@ describe("fixtures", function (): void {
});
});

describe("behavior-subject-value", () => {

it("should effect an 'rxjs-no-subject-value' error", () => {

const result = lint("behavior-subject-value", "tslint.json");

expect(result).to.have.property("errorCount", 2);
result.failures.forEach(failure => expect(failure).to.have.property("ruleName", "rxjs-no-subject-value"));
});
});

describe("subject-add-subscription", () => {

it("should effect an 'rxjs-no-subject-unsubscribe' error", () => {
Expand Down
4 changes: 2 additions & 2 deletions source/rules/rxjsNoSubjectUnsubscribeRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { couldBeType, isReferenceType } from "../support/util";
export class Rule extends Lint.Rules.TypedRule {

public static metadata: Lint.IRuleMetadata = {
description: "Disallows the calling of unsubscribe directly upon subject instances.",
description: "Disallows calling the unsubscribe method of a subject instance.",
options: null,
optionsDescription: "Not configurable.",
requiresTypeInfo: true,
Expand All @@ -21,7 +21,7 @@ export class Rule extends Lint.Rules.TypedRule {
typescriptOnly: true
};

public static FAILURE_STRING = "Calling unsubscribe on subjects is forbidden";
public static FAILURE_STRING = "Calling unsubscribe on a subject is forbidden";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {

Expand Down
53 changes: 53 additions & 0 deletions source/rules/rxjsNoSubjectValueRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @license Use of this source code is governed by an MIT-style license that
* can be found in the LICENSE file at https://github.com/cartant/rxjs-tslint-rules
*/
/*tslint:disable:no-use-before-declare*/

import * as Lint from "tslint";
import * as ts from "typescript";

import { couldBeType, isReferenceType } from "../support/util";

export class Rule extends Lint.Rules.TypedRule {

public static metadata: Lint.IRuleMetadata = {
description: "Disallows accessing the value property of a BehaviorSubject instance.",
options: null,
optionsDescription: "Not configurable.",
requiresTypeInfo: true,
ruleName: "rxjs-no-subject-value",
type: "functionality",
typescriptOnly: true
};

public static FAILURE_STRING = "Accessing the value property of a BehaviorSubject is forbidden";

public applyWithProgram(sourceFile: ts.SourceFile, program: ts.Program): Lint.RuleFailure[] {

return this.applyWithWalker(new Walker(sourceFile, this.getOptions(), program));
}
}

export class Walker extends Lint.ProgramAwareRuleWalker {

protected visitCallExpression(node: ts.CallExpression): void {

node.forEachChild((child) => {

if (child.kind === ts.SyntaxKind.PropertyAccessExpression) {

const propertyAccessExpression = child as ts.PropertyAccessExpression;
const name = propertyAccessExpression.name.getText();
const typeChecker = this.getTypeChecker();
const type = typeChecker.getTypeAtLocation(propertyAccessExpression.expression);

if (((name === "value") || (name === "getValue")) && isReferenceType(type) && couldBeType(type.target, "BehaviorSubject")) {
this.addFailureAtNode(propertyAccessExpression.name, Rule.FAILURE_STRING);
}
}
});

super.visitCallExpression(node);
}
}

0 comments on commit 9c8f966

Please sign in to comment.