Skip to content

Commit d3351f0

Browse files
committed
refactor: fix lint formatter lint
1 parent 4de3d33 commit d3351f0

File tree

6 files changed

+145
-142
lines changed

6 files changed

+145
-142
lines changed

tools/eslint-formatter-multiple-formats/eslint.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export default tseslint.config(
2121
ignoredDependencies: ['@nx/vite', 'vite', 'vite-plugin-dts'],
2222
},
2323
],
24+
'n/no-sync': 'off',
2425
},
2526
},
2627
);

tools/eslint-formatter-multiple-formats/project.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"$schema": "../../node_modules/nx/schemas/project-schema.json",
44
"sourceRoot": "tools/eslint-multi-format/src",
55
"projectType": "library",
6-
"tags": [],
6+
"tags": ["scope:tooling", "type:util"],
77
"// targets": "to see all targets run: nx show project eslint-multi-format with option --web for humans and --json for AI",
88
"targets": {
99
"lint": {},

tools/eslint-formatter-multiple-formats/src/lib/multiple-formats.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { ESLint } from 'eslint';
22
import path from 'node:path';
33
import * as process from 'node:process';
44
import type { FormatterConfig } from './types.js';
5-
import type { EslintFormat } from './utils.js';
65
import {
6+
type EslintFormat,
77
formatTerminalOutput,
88
findConfigFromEnv as getConfigFromEnv,
99
persistEslintReports,

tools/eslint-formatter-multiple-formats/src/lib/utils.ts

Lines changed: 140 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ function pluralize(word: string, count: number): string {
1313
// Simple function to strip ANSI codes for length calculation
1414
function stripAnsi(str: string): string {
1515
// eslint-disable-next-line no-control-regex
16-
return str.replace(/\u001b\[[0-9;]*m/g, '');
16+
return str.replace(/\u001B\[[\d;]*m/g, '');
1717
}
1818

1919
// Simple table formatting function
@@ -23,22 +23,31 @@ function createTable(
2323
): string {
2424
const { align = [], stringLength = s => s.length } = options;
2525

26-
if (data.length === 0) return '';
26+
if (data.length === 0) {
27+
return '';
28+
}
2729

2830
// Calculate column widths
29-
const colWidths: number[] = [];
30-
data.forEach(row => {
31-
row.forEach((cell, colIndex) => {
32-
const cellStr = String(cell);
33-
const width = stringLength(cellStr);
34-
colWidths[colIndex] = Math.max(colWidths[colIndex] || 0, width);
35-
});
36-
});
31+
const colWidths: number[] = data.reduce<number[]>(
32+
(widths, row) =>
33+
row.reduce<number[]>((acc, cell, colIndex) => {
34+
const cellStr = String(cell);
35+
const width = stringLength(cellStr);
36+
const currentWidth = acc[colIndex] || 0;
37+
const maxWidth = Math.max(currentWidth, width);
38+
return [
39+
...acc.slice(0, colIndex),
40+
maxWidth,
41+
...acc.slice(colIndex + 1),
42+
];
43+
}, widths),
44+
[],
45+
);
3746

3847
// Format rows
3948
return data
40-
.map(row => {
41-
return row
49+
.map(row =>
50+
row
4251
.map((cell, colIndex) => {
4352
const cellStr = String(cell);
4453
const width = colWidths[colIndex] || 0;
@@ -49,148 +58,139 @@ function createTable(
4958
}
5059
return cellStr + ' '.repeat(padding);
5160
})
52-
.join(' ');
53-
})
61+
.join(' '),
62+
)
5463
.join('\n');
5564
}
5665

57-
// Inline stylish formatter implementation using ansis
58-
function stylishFormatter(results: ESLint.LintResult[]): string {
59-
let output = '\n';
60-
let errorCount = 0;
61-
let warningCount = 0;
62-
let fixableErrorCount = 0;
63-
let fixableWarningCount = 0;
64-
let summaryColor = 'yellow' as 'yellow' | 'red';
65-
66-
results.forEach(result => {
67-
const messages = result.messages;
68-
69-
if (messages.length === 0) {
70-
return;
71-
}
66+
// Summary statistics for lint results
67+
type LintSummary = {
68+
errorCount: number;
69+
warningCount: number;
70+
fixableErrorCount: number;
71+
fixableWarningCount: number;
72+
summaryColor: 'yellow' | 'red';
73+
};
7274

73-
errorCount += result.errorCount;
74-
warningCount += result.warningCount;
75-
fixableErrorCount += result.fixableErrorCount;
76-
fixableWarningCount += result.fixableWarningCount;
77-
78-
output += `${underline(result.filePath)}\n`;
79-
80-
const tableData = messages.map(message => {
81-
let messageType: string;
82-
83-
if (message.fatal || message.severity === 2) {
84-
messageType = red('error');
85-
summaryColor = 'red';
86-
} else {
87-
messageType = yellow('warning');
88-
}
89-
90-
return [
91-
'',
92-
message.line || 0,
93-
message.column || 0,
94-
messageType,
95-
message.message.replace(/([^ ])\.$/u, '$1'),
96-
dim(message.ruleId || ''),
97-
];
98-
});
99-
100-
const table = createTable(tableData, {
101-
align: ['', 'r', 'l'],
102-
stringLength: (str: string) => stripAnsi(str).length,
103-
});
104-
105-
// Format line:column numbers with dim styling
106-
const formattedTable = table
107-
.split('\n')
108-
.map(line =>
109-
line.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => dim(`${p1}:${p2}`)),
110-
)
111-
.join('\n');
112-
113-
output += `${formattedTable}\n\n`;
75+
// Calculate summary statistics from results
76+
function calculateSummary(results: ESLint.LintResult[]): LintSummary {
77+
return results.reduce<LintSummary>(
78+
(summary, result) => ({
79+
errorCount: summary.errorCount + result.errorCount,
80+
warningCount: summary.warningCount + result.warningCount,
81+
fixableErrorCount: summary.fixableErrorCount + result.fixableErrorCount,
82+
fixableWarningCount:
83+
summary.fixableWarningCount + result.fixableWarningCount,
84+
summaryColor:
85+
result.errorCount > 0 ? ('red' as const) : summary.summaryColor,
86+
}),
87+
{
88+
errorCount: 0,
89+
warningCount: 0,
90+
fixableErrorCount: 0,
91+
fixableWarningCount: 0,
92+
summaryColor: 'yellow',
93+
},
94+
);
95+
}
96+
97+
// Format a single result file
98+
function formatResultFile(result: ESLint.LintResult): string {
99+
if (result.messages.length === 0) {
100+
return '';
101+
}
102+
103+
const header = `${underline(result.filePath)}\n`;
104+
105+
const tableData = result.messages.map(message => {
106+
const messageType =
107+
message.fatal || message.severity === 2
108+
? red('error')
109+
: yellow('warning');
110+
111+
return [
112+
'',
113+
message.line || 0,
114+
message.column || 0,
115+
messageType,
116+
message.message.replace(/([^ ])\.$/u, '$1'),
117+
dim(message.ruleId || ''),
118+
];
114119
});
115120

116-
const total = errorCount + warningCount;
121+
const table = createTable(tableData, {
122+
align: ['', 'r', 'l'],
123+
stringLength: (str: string) => stripAnsi(str).length,
124+
});
117125

118-
if (total > 0) {
119-
if (summaryColor === 'red') {
120-
output += bold(
121-
red(
122-
[
123-
'\u2716 ',
124-
total,
125-
pluralize(' problem', total),
126-
' (',
127-
errorCount,
128-
pluralize(' error', errorCount),
129-
', ',
130-
warningCount,
131-
pluralize(' warning', warningCount),
132-
')\n',
133-
].join(''),
134-
),
135-
);
126+
const formattedTable = table
127+
.split('\n')
128+
.map(line =>
129+
line.replace(/(\d+)\s+(\d+)/u, (m, p1, p2) => dim(`${p1}:${p2}`)),
130+
)
131+
.join('\n');
136132

137-
if (fixableErrorCount > 0 || fixableWarningCount > 0) {
138-
output += bold(
139-
red(
140-
[
141-
' ',
142-
fixableErrorCount,
143-
pluralize(' error', fixableErrorCount),
144-
' and ',
145-
fixableWarningCount,
146-
pluralize(' warning', fixableWarningCount),
147-
' potentially fixable with the `--fix` option.\n',
148-
].join(''),
149-
),
150-
);
151-
}
152-
} else {
153-
output += bold(
154-
yellow(
155-
[
156-
'\u2716 ',
157-
total,
158-
pluralize(' problem', total),
159-
' (',
160-
errorCount,
161-
pluralize(' error', errorCount),
162-
', ',
163-
warningCount,
164-
pluralize(' warning', warningCount),
165-
')\n',
166-
].join(''),
167-
),
168-
);
133+
return `${header}${formattedTable}\n\n`;
134+
}
169135

170-
if (fixableErrorCount > 0 || fixableWarningCount > 0) {
171-
output += bold(
172-
yellow(
173-
[
174-
' ',
175-
fixableErrorCount,
176-
pluralize(' error', fixableErrorCount),
177-
' and ',
178-
fixableWarningCount,
179-
pluralize(' warning', fixableWarningCount),
180-
' potentially fixable with the `--fix` option.\n',
181-
].join(''),
182-
),
183-
);
184-
}
185-
}
136+
// Format summary section
137+
function formatSummary(summary: LintSummary): string {
138+
const {
139+
errorCount,
140+
warningCount,
141+
fixableErrorCount,
142+
fixableWarningCount,
143+
summaryColor,
144+
} = summary;
145+
const total = errorCount + warningCount;
146+
147+
if (total === 0) {
148+
return '';
149+
}
150+
151+
const colorFn = summaryColor === 'red' ? red : yellow;
152+
const problemText = [
153+
'\u2716 ',
154+
total,
155+
pluralize(' problem', total),
156+
' (',
157+
errorCount,
158+
pluralize(' error', errorCount),
159+
', ',
160+
warningCount,
161+
pluralize(' warning', warningCount),
162+
')\n',
163+
].join('');
164+
165+
const problemOutput = bold(colorFn(problemText));
166+
167+
if (fixableErrorCount > 0 || fixableWarningCount > 0) {
168+
const fixableText = [
169+
' ',
170+
fixableErrorCount,
171+
pluralize(' error', fixableErrorCount),
172+
' and ',
173+
fixableWarningCount,
174+
pluralize(' warning', fixableWarningCount),
175+
' potentially fixable with the `--fix` option.\n',
176+
].join('');
177+
178+
return problemOutput + bold(colorFn(fixableText));
186179
}
187180

188-
// Reset output color to prevent changes at top level
189-
return total > 0 ? reset(output) : '';
181+
return problemOutput;
182+
}
183+
184+
function stylishFormatter(results: ESLint.LintResult[]): string {
185+
const summary = calculateSummary(results);
186+
const filesOutput = results.map(formatResultFile).join('');
187+
const summaryOutput = formatSummary(summary);
188+
189+
const total = summary.errorCount + summary.warningCount;
190+
return total > 0 ? reset(`\n${filesOutput}${summaryOutput}`) : '';
190191
}
191192

192193
export function stringifyError(error: unknown): string {
193-
// TODO: special handling for ZodError instances
194194
if (error instanceof Error) {
195195
if (error.name === 'Error' || error.message.startsWith(error.name)) {
196196
return error.message;

tools/eslint-formatter-multiple-formats/tsconfig.spec.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"vite.config.mts",
1616
"vitest.config.ts",
1717
"vitest.config.mts",
18+
"vitest.unit.config.ts",
1819
"src/**/*.test.ts",
1920
"src/**/*.spec.ts",
2021
"src/**/*.test.tsx",

tools/eslint-formatter-multiple-formats/vitest.unit.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/// <reference types="vitest" />
22
import { defineConfig } from 'vite';
3+
// eslint-disable-next-line import/no-useless-path-segments
34
import { tsconfigPathAliases } from '../../tools/vitest-tsconfig-path-aliases.js';
45

56
export default defineConfig({

0 commit comments

Comments
 (0)