@utoo/lint 0.2.3 → 0.2.4

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
@@ -41,6 +41,11 @@ Use machine-readable output:
41
41
  pnpm exec utoo-lint --format=json src
42
42
  ```
43
43
 
44
+ The default terminal output groups diagnostics by file, aligns their location
45
+ and severity, and summarizes errors and warnings separately. Color is detected
46
+ automatically; pass `--color` to force it or `--no-color` to disable it. JSON
47
+ output remains stable for editor and CI integrations.
48
+
44
49
  ## Autofix
45
50
 
46
51
  Apply safe fixes from all enabled rules that support autofix:
package/index.cjs CHANGED
@@ -747,6 +747,14 @@ function parseUtooLintCliArgs(args = []) {
747
747
  passthroughArgs.push(arg);
748
748
  continue;
749
749
  }
750
+ if (arg === "--color") {
751
+ options.color = true;
752
+ continue;
753
+ }
754
+ if (arg === "--no-color") {
755
+ options.color = false;
756
+ continue;
757
+ }
750
758
  if (arg.startsWith("-")) {
751
759
  passthroughArgs.push(arg);
752
760
  continue;
@@ -826,7 +834,7 @@ function runCli(args = [], options = {}) {
826
834
  if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
827
835
  return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
828
836
  }
829
- return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
837
+ return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report, cliOptions)}`, options);
830
838
  }
831
839
 
832
840
  function publicCliReport(report) {
@@ -838,18 +846,82 @@ function publicCliReport(report) {
838
846
  };
839
847
  }
840
848
 
