@sun-asterisk/sunlint 1.3.54 → 1.3.55
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/engines/heuristic-engine.js +126 -1
- package/package.json +1 -1
|
@@ -999,7 +999,11 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
999
999
|
// Group violations by file
|
|
1000
1000
|
const violationsByFile = this.groupViolationsByFile(ruleViolations);
|
|
1001
1001
|
|
|
1002
|
-
for (const [filePath,
|
|
1002
|
+
for (const [filePath, fileViolations] of violationsByFile) {
|
|
1003
|
+
|
|
1004
|
+
// Filter violations suppressed by inline disable comments
|
|
1005
|
+
const violations = this.filterViolationsByInlineDisable(filePath, fileViolations);
|
|
1006
|
+
if (violations.length === 0) continue;
|
|
1003
1007
|
|
|
1004
1008
|
// Apply per-file config overrides — skip violations disabled by overrides
|
|
1005
1009
|
let overriddenSeverity = null;
|
|
@@ -1343,6 +1347,127 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
1343
1347
|
return groups;
|
|
1344
1348
|
}
|
|
1345
1349
|
|
|
1350
|
+
/**
|
|
1351
|
+
* Filter violations suppressed by inline disable comments
|
|
1352
|
+
* Supports: sunlint-disable-next-line <ruleId>, sunlint-disable <ruleId>,
|
|
1353
|
+
* sunlint-disable-file <ruleId>, sunlint-disable (all rules)
|
|
1354
|
+
* Following Rule C006: Verb-noun naming
|
|
1355
|
+
* @param {string} filePath - Path to the source file
|
|
1356
|
+
* @param {Object[]} violations - Violations found in the file
|
|
1357
|
+
* @returns {Object[]} Filtered violations (without suppressed ones)
|
|
1358
|
+
*/
|
|
1359
|
+
filterViolationsByInlineDisable(filePath, violations) {
|
|
1360
|
+
if (!violations || violations.length === 0) return violations;
|
|
1361
|
+
|
|
1362
|
+
let lines;
|
|
1363
|
+
try {
|
|
1364
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
1365
|
+
lines = content.split('\n');
|
|
1366
|
+
} catch {
|
|
1367
|
+
return violations;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
// Pre-scan for file-level disables and block-level disable/enable ranges
|
|
1371
|
+
const fileDisabledRules = new Set();
|
|
1372
|
+
const disableRanges = []; // { ruleId: string|null, startLine: number, endLine: number }
|
|
1373
|
+
let activeDisables = new Map(); // ruleId|'*' → startLineNumber
|
|
1374
|
+
|
|
1375
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1376
|
+
const line = lines[i].trim();
|
|
1377
|
+
|
|
1378
|
+
// File-level disable: sunlint-disable-file or sunlint-disable-file S004
|
|
1379
|
+
const fileDisableMatch = line.match(/(?:\/\/|\/\*)\s*sunlint-disable-file(?:\s+([\w,\s]+))?\s*(?:\*\/)?/);
|
|
1380
|
+
if (fileDisableMatch) {
|
|
1381
|
+
const ruleIds = fileDisableMatch[1] ? fileDisableMatch[1].split(',').map(r => r.trim()) : [null];
|
|
1382
|
+
for (const ruleId of ruleIds) {
|
|
1383
|
+
fileDisabledRules.add(ruleId); // null means all rules
|
|
1384
|
+
}
|
|
1385
|
+
continue;
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// Block-level disable: sunlint-disable or sunlint-disable S004
|
|
1389
|
+
const blockDisableMatch = line.match(/(?:\/\/|\/\*)\s*sunlint-disable(?!\s*-(?:next-line|file))(?:\s+([\w,\s]+))?\s*(?:\*\/)?/);
|
|
1390
|
+
if (blockDisableMatch) {
|
|
1391
|
+
const ruleIds = blockDisableMatch[1] ? blockDisableMatch[1].split(',').map(r => r.trim()) : ['*'];
|
|
1392
|
+
for (const ruleId of ruleIds) {
|
|
1393
|
+
if (!activeDisables.has(ruleId)) {
|
|
1394
|
+
activeDisables.set(ruleId, i + 1); // 1-based line number
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
// Block-level enable: sunlint-enable or sunlint-enable S004
|
|
1401
|
+
const blockEnableMatch = line.match(/(?:\/\/|\/\*)\s*sunlint-enable(?:\s+([\w,\s]+))?\s*(?:\*\/)?/);
|
|
1402
|
+
if (blockEnableMatch) {
|
|
1403
|
+
const ruleIds = blockEnableMatch[1] ? blockEnableMatch[1].split(',').map(r => r.trim()) : ['*'];
|
|
1404
|
+
for (const ruleId of ruleIds) {
|
|
1405
|
+
if (activeDisables.has(ruleId)) {
|
|
1406
|
+
disableRanges.push({
|
|
1407
|
+
ruleId: ruleId === '*' ? null : ruleId,
|
|
1408
|
+
startLine: activeDisables.get(ruleId),
|
|
1409
|
+
endLine: i + 1
|
|
1410
|
+
});
|
|
1411
|
+
activeDisables.delete(ruleId);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// Close any unclosed disable blocks (extend to end of file)
|
|
1419
|
+
for (const [ruleId, startLine] of activeDisables) {
|
|
1420
|
+
disableRanges.push({
|
|
1421
|
+
ruleId: ruleId === '*' ? null : ruleId,
|
|
1422
|
+
startLine,
|
|
1423
|
+
endLine: lines.length
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
return violations.filter(violation => {
|
|
1428
|
+
const ruleId = violation.ruleId;
|
|
1429
|
+
const violationLine = violation.line;
|
|
1430
|
+
|
|
1431
|
+
// Check file-level disable
|
|
1432
|
+
if (fileDisabledRules.has(null) || fileDisabledRules.has(ruleId)) {
|
|
1433
|
+
return false;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
// Check block-level disable ranges
|
|
1437
|
+
for (const range of disableRanges) {
|
|
1438
|
+
if (violationLine >= range.startLine && violationLine <= range.endLine) {
|
|
1439
|
+
if (range.ruleId === null || range.ruleId === ruleId) {
|
|
1440
|
+
return false;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
// Check next-line directive (comment on previous line)
|
|
1446
|
+
if (violationLine > 1) {
|
|
1447
|
+
const prevLine = lines[violationLine - 2]?.trim() || '';
|
|
1448
|
+
const nextLinePattern = /(?:\/\/|\/\*)\s*sunlint-disable-next-line(?:\s+([\w,\s]+))?\s*(?:\*\/)?/;
|
|
1449
|
+
const nextLineMatch = prevLine.match(nextLinePattern);
|
|
1450
|
+
if (nextLineMatch) {
|
|
1451
|
+
if (!nextLineMatch[1]) return false; // No rule specified = disable all
|
|
1452
|
+
const disabledRules = nextLineMatch[1].split(',').map(r => r.trim());
|
|
1453
|
+
if (disabledRules.includes(ruleId)) return false;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
// Check same-line disable (comment on the violation line itself)
|
|
1458
|
+
const currentLine = lines[violationLine - 1] || '';
|
|
1459
|
+
const sameLinePattern = /(?:\/\/|\/\*)\s*sunlint-disable(?:-next-line)?(?:\s+([\w,\s]+))?\s*(?:\*\/)?/;
|
|
1460
|
+
const sameLineMatch = currentLine.match(sameLinePattern);
|
|
1461
|
+
if (sameLineMatch) {
|
|
1462
|
+
if (!sameLineMatch[1]) return false;
|
|
1463
|
+
const disabledRules = sameLineMatch[1].split(',').map(r => r.trim());
|
|
1464
|
+
if (disabledRules.includes(ruleId)) return false;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
return true;
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1346
1471
|
/**
|
|
1347
1472
|
* Get supported rules
|
|
1348
1473
|
* Following Rule C006: Verb-noun naming
|
package/package.json
CHANGED