Skip to content

Commit

Permalink
format
Browse files Browse the repository at this point in the history
  • Loading branch information
jwbay committed Sep 9, 2017
1 parent a3bb334 commit c64ef5d
Show file tree
Hide file tree
Showing 20 changed files with 392 additions and 330 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
},
"typescript.tsdk": "./node_modules/typescript/lib",
"tslint.autoFixOnSave": true,
"files.insertFinalNewline": true,
"editor.formatOnSave": true,
"files.insertFinalNewline": true
}
14 changes: 7 additions & 7 deletions helpers/getClassMethods.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import * as ts from 'typescript';
import { nodeIsKind } from './nodeIsKind';
import * as ts from 'typescript'
import { nodeIsKind } from './nodeIsKind'

export function getClassMethods(node: ts.ClassLikeDeclaration) {
if (!node.members) {
return [];
return []
}

return node.members.filter(m => {
if (nodeIsKind<ts.MethodDeclaration>(m, 'MethodDeclaration')) {
return true;
return true
}

if (nodeIsKind<ts.PropertyDeclaration>(m, 'PropertyDeclaration')) {
return nodeIsKind(m.initializer, 'ArrowFunction');
return nodeIsKind(m.initializer, 'ArrowFunction')
}

return false;
}) as (ts.MethodDeclaration | ts.PropertyDeclaration)[];
return false
}) as (ts.MethodDeclaration | ts.PropertyDeclaration)[]
}
12 changes: 8 additions & 4 deletions helpers/getJsxAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import * as ts from 'typescript';
import * as ts from 'typescript'

export function getJsxAttributes(node: ts.JsxOpeningLikeElement): ts.NodeArray<ts.JsxAttribute> {
return (node.attributes && node.attributes as any).properties || // >= TS 2.3
(node.attributes as any); // <= TS 2.2
export function getJsxAttributes(
node: ts.JsxOpeningLikeElement
): ts.NodeArray<ts.JsxAttribute> {
return (
(node.attributes && (node.attributes as any)).properties || // >= TS 2.3
(node.attributes as any) // <= TS 2.2
)
}
4 changes: 2 additions & 2 deletions helpers/getLeadingWhitespace.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Node } from 'typescript';
import { Node } from 'typescript'

export function getLeadingWhitespace(node: Node) {
return node.getFullText().slice(0, node.getStart() - node.getFullStart());
return node.getFullText().slice(0, node.getStart() - node.getFullStart())
}
6 changes: 3 additions & 3 deletions helpers/nodeIsKind.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as ts from 'typescript';
import * as ts from 'typescript'

export function nodeIsKind<T extends ts.Node>(
node: ts.Node,
kind: keyof typeof ts.SyntaxKind,
kind: keyof typeof ts.SyntaxKind
): node is T {
return node && node.kind === ts.SyntaxKind[kind];
return node && node.kind === ts.SyntaxKind[kind]
}
10 changes: 10 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
printWidth: 90,
tabWidth: 4,
useTabs: true,
semi: false,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
parser: 'typescript',
}
33 changes: 14 additions & 19 deletions rules/camelCaseLocalFunctionsRule.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as Lint from 'tslint/lib';
import * as ts from 'typescript';
import { nodeIsKind } from '../helpers/nodeIsKind';
import * as Lint from 'tslint/lib'
import * as ts from 'typescript'
import { nodeIsKind } from '../helpers/nodeIsKind'

export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile) {
return this.applyWithFunction(sourceFile, walk);
return this.applyWithFunction(sourceFile, walk)
}
}

Expand All @@ -14,25 +14,20 @@ function walk(ctx: Lint.WalkContext<void>) {
nodeIsKind<ts.CallExpression>(node, 'CallExpression') &&
nodeIsKind<ts.Identifier>(node.expression, 'Identifier')
) {
checkFunctionName(ctx, node.expression);
checkFunctionName(ctx, node.expression)
}
return ts.forEachChild(node, cb);
});
return ts.forEachChild(node, cb)
})
}

const whitelist = [
'Array',
'Boolean',
'Error',
'Function',
'Number',
'Object',
'String'
];
const whitelist = ['Array', 'Boolean', 'Error', 'Function', 'Number', 'Object', 'String']

