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 +3 -2
- package/dist/oxlint-runner.js +6 -11
- package/dist/suppression-manager.d.ts +3 -2
- package/dist/suppression-manager.js +23 -2
- package/package.json +1 -1
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
|
}
|
package/dist/oxlint-runner.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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)) {
|