Add custom formatter which has clickable links, reduce error duplication in gulp output (#18613)
This commit is contained in:
parent
d9951cbb8e
commit
1821735136
12
Gulpfile.ts
12
Gulpfile.ts
|
@ -674,11 +674,10 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
|
|||
});
|
||||
|
||||
function failWithStatus(err?: any, status?: number) {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
if (err || status) {
|
||||
process.exit(typeof status === "number" ? status : 2);
|
||||
}
|
||||
done(err || status);
|
||||
process.exit(status);
|
||||
done();
|
||||
}
|
||||
|
||||
function lintThenFinish() {
|
||||
|
@ -1051,10 +1050,11 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are:
|
|||
const fileMatcher = cmdLineOptions["files"];
|
||||
const files = fileMatcher
|
||||
? `src/**/${fileMatcher}`
|
||||
: "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
|
||||
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
|
||||
console.log("Linting: " + cmd);
|
||||
child_process.execSync(cmd, { stdio: [0, 1, 2] });
|
||||
if (fold.isTravis()) console.log(fold.end("lint"));
|
||||
});
|
||||
|
||||
gulp.task("default", "Runs 'local'", ["local"]);
|
||||
|
|
26
Jakefile.js
26
Jakefile.js
|
@ -1121,7 +1121,7 @@ task("update-sublime", ["local", serverFile], function () {
|
|||
jake.cpR(serverFile + ".map", "../TypeScript-Sublime-Plugin/tsserver/");
|
||||
});
|
||||
|
||||
var tslintRuleDir = "scripts/tslint";
|
||||
var tslintRuleDir = "scripts/tslint/rules";
|
||||
var tslintRules = [
|
||||
"booleanTriviaRule",
|
||||
"debugAssertRule",
|
||||
|
@ -1137,13 +1137,27 @@ var tslintRulesFiles = tslintRules.map(function (p) {
|
|||
return path.join(tslintRuleDir, p + ".ts");
|
||||
});
|
||||
var tslintRulesOutFiles = tslintRules.map(function (p) {
|
||||
return path.join(builtLocalDirectory, "tslint", p + ".js");
|
||||
return path.join(builtLocalDirectory, "tslint/rules", p + ".js");
|
||||
});
|
||||
var tslintFormattersDir = "scripts/tslint/formatters";
|
||||
var tslintFormatters = [
|
||||
"autolinkableStylishFormatter",
|
||||
];
|
||||
var tslintFormatterFiles = tslintFormatters.map(function (p) {
|
||||
return path.join(tslintFormattersDir, p + ".ts");
|
||||
});
|
||||
var tslintFormattersOutFiles = tslintFormatters.map(function (p) {
|
||||
return path.join(builtLocalDirectory, "tslint/formatters", p + ".js");
|
||||
});
|
||||
desc("Compiles tslint rules to js");
|
||||
task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"]));
|
||||
task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(tslintFormattersOutFiles).concat(["build-rules-end"]));
|
||||
tslintRulesFiles.forEach(function (ruleFile, i) {
|
||||
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint"), lib: "es6" });
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/rules"), lib: "es6" });
|
||||
});
|
||||
tslintFormatterFiles.forEach(function (ruleFile, i) {
|
||||
compileFile(tslintFormattersOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
|
||||
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint/formatters"), lib: "es6" });
|
||||
});
|
||||
|
||||
desc("Emit the start of the build-rules fold");
|
||||
|
@ -1211,8 +1225,8 @@ task("lint", ["build-rules"], () => {
|
|||
const fileMatcher = process.env.f || process.env.file || process.env.files;
|
||||
const files = fileMatcher
|
||||
? `src/**/${fileMatcher}`
|
||||
: "Gulpfile.ts 'scripts/tslint/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --format stylish`;
|
||||
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
|
||||
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
|
||||
console.log("Linting: " + cmd);
|
||||
jake.exec([cmd], { interactive: true }, () => {
|
||||
if (fold.isTravis()) console.log(fold.end("lint"));
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"devDependencies": {
|
||||
"@types/browserify": "latest",
|
||||
"@types/chai": "latest",
|
||||
"@types/colors": "latest",
|
||||
"@types/convert-source-map": "latest",
|
||||
"@types/del": "latest",
|
||||
"@types/glob": "latest",
|
||||
|
@ -48,8 +49,8 @@
|
|||
"@types/q": "latest",
|
||||
"@types/run-sequence": "latest",
|
||||
"@types/through2": "latest",
|
||||
"browserify": "latest",
|
||||
"browser-resolve": "^1.11.2",
|
||||
"browserify": "latest",
|
||||
"chai": "latest",
|
||||
"convert-source-map": "latest",
|
||||
"del": "latest",
|
||||
|
@ -75,6 +76,7 @@
|
|||
"travis-fold": "latest",
|
||||
"ts-node": "latest",
|
||||
"tslint": "latest",
|
||||
"colors": "latest",
|
||||
"typescript": "next"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
97
scripts/tslint/formatters/autolinkableStylishFormatter.ts
Normal file
97
scripts/tslint/formatters/autolinkableStylishFormatter.ts
Normal file
|
@ -0,0 +1,97 @@
|
|||
import * as Lint from "tslint";
|
||||
import * as colors from "colors";
|
||||
import { sep } from "path";
|
||||
function groupBy<T>(array: ReadonlyArray<T> | undefined, getGroupId: (elem: T, index: number) => number | string): T[][] {
|
||||
if (!array) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const groupIdToGroup: { [index: string]: T[] } = {};
|
||||
let result: T[][] | undefined; // Compacted array for return value
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
const value = array[index];
|
||||
const key = getGroupId(value, index);
|
||||
if (groupIdToGroup[key]) {
|
||||
groupIdToGroup[key].push(value);
|
||||
}
|
||||
else {
|
||||
const newGroup = [value];
|
||||
groupIdToGroup[key] = newGroup;
|
||||
if (!result) {
|
||||
result = [newGroup];
|
||||
}
|
||||
else {
|
||||
result.push(newGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result || [];
|
||||
}
|
||||
|
||||
function max<T>(array: ReadonlyArray<T> | undefined, selector: (elem: T) => number): number {
|
||||
if (!array) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let max = 0;
|
||||
for (const item of array) {
|
||||
const scalar = selector(item);
|
||||
if (scalar > max) {
|
||||
max = scalar;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
function getLink(failure: Lint.RuleFailure, color: boolean): string {
|
||||
const lineAndCharacter = failure.getStartPosition().getLineAndCharacter();
|
||||
const sev = failure.getRuleSeverity().toUpperCase();
|
||||
let path = failure.getFileName();
|
||||
// Most autolinks only become clickable if they contain a slash in some way; so we make a top level file into a relative path here
|
||||
if (path.indexOf("/") === -1 && path.indexOf("\\") === -1) {
|
||||
path = `.${sep}${path}`;
|
||||
}
|
||||
return `${color ? (sev === "WARNING" ? colors.blue(sev) : colors.red(sev)) : sev}: ${path}:${lineAndCharacter.line + 1}:${lineAndCharacter.character + 1}`;
|
||||
}
|
||||
|
||||
function getLinkMaxSize(failures: Lint.RuleFailure[]): number {
|
||||
return max(failures, f => getLink(f, /*color*/ false).length);
|
||||
}
|
||||
|
||||
function getNameMaxSize(failures: Lint.RuleFailure[]): number {
|
||||
return max(failures, f => f.getRuleName().length);
|
||||
}
|
||||
|
||||
function pad(str: string, visiblelen: number, len: number) {
|
||||
if (visiblelen >= len) return str;
|
||||
const count = len - visiblelen;
|
||||
for (let i = 0; i < count; i++) {
|
||||
str += " ";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
export class Formatter extends Lint.Formatters.AbstractFormatter {
|
||||
public static metadata: Lint.IFormatterMetadata = {
|
||||
formatterName: "autolinkableStylish",
|
||||
description: "Human-readable formatter which creates stylish messages with autolinkable filepaths.",
|
||||
descriptionDetails: Lint.Utils.dedent`
|
||||
Colorized output grouped by file, with autolinkable filepaths containing line and column information
|
||||
`,
|
||||
sample: Lint.Utils.dedent`
|
||||
src/myFile.ts
|
||||
ERROR: src/myFile.ts:1:14 semicolon Missing semicolon`,
|
||||
consumer: "human"
|
||||
};
|
||||
public format(failures: Lint.RuleFailure[]): string {
|
||||
return groupBy(failures, f => f.getFileName()).map(group => {
|
||||
const currentFile = group[0].getFileName();
|
||||
const linkMaxSize = getLinkMaxSize(group);
|
||||
const nameMaxSize = getNameMaxSize(group);
|
||||
return `
|
||||
${currentFile}
|
||||
${group.map(f => `${pad(getLink(f, /*color*/ true), getLink(f, /*color*/ false).length, linkMaxSize)} ${colors.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${colors.yellow(f.getFailure())}`).join("\n")}`;
|
||||
}).join("\n");
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"rulesDirectory": "built/local/tslint",
|
||||
"rulesDirectory": "built/local/tslint/rules",
|
||||
"rules": {
|
||||
"boolean-trivia": true,
|
||||
"class-name": true,
|
||||
|
|
Loading…
Reference in a new issue