function checkFunctionName(ctx: Lint.WalkContext<void>, name: ts.Identifier) {
const firstLetter = name.text.charAt(0);
if (firstLetter !== firstLetter.toLowerCase() && whitelist.indexOf(name.text) === -1) {
ctx.addFailureAtNode(name, 'local function names should be camelCase');
const firstLetter = name.text.charAt(0)
if (
firstLetter !== firstLetter.toLowerCase() &&
whitelist.indexOf(name.text) === -1
) {
ctx.addFailureAtNode(name, 'local function names should be camelCase')
}
}
53 changes: 26 additions & 27 deletions rules/classMethodNewlinesRule.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
import * as Lint from 'tslint/lib';
import * as ts from 'typescript';
import { getClassMethods } from '../helpers/getClassMethods';
import { getLeadingWhitespace } from '../helpers/getLeadingWhitespace';
import { nodeIsKind } from '../helpers/nodeIsKind';
import * as Lint from 'tslint/lib'
import * as ts from 'typescript'
import { getClassMethods } from '../helpers/getClassMethods'
import { getLeadingWhitespace } from '../helpers/getLeadingWhitespace'
import { nodeIsKind } from '../helpers/nodeIsKind'

export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile) {
return this.applyWithFunction(sourceFile, walk);
return this.applyWithFunction(sourceFile, walk)
}
}

function walk(ctx: Lint.WalkContext<void>) {
ts.forEachChild(ctx.sourceFile, function cb(node: ts.Node): void {
if (
nodeIsKind(node, 'ClassDeclaration') ||
nodeIsKind(node, 'ClassExpression')
) {
checkClass(ctx, node as ts.ClassLikeDeclaration);
if (nodeIsKind(node, 'ClassDeclaration') || nodeIsKind(node, 'ClassExpression')) {
checkClass(ctx, node as ts.ClassLikeDeclaration)
}
return ts.forEachChild(node, cb);
});
return ts.forEachChild(node, cb)
})
}

function checkClass(ctx: Lint.WalkContext<void>, node: ts.ClassLikeDeclaration) {
const sf = ctx.sourceFile;
const methods = getClassMethods(node);
const sf = ctx.sourceFile
const methods = getClassMethods(node)

methods.reduce((previousMethod, method) => {
const leadingWhitespace = getLeadingWhitespace(method);
const newlineCount = leadingWhitespace.match(/\n/g).length;
const hasComments = /\/\/|\/\*\*/g.test(leadingWhitespace);
const isFirstMethod = method === node.members[0];
const isInOverloadGroup = (
const leadingWhitespace = getLeadingWhitespace(method)
const newlineCount = leadingWhitespace.match(/\n/g).length
const hasComments = /\/\/|\/\*\*/g.test(leadingWhitespace)
const isFirstMethod = method === node.members[0]
const isInOverloadGroup =
method !== previousMethod &&
method.name.getText(sf) === previousMethod.name.getText(sf)
);
const expectedNewlines = isFirstMethod || isInOverloadGroup ? 1 : 2;
const expectedNewlines = isFirstMethod || isInOverloadGroup ? 1 : 2

if (
newlineCount < expectedNewlines ||
(newlineCount > expectedNewlines && !hasComments)
) {
const newLine = leadingWhitespace.match('\r\n') ? '\r\n' : '\n';
const newLine = leadingWhitespace.match('\r\n') ? '\r\n' : '\n'
ctx.addFailureAtNode(
method.name,
'class methods should be preceded by an empty line',
Lint.Replacement.appendText(method.getStart(sf) - leadingWhitespace.length, newLine)
);
Lint.Replacement.appendText(
method.getStart(sf) - leadingWhitespace.length,
newLine
)
)
}

return method;
}, methods[0]);
return method
}, methods[0])
}
71 changes: 41 additions & 30 deletions rules/declareClassMethodsAfterUseRule.ts
Original file line number Diff line number Diff line change
@@ -1,90 +1,101 @@
import * as Lint from 'tslint/lib';
import * as ts from 'typescript';
import { getClassMethods } from '../helpers/getClassMethods';
import { nodeIsKind } from '../helpers/nodeIsKind';
import * as Lint from 'tslint/lib'
import * as ts from 'typescript'
import { getClassMethods } from '../helpers/getClassMethods'
import { nodeIsKind } from '../helpers/nodeIsKind'

