Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Commit

Permalink
Merge branch 'master' into next
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Killian committed Jan 29, 2016
2 parents 74738d5 + e5b5ff1 commit a35071b
Show file tree
Hide file tree
Showing 291 changed files with 3,403 additions and 3,756 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ v3.3.0-dev.2
---
* Add TypeScript peer dependency `>=1.9.0-dev` to support latest nightlies

v3.3.0
---
* [bugfix] Tweak TSLint build so TSLint works with typescript@next (#926)

v3.3.0-dev.1
---
* [bugfix] Correctly handle more than one custom rules directory (#928)
Expand Down
18 changes: 12 additions & 6 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var checkBinTest;
if (process.platform === "win32") {
checkBinTest = [];
} else {
checkBinTest = ["run:test"];
checkBinTest = ["run:testBin"];
}

module.exports = function (grunt) {
Expand All @@ -22,13 +22,17 @@ module.exports = function (grunt) {
options: {
reporter: "spec"
},
src: ["build/test/**/*.js"]
src: ["build/test/**/*Tests.js", "build/test/assert.js"]
}
},

run: {
test: {
cmd: "./test/check-bin.sh"
testBin: {
cmd: "./test/check-bin.sh",
options: {quiet: Infinity}
},
testRules: {
args: ["./build/test/ruleTestRunner.js"]
}
},

Expand All @@ -50,7 +54,8 @@ module.exports = function (grunt) {
"src/*.ts",
"src/formatters/**/*.ts",
"src/language/**/*.ts",
"src/rules/**/*.ts"
"src/rules/**/*.ts",
"src/test/**/*.ts"
],
test: [
"test/**/*.ts",
Expand Down Expand Up @@ -87,7 +92,8 @@ module.exports = function (grunt) {
"clean:test",
"ts:test",
"tslint:test",
"mochaTest"
"mochaTest",
"run:testRules",
].concat(checkBinTest));

// create default task
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
"test": "grunt"
},
"dependencies": {
"colors": "^1.1.2",
"diff": "^2.2.1",
"findup-sync": "~0.2.1",
"glob": "^6.0.1",
"optimist": "~0.6.0",
Expand Down
2 changes: 2 additions & 0 deletions src/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as configuration from "./configuration";
import * as formatters from "./formatters";
import * as linter from "./tslint";
import * as rules from "./rules";
import * as test from "./test";
import {RuleFailure} from "./language/rule/rule";

export * from "./language/rule/rule";
Expand All @@ -34,6 +35,7 @@ export var Configuration = configuration;
export var Formatters = formatters;
export var Linter = linter;
export var Rules = rules;
export var Test = test;

export interface LintResult {
failureCount: number;
Expand Down
6 changes: 3 additions & 3 deletions src/rules/noSwitchCaseFallThroughRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export class NoSwitchCaseFallThroughWalker extends Lint.RuleWalker {
if (isFallingThrough && switchClause.statements.length > 0 && ((switchClauses.length - 1) > i)) {
if (!isFallThroughAllowed(switchClauses[i + 1])) {
this.addFailure(this.createFailure(
child.getEnd(),
1,
switchClauses[i + 1].getStart(),
"case".length,
`${Rule.FAILURE_STRING_PART}'case'`
));
}
Expand All @@ -52,7 +52,7 @@ export class NoSwitchCaseFallThroughWalker extends Lint.RuleWalker {
// case statement falling through a default
if (isFallingThrough && !isFallThroughAllowed(child)) {
const failureString = Rule.FAILURE_STRING_PART + "'default'";
this.addFailure(this.createFailure(switchClauses[i - 1].getEnd(), 1, failureString));
this.addFailure(this.createFailure(switchClauses[i].getStart(), "default".length, failureString));
}
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/rules/oneLineRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class OneLineWalker extends Lint.RuleWalker {
public visitVariableDeclaration(node: ts.VariableDeclaration) {
const initializer = node.initializer;
if (initializer != null && initializer.kind === ts.SyntaxKind.ObjectLiteralExpression) {
const equalsToken = node.getChildAt(1);
const equalsToken = node.getChildren().filter((n) => n.kind === ts.SyntaxKind.EqualsToken)[0];
const openBraceToken = initializer.getChildAt(0);
this.handleOpeningBrace(equalsToken, openBraceToken);
}
Expand Down
2 changes: 1 addition & 1 deletion src/rules/trailingCommaRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class TrailingCommaWalker extends Lint.RuleWalker {
if (hasTrailingComma && option === "never") {
this.addFailure(this.createFailure(lastGrandChild.getStart(), 1, Rule.FAILURE_STRING_NEVER));
} else if (!hasTrailingComma && option === "always") {
this.addFailure(this.createFailure(lastGrandChild.getEnd(), 1, Rule.FAILURE_STRING_ALWAYS));
this.addFailure(this.createFailure(lastGrandChild.getEnd() - 1, 1, Rule.FAILURE_STRING_ALWAYS));
}
}
}
Expand Down
120 changes: 120 additions & 0 deletions src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* @license
* Copyright 2016 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as colors from "colors";
import * as diff from "diff";
import * as fs from "fs";
import * as glob from "glob";
import * as path from "path";

import * as Linter from "./tslint";
import * as parse from "./test/parse";
import {LintError} from "./test/lintError";

const FILE_EXTENSION = ".lint";

export interface TestResult {
directory: string;
results: {
[fileName: string]: {
errorsFromMarkup: LintError[];
errorsFromLinter: LintError[];
markupFromLinter: string;
markupFromMarkup: string;
}
};
}

export function runTest(testDirectory: string): TestResult {
const filesToLint = glob.sync(path.join(testDirectory, `**/*${FILE_EXTENSION}`));
const tslintConfig = JSON.parse(fs.readFileSync(path.join(testDirectory, "tslint.json"), "utf8"));
const results: TestResult = { directory: testDirectory, results: {} };

for (const fileToLint of filesToLint) {
const fileBasename = path.basename(fileToLint, FILE_EXTENSION);
const fileText = fs.readFileSync(fileToLint, "utf8");
const fileTextWithoutMarkup = parse.removeErrorMarkup(fileText);
const errorsFromMarkup = parse.parseErrorsFromMarkup(fileText);

const lintOptions = {
configuration: tslintConfig,
formatter: "prose",
formattersDirectory: "",
rulesDirectory: "",
};
const linter = new Linter(fileBasename, fileTextWithoutMarkup, lintOptions);
const errorsFromLinter: LintError[] = linter.lint().failures.map((failure) => {
const startLineAndCharacter = failure.getStartPosition().getLineAndCharacter();
const endLineAndCharacter = failure.getEndPosition().getLineAndCharacter();

return {
endPos: {
col: endLineAndCharacter.character,
line: endLineAndCharacter.line
},
message: failure.getFailure(),
startPos: {
col: startLineAndCharacter.character,
line: startLineAndCharacter.line
},
};
});

results.results[fileToLint] = {
errorsFromMarkup,
errorsFromLinter,
markupFromLinter: parse.createMarkupFromErrors(fileTextWithoutMarkup, errorsFromMarkup),
markupFromMarkup: parse.createMarkupFromErrors(fileTextWithoutMarkup, errorsFromLinter),
};
}

return results;
}

export function consoleTestResultHandler(testResult: TestResult): boolean {
let didAllTestsPass = true;

for (const fileName of Object.keys(testResult.results)) {
const results = testResult.results[fileName];
process.stdout.write(`${fileName}:`);

const diffResults = diff.diffLines(results.markupFromMarkup, results.markupFromLinter);
const didTestPass = !diffResults.some((diff) => diff.added || diff.removed);

if (didTestPass) {
console.log(colors.green(" Passed"));
} else {
console.log(colors.red(" Failed!"));
console.log(colors.green(`Expected (from ${FILE_EXTENSION} file)`));
console.log(colors.red("Actual (from TSLint)"));

didAllTestsPass = false;

for (const diffResult of diffResults) {
let color = colors.gray;
if (diffResult.added) {
color = colors.green;
} else if (diffResult.removed) {
color = colors.red;
}
process.stdout.write(color(diffResult.value));
}
}
}

return didAllTestsPass;
}
109 changes: 109 additions & 0 deletions src/test/lines.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2016 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {replicateStr} from "./utils";

// Use classes here instead of interfaces because we want runtime type data
export class Line { }
export class CodeLine extends Line { constructor(public contents: string) { super(); } }
export class MessageSubstitutionLine extends Line { constructor(public key: string, public message: string) { super(); } }

export class ErrorLine extends Line { constructor(public startCol: number) { super(); } }
export class MultilineErrorLine extends ErrorLine { constructor(startCol: number) { super(startCol); } }
export class EndErrorLine extends ErrorLine {
constructor(startCol: number, public endCol: number, public message: string) { super(startCol); }
}

// example matches (between the quotes):
// " ~~~~~~~~"
const multilineErrorRegex = /^\s*(~+|~nil)$/;
// " ~~~~~~~~~ [some error message]"
const endErrorRegex = /^\s*(~+|~nil)\s*\[(.+)\]\s*$/;
// "[shortcut]: full messages goes here!! "
const messageSubstitutionRegex = /^\[([\w\-\_]+?)]: \s*(.+?)\s*$/;

export const ZERO_LENGTH_ERROR = "~nil";

/**
* Maps a line of text from a .lint file to an appropriate Line object
*/
export function parseLine(text: string): Line {
const multilineErrorMatch = text.match(multilineErrorRegex);
if (multilineErrorMatch != null) {
const startErrorCol = text.indexOf("~");
return new MultilineErrorLine(startErrorCol);
}

const endErrorMatch = text.match(endErrorRegex);
if (endErrorMatch != null) {
const [, squiggles, message] = endErrorMatch;
const startErrorCol = text.indexOf("~");
const zeroLengthError = (squiggles === ZERO_LENGTH_ERROR);
const endErrorCol = zeroLengthError ? startErrorCol : text.lastIndexOf("~") + 1;
return new EndErrorLine(startErrorCol, endErrorCol, message);
}

const messageSubstitutionMatch = text.match(messageSubstitutionRegex);
if (messageSubstitutionMatch != null) {
const [, key, message] = messageSubstitutionMatch;
return new MessageSubstitutionLine(key, message);
}

// line doesn't match any syntax for error markup, so it's a line of code to be linted
return new CodeLine(text);
}

/**
* Maps a Line object to a matching line of text that could be in a .lint file.
* This is almost the inverse of parseLine.
* If you ran `printLine(parseLine(someText), code)`, the whitespace in the result may be different than in someText
* @param line - A Line object to convert to text
* @param code - If line represents error markup, this is the line of code preceding the markup.
* Otherwise, this parameter is not required.
*/
export function printLine(line: Line, code?: string): string {
if (line instanceof ErrorLine) {
if (code == null) {
throw new Error("Must supply argument for code parameter when line is an ErrorLine");
}

const leadingSpaces = replicateStr(" ", line.startCol);
if (line instanceof MultilineErrorLine) {
// special case for when the line of code is simply a newline.
// use "~nil" to indicate the error continues on that line
if (code.length === 0 && line.startCol === 0) {
return ZERO_LENGTH_ERROR;
}

const tildes = replicateStr("~", code.length - leadingSpaces.length);
return `${leadingSpaces}${tildes}`;
} else if (line instanceof EndErrorLine) {
let tildes = replicateStr("~", line.endCol - line.startCol);
let endSpaces = replicateStr(" ", code.length - line.endCol);
if (tildes.length === 0) {
tildes = ZERO_LENGTH_ERROR;
// because we add "~nil" we need four less spaces than normal at the end
// always make sure we have at least one space though
endSpaces = endSpaces.substring(0, Math.max(endSpaces.length - 4, 1));
}
return `${leadingSpaces}${tildes}${endSpaces} [${line.message}]`;
}
} else if (line instanceof MessageSubstitutionLine) {
return `[${line.key}]: ${line.message}`;
} else if (line instanceof CodeLine) {
return line.contents;
}
}
44 changes: 44 additions & 0 deletions src/test/lintError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2016 Palantir Technologies, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export interface PositionInFile {
line: number;
col: number;
}

export interface LintError {
startPos: PositionInFile;
endPos: PositionInFile;
message: string;
}

export function errorComparator(err1: LintError, err2: LintError) {
if (err1.startPos.line !== err2.startPos.line) {
return err1.startPos.line - err2.startPos.line;
} else if (err1.startPos.col !== err2.startPos.col) {
return err1.startPos.col - err2.startPos.col;
} else if (err1.endPos.line !== err2.endPos.line) {
return err1.endPos.line - err2.endPos.line;
} else if (err1.endPos.col !== err2.endPos.col) {
return err1.endPos.col - err2.endPos.col;
} else {
return err1.message.localeCompare(err2.message);
}
}

export function lintSyntaxError(message: string) {
return new Error(`Lint File Syntax Error: ${message}`);
}
Loading

0 comments on commit a35071b

Please sign in to comment.