Skip to content

Commit 13383af

Browse files
committedOct 28, 2023
Require Node.js 18
1 parent 7289acd commit 13383af

File tree

5 files changed

+170
-162
lines changed

5 files changed

+170
-162
lines changed
 

‎index.d.ts

+8-23
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
11
import type {ESLint, Linter} from 'eslint';
22

3-
export = formatterPretty;
4-
53
/**
64
Pretty formatter for [ESLint](https://eslint.org).
75
86
@param results - Lint result for the individual files.
97
@param data - Extended information related to the analysis results.
108
@returns The formatted output.
119
*/
12-
declare function formatterPretty(
13-
results: readonly formatterPretty.LintResult[],
14-
data?: ESLint.LintResultData
10+
export default function eslintFormatterPretty(
11+
results: LintResult[],
12+
data?: LintResultData
1513
): string;
1614

17-
declare namespace formatterPretty {
18-
interface LintResult {
19-
readonly filePath: string;
20-
readonly errorCount: number;
21-
readonly warningCount: number;
22-
readonly messages: readonly LintMessage[];
23-
}
24-
25-
type Severity = Linter.Severity | 'warning' | 'error';
15+
export type LintResult = ESLint.LintResult;
16+
export type LintResultData = ESLint.LintResultData;
17+
export type Severity = Linter.Severity;
18+
export type LintMessage = Linter.LintMessage;
2619

27-
interface LintMessage {
28-
readonly severity: Severity;
29-
readonly fatal?: boolean;
30-
readonly line?: number;
31-
readonly column?: number;
32-
readonly message: string;
33-
readonly ruleId?: string | null;
34-
}
35-
}
20+
export {Linter} from 'eslint';

‎index.js

+74-76
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
'use strict';
2-
const path = require('path');
3-
const chalk = require('chalk');
4-
const logSymbols = require('log-symbols');
5-
const plur = require('plur');
6-
const stringWidth = require('string-width');
7-
const ansiEscapes = require('ansi-escapes');
8-
const {supportsHyperlink} = require('supports-hyperlinks');
9-
const getRuleDocs = require('eslint-rule-docs');
10-
11-
module.exports = (results, data) => {
1+
import process from 'node:process';
2+
import path from 'node:path';
3+
import chalk from 'chalk';
4+
import logSymbols from 'log-symbols';
5+
import plur from 'plur';
6+
import stringWidth from 'string-width';
7+
import ansiEscapes from 'ansi-escapes';
8+
import {supportsHyperlink} from 'supports-hyperlinks';
9+
import getRuleDocs from 'eslint-rule-docs';
10+
11+
export default function eslintFormatterPretty(results, data) {
1212
const lines = [];
1313
let errorCount = 0;
1414
let warningCount = 0;
@@ -17,7 +17,7 @@ module.exports = (results, data) => {
1717
let maxMessageWidth = 0;
1818
let showLineNumbers = false;
1919

20-
results
20+
for (const result of results
2121
.sort((a, b) => {
2222
if (a.errorCount === b.errorCount) {
2323
return b.warningCount - a.warningCount;
@@ -32,76 +32,74 @@ module.exports = (results, data) => {
3232
}
3333

3434
return b.errorCount - a.errorCount;
35-
})
36-
.forEach(result => {
37-
const {messages, filePath} = result;
35+
})) {
36+
const {messages, filePath} = result;
3837

39-
if (messages.length === 0) {
40-
return;
41-
}
38+
if (messages.length === 0) {
39+
continue;
40+
}
4241

43-
errorCount += result.errorCount;
44-
warningCount += result.warningCount;
42+
errorCount += result.errorCount;
43+
warningCount += result.warningCount;
4544

46-
if (lines.length !== 0) {
47-
lines.push({type: 'separator'});
48-
}
49-
50-
const firstErrorOrWarning = messages.find(({severity}) => severity === 2) || messages[0];
45+
if (lines.length > 0) {
46+
lines.push({type: 'separator'});
47+
}
5148

52-
lines.push({
53-
type: 'header',
54-
filePath,
55-
relativeFilePath: path.relative('.', filePath),
56-
firstLineCol: firstErrorOrWarning.line + ':' + firstErrorOrWarning.column
57-
});
49+
const firstErrorOrWarning = messages.find(({severity}) => severity === 2) ?? messages[0];
5850

59-
messages
60-
.sort((a, b) => {
61-
if (a.fatal === b.fatal && a.severity === b.severity) {
62-
if (a.line === b.line) {
63-
return a.column < b.column ? -1 : 1;
64-
}
51+
lines.push({
52+
type: 'header',
53+
filePath,
54+
relativeFilePath: path.relative('.', filePath),
55+
firstLineCol: firstErrorOrWarning.line + ':' + firstErrorOrWarning.column,
56+
});
6557

66-
return a.line < b.line ? -1 : 1;
58+
for (const x of messages
59+
.sort((a, b) => {
60+
if (a.fatal === b.fatal && a.severity === b.severity) {
61+
if (a.line === b.line) {
62+
return a.column < b.column ? -1 : 1;
6763
}
6864

69-
if ((a.fatal || a.severity === 2) && (!b.fatal || b.severity !== 2)) {
70-
return 1;
71-
}
65+
return a.line < b.line ? -1 : 1;
66+
}
7267

73-
return -1;
74-
})
75-
.forEach(x => {
76-
let {message} = x;
77-
78-
// Stylize inline code blocks
79-
message = message.replace(/\B`(.*?)`\B|\B'(.*?)'\B/g, (m, p1, p2) => chalk.bold(p1 || p2));
80-
81-
const line = String(x.line || 0);
82-
const column = String(x.column || 0);
83-
const lineWidth = stringWidth(line);
84-
const columnWidth = stringWidth(column);
85-
const messageWidth = stringWidth(message);
86-
87-
maxLineWidth = Math.max(lineWidth, maxLineWidth);
88-
maxColumnWidth = Math.max(columnWidth, maxColumnWidth);
89-
maxMessageWidth = Math.max(messageWidth, maxMessageWidth);
90-
showLineNumbers = showLineNumbers || x.line || x.column;
91-
92-
lines.push({
93-
type: 'message',
94-
severity: (x.fatal || x.severity === 2 || x.severity === 'error') ? 'error' : 'warning',
95-
line,
96-
lineWidth,
97-
column,
98-
columnWidth,
99-
message,
100-
messageWidth,
101-
ruleId: x.ruleId || ''
102-
});
103-
});
104-
});
68+
if ((a.fatal || a.severity === 2) && (!b.fatal || b.severity !== 2)) {
69+
return 1;
70+
}
71+
72+
return -1;
73+
})) {
74+
let {message} = x;
75+
76+
// Stylize inline code blocks
77+
message = message.replaceAll(/\B`(.*?)`\B|\B'(.*?)'\B/g, (m, p1, p2) => chalk.bold(p1 ?? p2));
78+
79+
const line = String(x.line ?? 0);
80+
const column = String(x.column ?? 0);
81+
const lineWidth = stringWidth(line);
82+
const columnWidth = stringWidth(column);
83+
const messageWidth = stringWidth(message);
84+
85+
maxLineWidth = Math.max(lineWidth, maxLineWidth);
86+
maxColumnWidth = Math.max(columnWidth, maxColumnWidth);
87+
maxMessageWidth = Math.max(messageWidth, maxMessageWidth);
88+
showLineNumbers = showLineNumbers || x.line || x.column;
89+
90+
lines.push({
91+
type: 'message',
92+
severity: (x.fatal || x.severity === 2 || x.severity === 'error') ? 'error' : 'warning',
93+
line,
94+
lineWidth,
95+
column,
96+
columnWidth,
97+
message,
98+
messageWidth,
99+
ruleId: x.ruleId ?? '',
100+
});
101+
}
102+
}
105103

106104
let output = '\n';
107105

@@ -135,8 +133,8 @@ module.exports = (results, data) => {
135133
x.severity === 'warning' ? logSymbols.warning : logSymbols.error,
136134
' '.repeat(maxLineWidth - x.lineWidth) + chalk.dim(x.line + chalk.gray(':') + x.column),
137135
' '.repeat(maxColumnWidth - x.columnWidth) + x.message,
138-
' '.repeat(maxMessageWidth - x.messageWidth) +
139-
(ruleUrl && supportsHyperlink(process.stdout) ? ansiEscapes.link(chalk.dim(x.ruleId), ruleUrl) : chalk.dim(x.ruleId))
136+
' '.repeat(maxMessageWidth - x.messageWidth)
137+
+ (ruleUrl && supportsHyperlink(process.stdout) ? ansiEscapes.link(chalk.dim(x.ruleId), ruleUrl) : chalk.dim(x.ruleId)),
140138
];
141139

142140
if (!showLineNumbers) {
@@ -158,4 +156,4 @@ module.exports = (results, data) => {
158156
}
159157

160158
return (errorCount + warningCount) > 0 ? output : '';
161-
};
159+
}

‎index.test-d.ts

+31-31
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,50 @@
11
import type {ESLint} from 'eslint';
22
import {expectType} from 'tsd';
3-
import formatterPretty = require('.');
4-
5-
// Test type exports
6-
type LintResult = formatterPretty.LintResult;
7-
type LintMessage = formatterPretty.LintMessage;
8-
type Severity = formatterPretty.Severity;
3+
import eslintFormatterPretty, {
4+
type LintResult,
5+
type LintMessage,
6+
type Severity,
7+
} from './index.js';
98

109
// Test LintResult interface members
1110
declare const lintResult: LintResult;
1211
expectType<string>(lintResult.filePath);
1312
expectType<number>(lintResult.errorCount);
1413
expectType<number>(lintResult.warningCount);
15-
expectType<readonly LintMessage[]>(lintResult.messages);
14+
expectType<LintMessage[]>(lintResult.messages);
1615

1716
// Test LintMessage interface members
1817
const lintMessage = lintResult.messages[0];
1918
expectType<Severity>(lintMessage.severity);
2019
expectType<string>(lintMessage.message);
21-
expectType<boolean | undefined>(lintMessage.fatal);
22-
expectType<number | undefined>(lintMessage.line);
23-
expectType<number | undefined>(lintMessage.column);
24-
expectType<string | null | undefined>(lintMessage.ruleId);
20+
expectType<true | undefined>(lintMessage.fatal);
21+
expectType<number>(lintMessage.line);
22+
expectType<number>(lintMessage.column);
23+
expectType<string | null >(lintMessage.ruleId); // eslint-disable-line @typescript-eslint/ban-types
2524

2625
// Test formatterPretty()
2726
declare const lintResults: LintResult[];
2827
declare const eslintLintResults: ESLint.LintResult[];
2928
declare const lintResultData: ESLint.LintResultData;
3029

31-
expectType<string>(formatterPretty(lintResults));
32-
expectType<string>(formatterPretty(eslintLintResults));
33-
expectType<string>(formatterPretty(eslintLintResults, lintResultData));
34-
35-
interface PartialLintResult {
36-
filePath: string;
37-
errorCount: number;
38-
warningCount: number;
39-
messages: Array<{
40-
fileName: string;
41-
message: string;
42-
severity: 'error' | 'warning';
43-
line?: number;
44-
column?: number;
45-
}>;
46-
}
47-
48-
declare const partialLintResults: PartialLintResult[];
49-
50-
expectType<string>(formatterPretty(partialLintResults));
30+
expectType<string>(eslintFormatterPretty(lintResults));
31+
expectType<string>(eslintFormatterPretty(eslintLintResults));
32+
expectType<string>(eslintFormatterPretty(eslintLintResults, lintResultData));
33+
34+
// FIXME
35+
// type PartialLintResult = {
36+
// filePath: string;
37+
// errorCount: number;
38+
// warningCount: number;
39+
// messages: Array<{
40+
// fileName: string;
41+
// message: string;
42+
// severity: 0 | 1 | 2;
43+
// line?: number;
44+
// column?: number;
45+
// }>;
46+
// };
47+
48+
// declare const partialLintResults: PartialLintResult[];
49+
50+
// expectType<string>(eslintFormatterPretty(partialLintResults));

‎package.json

+23-13
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,13 @@
1010
"email": "sindresorhus@gmail.com",
1111
"url": "https://sindresorhus.com"
1212
},
13+
"type": "module",
14+
"exports": {
15+
"types": "./index.d.ts",
16+
"default": "./index.js"
17+
},
1318
"engines": {
14-
"node": ">=14.16"
19+
"node": ">=18"
1520
},
1621
"scripts": {
1722
"test": "xo && ava && tsd"
@@ -29,23 +34,28 @@
2934
"validate"
3035
],
3136
"dependencies": {
32-
"@types/eslint": "^8.0.0",
33-
"ansi-escapes": "^4.2.1",
34-
"chalk": "^4.1.0",
37+
"@types/eslint": "^8.44.6",
38+
"ansi-escapes": "^6.2.0",
39+
"chalk": "^5.3.0",
3540
"eslint-rule-docs": "^1.1.235",
36-
"log-symbols": "^4.0.0",
37-
"plur": "^4.0.0",
38-
"string-width": "^4.2.0",
39-
"supports-hyperlinks": "^2.0.0"
41+
"log-symbols": "^6.0.0",
42+
"plur": "^5.1.0",
43+
"string-width": "^7.0.0",
44+
"supports-hyperlinks": "^3.0.0"
4045
},
4146
"devDependencies": {
42-
"ava": "^2.4.0",
43-
"strip-ansi": "^6.0.0",
44-
"tsd": "^0.17.0",
45-
"typescript": "^4.3.2",
46-
"xo": "^0.32.0"
47+
"ava": "^5.3.1",
48+
"strip-ansi": "^7.1.0",
49+
"tsd": "^0.29.0",
50+
"typescript": "^5.2.2",
51+
"xo": "^0.56.0"
4752
},
4853
"ava": {
4954
"serial": true
55+
},
56+
"xo": {
57+
"rules": {
58+
"import/no-extraneous-dependencies": "off"
59+
}
5060
}
5161
}

‎test/test.js

+34-19
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,34 @@
11
/* eslint "ava/no-import-test-files": "off" */
2+
import {createRequire} from 'node:module';
3+
import process from 'node:process';
24
import test from 'ava';
35
import stripAnsi from 'strip-ansi';
46
import ansiEscapes from 'ansi-escapes';
57
import chalk from 'chalk';
6-
import defaultFixture from './fixtures/default.json';
7-
import noLineNumbers from './fixtures/no-line-numbers.json';
8-
import lineNumbers from './fixtures/line-numbers.json';
9-
import sortOrder from './fixtures/sort-by-severity-then-line-then-column.json';
10-
import messages from './fixtures/messages.json';
11-
import data from './fixtures/data.json';
12-
import eslintFormatterPretty from '..';
8+
import eslintFormatterPretty from '../index.js'; // eslint-disable-line import/order
9+
10+
/// import defaultFixture from './fixtures/default.json';
11+
// import noLineNumbers from './fixtures/no-line-numbers.json';
12+
// import lineNumbers from './fixtures/line-numbers.json';
13+
// import sortOrder from './fixtures/sort-by-severity-then-line-then-column.json';
14+
// import messages from './fixtures/messages.json';
15+
// import data from './fixtures/data.json';
16+
17+
const require = createRequire(import.meta.url);
18+
19+
const defaultFixture = require('./fixtures/default.json');
20+
const noLineNumbers = require('./fixtures/no-line-numbers.json');
21+
const lineNumbers = require('./fixtures/line-numbers.json');
22+
const sortOrder = require('./fixtures/sort-by-severity-then-line-then-column.json');
23+
const messages = require('./fixtures/messages.json');
24+
const data = require('./fixtures/data.json');
1325

1426
const fakeMessages = (desiredSeverity, desiredCount) => {
1527
const ofDesiredSeverity = messages.filter(({severity}) => severity === desiredSeverity);
1628

1729
if (ofDesiredSeverity.length < desiredCount) {
1830
throw new Error(
19-
`requested ${desiredCount} messages with severity ${desiredSeverity}. Only found ${desiredSeverity.length}.`
31+
`requested ${desiredCount} messages with severity ${desiredSeverity}. Only found ${desiredSeverity.length}.`,
2032
);
2133
}
2234

@@ -27,7 +39,10 @@ const fakeReport = (errorCount, warningCount) => ({
2739
filePath: `${errorCount}-error.${warningCount}-warning.js`,
2840
errorCount,
2941
warningCount,
30-
messages: fakeMessages(1, warningCount).concat(fakeMessages(2, errorCount))
42+
messages: [
43+
...fakeMessages(1, warningCount),
44+
...fakeMessages(2, errorCount),
45+
],
3146
});
3247

3348
const enableHyperlinks = () => {
@@ -93,10 +108,10 @@ test('sort by severity, then line number, then column number', t => {
93108
sanitized.indexOf('✖ 3:1'),
94109
sanitized.indexOf('✖ 30:1'),
95110
sanitized.indexOf('✖ 40:5'),
96-
sanitized.indexOf('✖ 40:8')
111+
sanitized.indexOf('✖ 40:8'),
97112
];
98113
console.log(output);
99-
t.deepEqual(indexes, indexes.slice().sort((a, b) => a - b));
114+
t.deepEqual(indexes, [...indexes].sort((a, b) => a - b));
100115
});
101116

102117
test('display warning total before error total', t => {
@@ -105,10 +120,10 @@ test('display warning total before error total', t => {
105120
const sanitized = stripAnsi(output);
106121
const indexes = [
107122
sanitized.indexOf('2 warnings'),
108-
sanitized.indexOf('4 errors')
123+
sanitized.indexOf('4 errors'),
109124
];
110125
console.log(output);
111-
t.deepEqual(indexes, indexes.slice().sort((a, b) => a - b));
126+
t.deepEqual(indexes, [...indexes].sort((a, b) => a - b));
112127
});
113128

114129
test('files will be sorted with least errors at the bottom, but zero errors at the top', t => {
@@ -117,19 +132,19 @@ test('files will be sorted with least errors at the bottom, but zero errors at t
117132
fakeReport(1, 0),
118133
fakeReport(3, 0),
119134
fakeReport(0, 1),
120-
fakeReport(2, 2)
135+
fakeReport(2, 2),
121136
];
122137
const output = eslintFormatterPretty(reports);
123138
const sanitized = stripAnsi(output);
124139
const indexes = [
125140
sanitized.indexOf('0-error.1-warning.js'),
126141
sanitized.indexOf('3-error.0-warning.js'),
127142
sanitized.indexOf('2-error.2-warning.js'),
128-
sanitized.indexOf('1-error.0-warning.js')
143+
sanitized.indexOf('1-error.0-warning.js'),
129144
];
130145
console.log(output);
131146
t.is(indexes.length, reports.length);
132-
t.deepEqual(indexes, indexes.slice().sort((a, b) => a - b));
147+
t.deepEqual(indexes, [...indexes].sort((a, b) => a - b));
133148
});
134149

135150
test('files with similar errorCounts will sort according to warningCounts', t => {
@@ -142,7 +157,7 @@ test('files with similar errorCounts will sort according to warningCounts', t =>
142157
fakeReport(0, 2),
143158
fakeReport(0, 3),
144159
fakeReport(2, 2),
145-
fakeReport(2, 1)
160+
fakeReport(2, 1),
146161
];
147162
const output = eslintFormatterPretty(reports);
148163
const sanitized = stripAnsi(output);
@@ -154,11 +169,11 @@ test('files with similar errorCounts will sort according to warningCounts', t =>
154169
sanitized.indexOf('2-error.1-warning.js'),
155170
sanitized.indexOf('1-error.2-warning.js'),
156171
sanitized.indexOf('1-error.1-warning.js'),
157-
sanitized.indexOf('1-error.0-warning.js')
172+
sanitized.indexOf('1-error.0-warning.js'),
158173
];
159174
console.log(output);
160175
t.is(indexes.length, reports.length);
161-
t.deepEqual(indexes, indexes.slice().sort((a, b) => a - b));
176+
t.deepEqual(indexes, [...indexes].sort((a, b) => a - b));
162177
});
163178

164179
test('use the `rulesMeta` property to get docs URL', t => {

0 commit comments

Comments
 (0)
Please sign in to comment.