@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.
@@ -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, violations] of violationsByFile) {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.54",
3
+ "version": "1.3.55",
4
4
  "description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {