unguard 0.13.2 → 0.13.3

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.
@@ -653,7 +653,8 @@ var repeatedLiteralProperty = {
653
653
  }
654
654
  const line = ts8.getLineAndCharacterOfPosition(sourceFile, prop.getStart(sourceFile)).line + 1;
655
655
  const scopeId = findEnclosingFunctionStart(prop, sourceFile);
656
- list.push({ line, isAsConst: result.isAsConst, scopeId });
656
+ const key = ts8.isIdentifier(prop.name) ? prop.name.text : ts8.isStringLiteral(prop.name) ? prop.name.text : "";
657
+ list.push({ line, isAsConst: result.isAsConst, scopeId, key });
657
658
  }
658
659
  }
659
660
  ts8.forEachChild(node, visit2);
@@ -662,6 +663,8 @@ var repeatedLiteralProperty = {
662
663
  const valueMap = /* @__PURE__ */ new Map();
663
664
  ts8.forEachChild(sourceFile, visit2);
664
665
  for (const [value, occurrences] of valueMap) {
666
+ const uniqueKeys = new Set(occurrences.map((o) => o.key).filter((k) => k !== ""));
667
+ if (uniqueKeys.size === 1) continue;
665
668
  const hasAsConst = occurrences.some((o) => o.isAsConst);
666
669
  const uniqueScopes = new Set(occurrences.map((o) => o.scopeId));
667
670
  const threshold = hasAsConst ? 3 : 5;
@@ -753,7 +756,7 @@ var repeatedReturnShape = {
753
756
  const shapeMap = /* @__PURE__ */ new Map();
754
757
  for (const [file, { sourceFile }] of project.files) {
755
758
  let visit2 = function(node) {
756
- if (isFunctionLike(node)) {
759
+ if (isFunctionLike(node) && !isCallbackArgument(node)) {
757
760
  const functionName = deriveFunctionName(node, sourceFile);
758
761
  const line = ts10.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
759
762
  collectReturnShapes(node, file, line, functionName, sourceFile, shapeMap);
@@ -763,7 +766,9 @@ var repeatedReturnShape = {
763
766
  var visit = visit2;
764
767
  ts10.forEachChild(sourceFile, visit2);
765
768
  }
766
- for (const [, entries] of shapeMap) {
769
+ const knownTypeShapes = buildKnownTypeShapes(project.types.getAll());
770
+ for (const [shapeKey, entries] of shapeMap) {
771
+ if (knownTypeShapes.has(shapeKey)) continue;
767
772
  const byFunction = /* @__PURE__ */ new Map();
768
773
  for (const entry of entries) {
769
774
  const key = `${entry.file}:${entry.line}`;
@@ -773,6 +778,8 @@ var repeatedReturnShape = {
773
778
  }
774
779
  const unique = [...byFunction.values()];
775
780
  if (unique.length < THRESHOLD) continue;
781
+ const uniqueFiles = new Set(unique.map((e) => e.file));
782
+ if (uniqueFiles.size < 2) continue;
776
783
  const sorted = unique.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
777
784
  const first = sorted[0];
778
785
  if (first === void 0) continue;
@@ -832,6 +839,43 @@ function addShape(objLiteral, file, funcLine, functionName, sourceFile, shapeMap
832
839
  const { sorted, list } = getShapeGroup(shapeMap, props);
833
840
  list.push({ file, line: funcLine, functionName, props: sorted });
834
841
  }
842
+ function isCallbackArgument(node) {
843
+ let current = node;
844
+ if (ts10.isParenthesizedExpression(current.parent)) {
845
+ current = current.parent;
846
+ }
847
+ const parent = current.parent;
848
+ if (!ts10.isCallExpression(parent)) return false;
849
+ return parent.arguments.some((arg) => arg === current);
850
+ }
851
+ function buildKnownTypeShapes(typeEntries) {
852
+ const shapes = /* @__PURE__ */ new Set();
853
+ for (const entry of typeEntries) {
854
+ const props = extractTypePropertyNames(entry.node);
855
+ if (props && props.length >= 2) {
856
+ shapes.add([...props].sort().join("\0"));
857
+ }
858
+ }
859
+ return shapes;
860
+ }
861
+ function extractTypePropertyNames(node) {
862
+ let members;
863
+ if (ts10.isTypeLiteralNode(node)) {
864
+ members = node.members;
865
+ } else if (ts10.isInterfaceDeclaration(node)) {
866
+ members = node.members;
867
+ }
868
+ if (!members) return null;
869
+ const names = [];
870
+ for (const member of members) {
871
+ if (ts10.isPropertySignature(member) && member.name) {
872
+ if (ts10.isIdentifier(member.name)) names.push(member.name.text);
873
+ else if (ts10.isStringLiteral(member.name)) names.push(member.name.text);
874
+ else return null;
875
+ }
876
+ }
877
+ return names.length > 0 ? names : null;
878
+ }
835
879
  function deriveFunctionName(node, sourceFile) {
836
880
  if (ts10.isFunctionDeclaration(node) && node.name) {
837
881
  return node.name.text;
@@ -1290,174 +1334,15 @@ function hasOptionalChaining(node) {
1290
1334
  return false;
1291
1335
  }
1292
1336
 
1293
- // src/rules/ts/no-module-state-write.ts
1294
- import * as ts21 from "typescript";
1295
- var arrayMutators = /* @__PURE__ */ new Set([
1296
- "copyWithin",
1297
- "fill",
1298
- "pop",
1299
- "push",
1300
- "reverse",
1301
- "shift",
1302
- "sort",
1303
- "splice",
1304
- "unshift"
1305
- ]);
1306
- var mapMutators = /* @__PURE__ */ new Set(["clear", "delete", "set"]);
1307
- var setMutators = /* @__PURE__ */ new Set(["add", "clear", "delete"]);
1308
- var noModuleStateWrite = {
1309
- kind: "ts",
1310
- id: "no-module-state-write",
1311
- severity: "warning",
1312
- message: "Function mutates module-scope state; make the dependency explicit instead of writing ambient state",
1313
- visit(node, ctx) {
1314
- if (getEnclosingFunctionLike(node) === null) return;
1315
- const bindingName = getAmbientWriteBinding(node, ctx);
1316
- if (!bindingName) return;
1317
- ctx.report(node, `Function mutates module-scope state through "${bindingName}"`);
1318
- }
1319
- };
1320
- function getAmbientWriteBinding(node, ctx) {
1321
- if (ts21.isBinaryExpression(node) && isAssignmentOperator(node.operatorToken.kind)) {
1322
- return getAmbientBindingFromWriteTarget(node.left, ctx);
1323
- }
1324
- if (ts21.isPrefixUnaryExpression(node)) {
1325
- if (node.operator === ts21.SyntaxKind.PlusPlusToken || node.operator === ts21.SyntaxKind.MinusMinusToken) {
1326
- return getAmbientBindingFromWriteTarget(node.operand, ctx);
1327
- }
1328
- }
1329
- if (ts21.isPostfixUnaryExpression(node)) {
1330
- if (node.operator === ts21.SyntaxKind.PlusPlusToken || node.operator === ts21.SyntaxKind.MinusMinusToken) {
1331
- return getAmbientBindingFromWriteTarget(node.operand, ctx);
1332
- }
1333
- }
1334
- if (ts21.isDeleteExpression(node)) {
1335
- return getAmbientBindingFromWriteTarget(node.expression, ctx);
1336
- }
1337
- if (ts21.isCallExpression(node)) {
1338
- return getAmbientBindingFromMutatorCall(node, ctx);
1339
- }
1340
- return null;
1341
- }
1342
- function getAmbientBindingFromWriteTarget(node, ctx) {
1343
- const target = unwrapExpression2(node);
1344
- if (ts21.isIdentifier(target)) {
1345
- return isAmbientBinding(target, ctx) ? target.text : null;
1346
- }
1347
- if (ts21.isPropertyAccessExpression(target) || ts21.isElementAccessExpression(target)) {
1348
- const root = getRootIdentifier(target.expression);
1349
- if (!root) return null;
1350
- return isAmbientBinding(root, ctx) ? root.text : null;
1351
- }
1352
- return null;
1353
- }
1354
- function getAmbientBindingFromMutatorCall(node, ctx) {
1355
- const callee = unwrapExpression2(node.expression);
1356
- if (!ts21.isPropertyAccessExpression(callee)) return null;
1357
- const receiver = unwrapExpression2(callee.expression);
1358
- const root = getRootIdentifier(receiver);
1359
- if (!root || !isAmbientBinding(root, ctx)) return null;
1360
- const receiverType = ctx.checker.getTypeAtLocation(receiver);
1361
- const method = callee.name.text;
1362
- if (!isKnownMutator(receiverType, method, ctx.checker)) return null;
1363
- return root.text;
1364
- }
1365
- function isAmbientBinding(node, ctx) {
1366
- const symbol = ctx.checker.getSymbolAtLocation(node);
1367
- if (!symbol) return false;
1368
- for (const declaration of symbol.declarations ?? []) {
1369
- if (!isAmbientBindingDeclaration(declaration, ctx.sourceFile)) continue;
1370
- return true;
1371
- }
1372
- return false;
1373
- }
1374
- function isAmbientBindingDeclaration(node, sourceFile) {
1375
- if (node.getSourceFile() !== sourceFile) return false;
1376
- if (getEnclosingFunctionLike(node) !== null) return false;
1377
- return ts21.isVariableDeclaration(node) || ts21.isBindingElement(node) || ts21.isImportClause(node) || ts21.isImportSpecifier(node) || ts21.isNamespaceImport(node) || ts21.isImportEqualsDeclaration(node);
1378
- }
1379
- function getRootIdentifier(node) {
1380
- const target = unwrapExpression2(node);
1381
- if (ts21.isIdentifier(target)) return target;
1382
- if (ts21.isPropertyAccessExpression(target) || ts21.isElementAccessExpression(target)) {
1383
- return getRootIdentifier(target.expression);
1384
- }
1385
- return null;
1386
- }
1387
- function unwrapExpression2(node) {
1388
- let current = node;
1389
- while (true) {
1390
- if (ts21.isParenthesizedExpression(current)) {
1391
- current = current.expression;
1392
- continue;
1393
- }
1394
- if (ts21.isAsExpression(current) || ts21.isTypeAssertionExpression(current) || ts21.isNonNullExpression(current)) {
1395
- current = current.expression;
1396
- continue;
1397
- }
1398
- if (ts21.isSatisfiesExpression(current)) {
1399
- current = current.expression;
1400
- continue;
1401
- }
1402
- return current;
1403
- }
1404
- }
1405
- function isAssignmentOperator(kind) {
1406
- switch (kind) {
1407
- case ts21.SyntaxKind.EqualsToken:
1408
- case ts21.SyntaxKind.PlusEqualsToken:
1409
- case ts21.SyntaxKind.MinusEqualsToken:
1410
- case ts21.SyntaxKind.AsteriskEqualsToken:
1411
- case ts21.SyntaxKind.AsteriskAsteriskEqualsToken:
1412
- case ts21.SyntaxKind.SlashEqualsToken:
1413
- case ts21.SyntaxKind.PercentEqualsToken:
1414
- case ts21.SyntaxKind.AmpersandEqualsToken:
1415
- case ts21.SyntaxKind.BarEqualsToken:
1416
- case ts21.SyntaxKind.CaretEqualsToken:
1417
- case ts21.SyntaxKind.LessThanLessThanEqualsToken:
1418
- case ts21.SyntaxKind.GreaterThanGreaterThanEqualsToken:
1419
- case ts21.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken:
1420
- case ts21.SyntaxKind.BarBarEqualsToken:
1421
- case ts21.SyntaxKind.AmpersandAmpersandEqualsToken:
1422
- case ts21.SyntaxKind.QuestionQuestionEqualsToken:
1423
- return true;
1424
- default:
1425
- return false;
1426
- }
1427
- }
1428
- function getEnclosingFunctionLike(node) {
1429
- let current = node.parent;
1430
- while (current) {
1431
- if (ts21.isFunctionLike(current)) return current;
1432
- current = current.parent;
1433
- }
1434
- return null;
1435
- }
1436
- function isKnownMutator(type, method, checker) {
1437
- const apparent = checker.getApparentType(type);
1438
- if (checker.isArrayType(apparent) || checker.isTupleType(apparent)) {
1439
- return arrayMutators.has(method);
1440
- }
1441
- const symbol = apparent.getSymbol();
1442
- const name = symbol?.getName();
1443
- if (name === "Map" || name === "WeakMap") {
1444
- return mapMutators.has(method);
1445
- }
1446
- if (name === "Set" || name === "WeakSet") {
1447
- return setMutators.has(method);
1448
- }
1449
- return false;
1450
- }
1451
-
1452
1337
  // src/rules/ts/no-non-null-assertion.ts
1453
- import * as ts22 from "typescript";
1338
+ import * as ts21 from "typescript";
1454
1339
  var noNonNullAssertion = {
1455
1340
  kind: "ts",
1456
1341
  id: "no-non-null-assertion",
1457
1342
  severity: "warning",
1458
1343
  message: "Non-null assertion (!) overrides the type checker; narrow with a type guard or fix the type so it's not nullable",
1459
1344
  visit(node, ctx) {
1460
- if (!ts22.isNonNullExpression(node)) return;
1345
+ if (!ts21.isNonNullExpression(node)) return;
1461
1346
  const inner = node.expression;
1462
1347
  if (!ctx.isNullable(inner)) return;
1463
1348
  if (ctx.isExternal(inner)) return;
@@ -1468,32 +1353,32 @@ var noNonNullAssertion = {
1468
1353
  }
1469
1354
  };
1470
1355
  function isSplitElementAccess(node) {
1471
- if (!ts22.isElementAccessExpression(node)) return false;
1356
+ if (!ts21.isElementAccessExpression(node)) return false;
1472
1357
  const obj = node.expression;
1473
- if (!ts22.isCallExpression(obj)) return false;
1358
+ if (!ts21.isCallExpression(obj)) return false;
1474
1359
  const callee = obj.expression;
1475
- if (!ts22.isPropertyAccessExpression(callee)) return false;
1360
+ if (!ts21.isPropertyAccessExpression(callee)) return false;
1476
1361
  return callee.name.text === "split";
1477
1362
  }
1478
1363
  function isFilterElementAccess(node) {
1479
- if (ts22.isElementAccessExpression(node)) {
1364
+ if (ts21.isElementAccessExpression(node)) {
1480
1365
  const obj = node.expression;
1481
- if (ts22.isCallExpression(obj)) {
1366
+ if (ts21.isCallExpression(obj)) {
1482
1367
  const callee = obj.expression;
1483
- if (ts22.isPropertyAccessExpression(callee) && callee.name.text === "filter") return true;
1368
+ if (ts21.isPropertyAccessExpression(callee) && callee.name.text === "filter") return true;
1484
1369
  }
1485
- if (ts22.isIdentifier(obj)) {
1370
+ if (ts21.isIdentifier(obj)) {
1486
1371
  const init = findVariableInit(obj);
1487
- if (init && ts22.isCallExpression(init)) {
1372
+ if (init && ts21.isCallExpression(init)) {
1488
1373
  const callee = init.expression;
1489
- if (ts22.isPropertyAccessExpression(callee) && callee.name.text === "filter") return true;
1374
+ if (ts21.isPropertyAccessExpression(callee) && callee.name.text === "filter") return true;
1490
1375
  }
1491
1376
  }
1492
1377
  }
1493
1378
  return false;
1494
1379
  }
1495
1380
  function isLengthGuardedAccess(node) {
1496
- if (!ts22.isElementAccessExpression(node)) return false;
1381
+ if (!ts21.isElementAccessExpression(node)) return false;
1497
1382
  const arr = node.expression;
1498
1383
  const arrName = getIdentifierName(arr);
1499
1384
  if (!arrName) return false;
@@ -1504,25 +1389,25 @@ function isInsideForLoopBoundedBy(node, arrName) {
1504
1389
  let current = node;
1505
1390
  while (current.parent) {
1506
1391
  current = current.parent;
1507
- if (ts22.isForStatement(current) && current.condition) {
1392
+ if (ts21.isForStatement(current) && current.condition) {
1508
1393
  if (isLengthBoundCondition(current.condition, arrName)) return true;
1509
1394
  }
1510
1395
  }
1511
1396
  return false;
1512
1397
  }
1513
1398
  function isLengthBoundCondition(cond, arrName) {
1514
- if (!ts22.isBinaryExpression(cond)) return false;
1399
+ if (!ts21.isBinaryExpression(cond)) return false;
1515
1400
  const op = cond.operatorToken.kind;
1516
- if (op === ts22.SyntaxKind.LessThanToken || op === ts22.SyntaxKind.LessThanEqualsToken) {
1401
+ if (op === ts21.SyntaxKind.LessThanToken || op === ts21.SyntaxKind.LessThanEqualsToken) {
1517
1402
  return isLengthAccess(cond.right, arrName);
1518
1403
  }
1519
- if (op === ts22.SyntaxKind.GreaterThanToken || op === ts22.SyntaxKind.GreaterThanEqualsToken) {
1404
+ if (op === ts21.SyntaxKind.GreaterThanToken || op === ts21.SyntaxKind.GreaterThanEqualsToken) {
1520
1405
  return isLengthAccess(cond.left, arrName);
1521
1406
  }
1522
1407
  return false;
1523
1408
  }
1524
1409
  function isLengthAccess(node, arrName) {
1525
- if (!ts22.isPropertyAccessExpression(node)) return false;
1410
+ if (!ts21.isPropertyAccessExpression(node)) return false;
1526
1411
  if (node.name.text !== "length") return false;
1527
1412
  return getIdentifierName(node.expression) === arrName;
1528
1413
  }
@@ -1530,16 +1415,16 @@ function hasPrecedingLengthGuard(node, arrName) {
1530
1415
  let current = node;
1531
1416
  while (current.parent) {
1532
1417
  const parent = current.parent;
1533
- if (ts22.isBlock(parent)) {
1418
+ if (ts21.isBlock(parent)) {
1534
1419
  for (const stmt of parent.statements) {
1535
1420
  if (stmt === current || stmt.pos >= current.pos) break;
1536
- if (ts22.isIfStatement(stmt) && isLengthGuardWithEarlyExit(stmt, arrName)) return true;
1421
+ if (ts21.isIfStatement(stmt) && isLengthGuardWithEarlyExit(stmt, arrName)) return true;
1537
1422
  }
1538
1423
  }
1539
- if (ts22.isIfStatement(parent) && parent.thenStatement === current) {
1424
+ if (ts21.isIfStatement(parent) && parent.thenStatement === current) {
1540
1425
  if (isPositiveLengthCheck(parent.expression, arrName)) return true;
1541
1426
  }
1542
- if (ts22.isBlock(current) && ts22.isIfStatement(parent) && parent.thenStatement === current) {
1427
+ if (ts21.isBlock(current) && ts21.isIfStatement(parent) && parent.thenStatement === current) {
1543
1428
  if (isPositiveLengthCheck(parent.expression, arrName)) return true;
1544
1429
  }
1545
1430
  current = parent;
@@ -1551,79 +1436,79 @@ function isLengthGuardWithEarlyExit(stmt, arrName) {
1551
1436
  return isZeroLengthCheck(stmt.expression, arrName);
1552
1437
  }
1553
1438
  function isZeroLengthCheck(expr, arrName) {
1554
- if (!ts22.isBinaryExpression(expr)) return false;
1439
+ if (!ts21.isBinaryExpression(expr)) return false;
1555
1440
  const op = expr.operatorToken.kind;
1556
- if (op === ts22.SyntaxKind.EqualsEqualsEqualsToken || op === ts22.SyntaxKind.EqualsEqualsToken) {
1441
+ if (op === ts21.SyntaxKind.EqualsEqualsEqualsToken || op === ts21.SyntaxKind.EqualsEqualsToken) {
1557
1442
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralValue(expr.right, 0)) return true;
1558
1443
  if (isLengthAccess(expr.right, arrName) && isNumericLiteralValue(expr.left, 0)) return true;
1559
1444
  }
1560
- if (op === ts22.SyntaxKind.LessThanToken) {
1445
+ if (op === ts21.SyntaxKind.LessThanToken) {
1561
1446
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralGte(expr.right, 1)) return true;
1562
1447
  }
1563
- if (op === ts22.SyntaxKind.ExclamationEqualsEqualsToken || op === ts22.SyntaxKind.ExclamationEqualsToken) {
1448
+ if (op === ts21.SyntaxKind.ExclamationEqualsEqualsToken || op === ts21.SyntaxKind.ExclamationEqualsToken) {
1564
1449
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralGte(expr.right, 1)) return true;
1565
1450
  }
1566
1451
  return false;
1567
1452
  }
1568
1453
  function isPositiveLengthCheck(expr, arrName) {
1569
- if (!ts22.isBinaryExpression(expr)) return false;
1454
+ if (!ts21.isBinaryExpression(expr)) return false;
1570
1455
  const op = expr.operatorToken.kind;
1571
- if (op === ts22.SyntaxKind.GreaterThanToken) {
1456
+ if (op === ts21.SyntaxKind.GreaterThanToken) {
1572
1457
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralValue(expr.right, 0)) return true;
1573
1458
  }
1574
- if (op === ts22.SyntaxKind.GreaterThanEqualsToken) {
1459
+ if (op === ts21.SyntaxKind.GreaterThanEqualsToken) {
1575
1460
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralGte(expr.right, 1)) return true;
1576
1461
  }
1577
- if (op === ts22.SyntaxKind.ExclamationEqualsEqualsToken || op === ts22.SyntaxKind.ExclamationEqualsToken) {
1462
+ if (op === ts21.SyntaxKind.ExclamationEqualsEqualsToken || op === ts21.SyntaxKind.ExclamationEqualsToken) {
1578
1463
  if (isLengthAccess(expr.left, arrName) && isNumericLiteralValue(expr.right, 0)) return true;
1579
1464
  }
1580
1465
  return false;
1581
1466
  }
1582
1467
  function isEarlyExit(stmt) {
1583
- if (ts22.isReturnStatement(stmt) || ts22.isThrowStatement(stmt)) return true;
1584
- if (ts22.isBlock(stmt) && stmt.statements.length === 1) {
1468
+ if (ts21.isReturnStatement(stmt) || ts21.isThrowStatement(stmt)) return true;
1469
+ if (ts21.isBlock(stmt) && stmt.statements.length === 1) {
1585
1470
  const inner = stmt.statements[0];
1586
1471
  if (inner === void 0) return false;
1587
- return ts22.isReturnStatement(inner) || ts22.isThrowStatement(inner);
1472
+ return ts21.isReturnStatement(inner) || ts21.isThrowStatement(inner);
1588
1473
  }
1589
1474
  return false;
1590
1475
  }
1591
1476
  function isNumericLiteralValue(node, value) {
1592
- return ts22.isNumericLiteral(node) && node.text === String(value);
1477
+ return ts21.isNumericLiteral(node) && node.text === String(value);
1593
1478
  }
1594
1479
  function isNumericLiteralGte(node, min) {
1595
- return ts22.isNumericLiteral(node) && Number(node.text) >= min;
1480
+ return ts21.isNumericLiteral(node) && Number(node.text) >= min;
1596
1481
  }
1597
1482
  function getIdentifierName(node) {
1598
- if (ts22.isIdentifier(node)) return node.text;
1483
+ if (ts21.isIdentifier(node)) return node.text;
1599
1484
  return null;
1600
1485
  }
1601
1486
  function findVariableInit(id) {
1602
1487
  const sourceFile = id.getSourceFile();
1603
1488
  let result;
1604
1489
  function visit(node) {
1605
- if (ts22.isVariableDeclaration(node) && ts22.isIdentifier(node.name) && node.name.text === id.text && node.initializer) {
1490
+ if (ts21.isVariableDeclaration(node) && ts21.isIdentifier(node.name) && node.name.text === id.text && node.initializer) {
1606
1491
  result = node.initializer;
1607
1492
  }
1608
- if (!result) ts22.forEachChild(node, visit);
1493
+ if (!result) ts21.forEachChild(node, visit);
1609
1494
  }
1610
1495
  visit(sourceFile);
1611
1496
  return result;
1612
1497
  }
1613
1498
 
1614
1499
  // src/rules/ts/no-null-ternary-normalization.ts
1615
- import * as ts23 from "typescript";
1500
+ import * as ts22 from "typescript";
1616
1501
  var noNullTernaryNormalization = {
1617
1502
  kind: "ts",
1618
1503
  id: "no-null-ternary-normalization",
1619
1504
  severity: "warning",
1620
1505
  message: "Ternary null-normalization (x == null ? fallback : x); if the type guarantees non-null, remove the ternary; if not, fix the type upstream",
1621
1506
  visit(node, ctx) {
1622
- if (!ts23.isConditionalExpression(node)) return;
1507
+ if (!ts22.isConditionalExpression(node)) return;
1623
1508
  const test = node.condition;
1624
- if (!ts23.isBinaryExpression(test)) return;
1509
+ if (!ts22.isBinaryExpression(test)) return;
1625
1510
  const op = test.operatorToken.kind;
1626
- if (op !== ts23.SyntaxKind.EqualsEqualsEqualsToken && op !== ts23.SyntaxKind.ExclamationEqualsEqualsToken && op !== ts23.SyntaxKind.EqualsEqualsToken && op !== ts23.SyntaxKind.ExclamationEqualsToken) return;
1511
+ if (op !== ts22.SyntaxKind.EqualsEqualsEqualsToken && op !== ts22.SyntaxKind.ExclamationEqualsEqualsToken && op !== ts22.SyntaxKind.EqualsEqualsToken && op !== ts22.SyntaxKind.ExclamationEqualsToken) return;
1627
1512
  const hasNullishComparand = isNullish(test.left) || isNullish(test.right);
1628
1513
  if (!hasNullishComparand) return;
1629
1514
  if (isNullish(node.whenTrue) || isNullish(node.whenFalse)) {
@@ -1637,35 +1522,35 @@ var noNullTernaryNormalization = {
1637
1522
  }
1638
1523
  };
1639
1524
  function isNullish(node) {
1640
- if (node.kind === ts23.SyntaxKind.NullKeyword) return true;
1641
- if (ts23.isIdentifier(node) && node.text === "undefined") return true;
1642
- if (ts23.isVoidExpression(node)) return true;
1525
+ if (node.kind === ts22.SyntaxKind.NullKeyword) return true;
1526
+ if (ts22.isIdentifier(node) && node.text === "undefined") return true;
1527
+ if (ts22.isVoidExpression(node)) return true;
1643
1528
  return false;
1644
1529
  }
1645
1530
 
1646
1531
  // src/rules/ts/no-nullish-coalescing.ts
1647
- import * as ts24 from "typescript";
1532
+ import * as ts23 from "typescript";
1648
1533
  var noNullishCoalescing = {
1649
1534
  kind: "ts",
1650
1535
  id: "no-nullish-coalescing",
1651
1536
  severity: "warning",
1652
1537
  message: "Nullish coalescing (??) on a non-nullable type is unreachable; remove the fallback or fix the type upstream",
1653
1538
  visit(node, ctx) {
1654
- if (!ts24.isBinaryExpression(node)) return;
1655
- if (node.operatorToken.kind !== ts24.SyntaxKind.QuestionQuestionToken) return;
1539
+ if (!ts23.isBinaryExpression(node)) return;
1540
+ if (node.operatorToken.kind !== ts23.SyntaxKind.QuestionQuestionToken) return;
1656
1541
  if (isPossiblyMissingArrayBindingValue(node.left, ctx)) return;
1657
1542
  if (ctx.isNullable(node.left)) return;
1658
1543
  ctx.report(node);
1659
1544
  }
1660
1545
  };
1661
1546
  function isPossiblyMissingArrayBindingValue(node, ctx) {
1662
- if (!ts24.isIdentifier(node)) return false;
1547
+ if (!ts23.isIdentifier(node)) return false;
1663
1548
  const symbol = ctx.checker.getSymbolAtLocation(node);
1664
1549
  if (!symbol) return false;
1665
1550
  for (const declaration of symbol.declarations ?? []) {
1666
- if (!ts24.isBindingElement(declaration)) continue;
1551
+ if (!ts23.isBindingElement(declaration)) continue;
1667
1552
  if (declaration.initializer || declaration.dotDotDotToken) continue;
1668
- if (!ts24.isArrayBindingPattern(declaration.parent)) continue;
1553
+ if (!ts23.isArrayBindingPattern(declaration.parent)) continue;
1669
1554
  const pattern = declaration.parent;
1670
1555
  const index = pattern.elements.indexOf(declaration);
1671
1556
  if (index < 0) continue;
@@ -1693,14 +1578,14 @@ function isTupleTypeReference(type, checker) {
1693
1578
  }
1694
1579
 
1695
1580
  // src/rules/ts/no-optional-call.ts
1696
- import * as ts25 from "typescript";
1581
+ import * as ts24 from "typescript";
1697
1582
  var noOptionalCall = {
1698
1583
  kind: "ts",
1699
1584
  id: "no-optional-call",
1700
1585
  severity: "warning",
1701
1586
  message: "Optional call (?.) on a non-nullable function is redundant; call directly or fix the type upstream",
1702
1587
  visit(node, ctx) {
1703
- if (!ts25.isCallExpression(node)) return;
1588
+ if (!ts24.isCallExpression(node)) return;
1704
1589
  if (!node.questionDotToken) return;
1705
1590
  if (ctx.isNullable(node.expression)) return;
1706
1591
  ctx.report(node);
@@ -1708,14 +1593,14 @@ var noOptionalCall = {
1708
1593
  };
1709
1594
 
1710
1595
  // src/rules/ts/no-optional-element-access.ts
1711
- import * as ts26 from "typescript";
1596
+ import * as ts25 from "typescript";
1712
1597
  var noOptionalElementAccess = {
1713
1598
  kind: "ts",
1714
1599
  id: "no-optional-element-access",
1715
1600
  severity: "warning",
1716
1601
  message: "Optional element access (?.[]) on a non-nullable type is redundant; use direct access or fix the type upstream",
1717
1602
  visit(node, ctx) {
1718
- if (!ts26.isElementAccessExpression(node)) return;
1603
+ if (!ts25.isElementAccessExpression(node)) return;
1719
1604
  if (!node.questionDotToken) return;
1720
1605
  if (ctx.isNullable(node.expression)) return;
1721
1606
  ctx.report(node);
@@ -1723,14 +1608,14 @@ var noOptionalElementAccess = {
1723
1608
  };
1724
1609
 
1725
1610
  // src/rules/ts/no-optional-property-access.ts
1726
- import * as ts27 from "typescript";
1611
+ import * as ts26 from "typescript";
1727
1612
  var noOptionalPropertyAccess = {
1728
1613
  kind: "ts",
1729
1614
  id: "no-optional-property-access",
1730
1615
  severity: "warning",
1731
1616
  message: "Optional chaining (?.) on a non-nullable type is redundant; use direct access or fix the type upstream",
1732
1617
  visit(node, ctx) {
1733
- if (!ts27.isPropertyAccessExpression(node)) return;
1618
+ if (!ts26.isPropertyAccessExpression(node)) return;
1734
1619
  if (!node.questionDotToken) return;
1735
1620
  if (ctx.isNullable(node.expression)) return;
1736
1621
  ctx.report(node);
@@ -1738,27 +1623,27 @@ var noOptionalPropertyAccess = {
1738
1623
  };
1739
1624
 
1740
1625
  // src/rules/ts/no-redundant-existence-guard.ts
1741
- import * as ts28 from "typescript";
1626
+ import * as ts27 from "typescript";
1742
1627
  var noRedundantExistenceGuard = {
1743
1628
  kind: "ts",
1744
1629
  id: "no-redundant-existence-guard",
1745
1630
  severity: "warning",
1746
1631
  message: "Redundant existence guard (obj && obj.prop) on a non-nullable type; remove the guard or fix the type upstream",
1747
1632
  visit(node, ctx) {
1748
- if (!ts28.isBinaryExpression(node)) return;
1749
- if (node.operatorToken.kind !== ts28.SyntaxKind.AmpersandAmpersandToken) return;
1633
+ if (!ts27.isBinaryExpression(node)) return;
1634
+ if (node.operatorToken.kind !== ts27.SyntaxKind.AmpersandAmpersandToken) return;
1750
1635
  const left = node.left;
1751
1636
  const right = node.right;
1752
- if (ts28.isIdentifier(left) && accessesIdentifier(right, left.text)) {
1637
+ if (ts27.isIdentifier(left) && accessesIdentifier(right, left.text)) {
1753
1638
  if (ctx.isNullable(left)) return;
1754
1639
  ctx.report(node);
1755
1640
  return;
1756
1641
  }
1757
- if (ts28.isBinaryExpression(left) && isNullCheck(left)) {
1642
+ if (ts27.isBinaryExpression(left) && isNullCheck(left)) {
1758
1643
  const checked = getNullCheckedIdentifier(left);
1759
1644
  if (checked && accessesIdentifier(right, checked)) {
1760
- const identNode = ts28.isIdentifier(left.left) ? left.left : left.right;
1761
- if (ts28.isIdentifier(identNode) && identNode.text === checked && !ctx.isNullable(identNode)) {
1645
+ const identNode = ts27.isIdentifier(left.left) ? left.left : left.right;
1646
+ if (ts27.isIdentifier(identNode) && identNode.text === checked && !ctx.isNullable(identNode)) {
1762
1647
  ctx.report(node);
1763
1648
  }
1764
1649
  }
@@ -1767,34 +1652,34 @@ var noRedundantExistenceGuard = {
1767
1652
  };
1768
1653
  function accessesIdentifier(expr, name) {
1769
1654
  const root = getExpressionRoot(expr);
1770
- return ts28.isIdentifier(root) && root.text === name;
1655
+ return ts27.isIdentifier(root) && root.text === name;
1771
1656
  }
1772
1657
  function getExpressionRoot(node) {
1773
- if (ts28.isPropertyAccessExpression(node)) return getExpressionRoot(node.expression);
1774
- if (ts28.isElementAccessExpression(node)) return getExpressionRoot(node.expression);
1775
- if (ts28.isCallExpression(node)) return getExpressionRoot(node.expression);
1658
+ if (ts27.isPropertyAccessExpression(node)) return getExpressionRoot(node.expression);
1659
+ if (ts27.isElementAccessExpression(node)) return getExpressionRoot(node.expression);
1660
+ if (ts27.isCallExpression(node)) return getExpressionRoot(node.expression);
1776
1661
  return node;
1777
1662
  }
1778
1663
  function isNullCheck(expr) {
1779
1664
  const op = expr.operatorToken.kind;
1780
- if (op !== ts28.SyntaxKind.ExclamationEqualsToken && op !== ts28.SyntaxKind.ExclamationEqualsEqualsToken) return false;
1665
+ if (op !== ts27.SyntaxKind.ExclamationEqualsToken && op !== ts27.SyntaxKind.ExclamationEqualsEqualsToken) return false;
1781
1666
  return isNullishLiteral(expr.right) || isNullishLiteral(expr.left);
1782
1667
  }
1783
1668
  function getNullCheckedIdentifier(expr) {
1784
- if (ts28.isIdentifier(expr.left) && isNullishLiteral(expr.right)) return expr.left.text;
1785
- if (ts28.isIdentifier(expr.right) && isNullishLiteral(expr.left)) return expr.right.text;
1669
+ if (ts27.isIdentifier(expr.left) && isNullishLiteral(expr.right)) return expr.left.text;
1670
+ if (ts27.isIdentifier(expr.right) && isNullishLiteral(expr.left)) return expr.right.text;
1786
1671
  return null;
1787
1672
  }
1788
1673
 
1789
1674
  // src/rules/ts/no-ts-ignore.ts
1790
- import * as ts29 from "typescript";
1675
+ import * as ts28 from "typescript";
1791
1676
  var noTsIgnore = {
1792
1677
  kind: "ts",
1793
1678
  id: "no-ts-ignore",
1794
1679
  severity: "error",
1795
1680
  message: "@ts-ignore / @ts-expect-error suppresses type checking; fix the underlying type issue",
1796
1681
  visit(node, ctx) {
1797
- const ranges = ts29.getLeadingCommentRanges(ctx.source, node.getFullStart());
1682
+ const ranges = ts28.getLeadingCommentRanges(ctx.source, node.getFullStart());
1798
1683
  if (!ranges) return;
1799
1684
  for (const range of ranges) {
1800
1685
  const text = ctx.source.slice(range.pos, range.end);
@@ -1806,22 +1691,22 @@ var noTsIgnore = {
1806
1691
  };
1807
1692
 
1808
1693
  // src/rules/ts/no-type-assertion.ts
1809
- import * as ts30 from "typescript";
1694
+ import * as ts29 from "typescript";
1810
1695
  var noTypeAssertion = {
1811
1696
  kind: "ts",
1812
1697
  id: "no-type-assertion",
1813
1698
  severity: "error",
1814
1699
  message: "Double type assertion (`as unknown as T`) circumvents the type system; fix the upstream type or use a type guard",
1815
1700
  visit(node, ctx) {
1816
- if (!ts30.isAsExpression(node)) return;
1817
- if (!ts30.isAsExpression(node.expression)) return;
1818
- if (node.expression.type.kind !== ts30.SyntaxKind.UnknownKeyword) return;
1701
+ if (!ts29.isAsExpression(node)) return;
1702
+ if (!ts29.isAsExpression(node.expression)) return;
1703
+ if (node.expression.type.kind !== ts29.SyntaxKind.UnknownKeyword) return;
1819
1704
  ctx.report(node);
1820
1705
  }
1821
1706
  };
1822
1707
 
1823
1708
  // src/rules/ts/prefer-default-param-value.ts
1824
- import * as ts31 from "typescript";
1709
+ import * as ts30 from "typescript";
1825
1710
  var preferDefaultParamValue = {
1826
1711
  kind: "ts",
1827
1712
  id: "prefer-default-param-value",
@@ -1831,21 +1716,21 @@ var preferDefaultParamValue = {
1831
1716
  const result = getFirstFunctionStatement(node);
1832
1717
  if (result === null) return;
1833
1718
  const { firstStmt, fn } = result;
1834
- if (!ts31.isExpressionStatement(firstStmt)) return;
1719
+ if (!ts30.isExpressionStatement(firstStmt)) return;
1835
1720
  const expr = firstStmt.expression;
1836
- if (!ts31.isBinaryExpression(expr) || expr.operatorToken.kind !== ts31.SyntaxKind.EqualsToken) return;
1721
+ if (!ts30.isBinaryExpression(expr) || expr.operatorToken.kind !== ts30.SyntaxKind.EqualsToken) return;
1837
1722
  const right = expr.right;
1838
- if (!ts31.isBinaryExpression(right) || right.operatorToken.kind !== ts31.SyntaxKind.QuestionQuestionToken) return;
1839
- if (!ts31.isIdentifier(expr.left) || !ts31.isIdentifier(right.left)) return;
1723
+ if (!ts30.isBinaryExpression(right) || right.operatorToken.kind !== ts30.SyntaxKind.QuestionQuestionToken) return;
1724
+ if (!ts30.isIdentifier(expr.left) || !ts30.isIdentifier(right.left)) return;
1840
1725
  if (expr.left.text !== right.left.text) return;
1841
1726
  const paramName = expr.left.text;
1842
- const isParam = fn.parameters.some((p) => ts31.isIdentifier(p.name) && p.name.text === paramName);
1727
+ const isParam = fn.parameters.some((p) => ts30.isIdentifier(p.name) && p.name.text === paramName);
1843
1728
  if (isParam) ctx.report(firstStmt);
1844
1729
  }
1845
1730
  };
1846
1731
 
1847
1732
  // src/rules/ts/prefer-required-param-with-guard.ts
1848
- import * as ts32 from "typescript";
1733
+ import * as ts31 from "typescript";
1849
1734
  var preferRequiredParamWithGuard = {
1850
1735
  kind: "ts",
1851
1736
  id: "prefer-required-param-with-guard",
@@ -1855,26 +1740,26 @@ var preferRequiredParamWithGuard = {
1855
1740
  const result = getFirstFunctionStatement(node);
1856
1741
  if (result === null) return;
1857
1742
  const { firstStmt, fn } = result;
1858
- if (!ts32.isIfStatement(firstStmt)) return;
1743
+ if (!ts31.isIfStatement(firstStmt)) return;
1859
1744
  const test = firstStmt.expression;
1860
1745
  let guardedName = null;
1861
- if (ts32.isPrefixUnaryExpression(test) && test.operator === ts32.SyntaxKind.ExclamationToken) {
1862
- if (ts32.isIdentifier(test.operand)) guardedName = test.operand.text;
1746
+ if (ts31.isPrefixUnaryExpression(test) && test.operator === ts31.SyntaxKind.ExclamationToken) {
1747
+ if (ts31.isIdentifier(test.operand)) guardedName = test.operand.text;
1863
1748
  }
1864
- if (ts32.isBinaryExpression(test)) {
1749
+ if (ts31.isBinaryExpression(test)) {
1865
1750
  const op = test.operatorToken.kind;
1866
- if (op === ts32.SyntaxKind.EqualsEqualsEqualsToken || op === ts32.SyntaxKind.EqualsEqualsToken) {
1867
- if (ts32.isIdentifier(test.left) && ts32.isIdentifier(test.right) && test.right.text === "undefined") {
1751
+ if (op === ts31.SyntaxKind.EqualsEqualsEqualsToken || op === ts31.SyntaxKind.EqualsEqualsToken) {
1752
+ if (ts31.isIdentifier(test.left) && ts31.isIdentifier(test.right) && test.right.text === "undefined") {
1868
1753
  guardedName = test.left.text;
1869
1754
  }
1870
1755
  }
1871
1756
  }
1872
1757
  if (!guardedName) return;
1873
1758
  const consequent = firstStmt.thenStatement;
1874
- const isGuard = ts32.isReturnStatement(consequent) || ts32.isThrowStatement(consequent) || ts32.isBlock(consequent) && consequent.statements.length === 1 && consequent.statements[0] !== void 0 && (ts32.isReturnStatement(consequent.statements[0]) || ts32.isThrowStatement(consequent.statements[0]));
1759
+ const isGuard = ts31.isReturnStatement(consequent) || ts31.isThrowStatement(consequent) || ts31.isBlock(consequent) && consequent.statements.length === 1 && consequent.statements[0] !== void 0 && (ts31.isReturnStatement(consequent.statements[0]) || ts31.isThrowStatement(consequent.statements[0]));
1875
1760
  if (!isGuard) return;
1876
1761
  const isOptional = fn.parameters.some(
1877
- (p) => ts32.isIdentifier(p.name) && p.name.text === guardedName && p.questionToken !== void 0
1762
+ (p) => ts31.isIdentifier(p.name) && p.name.text === guardedName && p.questionToken !== void 0
1878
1763
  );
1879
1764
  if (isOptional) ctx.report(firstStmt);
1880
1765
  }
@@ -1901,7 +1786,6 @@ var allRules = [
1901
1786
  preferDefaultParamValue,
1902
1787
  preferRequiredParamWithGuard,
1903
1788
  noInlineParamType,
1904
- noModuleStateWrite,
1905
1789
  duplicateTypeDeclaration,
1906
1790
  duplicateFunctionDeclaration,
1907
1791
  optionalArgAlwaysUsed,
@@ -1944,7 +1828,6 @@ var ruleMetadata = {
1944
1828
  "prefer-default-param-value": { category: "interface-design", tags: ["api"] },
1945
1829
  "prefer-required-param-with-guard": { category: "interface-design", tags: ["api"] },
1946
1830
  "no-inline-param-type": { category: "interface-design", tags: ["api"] },
1947
- "no-module-state-write": { category: "state-management", tags: ["state"] },
1948
1831
  "duplicate-type-declaration": { category: "cross-file", tags: ["duplicate"] },
1949
1832
  "duplicate-type-name": { category: "cross-file", tags: ["duplicate"] },
1950
1833
  "duplicate-function-declaration": { category: "cross-file", tags: ["duplicate"] },
@@ -2021,32 +1904,36 @@ function isFailOn(value) {
2021
1904
  }
2022
1905
 
2023
1906
  // src/collect/index.ts
2024
- import * as ts35 from "typescript";
1907
+ import * as ts34 from "typescript";
2025
1908
  import { createHash as createHash2 } from "crypto";
2026
1909
 
1910
+ // src/collect/type-registry.ts
1911
+ import { existsSync } from "fs";
1912
+ import { dirname as dirname2, join } from "path";
1913
+
2027
1914
  // src/utils/hash.ts
2028
1915
  import { createHash } from "crypto";
2029
- import * as ts33 from "typescript";
1916
+ import * as ts32 from "typescript";
2030
1917
  function hashTypeShape(node, sourceFile) {
2031
1918
  const normalized = normalizeTypeNode(node, sourceFile);
2032
1919
  return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
2033
1920
  }
2034
1921
  function normalizeTypeNode(node, sourceFile) {
2035
- if (ts33.isTypeLiteralNode(node)) {
1922
+ if (ts32.isTypeLiteralNode(node)) {
2036
1923
  const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
2037
1924
  return `{${normalized}}`;
2038
1925
  }
2039
- if (ts33.isInterfaceDeclaration(node)) {
1926
+ if (ts32.isInterfaceDeclaration(node)) {
2040
1927
  const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
2041
1928
  return `{${normalized}}`;
2042
1929
  }
2043
- if (ts33.isPropertySignature(node)) {
1930
+ if (ts32.isPropertySignature(node)) {
2044
1931
  const keyName = node.name.getText(sourceFile);
2045
1932
  const optional = node.questionToken ? "?" : "";
2046
1933
  const type = node.type ? normalizeTypeNode(node.type, sourceFile) : "any";
2047
1934
  return `${keyName}${optional}:${type}`;
2048
1935
  }
2049
- if (ts33.isTypeAliasDeclaration(node)) {
1936
+ if (ts32.isTypeAliasDeclaration(node)) {
2050
1937
  return normalizeTypeNode(node.type, sourceFile);
2051
1938
  }
2052
1939
  return node.getText(sourceFile).replace(/\s+/g, " ").trim();
@@ -2149,22 +2036,42 @@ var TypeRegistry = class extends BaseRegistry {
2149
2036
  }
2150
2037
  };
2151
2038
  function getExportedNameCollisions(entries) {
2152
- const byName = /* @__PURE__ */ new Map();
2039
+ const byNameAndPackage = /* @__PURE__ */ new Map();
2153
2040
  for (const entry of entries) {
2154
2041
  if (!entry.exported) continue;
2155
- let list = byName.get(entry.name);
2042
+ const pkg = findPackageRoot(entry.file);
2043
+ const key = `${pkg}\0${entry.name}`;
2044
+ let list = byNameAndPackage.get(key);
2156
2045
  if (list === void 0) {
2157
2046
  list = [];
2158
- byName.set(entry.name, list);
2047
+ byNameAndPackage.set(key, list);
2159
2048
  }
2160
2049
  list.push(entry);
2161
2050
  }
2162
- return [...byName.values()].filter((group) => {
2051
+ return [...byNameAndPackage.values()].filter((group) => {
2163
2052
  if (group.length < 2) return false;
2164
2053
  const files = new Set(group.map((e) => e.file));
2165
2054
  return files.size > 1;
2166
2055
  });
2167
2056
  }
2057
+ var packageRootCache = /* @__PURE__ */ new Map();
2058
+ function findPackageRoot(filePath) {
2059
+ let dir = dirname2(filePath);
2060
+ const cached = packageRootCache.get(dir);
2061
+ if (cached !== void 0) return cached;
2062
+ const startDir = dir;
2063
+ const visited = [dir];
2064
+ while (dir !== dirname2(dir)) {
2065
+ if (existsSync(join(dir, "package.json"))) {
2066
+ for (const d of visited) packageRootCache.set(d, dir);
2067
+ return dir;
2068
+ }
2069
+ dir = dirname2(dir);
2070
+ visited.push(dir);
2071
+ }
2072
+ for (const d of visited) packageRootCache.set(d, dir);
2073
+ return dir;
2074
+ }
2168
2075
 
2169
2076
  // src/collect/function-registry.ts
2170
2077
  var FunctionRegistry = class extends DualHashRegistry {
@@ -2212,7 +2119,7 @@ var InlineParamTypeRegistry = class extends BaseRegistry {
2212
2119
  };
2213
2120
 
2214
2121
  // src/typecheck/walk.ts
2215
- import * as ts34 from "typescript";
2122
+ import * as ts33 from "typescript";
2216
2123
  function buildContext(rule, sourceFile, checker, source, filename, diagnostics) {
2217
2124
  return {
2218
2125
  filename,
@@ -2220,7 +2127,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
2220
2127
  sourceFile,
2221
2128
  checker,
2222
2129
  report(node, message) {
2223
- const { line, character } = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
2130
+ const { line, character } = ts33.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
2224
2131
  diagnostics.push({
2225
2132
  ruleId: rule.id,
2226
2133
  severity: rule.severity,
@@ -2231,7 +2138,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
2231
2138
  });
2232
2139
  },
2233
2140
  reportAtOffset(offset, message) {
2234
- const { line, character } = ts34.getLineAndCharacterOfPosition(sourceFile, offset);
2141
+ const { line, character } = ts33.getLineAndCharacterOfPosition(sourceFile, offset);
2235
2142
  diagnostics.push({
2236
2143
  ruleId: rule.id,
2237
2144
  severity: rule.severity,
@@ -2282,21 +2189,23 @@ function collectProject(program, tsRules, allowedFiles) {
2282
2189
  rule.visit(node, ctx);
2283
2190
  }
2284
2191
  }
2285
- ts35.forEachChild(node, visit2);
2192
+ ts34.forEachChild(node, visit2);
2286
2193
  };
2287
2194
  var visit = visit2;
2288
2195
  const file = sourceFile.fileName;
2289
2196
  if (sourceFile.isDeclarationFile) continue;
2290
2197
  if (file.includes("node_modules")) continue;
2291
- if (allowedFiles && !allowedFiles.has(file)) continue;
2198
+ const isReportable = !allowedFiles || allowedFiles.has(file);
2292
2199
  const source = sourceFile.getFullText();
2293
- const comments = collectAllComments(sourceFile);
2294
- fileMap.set(file, { source, sourceFile, comments });
2295
- const ruleContexts = tsRules?.map((rule) => ({
2200
+ if (isReportable) {
2201
+ const comments = collectAllComments(sourceFile);
2202
+ fileMap.set(file, { source, sourceFile, comments });
2203
+ }
2204
+ const ruleContexts = isReportable ? tsRules?.map((rule) => ({
2296
2205
  rule,
2297
2206
  ctx: buildContext(rule, sourceFile, checker, source, file, diagnostics)
2298
- }));
2299
- ts35.forEachChild(sourceFile, visit2);
2207
+ })) : void 0;
2208
+ ts34.forEachChild(sourceFile, visit2);
2300
2209
  }
2301
2210
  const fileHashes = /* @__PURE__ */ new Map();
2302
2211
  for (const [file, { source }] of fileMap) {
@@ -2312,52 +2221,52 @@ function collectProject(program, tsRules, allowedFiles) {
2312
2221
  return { index: { types, functions, constants, callSites, imports, files: fileMap, fileHashes, statementSequences, inlineParamTypes }, diagnostics };
2313
2222
  }
2314
2223
  function hasNonPublicModifier(node) {
2315
- if (!ts35.canHaveModifiers(node)) return false;
2316
- const mods = ts35.getModifiers(node);
2224
+ if (!ts34.canHaveModifiers(node)) return false;
2225
+ const mods = ts34.getModifiers(node);
2317
2226
  if (mods === void 0) return false;
2318
2227
  return mods.some(
2319
- (m) => m.kind === ts35.SyntaxKind.PrivateKeyword || m.kind === ts35.SyntaxKind.ProtectedKeyword
2228
+ (m) => m.kind === ts34.SyntaxKind.PrivateKeyword || m.kind === ts34.SyntaxKind.ProtectedKeyword
2320
2229
  );
2321
2230
  }
2322
2231
  function isExported(node) {
2323
- if (!ts35.canHaveModifiers(node)) return false;
2324
- const mods = ts35.getModifiers(node);
2232
+ if (!ts34.canHaveModifiers(node)) return false;
2233
+ const mods = ts34.getModifiers(node);
2325
2234
  if (mods === void 0) return false;
2326
- return mods.some((m) => m.kind === ts35.SyntaxKind.ExportKeyword);
2235
+ return mods.some((m) => m.kind === ts34.SyntaxKind.ExportKeyword);
2327
2236
  }
2328
2237
  function collectTypes(node, file, sourceFile, registry) {
2329
- if (ts35.isTypeAliasDeclaration(node)) {
2330
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2238
+ if (ts34.isTypeAliasDeclaration(node)) {
2239
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2331
2240
  registry.add(node.name.text, file, line, node.type, sourceFile, isExported(node));
2332
2241
  }
2333
- if (ts35.isInterfaceDeclaration(node)) {
2334
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2242
+ if (ts34.isInterfaceDeclaration(node)) {
2243
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2335
2244
  registry.add(node.name.text, file, line, node, sourceFile, isExported(node));
2336
2245
  }
2337
2246
  }
2338
2247
  function isConstantValue(node) {
2339
- if (ts35.isStringLiteral(node)) return true;
2340
- if (ts35.isNumericLiteral(node)) return true;
2341
- if (ts35.isNoSubstitutionTemplateLiteral(node)) return true;
2342
- if (ts35.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
2343
- if (ts35.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
2248
+ if (ts34.isStringLiteral(node)) return true;
2249
+ if (ts34.isNumericLiteral(node)) return true;
2250
+ if (ts34.isNoSubstitutionTemplateLiteral(node)) return true;
2251
+ if (ts34.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
2252
+ if (ts34.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
2344
2253
  return false;
2345
2254
  }
2346
2255
  function collectConstants(node, file, sourceFile, registry) {
2347
- if (!ts35.isVariableStatement(node)) return;
2348
- if (!(node.declarationList.flags & ts35.NodeFlags.Const)) return;
2256
+ if (!ts34.isVariableStatement(node)) return;
2257
+ if (!(node.declarationList.flags & ts34.NodeFlags.Const)) return;
2349
2258
  const exported = isExported(node);
2350
2259
  for (const decl of node.declarationList.declarations) {
2351
- if (!decl.initializer || !ts35.isIdentifier(decl.name)) continue;
2260
+ if (!decl.initializer || !ts34.isIdentifier(decl.name)) continue;
2352
2261
  if (!isConstantValue(decl.initializer)) continue;
2353
2262
  const valueText = decl.initializer.getText(sourceFile).replace(/\s+/g, " ").trim();
2354
2263
  const valueHash = createHash2("sha256").update(valueText).digest("hex").slice(0, 16);
2355
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
2264
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
2356
2265
  registry.add({ name: decl.name.text, file, line, valueHash, valueText, exported });
2357
2266
  }
2358
2267
  }
2359
2268
  function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name, extra) {
2360
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
2269
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
2361
2270
  const params = extractParams(parameters, sourceFile);
2362
2271
  const paramNames = params.map((p) => p.name);
2363
2272
  const hash = hashFunctionBody(body, sourceFile);
@@ -2379,17 +2288,17 @@ function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name,
2379
2288
  };
2380
2289
  }
2381
2290
  function collectFunctions(node, file, sourceFile, checker, registry) {
2382
- if (ts35.isFunctionDeclaration(node) && node.name && node.body) {
2291
+ if (ts34.isFunctionDeclaration(node) && node.name && node.body) {
2383
2292
  registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, node.name.text, {
2384
2293
  exported: isExported(node),
2385
2294
  symbol: checker.getSymbolAtLocation(node.name),
2386
2295
  node
2387
2296
  }));
2388
2297
  }
2389
- if (ts35.isVariableStatement(node)) {
2298
+ if (ts34.isVariableStatement(node)) {
2390
2299
  const exported = isExported(node);
2391
2300
  for (const decl of node.declarationList.declarations) {
2392
- if (decl.initializer && ts35.isArrowFunction(decl.initializer) && ts35.isIdentifier(decl.name)) {
2301
+ if (decl.initializer && ts34.isArrowFunction(decl.initializer) && ts34.isIdentifier(decl.name)) {
2393
2302
  const arrow = decl.initializer;
2394
2303
  registry.add(buildFunctionEntry(arrow.body, arrow.parameters, sourceFile, file, decl, decl.name.text, {
2395
2304
  exported,
@@ -2397,7 +2306,7 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
2397
2306
  node: arrow
2398
2307
  }));
2399
2308
  }
2400
- if (decl.initializer && ts35.isFunctionExpression(decl.initializer) && ts35.isIdentifier(decl.name)) {
2309
+ if (decl.initializer && ts34.isFunctionExpression(decl.initializer) && ts34.isIdentifier(decl.name)) {
2401
2310
  const fn = decl.initializer;
2402
2311
  if (fn.body) {
2403
2312
  registry.add(buildFunctionEntry(fn.body, fn.parameters, sourceFile, file, decl, decl.name.text, {
@@ -2409,22 +2318,22 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
2409
2318
  }
2410
2319
  }
2411
2320
  }
2412
- if (ts35.isPropertyAssignment(node) && (ts35.isIdentifier(node.name) || ts35.isStringLiteral(node.name))) {
2321
+ if (ts34.isPropertyAssignment(node) && (ts34.isIdentifier(node.name) || ts34.isStringLiteral(node.name))) {
2413
2322
  const init = node.initializer;
2414
2323
  const propName = node.name.text;
2415
- if (ts35.isArrowFunction(init)) {
2324
+ if (ts34.isArrowFunction(init)) {
2416
2325
  registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
2417
2326
  }
2418
- if (ts35.isFunctionExpression(init) && init.body) {
2327
+ if (ts34.isFunctionExpression(init) && init.body) {
2419
2328
  registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
2420
2329
  }
2421
2330
  }
2422
- if (ts35.isArrowFunction(node) || ts35.isFunctionExpression(node)) {
2331
+ if (ts34.isArrowFunction(node) || ts34.isFunctionExpression(node)) {
2423
2332
  const parent = node.parent;
2424
- if (ts35.isVariableDeclaration(parent) && ts35.isIdentifier(parent.name)) {
2425
- } else if (ts35.isPropertyAssignment(parent)) {
2333
+ if (ts34.isVariableDeclaration(parent) && ts34.isIdentifier(parent.name)) {
2334
+ } else if (ts34.isPropertyAssignment(parent)) {
2426
2335
  } else {
2427
- const body = ts35.isArrowFunction(node) ? node.body : node.body;
2336
+ const body = ts34.isArrowFunction(node) ? node.body : node.body;
2428
2337
  if (body) {
2429
2338
  const MIN_ANON_BODY = 64;
2430
2339
  if (bodyTextLength(body, sourceFile) >= MIN_ANON_BODY) {
@@ -2434,14 +2343,14 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
2434
2343
  }
2435
2344
  }
2436
2345
  }
2437
- if (ts35.isMethodDeclaration(node) && node.body && ts35.isIdentifier(node.name)) {
2346
+ if (ts34.isMethodDeclaration(node) && node.body && ts34.isIdentifier(node.name)) {
2438
2347
  const parent = node.parent;
2439
- if (ts35.isClassDeclaration(parent)) {
2348
+ if (ts34.isClassDeclaration(parent)) {
2440
2349
  if (hasNonPublicModifier(node)) return;
2441
2350
  const className = parent.name ? parent.name.text : "<anonymous>";
2442
2351
  const name = `${className}.${node.name.text}`;
2443
2352
  const implementsInterface = parent.heritageClauses?.some(
2444
- (c) => c.token === ts35.SyntaxKind.ImplementsKeyword
2353
+ (c) => c.token === ts34.SyntaxKind.ImplementsKeyword
2445
2354
  ) ?? false;
2446
2355
  registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, name, {
2447
2356
  exported: isExported(parent),
@@ -2464,16 +2373,16 @@ function extractParams(parameters, sourceFile) {
2464
2373
  }
2465
2374
  function deriveAnonymousName(node, sourceFile) {
2466
2375
  const parent = node.parent;
2467
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2468
- if (ts35.isCallExpression(parent)) {
2376
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2377
+ if (ts34.isCallExpression(parent)) {
2469
2378
  const grandparent = parent.parent;
2470
- if (ts35.isPropertyAssignment(grandparent) && ts35.isIdentifier(grandparent.name)) {
2379
+ if (ts34.isPropertyAssignment(grandparent) && ts34.isIdentifier(grandparent.name)) {
2471
2380
  return grandparent.name.text;
2472
2381
  }
2473
2382
  let calleeName = null;
2474
- if (ts35.isIdentifier(parent.expression)) {
2383
+ if (ts34.isIdentifier(parent.expression)) {
2475
2384
  calleeName = parent.expression.text;
2476
- } else if (ts35.isPropertyAccessExpression(parent.expression)) {
2385
+ } else if (ts34.isPropertyAccessExpression(parent.expression)) {
2477
2386
  calleeName = parent.expression.name.text;
2478
2387
  }
2479
2388
  if (calleeName) {
@@ -2484,7 +2393,7 @@ function deriveAnonymousName(node, sourceFile) {
2484
2393
  return `<anonymous>:${line}`;
2485
2394
  }
2486
2395
  function collectImports(node, file, imports) {
2487
- if (ts35.isImportDeclaration(node) && ts35.isStringLiteral(node.moduleSpecifier)) {
2396
+ if (ts34.isImportDeclaration(node) && ts34.isStringLiteral(node.moduleSpecifier)) {
2488
2397
  const moduleSource = node.moduleSpecifier.text;
2489
2398
  const clause = node.importClause;
2490
2399
  if (!clause) return;
@@ -2496,7 +2405,7 @@ function collectImports(node, file, imports) {
2496
2405
  source: moduleSource
2497
2406
  });
2498
2407
  }
2499
- if (clause.namedBindings && ts35.isNamedImports(clause.namedBindings)) {
2408
+ if (clause.namedBindings && ts34.isNamedImports(clause.namedBindings)) {
2500
2409
  for (const el of clause.namedBindings.elements) {
2501
2410
  imports.push({
2502
2411
  file,
@@ -2507,9 +2416,9 @@ function collectImports(node, file, imports) {
2507
2416
  }
2508
2417
  }
2509
2418
  }
2510
- if (ts35.isExportDeclaration(node) && node.moduleSpecifier && ts35.isStringLiteral(node.moduleSpecifier)) {
2419
+ if (ts34.isExportDeclaration(node) && node.moduleSpecifier && ts34.isStringLiteral(node.moduleSpecifier)) {
2511
2420
  const moduleSource = node.moduleSpecifier.text;
2512
- if (node.exportClause && ts35.isNamedExports(node.exportClause)) {
2421
+ if (node.exportClause && ts34.isNamedExports(node.exportClause)) {
2513
2422
  for (const el of node.exportClause.elements) {
2514
2423
  imports.push({
2515
2424
  file,
@@ -2522,7 +2431,7 @@ function collectImports(node, file, imports) {
2522
2431
  }
2523
2432
  }
2524
2433
  function collectStatementSequences(node, file, sourceFile, registry) {
2525
- if (!ts35.isBlock(node)) return;
2434
+ if (!ts34.isBlock(node)) return;
2526
2435
  const stmts = node.statements;
2527
2436
  const n = stmts.length;
2528
2437
  if (n < 3) return;
@@ -2539,32 +2448,32 @@ function collectStatementSequences(node, file, sourceFile, registry) {
2539
2448
  const firstStmt = window[0];
2540
2449
  const lastStmt = window[window.length - 1];
2541
2450
  if (firstStmt === void 0 || lastStmt === void 0) continue;
2542
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
2543
- const endLine = ts35.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
2451
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
2452
+ const endLine = ts34.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
2544
2453
  registry.add({ file, line, endLine, hash, normalizedHash, statementCount: size, normalizedBodyLength: normalized.length });
2545
2454
  }
2546
2455
  }
2547
2456
  }
2548
2457
  function collectInlineParamTypes(node, file, sourceFile, registry) {
2549
2458
  if (!isInlineParamType(node)) return;
2550
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2459
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2551
2460
  registry.add(file, line, node, sourceFile);
2552
2461
  }
2553
2462
  function collectCallSites(node, file, sourceFile, checker, sites) {
2554
- if (!ts35.isCallExpression(node)) return;
2463
+ if (!ts34.isCallExpression(node)) return;
2555
2464
  let calleeName = null;
2556
- if (ts35.isIdentifier(node.expression)) {
2465
+ if (ts34.isIdentifier(node.expression)) {
2557
2466
  calleeName = node.expression.text;
2558
- } else if (ts35.isPropertyAccessExpression(node.expression)) {
2467
+ } else if (ts34.isPropertyAccessExpression(node.expression)) {
2559
2468
  calleeName = node.expression.name.text;
2560
2469
  }
2561
2470
  if (calleeName) {
2562
- const line = ts35.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2471
+ const line = ts34.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
2563
2472
  let symbol;
2564
2473
  let resolvedDeclaration;
2565
2474
  try {
2566
2475
  symbol = checker.getSymbolAtLocation(node.expression);
2567
- if (symbol && symbol.flags & ts35.SymbolFlags.Alias) {
2476
+ if (symbol && symbol.flags & ts34.SymbolFlags.Alias) {
2568
2477
  symbol = checker.getAliasedSymbol(symbol);
2569
2478
  }
2570
2479
  const signature = checker.getResolvedSignature(node);
@@ -2591,7 +2500,7 @@ function collectAllComments(sourceFile) {
2591
2500
  for (const r of ranges) {
2592
2501
  if (seen.has(r.pos)) continue;
2593
2502
  seen.add(r.pos);
2594
- const isLine = r.kind === ts35.SyntaxKind.SingleLineCommentTrivia;
2503
+ const isLine = r.kind === ts34.SyntaxKind.SingleLineCommentTrivia;
2595
2504
  comments.push({
2596
2505
  type: isLine ? "Line" : "Block",
2597
2506
  value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
@@ -2601,53 +2510,63 @@ function collectAllComments(sourceFile) {
2601
2510
  }
2602
2511
  }
2603
2512
  function visit(node) {
2604
- addRanges(ts35.getLeadingCommentRanges(source, node.getFullStart()));
2605
- addRanges(ts35.getTrailingCommentRanges(source, node.getEnd()));
2606
- ts35.forEachChild(node, visit);
2513
+ addRanges(ts34.getLeadingCommentRanges(source, node.getFullStart()));
2514
+ addRanges(ts34.getTrailingCommentRanges(source, node.getEnd()));
2515
+ ts34.forEachChild(node, visit);
2607
2516
  }
2608
2517
  visit(sourceFile);
2609
2518
  return comments;
2610
2519
  }
2611
2520
 
2612
2521
  // src/typecheck/program.ts
2613
- import * as ts36 from "typescript";
2614
- import { dirname as dirname2 } from "path";
2522
+ import * as ts35 from "typescript";
2523
+ import { dirname as dirname3 } from "path";
2615
2524
  function createProgramFromFiles(files) {
2616
2525
  let configPath;
2617
2526
  if (files.length > 0) {
2618
- configPath = ts36.findConfigFile(dirname2(files[0]), ts36.sys.fileExists, "tsconfig.json");
2527
+ configPath = ts35.findConfigFile(dirname3(files[0]), ts35.sys.fileExists, "tsconfig.json");
2619
2528
  }
2620
2529
  if (configPath) {
2621
- const configFile = ts36.readConfigFile(configPath, ts36.sys.readFile);
2622
- const parsed = ts36.parseJsonConfigFileContent(configFile.config, ts36.sys, dirname2(configPath));
2623
- return ts36.createProgram({
2530
+ const configFile = ts35.readConfigFile(configPath, ts35.sys.readFile);
2531
+ const parsed = ts35.parseJsonConfigFileContent(configFile.config, ts35.sys, dirname3(configPath));
2532
+ return ts35.createProgram({
2624
2533
  rootNames: files,
2625
2534
  options: { ...parsed.options, skipLibCheck: true }
2626
2535
  });
2627
2536
  }
2628
- return ts36.createProgram({
2537
+ return ts35.createProgram({
2629
2538
  rootNames: files,
2630
2539
  options: {
2631
- target: ts36.ScriptTarget.ESNext,
2632
- module: ts36.ModuleKind.ESNext,
2633
- moduleResolution: ts36.ModuleResolutionKind.Bundler,
2540
+ target: ts35.ScriptTarget.ESNext,
2541
+ module: ts35.ModuleKind.ESNext,
2542
+ moduleResolution: ts35.ModuleResolutionKind.Bundler,
2634
2543
  strict: true,
2635
2544
  skipLibCheck: true,
2636
2545
  noEmit: true
2637
2546
  }
2638
2547
  });
2639
2548
  }
2549
+ function resolveProjectFiles(scanFiles) {
2550
+ if (scanFiles.length === 0) return [];
2551
+ const configPath = ts35.findConfigFile(dirname3(scanFiles[0]), ts35.sys.fileExists, "tsconfig.json");
2552
+ if (!configPath) return scanFiles;
2553
+ const configFile = ts35.readConfigFile(configPath, ts35.sys.readFile);
2554
+ const parsed = ts35.parseJsonConfigFileContent(configFile.config, ts35.sys, dirname3(configPath));
2555
+ return [.../* @__PURE__ */ new Set([...scanFiles, ...parsed.fileNames])];
2556
+ }
2640
2557
 
2641
2558
  // src/scan/analyze.ts
2642
2559
  function analyzeFiles(files, rules) {
2643
2560
  const tsRules = rules.filter(isTSRule);
2644
2561
  const crossFileRules = rules.filter((r) => !isTSRule(r));
2645
- const program = files.length > 0 ? createProgramFromFiles(files) : null;
2562
+ const projectFiles = resolveProjectFiles(files);
2563
+ const program = projectFiles.length > 0 ? createProgramFromFiles(projectFiles) : null;
2646
2564
  if (!program) return [];
2647
2565
  const allowedFiles = new Set(files);
2648
2566
  const { index, diagnostics } = collectProject(program, tsRules, allowedFiles);
2649
2567
  for (const rule of crossFileRules) {
2650
- diagnostics.push(...rule.analyze(index));
2568
+ const ruleDiagnostics = rule.analyze(index);
2569
+ diagnostics.push(...ruleDiagnostics.filter((d) => allowedFiles.has(d.file)));
2651
2570
  }
2652
2571
  return finalizeDiagnostics(diagnostics, index.files);
2653
2572
  }
@@ -2791,7 +2710,7 @@ function lineAt(source, offset) {
2791
2710
  // src/scan/discover.ts
2792
2711
  import fg from "fast-glob";
2793
2712
  import ignore from "ignore";
2794
- import { existsSync, readFileSync } from "fs";
2713
+ import { existsSync as existsSync2, readFileSync } from "fs";
2795
2714
  import { relative, resolve as resolve2, sep } from "path";
2796
2715
  async function discoverFiles(config) {
2797
2716
  const globs = expandGlobs(config.paths);
@@ -2814,7 +2733,7 @@ function expandGlobs(paths) {
2814
2733
  }
2815
2734
  function applyGitIgnore(files) {
2816
2735
  const gitIgnorePath = resolve2(process.cwd(), ".gitignore");
2817
- if (!existsSync(gitIgnorePath)) return files;
2736
+ if (!existsSync2(gitIgnorePath)) return files;
2818
2737
  const matcher = ignore().add(readFileSync(gitIgnorePath, "utf8"));
2819
2738
  return files.filter((file) => {
2820
2739
  const rel = relative(process.cwd(), file);
@@ -2929,4 +2848,4 @@ export {
2929
2848
  executeScan,
2930
2849
  scan
2931
2850
  };
2932
- //# sourceMappingURL=chunk-2QER6GG7.js.map
2851
+ //# sourceMappingURL=chunk-WUH4JLMB.js.map