97 lines
No EOL
3.5 KiB
TypeScript
97 lines
No EOL
3.5 KiB
TypeScript
import * as Lint from "tslint";
|
|
import chalk from "chalk";
|
|
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" ? chalk.blue(sev) : chalk.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)} ${chalk.grey(pad(f.getRuleName(), f.getRuleName().length, nameMaxSize))} ${chalk.yellow(f.getFailure())}`).join("\n")}`;
|
|
}).join("\n");
|
|
}
|
|
} |