@utoo/lint 0.3.7 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -244,6 +244,20 @@ const results = await eslint.lintFiles(["src"]);
244
244
  await ESLint.outputFixes(results);
245
245
  ```
246
246
 
247
+ `await eslint.loadFormatter()` and `cliEngine.getFormatter()` default to an
248
+ ESLint-style `stylish` report: aligned columns, blank lines between files,
249
+ diagnostics sorted by line and column, and a summary of fixable errors and
250
+ warnings. Rule messages (including punctuation), result arrays, and exit codes
251
+ are unchanged. Only actual autofixes count toward the `--fix` hint, not editor
252
+ suggestions; missing JSX keys still need a manually chosen stable, unique key.
253
+
254
+ The stylish formatter detects color support on stdout. Pass `color: true` or
255
+ `color: false` to the `ESLint` or `CLIEngine` constructor to override detection.
256
+ Otherwise, `NO_COLOR` or `NODE_DISABLE_COLORS` disables color; `FORCE_COLOR`
257
+ forces it (`0` disables), followed by `CLICOLOR_FORCE` and stdout TTY detection
258
+ (`TERM=dumb` disables automatic color). Constructor `env` overrides also apply.
259
+ JSON, JSON-with-metadata, compact, unix, and native CLI text formats are unchanged.
260
+
247
261
  Raw reports include active diagnostics, suppressed diagnostics, fixed outputs,
248
262
  and an exit code. The compatibility layer also exports `Linter`, `CLIEngine`,
249
263
  `RuleTester`, and `SourceCode` APIs.
package/index.cjs CHANGED
@@ -6,6 +6,7 @@ const { dirname, extname, isAbsolute, join, relative, resolve: resolvePath } = r
6
6
 
7
7
  const { platformPackageName, resolveBinary } = require("./lib/binary.cjs");
8
8
  const { createLegacyConfigResolver } = require("./lib/legacy-config.cjs");
9
+ const { formatESLintResults } = require("./lib/stylish-formatter.cjs");
9
10
  const {
10
11
  findConfigPath: findConfigPathFromDirectory,
11
12
  readConfig
@@ -626,7 +627,7 @@ class UtooLint {
626
627
  format(results) {
627
628
  return formatResultsByName(results, name, {
628
629
  rulesMeta: (results) => eslint.getRulesMetaForResults(results)
629
- });
630
+ }, eslint.options);
630
631
  }
631
632
  };
632
633
  }
@@ -704,7 +705,7 @@ class CLIEngine {
704
705
 
705
706
  getFormatter(name = "stylish") {
706
707
  return (results) => {
707
- return formatResultsByName(results, name);
708
+ return formatResultsByName(results, name, {}, this.options);
708
709
  };
709
710
  }
710
711
 
@@ -5004,31 +5005,7 @@ function maybeFilterQuietResults(results, options = {}) {
5004
5005
  return results;
5005
5006
  }
5006
5007
 
5007
- function formatESLintResults(results) {
5008
- const lines = [];
5009
- let errorCount = 0;
5010
- let warningCount = 0;
5011
-
5012
- for (const result of results) {
5013
- if (result.messages.length === 0) continue;
5014
- lines.push(result.filePath);
5015
- for (const message of result.messages) {
5016
- const severity = message.severity === 2 ? "error" : "warning";
5017
- lines.push(` ${message.line}:${message.column} ${severity} ${message.message} ${message.ruleId}`);
5018
- }
5019
- errorCount += result.errorCount;
5020
- warningCount += result.warningCount;
5021
- }
5022
-
5023
- if (errorCount || warningCount) {
5024
- lines.push("");
5025
- lines.push(`x ${errorCount + warningCount} problem${errorCount + warningCount === 1 ? "" : "s"} (${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"})`);
5026
- }
5027
-
5028
- return lines.join("\n");
5029
- }
5030
-
5031
- function formatResultsByName(input, name = "stylish", metadata = {}) {
5008
+ function formatResultsByName(input, name = "stylish", metadata = {}, options = {}) {
5032
5009
  const results = formatterResults(input);
5033
5010
  if (name === "json") {
5034
5011
  return JSON.stringify(input);
@@ -5047,7 +5024,7 @@ function formatResultsByName(input, name = "stylish", metadata = {}) {
5047
5024
  if (name === "unix") {
5048
5025
  return formatUnixResults(results);
5049
5026
  }
5050
- return formatESLintResults(results);
5027
+ return formatESLintResults(results, options);
5051
5028
  }
5052
5029
 
5053
5030
  function formatterResults(input) {
package/index.js CHANGED
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  import { resolveBinary } from "./lib/binary.js";
9
9
  import { createLegacyConfigResolver } from "./lib/legacy-config.cjs";
10
+ import { formatESLintResults } from "./lib/stylish-formatter.cjs";
10
11
  import {
11
12
  findConfigPath as findConfigPathFromDirectory,
12
13
  readConfig
@@ -629,7 +630,7 @@ export class UtooLint {
629
630
  format(results) {
630
631
  return formatResultsByName(results, name, {
631
632
  rulesMeta: (results) => eslint.getRulesMetaForResults(results)
632
- });
633
+ }, eslint.options);
633
634
  }
634
635
  };
635
636
  }
@@ -2603,7 +2604,7 @@ export class CLIEngine {
2603
2604
 
2604
2605
  getFormatter(name = "stylish") {
2605
2606
  return (results) => {
2606
- return formatResultsByName(results, name);
2607
+ return formatResultsByName(results, name, {}, this.options);
2607
2608
  };
2608
2609
  }
2609
2610
 
@@ -5007,31 +5008,7 @@ function maybeFilterQuietResults(results, options = {}) {
5007
5008
  return results;
5008
5009
  }
5009
5010
 
5010
- function formatESLintResults(results) {
5011
- const lines = [];
5012
- let errorCount = 0;
5013
- let warningCount = 0;
5014
-
5015
- for (const result of results) {
5016
- if (result.messages.length === 0) continue;
5017
- lines.push(result.filePath);
5018
- for (const message of result.messages) {
5019
- const severity = message.severity === 2 ? "error" : "warning";
5020
- lines.push(` ${message.line}:${message.column} ${severity} ${message.message} ${message.ruleId}`);
5021
- }
5022
- errorCount += result.errorCount;
5023
- warningCount += result.warningCount;
5024
- }
5025
-
5026
- if (errorCount || warningCount) {
5027
- lines.push("");
5028
- lines.push(`x ${errorCount + warningCount} problem${errorCount + warningCount === 1 ? "" : "s"} (${errorCount} error${errorCount === 1 ? "" : "s"}, ${warningCount} warning${warningCount === 1 ? "" : "s"})`);
5029
- }
5030
-
5031
- return lines.join("\n");
5032
- }
5033
-
5034
- function formatResultsByName(input, name = "stylish", metadata = {}) {
5011
+ function formatResultsByName(input, name = "stylish", metadata = {}, options = {}) {
5035
5012
  const results = formatterResults(input);
5036
5013
  if (name === "json") {
5037
5014
  return JSON.stringify(input);
@@ -5050,7 +5027,7 @@ function formatResultsByName(input, name = "stylish", metadata = {}) {
5050
5027
  if (name === "unix") {
5051
5028
  return formatUnixResults(results);
5052
5029
  }
5053
- return formatESLintResults(results);
5030
+ return formatESLintResults(results, options);
5054
5031
  }
5055
5032
 
5056
5033
  function formatterResults(input) {
@@ -0,0 +1,79 @@
1
+ const { stripVTControlCharacters } = require("node:util");
2
+
3
+ function formatESLintResults(results, options = {}) {
4
+ const color = shouldUseColor(options);
5
+ const paint = (text, open, close) => color ? `\u001b[${open}m${text}\u001b[${close}m` : text;
6
+ const lines = [""];
7
+ let errorCount = 0;
8
+ let warningCount = 0;
9
+ let fixableErrorCount = 0;
10
+ let fixableWarningCount = 0;
11
+
12
+ for (const result of results) {
13
+ if (result.messages.length === 0) continue;
14
+ lines.push(paint(result.filePath, 4, 24));
15
+ // Sort a copy: formatting must not reorder the caller's results or JSON output.
16
+ const messages = [...result.messages].sort((left, right) =>
17
+ (left.line ?? 0) - (right.line ?? 0) || (left.column ?? 0) - (right.column ?? 0)
18
+ );
19
+ const rows = messages.map((message) => [
20
+ String(message.line || 0),
21
+ String(message.column || 0),
22
+ message.fatal || message.severity === 2 ? "error" : "warning",
23
+ // Unlike ESLint stylish, retain punctuation in the original rule message.
24
+ message.message,
25
+ message.ruleId || "",
26
+ ]);
27
+ const widths = [0, 0, 0, 0];
28
+ for (const row of rows) {
29
+ for (let index = 0; index < widths.length; index += 1) {
30
+ widths[index] = Math.max(widths[index], stripVTControlCharacters(row[index]).length);
31
+ }
32
+ }
33
+ for (const [line, column, severity, message, ruleId] of rows) {
34
+ const location = " ".repeat(widths[0] - line.length)
35
+ + paint(`${line}:${column}`, 2, 22)
36
+ + " ".repeat(widths[1] - column.length);
37
+ const label = paint(severity, severity === "error" ? 31 : 33, 39)
38
+ + " ".repeat(widths[2] - severity.length);
39
+ const padding = " ".repeat(widths[3] - stripVTControlCharacters(message).length);
40
+ const description = ruleId ? `${message}${padding} ${paint(ruleId, 2, 22)}` : message;
41
+ lines.push(` ${location} ${label} ${description}`);
42
+ }
43
+ lines.push("");
44
+ errorCount += result.errorCount;
45
+ warningCount += result.warningCount;
46
+ fixableErrorCount += result.fixableErrorCount ?? 0;
47
+ fixableWarningCount += result.fixableWarningCount ?? 0;
48
+ }
49
+
50
+ if (errorCount || warningCount) {
51
+ const summary = (text) => paint(paint(text, 1, 22), errorCount ? 31 : 33, 39);
52
+ lines.push(summary(
53
+ `✖ ${pluralize(errorCount + warningCount, "problem")} (${pluralize(errorCount, "error")}, ${pluralize(warningCount, "warning")})`
54
+ ));
55
+ if (fixableErrorCount || fixableWarningCount) {
56
+ lines.push(summary(
57
+ ` ${pluralize(fixableErrorCount, "error")} and ${pluralize(fixableWarningCount, "warning")} potentially fixable with the \`--fix\` option.`
58
+ ));
59
+ }
60
+ lines.push("");
61
+ }
62
+
63
+ return lines.join("\n");
64
+ }
65
+
66
+ function pluralize(count, noun) {
67
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
68
+ }
69
+
70
+ function shouldUseColor(options) {
71
+ if (typeof options.color === "boolean") return options.color;
72
+ const env = options.env ? { ...process.env, ...options.env } : process.env;
73
+ if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
74
+ if (env.FORCE_COLOR !== undefined) return env.FORCE_COLOR !== "0";
75
+ if (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0") return true;
76
+ return env.TERM !== "dumb" && Boolean(process.stdout.isTTY);
77
+ }
78
+
79
+ module.exports = { formatESLintResults };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "High-performance JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -76,11 +76,11 @@
76
76
  "use-at-your-own-risk.js"
77
77
  ],
78
78
  "optionalDependencies": {
79
- "@utoo/lint-darwin-arm64": "0.3.7",
80
- "@utoo/lint-darwin-x64": "0.3.7",
81
- "@utoo/lint-linux-arm64": "0.3.7",
82
- "@utoo/lint-linux-x64": "0.3.7",
83
- "@utoo/lint-win32-x64": "0.3.7"
79
+ "@utoo/lint-darwin-arm64": "0.3.8",
80
+ "@utoo/lint-darwin-x64": "0.3.8",
81
+ "@utoo/lint-linux-arm64": "0.3.8",
82
+ "@utoo/lint-linux-x64": "0.3.8",
83
+ "@utoo/lint-win32-x64": "0.3.8"
84
84
  },
85
85
  "dependencies": {
86
86
  "@eslint/eslintrc": "2.1.4",