841
- function formatNativeTextReport(report) {
849
+ function formatNativeTextReport(report, options = {}) {
850
+ const diagnostics = report.diagnostics ?? [];
851
+ const files = report.files ?? 0;
852
+ const color = shouldUseColor(options);
853
+
854
+ if (diagnostics.length === 0) {
855
+ return `${paint(color, "\u001b[32m", "✓")} ${pluralize(files, "file")} checked, no problems found\n`;
856
+ }
857
+
842
858
  const lines = [];
843
- for (const diagnostic of report.diagnostics ?? []) {
844
- const rule = diagnostic.ruleId ? ` [${diagnostic.ruleId}]` : "";
859
+ const locationWidth = diagnostics.reduce(
860
+ (width, diagnostic) => Math.max(width, diagnosticLocation(diagnostic).length),
861
+ 0
862
+ );
863
+ let currentFile;
864
+
865
+ for (const diagnostic of diagnostics) {
866
+ const filePath = diagnostic.filePath ?? "<unknown>";
867
+ if (filePath !== currentFile) {
868
+ if (lines.length > 0) lines.push("");
869
+ lines.push(paint(color, "\u001b[1m", filePath));
870
+ currentFile = filePath;
871
+ }
872
+
873
+ const location = diagnosticLocation(diagnostic).padStart(locationWidth);
874
+ const severity = diagnostic.severity === "warning" || diagnostic.severity === 1 ? "warning" : "error";
875
+ const severityColor = severity === "error" ? "\u001b[31m" : "\u001b[33m";
876
+ const message = String(diagnostic.message ?? "").replace(/\s*[\r\n]+\s*/g, " ");
877
+ const rule = diagnostic.ruleId ? ` ${paint(color, "\u001b[2m", diagnostic.ruleId)}` : "";
845
878
  lines.push(
846
- `${diagnostic.filePath}:${diagnostic.line ?? 0}:${diagnostic.column ?? 0}: ${diagnostic.severity ?? "error"}: ${diagnostic.message}${rule}`
879
+ ` ${paint(color, "\u001b[2m", location)} ${paint(color, severityColor, severity.padEnd(7))} ${message}${rule}`
847
880
  );
848
881
  }
849
- lines.push(`${report.files ?? 0} file(s) checked, ${(report.diagnostics ?? []).length} diagnostic(s)`);
882
+
883
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error" || diagnostic.severity === 2).length;
884
+ const warnings = diagnostics.length - errors;
885
+ const markerColor = errors > 0 ? "\u001b[31m" : "\u001b[33m";
886
+ lines.push("");
887
+ lines.push(
888
+ `${paint(color, markerColor, "✖")} ${pluralize(diagnostics.length, "problem")} (${pluralize(errors, "error")}, ${pluralize(warnings, "warning")})`
889
+ );
890
+
891
+ const fixable = diagnostics.filter(diagnosticIsFixable).length;
892
+ const fixRequested = options.fix || options.extraArgs?.includes("--fix") || options.extraArgs?.includes("--fix-dry-run");
893
+ if (fixable > 0 && !fixRequested) {
894
+ lines.push(` ${pluralize(fixable, "problem")} potentially fixable with the \`--fix\` option.`);
895
+ }
896
+ lines.push(` ${pluralize(files, "file")} checked`);
850
897
  return `${lines.join("\n")}\n`;
851
898
  }
852
899
 
900
+ function diagnosticLocation(diagnostic) {
901
+ return `${diagnostic.line ?? 0}:${diagnostic.column ?? 0}`;
902
+ }
903
+
904
+ function diagnosticIsFixable(diagnostic) {
905
+ return Boolean(diagnostic.fix) || (Array.isArray(diagnostic.fixes) && diagnostic.fixes.length > 0);
906
+ }
907
+
908
+ function pluralize(count, noun) {
909
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
910
+ }
911
+
912
+ function paint(enabled, code, text) {
913
+ return enabled ? `${code}${text}\u001b[0m` : text;
914
+ }
915
+
916
+ function shouldUseColor(options = {}) {
917
+ if (options.color === true) return true;
918
+ if (options.color === false) return false;
919
+ const env = options.env ? { ...process.env, ...options.env } : process.env;
920
+ if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
921
+ if ((env.FORCE_COLOR && env.FORCE_COLOR !== "0") || (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0")) return true;
922
+ return Boolean(process.stderr.isTTY);
923
+ }
924
+
853
925
  function runFishlint(args = [], options = {}) {
854
926
  const cliArgs = normalizeStringArray(args, "args");
855
927
  const env = options.env ? { ...process.env, ...options.env } : { ...process.env };
package/index.d.ts CHANGED
@@ -88,6 +88,7 @@ export interface LintOptions {
88
88
  threads?: number;
89
89
  rules?: string[] | string;
90
90
  format?: "text" | "json" | string;
91
+ color?: boolean;
91
92
  config?: string;
92
93
  configFile?: string;
93
94
  noConfig?: boolean;
package/index.js CHANGED
@@ -2583,6 +2583,14 @@ function parseUtooLintCliArgs(args = []) {
2583
2583
  passthroughArgs.push(arg);
2584
2584
  continue;
2585
2585
  }
2586
+ if (arg === "--color") {
2587
+ options.color = true;
2588
+ continue;
2589
+ }
2590
+ if (arg === "--no-color") {
2591
+ options.color = false;
2592
+ continue;
2593
+ }
2586
2594
  if (arg.startsWith("-")) {
2587
2595
  passthroughArgs.push(arg);
2588
2596
  continue;
@@ -2662,7 +2670,7 @@ export function runCli(args = [], options = {}) {
2662
2670
  if (cliOptions.format === "json" || parsed.passthroughArgs.includes("--json")) {
2663
2671
  return completedRunResult(status, `${JSON.stringify(publicCliReport(report))}\n`, report.stderr ?? "", options);
2664
2672
  }
2665
- return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report)}`, options);
2673
+ return completedRunResult(status, "", `${report.stderr ?? ""}${formatNativeTextReport(report, cliOptions)}`, options);
2666
2674
  }
2667
2675
 
2668
2676
  function publicCliReport(report) {
@@ -2674,18 +2682,82 @@ function publicCliReport(report) {
2674
2682
  };
2675
2683
  }
2676
2684
 
2677
- function formatNativeTextReport(report) {
2685
+ function formatNativeTextReport(report, options = {}) {
2686
+ const diagnostics = report.diagnostics ?? [];
2687
+ const files = report.files ?? 0;
2688
+ const color = shouldUseColor(options);
2689
+
2690
+ if (diagnostics.length === 0) {
2691
+ return `${paint(color, "\u001b[32m", "✓")} ${pluralize(files, "file")} checked, no problems found\n`;
2692
+ }
2693
+
2678
2694
  const lines = [];
2679
- for (const diagnostic of report.diagnostics ?? []) {
2680
- const rule = diagnostic.ruleId ? ` [${diagnostic.ruleId}]` : "";
2695
+ const locationWidth = diagnostics.reduce(
2696
+ (width, diagnostic) => Math.max(width, diagnosticLocation(diagnostic).length),
2697
+ 0
2698
+ );
2699
+ let currentFile;
2700
+
2701
+ for (const diagnostic of diagnostics) {
2702
+ const filePath = diagnostic.filePath ?? "<unknown>";
2703
+ if (filePath !== currentFile) {
2704
+ if (lines.length > 0) lines.push("");
2705
+ lines.push(paint(color, "\u001b[1m", filePath));
2706
+ currentFile = filePath;
2707
+ }
2708
+
2709
+ const location = diagnosticLocation(diagnostic).padStart(locationWidth);
2710
+ const severity = diagnostic.severity === "warning" || diagnostic.severity === 1 ? "warning" : "error";
2711
+ const severityColor = severity === "error" ? "\u001b[31m" : "\u001b[33m";
2712
+ const message = String(diagnostic.message ?? "").replace(/\s*[\r\n]+\s*/g, " ");
2713
+ const rule = diagnostic.ruleId ? ` ${paint(color, "\u001b[2m", diagnostic.ruleId)}` : "";
2681
2714
  lines.push(
2682
- `${diagnostic.filePath}:${diagnostic.line ?? 0}:${diagnostic.column ?? 0}: ${diagnostic.severity ?? "error"}: ${diagnostic.message}${rule}`
2715
+ ` ${paint(color, "\u001b[2m", location)} ${paint(color, severityColor, severity.padEnd(7))} ${message}${rule}`
2683
2716
  );
2684
2717
  }
2685
- lines.push(`${report.files ?? 0} file(s) checked, ${(report.diagnostics ?? []).length} diagnostic(s)`);
2718
+
2719
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error" || diagnostic.severity === 2).length;
2720
+ const warnings = diagnostics.length - errors;
2721
+ const markerColor = errors > 0 ? "\u001b[31m" : "\u001b[33m";
2722
+ lines.push("");
2723
+ lines.push(
2724
+ `${paint(color, markerColor, "✖")} ${pluralize(diagnostics.length, "problem")} (${pluralize(errors, "error")}, ${pluralize(warnings, "warning")})`
2725
+ );
2726
+
2727
+ const fixable = diagnostics.filter(diagnosticIsFixable).length;
2728
+ const fixRequested = options.fix || options.extraArgs?.includes("--fix") || options.extraArgs?.includes("--fix-dry-run");
2729
+ if (fixable > 0 && !fixRequested) {
2730
+ lines.push(` ${pluralize(fixable, "problem")} potentially fixable with the \`--fix\` option.`);
2731
+ }
2732
+ lines.push(` ${pluralize(files, "file")} checked`);
2686
2733
  return `${lines.join("\n")}\n`;
2687
2734
  }
2688
2735
 
2736
+ function diagnosticLocation(diagnostic) {
2737
+ return `${diagnostic.line ?? 0}:${diagnostic.column ?? 0}`;
2738
+ }
2739
+
2740
+ function diagnosticIsFixable(diagnostic) {
2741
+ return Boolean(diagnostic.fix) || (Array.isArray(diagnostic.fixes) && diagnostic.fixes.length > 0);
2742
+ }
2743
+
2744
+ function pluralize(count, noun) {
2745
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
2746
+ }
2747
+
2748
+ function paint(enabled, code, text) {
2749
+ return enabled ? `${code}${text}\u001b[0m` : text;
2750
+ }
2751
+
2752
+ function shouldUseColor(options = {}) {
2753
+ if (options.color === true) return true;
2754
+ if (options.color === false) return false;
2755
+ const env = options.env ? { ...process.env, ...options.env } : process.env;
2756
+ if (env.NO_COLOR !== undefined || env.NODE_DISABLE_COLORS !== undefined) return false;
2757
+ if ((env.FORCE_COLOR && env.FORCE_COLOR !== "0") || (env.CLICOLOR_FORCE && env.CLICOLOR_FORCE !== "0")) return true;
2758
+ return Boolean(process.stderr.isTTY);
2759
+ }
2760
+
2689
2761
  export function runFishlint(args = [], options = {}) {
2690
2762
  const cliArgs = normalizeStringArray(args, "args");
2691
2763
  const env = options.env ? { ...process.env, ...options.env } : { ...process.env };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utoo/lint",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "Experimental JavaScript and TypeScript linter powered by Yuku",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -73,11 +73,11 @@
73
73
  "use-at-your-own-risk.js"
74
74
  ],
75
75
  "optionalDependencies": {
76
- "@utoo/lint-darwin-arm64": "0.2.3",
77
- "@utoo/lint-darwin-x64": "0.2.3",
78
- "@utoo/lint-linux-arm64": "0.2.3",
79
- "@utoo/lint-linux-x64": "0.2.3",
80
- "@utoo/lint-win32-x64": "0.2.3"
76
+ "@utoo/lint-darwin-arm64": "0.2.4",
77
+ "@utoo/lint-darwin-x64": "0.2.4",
78
+ "@utoo/lint-linux-arm64": "0.2.4",
79
+ "@utoo/lint-linux-x64": "0.2.4",
80
+ "@utoo/lint-win32-x64": "0.2.4"
81
81
  },
82
82
  "dependencies": {
83
83
  "jiti": "2.7.0"