oxlint-harness 1.0.8 → 1.0.10

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/dist/command.js CHANGED
@@ -270,10 +270,11 @@ The suppression file format uses counts per rule per file:
270
270
  }
271
271
  // Handle suppression logic
272
272
  const suppressionManager = new SuppressionManager(flags.suppressions);
273
+ const targetedPaths = oxlintArgs.filter(arg => !arg.startsWith('-'));
273
274
  if (flags.update) {
274
275
  // Update mode: generate/update suppression file
275
276
  const currentSuppressions = suppressionManager.loadSuppressions();
276
- const updatedSuppressions = suppressionManager.updateSuppressions(currentSuppressions, diagnostics);
277
+ const updatedSuppressions = suppressionManager.updateSuppressions(currentSuppressions, diagnostics, targetedPaths.length > 0 ? targetedPaths : undefined);
277
278
  suppressionManager.saveSuppressions(updatedSuppressions);
278
279
  this.log(`${colors.success("Updated suppression file:")} ${colors.filename(flags.suppressions)}`);
279
280
  this.log(`${colors.info("Total diagnostics:")} ${colors.emphasis(diagnostics.length.toString())}`);
@@ -290,7 +291,7 @@ The suppression file format uses counts per rule per file:
290
291
  }
291
292
  if (shouldTighten) {
292
293
  // Tighten suppressions by removing/reducing cleaned-up violations
293
- const tightenedSuppressions = suppressionManager.tightenSuppressions(suppressions, diagnostics);
294
+ const tightenedSuppressions = suppressionManager.tightenSuppressions(suppressions, diagnostics, targetedPaths.length > 0 ? targetedPaths : undefined);
294
295
  suppressionManager.saveSuppressions(tightenedSuppressions);
295
296
  this.log(`${colors.success("Tightened suppression file:")} ${colors.filename(flags.suppressions)}`);
296
297
  }
@@ -58,15 +58,7 @@ export class OxlintRunner {
58
58
  try {
59
59
  // oxlint exits with non-zero when linting issues are found
60
60
  // We still want to parse the JSON output
61
- const diagnostics = this.parseOxlintOutput(stdout);
62
- // If oxlint produced no diagnostics but exited with a code other than
63
- // 0 (success) or 1 (lint violations found), treat it as a failure.
64
- // This prevents tighten from wiping the suppression file when oxlint
65
- // fails silently (e.g. missing tsconfig for --type-aware).
66
- if (diagnostics.length === 0 && code !== 0 && code !== 1) {
67
- reject(new Error(`oxlint exited with code ${code} and produced no output.\nStderr: ${stderr}`));
68
- return;
69
- }
61
+ const diagnostics = this.parseOxlintOutput(stdout, code, stderr);
70
62
  resolve(diagnostics);
71
63
  }
72
64
  catch (error) {
@@ -78,9 +70,12 @@ export class OxlintRunner {
78
70
  });
79
71
  });
80
72
  }
81
- parseOxlintOutput(output) {
73
+ parseOxlintOutput(output, exitCode = null, stderr = '') {
82
74
  if (!output.trim()) {
83
- return [];
75
+ // oxlint -f json should always produce JSON output. Empty stdout means
76
+ // it failed to run (missing tsconfig, binary not found, signal kill, etc).
77
+ // Returning [] here would cause tighten to wipe the suppression file.
78
+ throw new Error(`oxlint produced no output (exit code: ${exitCode}).${stderr ? `\nStderr: ${stderr}` : ''}`);
84
79
  }
85
80
  try {
86
81
  const parsed = JSON.parse(output);
@@ -7,6 +7,7 @@ export declare class SuppressionManager {
7
7
  saveSuppressions(suppressions: SuppressionFile): void;
8
8
  generateSuppressions(diagnostics: ProcessedDiagnostic[]): SuppressionFile;
9
9
  findExcessErrors(diagnostics: ProcessedDiagnostic[], suppressions: SuppressionFile): ExcessError[];
10
- updateSuppressions(currentSuppressions: SuppressionFile, diagnostics: ProcessedDiagnostic[]): SuppressionFile;
11
- tightenSuppressions(currentSuppressions: SuppressionFile, diagnostics: ProcessedDiagnostic[]): SuppressionFile;
10
+ private isFileCoveredByPaths;
11
+ updateSuppressions(currentSuppressions: SuppressionFile, diagnostics: ProcessedDiagnostic[], targetedPaths?: string[]): SuppressionFile;
12
+ tightenSuppressions(currentSuppressions: SuppressionFile, diagnostics: ProcessedDiagnostic[], targetedPaths?: string[]): SuppressionFile;
12
13
  }
@@ -31,6 +31,9 @@ export class SuppressionManager {
31
31
  }
32
32
  }
33
33
  saveSuppressions(suppressions) {
34
+ if (Object.keys(suppressions).length === 0) {
35
+ return;
36
+ }
34
37
  try {
35
38
  const sorted = this.sortSuppressions(suppressions);
36
39
  const content = JSON.stringify(sorted, null, 2);
@@ -93,10 +96,24 @@ export class SuppressionManager {
93
96
  }
94
97
  return excessErrors;
95
98
  }
96
- updateSuppressions(currentSuppressions, diagnostics) {
99
+ isFileCoveredByPaths(filename, paths) {
100
+ return paths.some(path => {
101
+ const normalizedPath = path.replace(/\/+$/, '');
102
+ return filename === normalizedPath || filename.startsWith(normalizedPath + '/');
103
+ });
104
+ }
105
+ updateSuppressions(currentSuppressions, diagnostics, targetedPaths) {
97
106
  const newSuppressions = this.generateSuppressions(diagnostics);
98
107
  // Merge with existing suppressions, using new counts
99
108
  const updated = { ...currentSuppressions };
109
+ // Remove entries for targeted files that have no new diagnostics
110
+ if (targetedPaths && targetedPaths.length > 0) {
111
+ for (const filename of Object.keys(updated)) {
112
+ if (this.isFileCoveredByPaths(filename, targetedPaths) && !newSuppressions[filename]) {
113
+ delete updated[filename];
114
+ }
115
+ }
116
+ }
100
117
  for (const [filename, rules] of Object.entries(newSuppressions)) {
101
118
  updated[filename] = { ...updated[filename], ...rules };
102
119
  }
@@ -108,7 +125,7 @@ export class SuppressionManager {
108
125
  }
109
126
  return this.sortSuppressions(updated);
110
127
  }
111
- tightenSuppressions(currentSuppressions, diagnostics) {
128
+ tightenSuppressions(currentSuppressions, diagnostics, targetedPaths) {
112
129
  // Group diagnostics by file and rule to get actual counts
113
130
  const actualCounts = new Map();
114
131
  for (const diagnostic of diagnostics) {
@@ -123,6 +140,10 @@ export class SuppressionManager {
123
140
  const tightened = { ...currentSuppressions };
124
141
  // Process each file in current suppressions
125
142
  for (const [filename, rules] of Object.entries(tightened)) {
143
+ // Skip files not covered by targeted paths
144
+ if (targetedPaths && targetedPaths.length > 0 && !this.isFileCoveredByPaths(filename, targetedPaths)) {
145
+ continue;
146
+ }
126
147
  const fileRules = { ...rules };
127
148
  // Process each rule in the file
128
149
  for (const [rule, suppression] of Object.entries(fileRules)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-harness",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "A harness for oxlint with bulk suppressions support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",