unguard 0.11.0 → 0.12.0
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/README.md +1 -0
- package/dist/{chunk-ZNOFYNU4.js → chunk-HE647T6E.js} +262 -85
- package/dist/chunk-HE647T6E.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ZNOFYNU4.js.map +0 -1
package/README.md
CHANGED
|
@@ -165,6 +165,7 @@ These rules use the TypeScript type checker. Non-nullable types suppress the dia
|
|
|
165
165
|
| `unused-export` | info | Exported function with no usages in the project |
|
|
166
166
|
| `optional-arg-always-used` | warning | Optional param provided at every call site |
|
|
167
167
|
| `explicit-null-arg` | warning | `fn(null)` / `fn(undefined)` to project functions |
|
|
168
|
+
| `dead-overload` | warning | Overload signature with zero matching project call sites |
|
|
168
169
|
|
|
169
170
|
### Imports
|
|
170
171
|
|
|
@@ -1304,6 +1304,177 @@ var duplicateStatementSequence = {
|
|
|
1304
1304
|
}
|
|
1305
1305
|
};
|
|
1306
1306
|
|
|
1307
|
+
// src/rules/cross-file/dead-overload.ts
|
|
1308
|
+
import * as ts28 from "typescript";
|
|
1309
|
+
var deadOverload = {
|
|
1310
|
+
id: "dead-overload",
|
|
1311
|
+
severity: "warning",
|
|
1312
|
+
message: "Overload signature has no matching call sites in the project",
|
|
1313
|
+
analyze(project) {
|
|
1314
|
+
const diagnostics = [];
|
|
1315
|
+
for (const [file, { sourceFile }] of project.files) {
|
|
1316
|
+
for (const family of collectOverloadFamilies(sourceFile, file)) {
|
|
1317
|
+
const matchedOverloads = /* @__PURE__ */ new Set();
|
|
1318
|
+
for (const site of project.callSites) {
|
|
1319
|
+
const declaration = site.resolvedDeclaration;
|
|
1320
|
+
if (declaration === void 0) continue;
|
|
1321
|
+
if (family.overloads.includes(declaration)) {
|
|
1322
|
+
matchedOverloads.add(declaration);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
if (matchedOverloads.size === 0) continue;
|
|
1326
|
+
const deadOverloads = family.overloads.filter((overload) => !matchedOverloads.has(overload));
|
|
1327
|
+
const liveOverloads = family.overloads.filter((overload) => matchedOverloads.has(overload));
|
|
1328
|
+
for (const overload of deadOverloads) {
|
|
1329
|
+
diagnostics.push({
|
|
1330
|
+
ruleId: this.id,
|
|
1331
|
+
severity: this.severity,
|
|
1332
|
+
message: buildMessage(family, overload, deadOverloads, liveOverloads),
|
|
1333
|
+
file,
|
|
1334
|
+
line: lineOf(sourceFile, overload),
|
|
1335
|
+
column: 1
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return diagnostics;
|
|
1341
|
+
}
|
|
1342
|
+
};
|
|
1343
|
+
function buildMessage(family, overload, deadOverloads, liveOverloads) {
|
|
1344
|
+
const base = `Overload signature for "${family.name}" has no matching call sites in the project`;
|
|
1345
|
+
if (!shouldMentionCascade(family, overload, deadOverloads, liveOverloads)) {
|
|
1346
|
+
return base;
|
|
1347
|
+
}
|
|
1348
|
+
return `${base}; removing it may let you collapse to the remaining constrained signature and drop implementation casts`;
|
|
1349
|
+
}
|
|
1350
|
+
function shouldMentionCascade(family, overload, deadOverloads, liveOverloads) {
|
|
1351
|
+
if (deadOverloads.length !== 1 || liveOverloads.length !== 1) return false;
|
|
1352
|
+
if (deadOverloads[0] !== overload) return false;
|
|
1353
|
+
const liveOverload = liveOverloads[0];
|
|
1354
|
+
if (liveOverload === void 0) return false;
|
|
1355
|
+
if (family.implementation.typeParameters === void 0 || liveOverload.typeParameters === void 0) return false;
|
|
1356
|
+
const body = family.implementation.body;
|
|
1357
|
+
if (body === void 0) return false;
|
|
1358
|
+
const implementationTypeParams = family.implementation.typeParameters;
|
|
1359
|
+
const liveTypeParams = liveOverload.typeParameters;
|
|
1360
|
+
if (implementationTypeParams.length === 0 || implementationTypeParams.length !== liveTypeParams.length) return false;
|
|
1361
|
+
const constrainedParams = implementationTypeParams.flatMap((typeParam, index) => {
|
|
1362
|
+
const liveTypeParam = liveTypeParams[index];
|
|
1363
|
+
if (liveTypeParam === void 0) return [];
|
|
1364
|
+
if (typeParam.constraint !== void 0) return [];
|
|
1365
|
+
if (liveTypeParam.constraint === void 0) return [];
|
|
1366
|
+
return [{
|
|
1367
|
+
paramName: typeParam.name.text,
|
|
1368
|
+
constraintText: normalizeText(liveTypeParam.constraint.getText(family.sourceFile))
|
|
1369
|
+
}];
|
|
1370
|
+
});
|
|
1371
|
+
if (constrainedParams.length === 0) return false;
|
|
1372
|
+
return constrainedParams.some(
|
|
1373
|
+
({ paramName, constraintText }) => hasIntersectionCast(body, family.sourceFile, paramName, constraintText)
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
function hasIntersectionCast(body, sourceFile, paramName, constraintText) {
|
|
1377
|
+
let found = false;
|
|
1378
|
+
function visit(node) {
|
|
1379
|
+
if (found) return;
|
|
1380
|
+
if (ts28.isAsExpression(node) || ts28.isTypeAssertionExpression(node)) {
|
|
1381
|
+
if (isConstraintIntersection(node.type, sourceFile, paramName, constraintText)) {
|
|
1382
|
+
found = true;
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
ts28.forEachChild(node, visit);
|
|
1387
|
+
}
|
|
1388
|
+
visit(body);
|
|
1389
|
+
return found;
|
|
1390
|
+
}
|
|
1391
|
+
function isConstraintIntersection(typeNode, sourceFile, paramName, constraintText) {
|
|
1392
|
+
const target = unwrapParenthesizedType(typeNode);
|
|
1393
|
+
if (!ts28.isIntersectionTypeNode(target)) return false;
|
|
1394
|
+
let hasParam = false;
|
|
1395
|
+
let hasConstraint = false;
|
|
1396
|
+
for (const part of target.types) {
|
|
1397
|
+
const text = normalizeText(unwrapParenthesizedType(part).getText(sourceFile));
|
|
1398
|
+
if (text === paramName) hasParam = true;
|
|
1399
|
+
if (text === constraintText) hasConstraint = true;
|
|
1400
|
+
}
|
|
1401
|
+
return hasParam && hasConstraint;
|
|
1402
|
+
}
|
|
1403
|
+
function unwrapParenthesizedType(typeNode) {
|
|
1404
|
+
let current = typeNode;
|
|
1405
|
+
while (ts28.isParenthesizedTypeNode(current)) {
|
|
1406
|
+
current = current.type;
|
|
1407
|
+
}
|
|
1408
|
+
return current;
|
|
1409
|
+
}
|
|
1410
|
+
function collectOverloadFamilies(sourceFile, file) {
|
|
1411
|
+
const families = [];
|
|
1412
|
+
collectFromList(sourceFile.statements, sourceFile, file, families);
|
|
1413
|
+
function visit(node) {
|
|
1414
|
+
if (ts28.isClassDeclaration(node) || ts28.isClassExpression(node)) {
|
|
1415
|
+
collectFromList(node.members, sourceFile, file, families);
|
|
1416
|
+
}
|
|
1417
|
+
ts28.forEachChild(node, visit);
|
|
1418
|
+
}
|
|
1419
|
+
ts28.forEachChild(sourceFile, visit);
|
|
1420
|
+
return families;
|
|
1421
|
+
}
|
|
1422
|
+
function collectFromList(nodes, sourceFile, file, families) {
|
|
1423
|
+
for (let index = 0; index < nodes.length; index++) {
|
|
1424
|
+
const current = asOverloadDeclaration(nodes[index]);
|
|
1425
|
+
const name = current ? getDeclarationName(current) : null;
|
|
1426
|
+
if (current === null || name === null) continue;
|
|
1427
|
+
const run = [current];
|
|
1428
|
+
let nextIndex = index + 1;
|
|
1429
|
+
while (nextIndex < nodes.length) {
|
|
1430
|
+
const next = asOverloadDeclaration(nodes[nextIndex]);
|
|
1431
|
+
if (next === null) break;
|
|
1432
|
+
if (getDeclarationName(next) !== name) break;
|
|
1433
|
+
run.push(next);
|
|
1434
|
+
nextIndex++;
|
|
1435
|
+
}
|
|
1436
|
+
const family = toOverloadFamily(run, name, file, sourceFile);
|
|
1437
|
+
if (family !== null) {
|
|
1438
|
+
families.push(family);
|
|
1439
|
+
}
|
|
1440
|
+
index = nextIndex - 1;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
function toOverloadFamily(run, name, file, sourceFile) {
|
|
1444
|
+
if (run.length < 2) return null;
|
|
1445
|
+
const implementation = run.at(-1);
|
|
1446
|
+
if (implementation === void 0 || implementation.body === void 0) return null;
|
|
1447
|
+
if (run.slice(0, -1).some((declaration) => declaration.body !== void 0)) return null;
|
|
1448
|
+
return {
|
|
1449
|
+
name,
|
|
1450
|
+
file,
|
|
1451
|
+
sourceFile,
|
|
1452
|
+
overloads: run.slice(0, -1),
|
|
1453
|
+
implementation
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
function asOverloadDeclaration(node) {
|
|
1457
|
+
if (node === void 0) return null;
|
|
1458
|
+
if (ts28.isFunctionDeclaration(node)) return node;
|
|
1459
|
+
if (ts28.isMethodDeclaration(node)) return node;
|
|
1460
|
+
return null;
|
|
1461
|
+
}
|
|
1462
|
+
function getDeclarationName(node) {
|
|
1463
|
+
if (ts28.isFunctionDeclaration(node)) {
|
|
1464
|
+
return node.name?.text ?? null;
|
|
1465
|
+
}
|
|
1466
|
+
if (ts28.isIdentifier(node.name) || ts28.isStringLiteral(node.name) || ts28.isNumericLiteral(node.name)) {
|
|
1467
|
+
return node.name.text;
|
|
1468
|
+
}
|
|
1469
|
+
return null;
|
|
1470
|
+
}
|
|
1471
|
+
function lineOf(sourceFile, node) {
|
|
1472
|
+
return ts28.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1473
|
+
}
|
|
1474
|
+
function normalizeText(text) {
|
|
1475
|
+
return text.replace(/\s+/g, "");
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1307
1478
|
// src/rules/index.ts
|
|
1308
1479
|
var allRules = [
|
|
1309
1480
|
noEmptyCatch,
|
|
@@ -1338,7 +1509,8 @@ var allRules = [
|
|
|
1338
1509
|
trivialWrapper,
|
|
1339
1510
|
unusedExport,
|
|
1340
1511
|
duplicateFile,
|
|
1341
|
-
duplicateStatementSequence
|
|
1512
|
+
duplicateStatementSequence,
|
|
1513
|
+
deadOverload
|
|
1342
1514
|
];
|
|
1343
1515
|
var ruleMetadata = {
|
|
1344
1516
|
"no-any-cast": { category: "type-evasion", tags: ["safety"] },
|
|
@@ -1373,7 +1545,8 @@ var ruleMetadata = {
|
|
|
1373
1545
|
"trivial-wrapper": { category: "cross-file", tags: ["duplicate"] },
|
|
1374
1546
|
"unused-export": { category: "cross-file", tags: ["api"] },
|
|
1375
1547
|
"duplicate-file": { category: "cross-file", tags: ["duplicate"] },
|
|
1376
|
-
"duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] }
|
|
1548
|
+
"duplicate-statement-sequence": { category: "cross-file", tags: ["duplicate"] },
|
|
1549
|
+
"dead-overload": { category: "cross-file", tags: ["api", "type-evasion"] }
|
|
1377
1550
|
};
|
|
1378
1551
|
function getRuleMetadata(ruleId) {
|
|
1379
1552
|
const metadata = ruleMetadata[ruleId];
|
|
@@ -1433,32 +1606,32 @@ function isFailOn(value) {
|
|
|
1433
1606
|
}
|
|
1434
1607
|
|
|
1435
1608
|
// src/collect/index.ts
|
|
1436
|
-
import * as
|
|
1609
|
+
import * as ts31 from "typescript";
|
|
1437
1610
|
import { createHash as createHash2 } from "crypto";
|
|
1438
1611
|
|
|
1439
1612
|
// src/utils/hash.ts
|
|
1440
1613
|
import { createHash } from "crypto";
|
|
1441
|
-
import * as
|
|
1614
|
+
import * as ts29 from "typescript";
|
|
1442
1615
|
function hashTypeShape(node, sourceFile) {
|
|
1443
1616
|
const normalized = normalizeTypeNode(node, sourceFile);
|
|
1444
1617
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
1445
1618
|
}
|
|
1446
1619
|
function normalizeTypeNode(node, sourceFile) {
|
|
1447
|
-
if (
|
|
1620
|
+
if (ts29.isTypeLiteralNode(node)) {
|
|
1448
1621
|
const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
|
|
1449
1622
|
return `{${normalized}}`;
|
|
1450
1623
|
}
|
|
1451
|
-
if (
|
|
1624
|
+
if (ts29.isInterfaceDeclaration(node)) {
|
|
1452
1625
|
const normalized = node.members.map((m) => normalizeTypeNode(m, sourceFile)).sort().join(";");
|
|
1453
1626
|
return `{${normalized}}`;
|
|
1454
1627
|
}
|
|
1455
|
-
if (
|
|
1628
|
+
if (ts29.isPropertySignature(node)) {
|
|
1456
1629
|
const keyName = node.name.getText(sourceFile);
|
|
1457
1630
|
const optional = node.questionToken ? "?" : "";
|
|
1458
1631
|
const type = node.type ? normalizeTypeNode(node.type, sourceFile) : "any";
|
|
1459
1632
|
return `${keyName}${optional}:${type}`;
|
|
1460
1633
|
}
|
|
1461
|
-
if (
|
|
1634
|
+
if (ts29.isTypeAliasDeclaration(node)) {
|
|
1462
1635
|
return normalizeTypeNode(node.type, sourceFile);
|
|
1463
1636
|
}
|
|
1464
1637
|
return node.getText(sourceFile).replace(/\s+/g, " ").trim();
|
|
@@ -1491,7 +1664,7 @@ function normalizeBody(node, sourceFile, paramNames) {
|
|
|
1491
1664
|
text = text.replace(/\s+/g, " ").trim();
|
|
1492
1665
|
return text;
|
|
1493
1666
|
}
|
|
1494
|
-
function
|
|
1667
|
+
function normalizeText2(text) {
|
|
1495
1668
|
let t = text.replace(/\/\/[^\n]*/g, "");
|
|
1496
1669
|
t = t.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
1497
1670
|
t = t.replace(/"(?:[^"\\]|\\.)*"/g, '"__STR__"');
|
|
@@ -1624,7 +1797,7 @@ var InlineParamTypeRegistry = class extends BaseRegistry {
|
|
|
1624
1797
|
};
|
|
1625
1798
|
|
|
1626
1799
|
// src/typecheck/walk.ts
|
|
1627
|
-
import * as
|
|
1800
|
+
import * as ts30 from "typescript";
|
|
1628
1801
|
function buildContext(rule, sourceFile, checker, source, filename, diagnostics) {
|
|
1629
1802
|
return {
|
|
1630
1803
|
filename,
|
|
@@ -1632,7 +1805,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
|
|
|
1632
1805
|
sourceFile,
|
|
1633
1806
|
checker,
|
|
1634
1807
|
report(node, message) {
|
|
1635
|
-
const { line, character } =
|
|
1808
|
+
const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
|
|
1636
1809
|
diagnostics.push({
|
|
1637
1810
|
ruleId: rule.id,
|
|
1638
1811
|
severity: rule.severity,
|
|
@@ -1643,7 +1816,7 @@ function buildContext(rule, sourceFile, checker, source, filename, diagnostics)
|
|
|
1643
1816
|
});
|
|
1644
1817
|
},
|
|
1645
1818
|
reportAtOffset(offset, message) {
|
|
1646
|
-
const { line, character } =
|
|
1819
|
+
const { line, character } = ts30.getLineAndCharacterOfPosition(sourceFile, offset);
|
|
1647
1820
|
diagnostics.push({
|
|
1648
1821
|
ruleId: rule.id,
|
|
1649
1822
|
severity: rule.severity,
|
|
@@ -1694,7 +1867,7 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1694
1867
|
rule.visit(node, ctx);
|
|
1695
1868
|
}
|
|
1696
1869
|
}
|
|
1697
|
-
|
|
1870
|
+
ts31.forEachChild(node, visit2);
|
|
1698
1871
|
};
|
|
1699
1872
|
var visit = visit2;
|
|
1700
1873
|
const file = sourceFile.fileName;
|
|
@@ -1708,7 +1881,7 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1708
1881
|
rule,
|
|
1709
1882
|
ctx: buildContext(rule, sourceFile, checker, source, file, diagnostics)
|
|
1710
1883
|
}));
|
|
1711
|
-
|
|
1884
|
+
ts31.forEachChild(sourceFile, visit2);
|
|
1712
1885
|
}
|
|
1713
1886
|
const fileHashes = /* @__PURE__ */ new Map();
|
|
1714
1887
|
for (const [file, { source }] of fileMap) {
|
|
@@ -1724,52 +1897,52 @@ function collectProject(program, tsRules, allowedFiles) {
|
|
|
1724
1897
|
return { index: { types, functions, constants, callSites, imports, files: fileMap, fileHashes, statementSequences, inlineParamTypes }, diagnostics };
|
|
1725
1898
|
}
|
|
1726
1899
|
function hasNonPublicModifier(node) {
|
|
1727
|
-
if (!
|
|
1728
|
-
const mods =
|
|
1900
|
+
if (!ts31.canHaveModifiers(node)) return false;
|
|
1901
|
+
const mods = ts31.getModifiers(node);
|
|
1729
1902
|
if (mods === void 0) return false;
|
|
1730
1903
|
return mods.some(
|
|
1731
|
-
(m) => m.kind ===
|
|
1904
|
+
(m) => m.kind === ts31.SyntaxKind.PrivateKeyword || m.kind === ts31.SyntaxKind.ProtectedKeyword
|
|
1732
1905
|
);
|
|
1733
1906
|
}
|
|
1734
1907
|
function isExported(node) {
|
|
1735
|
-
if (!
|
|
1736
|
-
const mods =
|
|
1908
|
+
if (!ts31.canHaveModifiers(node)) return false;
|
|
1909
|
+
const mods = ts31.getModifiers(node);
|
|
1737
1910
|
if (mods === void 0) return false;
|
|
1738
|
-
return mods.some((m) => m.kind ===
|
|
1911
|
+
return mods.some((m) => m.kind === ts31.SyntaxKind.ExportKeyword);
|
|
1739
1912
|
}
|
|
1740
1913
|
function collectTypes(node, file, sourceFile, registry) {
|
|
1741
|
-
if (
|
|
1742
|
-
const line =
|
|
1914
|
+
if (ts31.isTypeAliasDeclaration(node)) {
|
|
1915
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1743
1916
|
registry.add(node.name.text, file, line, node.type, sourceFile, isExported(node));
|
|
1744
1917
|
}
|
|
1745
|
-
if (
|
|
1746
|
-
const line =
|
|
1918
|
+
if (ts31.isInterfaceDeclaration(node)) {
|
|
1919
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1747
1920
|
registry.add(node.name.text, file, line, node, sourceFile, isExported(node));
|
|
1748
1921
|
}
|
|
1749
1922
|
}
|
|
1750
1923
|
function isConstantValue(node) {
|
|
1751
|
-
if (
|
|
1752
|
-
if (
|
|
1753
|
-
if (
|
|
1754
|
-
if (
|
|
1755
|
-
if (
|
|
1924
|
+
if (ts31.isStringLiteral(node)) return true;
|
|
1925
|
+
if (ts31.isNumericLiteral(node)) return true;
|
|
1926
|
+
if (ts31.isNoSubstitutionTemplateLiteral(node)) return true;
|
|
1927
|
+
if (ts31.isPrefixUnaryExpression(node)) return isConstantValue(node.operand);
|
|
1928
|
+
if (ts31.isBinaryExpression(node)) return isConstantValue(node.left) && isConstantValue(node.right);
|
|
1756
1929
|
return false;
|
|
1757
1930
|
}
|
|
1758
1931
|
function collectConstants(node, file, sourceFile, registry) {
|
|
1759
|
-
if (!
|
|
1760
|
-
if (!(node.declarationList.flags &
|
|
1932
|
+
if (!ts31.isVariableStatement(node)) return;
|
|
1933
|
+
if (!(node.declarationList.flags & ts31.NodeFlags.Const)) return;
|
|
1761
1934
|
const exported = isExported(node);
|
|
1762
1935
|
for (const decl of node.declarationList.declarations) {
|
|
1763
|
-
if (!decl.initializer || !
|
|
1936
|
+
if (!decl.initializer || !ts31.isIdentifier(decl.name)) continue;
|
|
1764
1937
|
if (!isConstantValue(decl.initializer)) continue;
|
|
1765
1938
|
const valueText = decl.initializer.getText(sourceFile).replace(/\s+/g, " ").trim();
|
|
1766
1939
|
const valueHash = createHash2("sha256").update(valueText).digest("hex").slice(0, 16);
|
|
1767
|
-
const line =
|
|
1940
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, decl.getStart(sourceFile)).line + 1;
|
|
1768
1941
|
registry.add({ name: decl.name.text, file, line, valueHash, valueText, exported });
|
|
1769
1942
|
}
|
|
1770
1943
|
}
|
|
1771
1944
|
function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name, extra) {
|
|
1772
|
-
const line =
|
|
1945
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, lineNode.getStart(sourceFile)).line + 1;
|
|
1773
1946
|
const params = extractParams(parameters, sourceFile);
|
|
1774
1947
|
const paramNames = params.map((p) => p.name);
|
|
1775
1948
|
const hash = hashFunctionBody(body, sourceFile);
|
|
@@ -1791,17 +1964,17 @@ function buildFunctionEntry(body, parameters, sourceFile, file, lineNode, name,
|
|
|
1791
1964
|
};
|
|
1792
1965
|
}
|
|
1793
1966
|
function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
1794
|
-
if (
|
|
1967
|
+
if (ts31.isFunctionDeclaration(node) && node.name && node.body) {
|
|
1795
1968
|
registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, node.name.text, {
|
|
1796
1969
|
exported: isExported(node),
|
|
1797
1970
|
symbol: checker.getSymbolAtLocation(node.name),
|
|
1798
1971
|
node
|
|
1799
1972
|
}));
|
|
1800
1973
|
}
|
|
1801
|
-
if (
|
|
1974
|
+
if (ts31.isVariableStatement(node)) {
|
|
1802
1975
|
const exported = isExported(node);
|
|
1803
1976
|
for (const decl of node.declarationList.declarations) {
|
|
1804
|
-
if (decl.initializer &&
|
|
1977
|
+
if (decl.initializer && ts31.isArrowFunction(decl.initializer) && ts31.isIdentifier(decl.name)) {
|
|
1805
1978
|
const arrow = decl.initializer;
|
|
1806
1979
|
registry.add(buildFunctionEntry(arrow.body, arrow.parameters, sourceFile, file, decl, decl.name.text, {
|
|
1807
1980
|
exported,
|
|
@@ -1809,7 +1982,7 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1809
1982
|
node: arrow
|
|
1810
1983
|
}));
|
|
1811
1984
|
}
|
|
1812
|
-
if (decl.initializer &&
|
|
1985
|
+
if (decl.initializer && ts31.isFunctionExpression(decl.initializer) && ts31.isIdentifier(decl.name)) {
|
|
1813
1986
|
const fn = decl.initializer;
|
|
1814
1987
|
if (fn.body) {
|
|
1815
1988
|
registry.add(buildFunctionEntry(fn.body, fn.parameters, sourceFile, file, decl, decl.name.text, {
|
|
@@ -1821,22 +1994,22 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1821
1994
|
}
|
|
1822
1995
|
}
|
|
1823
1996
|
}
|
|
1824
|
-
if (
|
|
1997
|
+
if (ts31.isPropertyAssignment(node) && (ts31.isIdentifier(node.name) || ts31.isStringLiteral(node.name))) {
|
|
1825
1998
|
const init = node.initializer;
|
|
1826
1999
|
const propName = node.name.text;
|
|
1827
|
-
if (
|
|
2000
|
+
if (ts31.isArrowFunction(init)) {
|
|
1828
2001
|
registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
|
|
1829
2002
|
}
|
|
1830
|
-
if (
|
|
2003
|
+
if (ts31.isFunctionExpression(init) && init.body) {
|
|
1831
2004
|
registry.add(buildFunctionEntry(init.body, init.parameters, sourceFile, file, node, propName, { node: init }));
|
|
1832
2005
|
}
|
|
1833
2006
|
}
|
|
1834
|
-
if (
|
|
2007
|
+
if (ts31.isArrowFunction(node) || ts31.isFunctionExpression(node)) {
|
|
1835
2008
|
const parent = node.parent;
|
|
1836
|
-
if (
|
|
1837
|
-
} else if (
|
|
2009
|
+
if (ts31.isVariableDeclaration(parent) && ts31.isIdentifier(parent.name)) {
|
|
2010
|
+
} else if (ts31.isPropertyAssignment(parent)) {
|
|
1838
2011
|
} else {
|
|
1839
|
-
const body =
|
|
2012
|
+
const body = ts31.isArrowFunction(node) ? node.body : node.body;
|
|
1840
2013
|
if (body) {
|
|
1841
2014
|
const MIN_ANON_BODY = 64;
|
|
1842
2015
|
if (bodyTextLength(body, sourceFile) >= MIN_ANON_BODY) {
|
|
@@ -1846,14 +2019,14 @@ function collectFunctions(node, file, sourceFile, checker, registry) {
|
|
|
1846
2019
|
}
|
|
1847
2020
|
}
|
|
1848
2021
|
}
|
|
1849
|
-
if (
|
|
2022
|
+
if (ts31.isMethodDeclaration(node) && node.body && ts31.isIdentifier(node.name)) {
|
|
1850
2023
|
const parent = node.parent;
|
|
1851
|
-
if (
|
|
2024
|
+
if (ts31.isClassDeclaration(parent)) {
|
|
1852
2025
|
if (hasNonPublicModifier(node)) return;
|
|
1853
2026
|
const className = parent.name ? parent.name.text : "<anonymous>";
|
|
1854
2027
|
const name = `${className}.${node.name.text}`;
|
|
1855
2028
|
const implementsInterface = parent.heritageClauses?.some(
|
|
1856
|
-
(c) => c.token ===
|
|
2029
|
+
(c) => c.token === ts31.SyntaxKind.ImplementsKeyword
|
|
1857
2030
|
) ?? false;
|
|
1858
2031
|
registry.add(buildFunctionEntry(node.body, node.parameters, sourceFile, file, node, name, {
|
|
1859
2032
|
exported: isExported(parent),
|
|
@@ -1876,16 +2049,16 @@ function extractParams(parameters, sourceFile) {
|
|
|
1876
2049
|
}
|
|
1877
2050
|
function deriveAnonymousName(node, sourceFile) {
|
|
1878
2051
|
const parent = node.parent;
|
|
1879
|
-
const line =
|
|
1880
|
-
if (
|
|
2052
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
2053
|
+
if (ts31.isCallExpression(parent)) {
|
|
1881
2054
|
const grandparent = parent.parent;
|
|
1882
|
-
if (
|
|
2055
|
+
if (ts31.isPropertyAssignment(grandparent) && ts31.isIdentifier(grandparent.name)) {
|
|
1883
2056
|
return grandparent.name.text;
|
|
1884
2057
|
}
|
|
1885
2058
|
let calleeName = null;
|
|
1886
|
-
if (
|
|
2059
|
+
if (ts31.isIdentifier(parent.expression)) {
|
|
1887
2060
|
calleeName = parent.expression.text;
|
|
1888
|
-
} else if (
|
|
2061
|
+
} else if (ts31.isPropertyAccessExpression(parent.expression)) {
|
|
1889
2062
|
calleeName = parent.expression.name.text;
|
|
1890
2063
|
}
|
|
1891
2064
|
if (calleeName) {
|
|
@@ -1896,7 +2069,7 @@ function deriveAnonymousName(node, sourceFile) {
|
|
|
1896
2069
|
return `<anonymous>:${line}`;
|
|
1897
2070
|
}
|
|
1898
2071
|
function collectImports(node, file, imports) {
|
|
1899
|
-
if (
|
|
2072
|
+
if (ts31.isImportDeclaration(node) && ts31.isStringLiteral(node.moduleSpecifier)) {
|
|
1900
2073
|
const moduleSource = node.moduleSpecifier.text;
|
|
1901
2074
|
const clause = node.importClause;
|
|
1902
2075
|
if (!clause) return;
|
|
@@ -1908,7 +2081,7 @@ function collectImports(node, file, imports) {
|
|
|
1908
2081
|
source: moduleSource
|
|
1909
2082
|
});
|
|
1910
2083
|
}
|
|
1911
|
-
if (clause.namedBindings &&
|
|
2084
|
+
if (clause.namedBindings && ts31.isNamedImports(clause.namedBindings)) {
|
|
1912
2085
|
for (const el of clause.namedBindings.elements) {
|
|
1913
2086
|
imports.push({
|
|
1914
2087
|
file,
|
|
@@ -1919,9 +2092,9 @@ function collectImports(node, file, imports) {
|
|
|
1919
2092
|
}
|
|
1920
2093
|
}
|
|
1921
2094
|
}
|
|
1922
|
-
if (
|
|
2095
|
+
if (ts31.isExportDeclaration(node) && node.moduleSpecifier && ts31.isStringLiteral(node.moduleSpecifier)) {
|
|
1923
2096
|
const moduleSource = node.moduleSpecifier.text;
|
|
1924
|
-
if (node.exportClause &&
|
|
2097
|
+
if (node.exportClause && ts31.isNamedExports(node.exportClause)) {
|
|
1925
2098
|
for (const el of node.exportClause.elements) {
|
|
1926
2099
|
imports.push({
|
|
1927
2100
|
file,
|
|
@@ -1934,7 +2107,7 @@ function collectImports(node, file, imports) {
|
|
|
1934
2107
|
}
|
|
1935
2108
|
}
|
|
1936
2109
|
function collectStatementSequences(node, file, sourceFile, registry) {
|
|
1937
|
-
if (!
|
|
2110
|
+
if (!ts31.isBlock(node)) return;
|
|
1938
2111
|
const stmts = node.statements;
|
|
1939
2112
|
const n = stmts.length;
|
|
1940
2113
|
if (n < 3) return;
|
|
@@ -1944,43 +2117,46 @@ function collectStatementSequences(node, file, sourceFile, registry) {
|
|
|
1944
2117
|
const window = stmts.slice(start, start + size);
|
|
1945
2118
|
const texts = window.map((s) => s.getText(sourceFile));
|
|
1946
2119
|
const joined = texts.join("\n");
|
|
1947
|
-
const normalized =
|
|
2120
|
+
const normalized = normalizeText2(joined);
|
|
1948
2121
|
if (normalized.length < 128) continue;
|
|
1949
2122
|
const hash = hashText(joined.replace(/\s+/g, " ").trim());
|
|
1950
2123
|
const normalizedHash = hashText(normalized);
|
|
1951
2124
|
const firstStmt = window[0];
|
|
1952
2125
|
const lastStmt = window[window.length - 1];
|
|
1953
2126
|
if (firstStmt === void 0 || lastStmt === void 0) continue;
|
|
1954
|
-
const line =
|
|
1955
|
-
const endLine =
|
|
2127
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, firstStmt.getStart(sourceFile)).line + 1;
|
|
2128
|
+
const endLine = ts31.getLineAndCharacterOfPosition(sourceFile, lastStmt.getEnd()).line + 1;
|
|
1956
2129
|
registry.add({ file, line, endLine, hash, normalizedHash, statementCount: size, normalizedBodyLength: normalized.length });
|
|
1957
2130
|
}
|
|
1958
2131
|
}
|
|
1959
2132
|
}
|
|
1960
2133
|
function collectInlineParamTypes(node, file, sourceFile, registry) {
|
|
1961
|
-
if (!
|
|
2134
|
+
if (!ts31.isTypeLiteralNode(node)) return;
|
|
1962
2135
|
const parent = node.parent;
|
|
1963
|
-
if (!parent || !
|
|
2136
|
+
if (!parent || !ts31.isParameter(parent)) return;
|
|
1964
2137
|
if (parent.type !== node) return;
|
|
1965
|
-
const line =
|
|
2138
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1966
2139
|
registry.add(file, line, node, sourceFile);
|
|
1967
2140
|
}
|
|
1968
2141
|
function collectCallSites(node, file, sourceFile, checker, sites) {
|
|
1969
|
-
if (!
|
|
2142
|
+
if (!ts31.isCallExpression(node)) return;
|
|
1970
2143
|
let calleeName = null;
|
|
1971
|
-
if (
|
|
2144
|
+
if (ts31.isIdentifier(node.expression)) {
|
|
1972
2145
|
calleeName = node.expression.text;
|
|
1973
|
-
} else if (
|
|
2146
|
+
} else if (ts31.isPropertyAccessExpression(node.expression)) {
|
|
1974
2147
|
calleeName = node.expression.name.text;
|
|
1975
2148
|
}
|
|
1976
2149
|
if (calleeName) {
|
|
1977
|
-
const line =
|
|
2150
|
+
const line = ts31.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile)).line + 1;
|
|
1978
2151
|
let symbol;
|
|
2152
|
+
let resolvedDeclaration;
|
|
1979
2153
|
try {
|
|
1980
2154
|
symbol = checker.getSymbolAtLocation(node.expression);
|
|
1981
|
-
if (symbol && symbol.flags &
|
|
2155
|
+
if (symbol && symbol.flags & ts31.SymbolFlags.Alias) {
|
|
1982
2156
|
symbol = checker.getAliasedSymbol(symbol);
|
|
1983
2157
|
}
|
|
2158
|
+
const signature = checker.getResolvedSignature(node);
|
|
2159
|
+
resolvedDeclaration = signature?.declaration;
|
|
1984
2160
|
} catch {
|
|
1985
2161
|
}
|
|
1986
2162
|
sites.push({
|
|
@@ -1989,7 +2165,8 @@ function collectCallSites(node, file, sourceFile, checker, sites) {
|
|
|
1989
2165
|
line,
|
|
1990
2166
|
argCount: node.arguments.length,
|
|
1991
2167
|
node,
|
|
1992
|
-
symbol
|
|
2168
|
+
symbol,
|
|
2169
|
+
resolvedDeclaration
|
|
1993
2170
|
});
|
|
1994
2171
|
}
|
|
1995
2172
|
}
|
|
@@ -1998,12 +2175,12 @@ function collectAllComments(sourceFile) {
|
|
|
1998
2175
|
const source = sourceFile.getFullText();
|
|
1999
2176
|
const seen = /* @__PURE__ */ new Set();
|
|
2000
2177
|
function visit(node) {
|
|
2001
|
-
const leading =
|
|
2178
|
+
const leading = ts31.getLeadingCommentRanges(source, node.getFullStart());
|
|
2002
2179
|
if (leading) {
|
|
2003
2180
|
for (const r of leading) {
|
|
2004
2181
|
if (seen.has(r.pos)) continue;
|
|
2005
2182
|
seen.add(r.pos);
|
|
2006
|
-
const isLine = r.kind ===
|
|
2183
|
+
const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
|
|
2007
2184
|
comments.push({
|
|
2008
2185
|
type: isLine ? "Line" : "Block",
|
|
2009
2186
|
value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
|
|
@@ -2012,12 +2189,12 @@ function collectAllComments(sourceFile) {
|
|
|
2012
2189
|
});
|
|
2013
2190
|
}
|
|
2014
2191
|
}
|
|
2015
|
-
const trailing =
|
|
2192
|
+
const trailing = ts31.getTrailingCommentRanges(source, node.getEnd());
|
|
2016
2193
|
if (trailing) {
|
|
2017
2194
|
for (const r of trailing) {
|
|
2018
2195
|
if (seen.has(r.pos)) continue;
|
|
2019
2196
|
seen.add(r.pos);
|
|
2020
|
-
const isLine = r.kind ===
|
|
2197
|
+
const isLine = r.kind === ts31.SyntaxKind.SingleLineCommentTrivia;
|
|
2021
2198
|
comments.push({
|
|
2022
2199
|
type: isLine ? "Line" : "Block",
|
|
2023
2200
|
value: source.slice(r.pos + 2, isLine ? r.end : r.end - 2),
|
|
@@ -2026,34 +2203,34 @@ function collectAllComments(sourceFile) {
|
|
|
2026
2203
|
});
|
|
2027
2204
|
}
|
|
2028
2205
|
}
|
|
2029
|
-
|
|
2206
|
+
ts31.forEachChild(node, visit);
|
|
2030
2207
|
}
|
|
2031
2208
|
visit(sourceFile);
|
|
2032
2209
|
return comments;
|
|
2033
2210
|
}
|
|
2034
2211
|
|
|
2035
2212
|
// src/typecheck/program.ts
|
|
2036
|
-
import * as
|
|
2213
|
+
import * as ts32 from "typescript";
|
|
2037
2214
|
import { dirname as dirname2 } from "path";
|
|
2038
2215
|
function createProgramFromFiles(files) {
|
|
2039
2216
|
let configPath;
|
|
2040
2217
|
if (files.length > 0) {
|
|
2041
|
-
configPath =
|
|
2218
|
+
configPath = ts32.findConfigFile(dirname2(files[0]), ts32.sys.fileExists, "tsconfig.json");
|
|
2042
2219
|
}
|
|
2043
2220
|
if (configPath) {
|
|
2044
|
-
const configFile =
|
|
2045
|
-
const parsed =
|
|
2046
|
-
return
|
|
2221
|
+
const configFile = ts32.readConfigFile(configPath, ts32.sys.readFile);
|
|
2222
|
+
const parsed = ts32.parseJsonConfigFileContent(configFile.config, ts32.sys, dirname2(configPath));
|
|
2223
|
+
return ts32.createProgram({
|
|
2047
2224
|
rootNames: files,
|
|
2048
2225
|
options: { ...parsed.options, skipLibCheck: true }
|
|
2049
2226
|
});
|
|
2050
2227
|
}
|
|
2051
|
-
return
|
|
2228
|
+
return ts32.createProgram({
|
|
2052
2229
|
rootNames: files,
|
|
2053
2230
|
options: {
|
|
2054
|
-
target:
|
|
2055
|
-
module:
|
|
2056
|
-
moduleResolution:
|
|
2231
|
+
target: ts32.ScriptTarget.ESNext,
|
|
2232
|
+
module: ts32.ModuleKind.ESNext,
|
|
2233
|
+
moduleResolution: ts32.ModuleResolutionKind.Bundler,
|
|
2057
2234
|
strict: true,
|
|
2058
2235
|
skipLibCheck: true,
|
|
2059
2236
|
noEmit: true
|
|
@@ -2293,4 +2470,4 @@ export {
|
|
|
2293
2470
|
executeScan,
|
|
2294
2471
|
scan
|
|
2295
2472
|
};
|
|
2296
|
-
//# sourceMappingURL=chunk-
|
|
2473
|
+
//# sourceMappingURL=chunk-HE647T6E.js.map
|