TypeScript/scripts/tslint/formatters/autolinkableStylishFormatter.ts
Josh Goldberg 423ab2e03d Removed colors devDependency (#20430)
* Replaced colors with chalk

* Used chalk's preferred import style
2017-12-14 16:05:25 -08:00

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");
}
}