-
-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add rxjs-no-exposed-subjects rule + tests (#73)
* add no exposed subjects rule * create new folder * Delete default * create fixture.ts.lint * add testing files * Fix logic to account for set, get accessors and non-private methods * Add extra test cases * Remove options from test tslint.json * Fix some linting issues
- Loading branch information
Showing
4 changed files
with
190 additions
and
0 deletions.
There are no files selected for viewing
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,89 @@ | ||
/** | ||
* @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 } from "../support/util"; | ||
|
||
export class Rule extends Lint.Rules.TypedRule { | ||
public static metadata: Lint.IRuleMetadata = { | ||
description: "Ensures that subjects have access level modifier 'private'.", | ||
options: null, | ||
optionsDescription: "Not configurable.", | ||
requiresTypeInfo: true, | ||
ruleName: '"rxjs-no-exposed-subjects"', | ||
type: "style", | ||
typescriptOnly: true | ||
}; | ||
|
||
public applyWithProgram( | ||
sourceFile: ts.SourceFile, | ||
program: ts.Program | ||
): Lint.RuleFailure[] { | ||
return this.applyWithWalker( | ||
new RxjsNoExposedSubjects(sourceFile, this.getOptions(), program) | ||
); | ||
} | ||
} | ||
|
||
class RxjsNoExposedSubjects extends Lint.ProgramAwareRuleWalker { | ||
constructor( | ||
sourceFile: ts.SourceFile, | ||
rawOptions: Lint.IOptions, | ||
program: ts.Program | ||
) { | ||
super(sourceFile, rawOptions, program); | ||
} | ||
|
||
// CASE: Properties of classes declared in constructor | ||
protected visitConstructorDeclaration(node: ts.ConstructorDeclaration): void { | ||
node.parameters.forEach(param => this.validateNode(param)); | ||
super.visitConstructorDeclaration(node); | ||
} | ||
|
||
// CASE: Standard properties of Classes. | ||
protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void { | ||
this.validateNode(node); | ||
super.visitPropertyDeclaration(node); | ||
} | ||
|
||
protected visitMethodDeclaration(node: ts.MethodDeclaration): void { | ||
this.validateNode(node, node.type); | ||
super.visitMethodDeclaration(node); | ||
} | ||
|
||
protected visitMethodSignature(node: ts.SignatureDeclaration): void { | ||
this.validateNode(node, node.type); | ||
super.visitMethodSignature(node); | ||
} | ||
|
||
protected visitGetAccessor(node: ts.AccessorDeclaration): void { | ||
this.validateNode(node); | ||
super.visitGetAccessor(node); | ||
} | ||
|
||
protected visitSetAccessor(node: ts.AccessorDeclaration): void { | ||
this.validateNode(node); | ||
super.visitSetAccessor(node); | ||
} | ||
|
||
private validateNode(node: ts.Node, typeNode?: ts.Node): void { | ||
const { name } = node as any; | ||
if (name) { | ||
const text = name.getText(); | ||
const privateModifier = node.modifiers | ||
? node.modifiers.find( | ||
modifier => modifier.kind === ts.SyntaxKind.PrivateKeyword | ||
) | ||
: null; | ||
const type = this.getTypeChecker().getTypeAtLocation(typeNode || node); | ||
|
||
if (!privateModifier && couldBeType(type, "Subject")) { | ||
this.addFailureAtNode(name, `Subject '${text}' must be private.`); | ||
} | ||
} | ||
} | ||
} |
80 changes: 80 additions & 0 deletions
80
test/v6/fixtures/no-exposed-subjects/default/fixture.ts.lint
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,80 @@ | ||
import * as Rx from 'rxjs'; | ||
|
||
const variable = new Rx.Subject<void>(); | ||
|
||
class Mock { | ||
|
||
// valid properties | ||
private _submitSubject$ = new Rx.Subject<void>(); | ||
private readonly rSubmitSubject$ = new Rx.Subject<void>(); | ||
private _age: number; | ||
|
||
// invalid properties | ||
public property$ = new Rx.Subject<void>(); | ||
~~~~~~~~~ [Subject 'property$' must be private.] | ||
|
||
protected property2$ = new Rx.Subject<void>(); | ||
~~~~~~~~~~ [Subject 'property2$' must be private.] | ||
|
||
qualitySubject$ = new Rx.Subject<any>(); | ||
~~~~~~~~~~~~~~~ [Subject 'qualitySubject$' must be private.] | ||
|
||
public readonly rPropertySubject$ = new Rx.Subject<void>(); | ||
~~~~~~~~~~~~~~~~~ [Subject 'rPropertySubject$' must be private.] | ||
|
||
readonly rSubject$ = new Rx.Subject<void>(); | ||
~~~~~~~~~ [Subject 'rSubject$' must be private.] | ||
|
||
constructor( | ||
public streamSubject$: Rx.Subject<any>, | ||
~~~~~~~~~~~~~~ [Subject 'streamSubject$' must be private.] | ||
protected pSubject$: Rx.Subject<any>, | ||
~~~~~~~~~ [Subject 'pSubject$' must be private.] | ||
private secondParamSubject$: Rx.Subject<any>, | ||
anySubject$: Rx.Subject<any>, | ||
~~~~~~~~~~~ [Subject 'anySubject$' must be private.] | ||
observable: Rx.Observable<any>, | ||
) { | ||
|
||
console.log(this.submitSubject$); | ||
console.log(this.property$); | ||
console.log(this.propertySubject$); | ||
} | ||
|
||
get submitSubject$(): Rx.Subject<any> { | ||
~~~~~~~~~~~~~~ [Subject 'submitSubject$' must be private.] | ||
return this._submitSubject$; | ||
} | ||
|
||
set submitSubject$(set$: Rx.Subject<any>) { | ||
~~~~~~~~~~~~~~ [Subject 'submitSubject$' must be private.] | ||
this._submitSubject$ = set$; | ||
} | ||
|
||
get age(): number { | ||
return this.age; | ||
} | ||
|
||
set age(newNum: number) { | ||
this._age = newNum; | ||
} | ||
|
||
public invalidMethod(): Rx.Subject<any> { | ||
~~~~~~~~~~~~~ [Subject 'invalidMethod' must be private.] | ||
return new Rx.Subject<any>(); | ||
} | ||
|
||
invalidMethod2(): Rx.Subject<any> { | ||
~~~~~~~~~~~~~~ [Subject 'invalidMethod2' must be private.] | ||
return new Rx.Subject<any>(); | ||
} | ||
|
||
private validMethod(): Rx.Subject<any> { | ||
return new Rx.Subject<any>(); | ||
} | ||
|
||
} | ||
|
||
function foo(xSubject$: Rx.Subject<any>): Rx.Subject<any> { | ||
return xSubject$; | ||
} |
13 changes: 13 additions & 0 deletions
13
test/v6/fixtures/no-exposed-subjects/default/tsconfig.json
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,13 @@ | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": ".", | ||
"lib": ["es2015"], | ||
"noEmit": true, | ||
"paths": { | ||
"rxjs": ["../../node_modules/rxjs"] | ||
}, | ||
"skipLibCheck": true, | ||
"target": "es5" | ||
}, | ||
"include": ["fixture.ts"] | ||
} |
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,8 @@ | ||
{ | ||
"defaultSeverity": "error", | ||
"jsRules": {}, | ||
"rules": { | ||
"rxjs-no-exposed-subjects": { "severity": "error" } | ||
}, | ||
"rulesDirectory": "../../../../../build/rules" | ||
} |