export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile) {
return this.applyWithWalker(new DeclareClassMethodsAfterUseWalker(sourceFile, this.ruleName, this.getOptions()));
return this.applyWithWalker(
new DeclareClassMethodsAfterUseWalker(
sourceFile,
this.ruleName,
this.getOptions()
)
)
}
}

class DeclareClassMethodsAfterUseWalker extends Lint.AbstractWalker<{}> {
private currentMethodName: string;
private visitedMethodDeclarations: string[];
private visitedMethodCalls: string[];
private currentMethodName: string
private visitedMethodDeclarations: string[]
private visitedMethodCalls: string[]

public walk(sourceFile: ts.SourceFile) {
const cb = (node: ts.Node): void => {
if (
nodeIsKind(node, 'ClassDeclaration') ||
nodeIsKind(node, 'ClassExpression')
) {
this.validate(node as ts.ClassLikeDeclaration);
this.validate(node as ts.ClassLikeDeclaration)
}
return ts.forEachChild(node, cb);
};
return ts.forEachChild(node, cb)
}

ts.forEachChild(sourceFile, cb);
ts.forEachChild(sourceFile, cb)
}

private validate(node: ts.ClassLikeDeclaration) {
this.visitedMethodDeclarations = [];
this.visitedMethodCalls = [];
this.visitedMethodDeclarations = []
this.visitedMethodCalls = []

for (const method of getClassMethods(node)) {
this.currentMethodName = method.name.getText(this.getSourceFile());
this.visitedMethodDeclarations.push(this.currentMethodName);
this.currentMethodName = method.name.getText(this.getSourceFile())
this.visitedMethodDeclarations.push(this.currentMethodName)
ts.forEachChild(method, child => {
this.visitChildren(child);
});
this.visitChildren(child)
})
}
}

private visitChildren(node: ts.Node) {
ts.forEachChild(node, child => {
if (nodeIsKind<ts.CallExpression>(child, 'CallExpression')) {
this.visitCallExpressionInMethod(child);
this.visitCallExpressionInMethod(child)
}

this.visitChildren(child);
});
this.visitChildren(child)
})
}

private visitCallExpressionInMethod(node: ts.CallExpression) {
if (!this.callExpressionBelongsToThis(node.expression)) { return; }
if (!this.callExpressionBelongsToThis(node.expression)) {
return
}

const propertyExpression = node.expression as ts.PropertyAccessExpression;
const methodName = propertyExpression.name.text;
const propertyExpression = node.expression as ts.PropertyAccessExpression
const methodName = propertyExpression.name.text

if (this.methodHasBeenDeclared(methodName)) {
if (!this.isRecursion(methodName) && !this.methodHasBeenCalled(methodName)) {
this.addFailureAtNode(propertyExpression, 'declare class methods after use');
this.addFailureAtNode(
propertyExpression,
'declare class methods after use'
)
}
} else {
// declaration needs to come after first use, not all uses.
// once we've seen a callsite before a declaration, don't
// error on any future callsites for that method
this.visitedMethodCalls.push(methodName);
this.visitedMethodCalls.push(methodName)
}
}

private callExpressionBelongsToThis(node: ts.Expression) {
return (
nodeIsKind<ts.PropertyAccessExpression>(node, 'PropertyAccessExpression') &&
nodeIsKind(node.expression, 'ThisKeyword')
);
)
}

private methodHasBeenDeclared(name: string) {
return this.visitedMethodDeclarations.indexOf(name) > -1;
return this.visitedMethodDeclarations.indexOf(name) > -1
}

private methodHasBeenCalled(name: string) {
return this.visitedMethodCalls.indexOf(name) > -1;
return this.visitedMethodCalls.indexOf(name) > -1
}

private isRecursion(name: string) {
return this.currentMethodName === name;
return this.currentMethodName === name
}
}
Loading

0 comments on commit c64ef5d

Please sign in to comment.