Skip to content

Commit

Permalink
feat(finnish): Add Finnish notation rule.
Browse files Browse the repository at this point in the history
  • Loading branch information
cartant committed Jan 19, 2018
1 parent 07912fb commit 6adbd05
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 0 deletions.
28 changes: 28 additions & 0 deletions fixtures/finnish-with-$/fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Observable } from "rxjs";
import "rxjs/add/observable/of";

const someObservable$ = Observable.of(0);

const someObject = { someKey$: someObservable$ };
const { someKey$ } = someObject;
const { someKey$: someRenamedKey$ } = someObject;

const someArray: Observable<any>[] = [someConst$];
const [someElement$] = someArray;
someArray.forEach(function (element$: Observable<any>): void {});
someArray.forEach((element$: Observable<any>) => {});

function someFunction$(someParam$: Observable<any>): Observable<any> { return someParam$; }

class SomeClass {
someProperty$: Observable<any>;
constructor (someParam$: Observable<any>) {}
get someGetter$(): Observable<any> { throw new Error("Some error."); }
set someSetter$(someParam$: Observable<any>) {}
someMethod$(someParam$: Observable<any>): Observable<any> { return someParam$; }
}

interface SomeInterface {
someProperty$: Observable<any>;
someMethod$(someParam$: Observable<any>): Observable<any>;
}
13 changes: 13 additions & 0 deletions fixtures/finnish-with-$/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/finnish-with-$/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"defaultSeverity": "error",
"jsRules": {},
"rules": {
"rxjs-finnish": { "severity": "error" }
},
"rulesDirectory": "../../build/rules"
}
28 changes: 28 additions & 0 deletions fixtures/finnish-without-$/fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Observable } from "rxjs";
import "rxjs/add/observable/of";

const someObservable = Observable.of(0);

const someObject = { someKey: someObservable };
const { someKey } = someObject;
const { someKey: someRenamedKey } = someObject;

const someArray: Observable<any>[] = [someConst];
const [someElement] = someArray;
someArray.forEach(function (element: Observable<any>): void {});
someArray.forEach((element: Observable<any>) => {});

function someFunction(someParam: Observable<any>): Observable<any> { return someParam; }

class SomeClass {
someProperty: Observable<any>;
constructor (someParam: Observable<any>) {}
get someGetter(): Observable<any> { throw new Error("Some error."); }
set someSetter(someParam: Observable<any>) {}
someMethod(someParam: Observable<any>): Observable<any> { return someParam; }
}

interface SomeInterface {
someProperty: Observable<any>;
someMethod(someParam: Observable<any>): Observable<any>;
}
13 changes: 13 additions & 0 deletions fixtures/finnish-without-$/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/finnish-without-$/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"defaultSeverity": "error",
"jsRules": {},
"rules": {
"rxjs-finnish": { "severity": "error" }
},
"rulesDirectory": "../../build/rules"
}
21 changes: 21 additions & 0 deletions source/fixtures-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,27 @@ describe("fixtures", function (): void {
});
});

describe("finnish-with-$", () => {

it("should effect no errors", () => {

const result = lint("finnish-with-$", "tslint.json");

expect(result).to.have.property("errorCount", 0);
});
});

describe("finnish-without-$", () => {

it("should effect 'rxjs-finnish' errors", () => {

const result = lint("finnish-without-$", "tslint.json");

expect(result).to.have.property("errorCount", 19);
result.failures.forEach(failure => expect(failure).to.have.property("ruleName", "rxjs-finnish"));
});
});

describe("flat-map", () => {

it("should effect no errors", () => {
Expand Down
116 changes: 116 additions & 0 deletions source/rules/rxjsFinnishRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* @license Copyright © 2017 Nicholas Jamieson. All Rights Reserved.
* 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 * as tsutils from "tsutils";
import { couldBeType } from "../support/util";

export class Rule extends Lint.Rules.TypedRule {

public static metadata: Lint.IRuleMetadata = {
description: "Enforces the use of Finnish notation.",
options: null,
optionsDescription: "Not configurable.",
requiresTypeInfo: true,
ruleName: "rxjs-finnish",
type: "style",
typescriptOnly: true
};

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

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

class Walker extends Lint.ProgramAwareRuleWalker {

protected visitFunctionDeclaration(node: ts.FunctionDeclaration): void {

this.validateNode(node, node.type);
super.visitFunctionDeclaration(node);
}

protected visitFunctionExpression(node: ts.FunctionExpression): void {

this.validateNode(node, node.type);
super.visitFunctionExpression(node);
}

protected visitGetAccessor(node: ts.AccessorDeclaration): void {

this.validateNode(node);
super.visitGetAccessor(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 visitObjectLiteralExpression(node: ts.ObjectLiteralExpression): void {

node.properties.forEach(property => {
if (property.name.kind !== ts.SyntaxKind.ComputedPropertyName) {
this.validateNode(property);
}
});
super.visitObjectLiteralExpression(node);
}

protected visitParameterDeclaration(node: ts.ParameterDeclaration): void {

this.validateNode(node);
super.visitParameterDeclaration(node);
}

protected visitPropertyDeclaration(node: ts.PropertyDeclaration): void {

this.validateNode(node);
super.visitPropertyDeclaration(node);
}

protected visitPropertySignature(node: ts.Node): void {

this.validateNode(node);
super.visitPropertySignature(node);
}

protected visitSetAccessor(node: ts.AccessorDeclaration): void {

this.validateNode(node);
super.visitSetAccessor(node);
}

protected visitVariableDeclarationList(node: ts.VariableDeclarationList): void {

tsutils.forEachDeclaredVariable(node, variable => {
this.validateNode(variable);
});
super.visitVariableDeclarationList(node);
}

private validateNode(node: ts.Node, typeNode?: ts.Node): void {

const { name } = node as any;
if (name) {
const text = name.getText();
const type = this.getTypeChecker().getTypeAtLocation(typeNode || node);
if (!/\$$/.test(text) && couldBeType(type, "Observable")) {
this.addFailureAtNode(node, `Finnish notation required for ${text}`);
}
}
}
}

0 comments on commit 6adbd05

Please sign in to comment.