codemap-ai 3.4.0 → 3.5.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 +28 -1
- package/dist/{chunk-VB74K47A.js → chunk-BRVRY5KT.js} +62 -1
- package/dist/chunk-BRVRY5KT.js.map +1 -0
- package/dist/{chunk-LXZ73T7X.js → chunk-EEMILSZ4.js} +58 -3
- package/dist/chunk-EEMILSZ4.js.map +1 -0
- package/dist/cli.js +638 -14
- package/dist/cli.js.map +1 -1
- package/dist/{flow-server-4XSR5P4N.js → flow-server-FMPWJSLK.js} +3 -3
- package/dist/{flow-server-4XSR5P4N.js.map → flow-server-FMPWJSLK.js.map} +1 -1
- package/dist/index.js +628 -4
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +2 -2
- package/dist/mcp-server.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-LXZ73T7X.js.map +0 -1
- package/dist/chunk-VB74K47A.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FlowStorage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-EEMILSZ4.js";
|
|
4
4
|
|
|
5
5
|
// src/flow/builder.ts
|
|
6
|
-
import { readFileSync, statSync } from "fs";
|
|
6
|
+
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
7
7
|
import { createHash } from "crypto";
|
|
8
8
|
import { glob } from "glob";
|
|
9
9
|
import { resolve, relative, extname } from "path";
|
|
@@ -473,10 +473,13 @@ var TypeScriptParser = class extends BaseParser {
|
|
|
473
473
|
language = "typescript";
|
|
474
474
|
extensions = [".ts", ".tsx"];
|
|
475
475
|
parser;
|
|
476
|
+
treeSitterParser;
|
|
477
|
+
// Expose for Phase 3
|
|
476
478
|
constructor() {
|
|
477
479
|
super();
|
|
478
480
|
this.parser = new Parser();
|
|
479
481
|
this.parser.setLanguage(TypeScript.typescript);
|
|
482
|
+
this.treeSitterParser = this.parser;
|
|
480
483
|
}
|
|
481
484
|
parse(filePath, content) {
|
|
482
485
|
const analysis = this.createEmptyAnalysis(filePath);
|
|
@@ -764,6 +767,7 @@ var JavaScriptParser = class extends TypeScriptParser {
|
|
|
764
767
|
super();
|
|
765
768
|
this.parser = new Parser();
|
|
766
769
|
this.parser.setLanguage(JavaScript);
|
|
770
|
+
this.treeSitterParser = this.parser;
|
|
767
771
|
}
|
|
768
772
|
parse(filePath, content) {
|
|
769
773
|
const sourceCode = typeof content === "string" ? content : String(content);
|
|
@@ -788,10 +792,13 @@ var PythonParser = class extends BaseParser {
|
|
|
788
792
|
language = "python";
|
|
789
793
|
extensions = [".py"];
|
|
790
794
|
parser;
|
|
795
|
+
treeSitterParser;
|
|
796
|
+
// Expose for Phase 3
|
|
791
797
|
constructor() {
|
|
792
798
|
super();
|
|
793
799
|
this.parser = new Parser2();
|
|
794
800
|
this.parser.setLanguage(Python);
|
|
801
|
+
this.treeSitterParser = this.parser;
|
|
795
802
|
}
|
|
796
803
|
parse(filePath, content) {
|
|
797
804
|
const analysis = this.createEmptyAnalysis(filePath);
|
|
@@ -1352,6 +1359,614 @@ function registerAllParsers() {
|
|
|
1352
1359
|
}
|
|
1353
1360
|
registerAllParsers();
|
|
1354
1361
|
|
|
1362
|
+
// src/flow/phase3-analyzer.ts
|
|
1363
|
+
import { readFileSync } from "fs";
|
|
1364
|
+
|
|
1365
|
+
// src/analysis/property-tracker.ts
|
|
1366
|
+
function extractPropertyAccesses(functionNode, scopeId, scopeName, filePath) {
|
|
1367
|
+
const accesses = [];
|
|
1368
|
+
const isJS = /\.(js|ts|jsx|tsx)$/.test(filePath);
|
|
1369
|
+
const isPython = /\.py$/.test(filePath);
|
|
1370
|
+
if (isJS) {
|
|
1371
|
+
extractJavaScriptPropertyAccesses(functionNode, scopeId, scopeName, accesses);
|
|
1372
|
+
} else if (isPython) {
|
|
1373
|
+
extractPythonPropertyAccesses(functionNode, scopeId, scopeName, accesses);
|
|
1374
|
+
}
|
|
1375
|
+
return accesses;
|
|
1376
|
+
}
|
|
1377
|
+
function extractJavaScriptPropertyAccesses(node, scopeId, scopeName, accesses) {
|
|
1378
|
+
traverseNode(node, (current) => {
|
|
1379
|
+
if (current.type === "member_expression") {
|
|
1380
|
+
const access = parseJavaScriptMemberExpression(current, scopeId);
|
|
1381
|
+
if (access) {
|
|
1382
|
+
accesses.push(access);
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
if (current.type === "optional_chain") {
|
|
1386
|
+
const memberExpr = current.children.find((c) => c.type === "member_expression");
|
|
1387
|
+
if (memberExpr) {
|
|
1388
|
+
const access = parseJavaScriptMemberExpression(memberExpr, scopeId);
|
|
1389
|
+
if (access) {
|
|
1390
|
+
access.accessType = "optional";
|
|
1391
|
+
accesses.push(access);
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (current.type === "subscript_expression") {
|
|
1396
|
+
const access = parseJavaScriptSubscript(current, scopeId);
|
|
1397
|
+
if (access) {
|
|
1398
|
+
accesses.push(access);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
function parseJavaScriptMemberExpression(node, scopeId) {
|
|
1404
|
+
const propertyPath = [];
|
|
1405
|
+
let objectName = "";
|
|
1406
|
+
let currentNode = node;
|
|
1407
|
+
while (currentNode && currentNode.type === "member_expression") {
|
|
1408
|
+
const propertyNode = currentNode.childForFieldName("property");
|
|
1409
|
+
if (propertyNode) {
|
|
1410
|
+
propertyPath.unshift(propertyNode.text);
|
|
1411
|
+
}
|
|
1412
|
+
const objectNode = currentNode.childForFieldName("object");
|
|
1413
|
+
if (!objectNode) break;
|
|
1414
|
+
if (objectNode.type === "identifier") {
|
|
1415
|
+
objectName = objectNode.text;
|
|
1416
|
+
break;
|
|
1417
|
+
} else if (objectNode.type === "member_expression") {
|
|
1418
|
+
currentNode = objectNode;
|
|
1419
|
+
} else {
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (!objectName || isCommonJavaScriptObject(objectName)) {
|
|
1424
|
+
return null;
|
|
1425
|
+
}
|
|
1426
|
+
if (propertyPath.length === 0) {
|
|
1427
|
+
return null;
|
|
1428
|
+
}
|
|
1429
|
+
return {
|
|
1430
|
+
objectName,
|
|
1431
|
+
propertyPath,
|
|
1432
|
+
fullPath: `${objectName}.${propertyPath.join(".")}`,
|
|
1433
|
+
line: node.startPosition.row + 1,
|
|
1434
|
+
scopeId,
|
|
1435
|
+
accessType: "dot"
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
function parseJavaScriptSubscript(node, scopeId) {
|
|
1439
|
+
const objectNode = node.childForFieldName("object");
|
|
1440
|
+
const indexNode = node.childForFieldName("index");
|
|
1441
|
+
if (!objectNode || !indexNode) return null;
|
|
1442
|
+
if (indexNode.type !== "string") return null;
|
|
1443
|
+
const objectName = objectNode.text;
|
|
1444
|
+
const propertyName = indexNode.text.replace(/['"]/g, "");
|
|
1445
|
+
if (isCommonJavaScriptObject(objectName)) {
|
|
1446
|
+
return null;
|
|
1447
|
+
}
|
|
1448
|
+
return {
|
|
1449
|
+
objectName,
|
|
1450
|
+
propertyPath: [propertyName],
|
|
1451
|
+
fullPath: `${objectName}.${propertyName}`,
|
|
1452
|
+
line: node.startPosition.row + 1,
|
|
1453
|
+
scopeId,
|
|
1454
|
+
accessType: "bracket"
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
function extractPythonPropertyAccesses(node, scopeId, scopeName, accesses) {
|
|
1458
|
+
traverseNode(node, (current) => {
|
|
1459
|
+
if (current.type === "attribute") {
|
|
1460
|
+
const access = parsePythonAttribute(current, scopeId);
|
|
1461
|
+
if (access) {
|
|
1462
|
+
accesses.push(access);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (current.type === "subscript") {
|
|
1466
|
+
const access = parsePythonSubscript(current, scopeId);
|
|
1467
|
+
if (access) {
|
|
1468
|
+
accesses.push(access);
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
function parsePythonAttribute(node, scopeId) {
|
|
1474
|
+
const propertyPath = [];
|
|
1475
|
+
let objectName = "";
|
|
1476
|
+
let currentNode = node;
|
|
1477
|
+
while (currentNode && currentNode.type === "attribute") {
|
|
1478
|
+
const attrNode = currentNode.childForFieldName("attribute");
|
|
1479
|
+
if (attrNode) {
|
|
1480
|
+
propertyPath.unshift(attrNode.text);
|
|
1481
|
+
}
|
|
1482
|
+
const objectNode = currentNode.childForFieldName("object");
|
|
1483
|
+
if (!objectNode) break;
|
|
1484
|
+
if (objectNode.type === "identifier") {
|
|
1485
|
+
objectName = objectNode.text;
|
|
1486
|
+
break;
|
|
1487
|
+
} else if (objectNode.type === "attribute") {
|
|
1488
|
+
currentNode = objectNode;
|
|
1489
|
+
} else {
|
|
1490
|
+
return null;
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
if (!objectName || isCommonPythonObject(objectName)) {
|
|
1494
|
+
return null;
|
|
1495
|
+
}
|
|
1496
|
+
if (propertyPath.length === 0) {
|
|
1497
|
+
return null;
|
|
1498
|
+
}
|
|
1499
|
+
return {
|
|
1500
|
+
objectName,
|
|
1501
|
+
propertyPath,
|
|
1502
|
+
fullPath: `${objectName}.${propertyPath.join(".")}`,
|
|
1503
|
+
line: node.startPosition.row + 1,
|
|
1504
|
+
scopeId,
|
|
1505
|
+
accessType: "dot"
|
|
1506
|
+
};
|
|
1507
|
+
}
|
|
1508
|
+
function parsePythonSubscript(node, scopeId) {
|
|
1509
|
+
const valueNode = node.childForFieldName("value");
|
|
1510
|
+
const subscriptChildren = node.children.filter(
|
|
1511
|
+
(c) => c.type === "string" || c.type === "identifier"
|
|
1512
|
+
);
|
|
1513
|
+
if (!valueNode || subscriptChildren.length === 0) return null;
|
|
1514
|
+
const keyNode = subscriptChildren.find((c) => c.type === "string");
|
|
1515
|
+
if (!keyNode) return null;
|
|
1516
|
+
const objectName = valueNode.text;
|
|
1517
|
+
const stringContent = keyNode.children.find((c) => c.type === "string_content");
|
|
1518
|
+
const propertyName = stringContent ? stringContent.text : keyNode.text.replace(/['"]/g, "");
|
|
1519
|
+
if (isCommonPythonObject(objectName)) {
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
return {
|
|
1523
|
+
objectName,
|
|
1524
|
+
propertyPath: [propertyName],
|
|
1525
|
+
fullPath: `${objectName}.${propertyName}`,
|
|
1526
|
+
line: node.startPosition.row + 1,
|
|
1527
|
+
scopeId,
|
|
1528
|
+
accessType: "bracket"
|
|
1529
|
+
};
|
|
1530
|
+
}
|
|
1531
|
+
function traverseNode(node, visitor) {
|
|
1532
|
+
visitor(node);
|
|
1533
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1534
|
+
const child = node.child(i);
|
|
1535
|
+
if (child) {
|
|
1536
|
+
traverseNode(child, visitor);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
function isCommonJavaScriptObject(name) {
|
|
1541
|
+
const commonObjects = /* @__PURE__ */ new Set([
|
|
1542
|
+
// Global objects
|
|
1543
|
+
"console",
|
|
1544
|
+
"window",
|
|
1545
|
+
"document",
|
|
1546
|
+
"process",
|
|
1547
|
+
"global",
|
|
1548
|
+
// Common libraries
|
|
1549
|
+
"Math",
|
|
1550
|
+
"JSON",
|
|
1551
|
+
"Date",
|
|
1552
|
+
"Array",
|
|
1553
|
+
"Object",
|
|
1554
|
+
"String",
|
|
1555
|
+
"Number",
|
|
1556
|
+
// Keywords
|
|
1557
|
+
"this",
|
|
1558
|
+
"super",
|
|
1559
|
+
// Common module patterns
|
|
1560
|
+
"exports",
|
|
1561
|
+
"module",
|
|
1562
|
+
"require"
|
|
1563
|
+
]);
|
|
1564
|
+
return commonObjects.has(name);
|
|
1565
|
+
}
|
|
1566
|
+
function isCommonPythonObject(name) {
|
|
1567
|
+
const commonObjects = /* @__PURE__ */ new Set([
|
|
1568
|
+
// Keywords
|
|
1569
|
+
"self",
|
|
1570
|
+
"cls",
|
|
1571
|
+
// Built-in types
|
|
1572
|
+
"str",
|
|
1573
|
+
"int",
|
|
1574
|
+
"float",
|
|
1575
|
+
"dict",
|
|
1576
|
+
"list",
|
|
1577
|
+
"tuple",
|
|
1578
|
+
"set",
|
|
1579
|
+
// Common modules
|
|
1580
|
+
"os",
|
|
1581
|
+
"sys",
|
|
1582
|
+
"json",
|
|
1583
|
+
"math",
|
|
1584
|
+
"datetime"
|
|
1585
|
+
]);
|
|
1586
|
+
return commonObjects.has(name);
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
// src/analysis/return-analyzer.ts
|
|
1590
|
+
function analyzeReturnStatements(functionNode, functionId, functionName, filePath) {
|
|
1591
|
+
const returns = [];
|
|
1592
|
+
const isJS = /\.(js|ts|jsx|tsx)$/.test(filePath);
|
|
1593
|
+
const isPython = /\.py$/.test(filePath);
|
|
1594
|
+
if (isJS) {
|
|
1595
|
+
extractJavaScriptReturns(functionNode, functionId, functionName, returns);
|
|
1596
|
+
} else if (isPython) {
|
|
1597
|
+
extractPythonReturns(functionNode, functionId, functionName, returns);
|
|
1598
|
+
}
|
|
1599
|
+
return returns;
|
|
1600
|
+
}
|
|
1601
|
+
function extractJavaScriptReturns(node, functionId, functionName, returns) {
|
|
1602
|
+
traverseNode2(node, (current) => {
|
|
1603
|
+
if (current.type === "return_statement") {
|
|
1604
|
+
const returnNode = current.children.find(
|
|
1605
|
+
(c) => c.type !== "return" && c.type !== ";"
|
|
1606
|
+
);
|
|
1607
|
+
if (returnNode) {
|
|
1608
|
+
const returnInfo = parseJavaScriptReturnValue(
|
|
1609
|
+
returnNode,
|
|
1610
|
+
functionId,
|
|
1611
|
+
functionName
|
|
1612
|
+
);
|
|
1613
|
+
if (returnInfo) {
|
|
1614
|
+
returns.push(returnInfo);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
function parseJavaScriptReturnValue(returnNode, functionId, functionName) {
|
|
1621
|
+
const line = returnNode.startPosition.row + 1;
|
|
1622
|
+
const returnExpression = returnNode.text;
|
|
1623
|
+
let returnType = null;
|
|
1624
|
+
let objectShape = null;
|
|
1625
|
+
if (returnNode.type === "object") {
|
|
1626
|
+
objectShape = extractJavaScriptObjectShape(returnNode);
|
|
1627
|
+
returnType = "object";
|
|
1628
|
+
} else if (returnNode.type === "array") {
|
|
1629
|
+
returnType = "array";
|
|
1630
|
+
objectShape = {
|
|
1631
|
+
properties: [],
|
|
1632
|
+
propertyTypes: /* @__PURE__ */ new Map(),
|
|
1633
|
+
nestedShapes: /* @__PURE__ */ new Map(),
|
|
1634
|
+
isArray: true,
|
|
1635
|
+
isDictionary: false,
|
|
1636
|
+
isOptional: false
|
|
1637
|
+
};
|
|
1638
|
+
} else if (returnNode.type === "new_expression") {
|
|
1639
|
+
const constructorNode = returnNode.childForFieldName("constructor");
|
|
1640
|
+
if (constructorNode) {
|
|
1641
|
+
returnType = constructorNode.text;
|
|
1642
|
+
}
|
|
1643
|
+
} else if (returnNode.type === "identifier") {
|
|
1644
|
+
returnType = `@var:${returnNode.text}`;
|
|
1645
|
+
} else if (returnNode.type === "call_expression") {
|
|
1646
|
+
const funcNode = returnNode.childForFieldName("function");
|
|
1647
|
+
if (funcNode) {
|
|
1648
|
+
returnType = `@call:${funcNode.text}`;
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
return {
|
|
1652
|
+
functionId,
|
|
1653
|
+
functionName,
|
|
1654
|
+
returnType,
|
|
1655
|
+
objectShape,
|
|
1656
|
+
line,
|
|
1657
|
+
returnExpression
|
|
1658
|
+
};
|
|
1659
|
+
}
|
|
1660
|
+
function extractJavaScriptObjectShape(objectNode) {
|
|
1661
|
+
const properties = [];
|
|
1662
|
+
const propertyTypes = /* @__PURE__ */ new Map();
|
|
1663
|
+
const nestedShapes = /* @__PURE__ */ new Map();
|
|
1664
|
+
for (const child of objectNode.children) {
|
|
1665
|
+
if (child.type === "pair") {
|
|
1666
|
+
const keyNode = child.childForFieldName("key");
|
|
1667
|
+
const valueNode = child.childForFieldName("value");
|
|
1668
|
+
if (keyNode) {
|
|
1669
|
+
const propertyName = keyNode.text.replace(/['"]/g, "");
|
|
1670
|
+
properties.push(propertyName);
|
|
1671
|
+
if (valueNode) {
|
|
1672
|
+
const propertyType = inferJavaScriptType(valueNode);
|
|
1673
|
+
propertyTypes.set(propertyName, propertyType);
|
|
1674
|
+
if (valueNode.type === "object") {
|
|
1675
|
+
const nestedShape = extractJavaScriptObjectShape(valueNode);
|
|
1676
|
+
nestedShapes.set(propertyName, nestedShape);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
return {
|
|
1683
|
+
properties,
|
|
1684
|
+
propertyTypes,
|
|
1685
|
+
nestedShapes,
|
|
1686
|
+
isArray: false,
|
|
1687
|
+
isDictionary: false,
|
|
1688
|
+
isOptional: false
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
function inferJavaScriptType(valueNode) {
|
|
1692
|
+
switch (valueNode.type) {
|
|
1693
|
+
case "number":
|
|
1694
|
+
return "number";
|
|
1695
|
+
case "string":
|
|
1696
|
+
case "template_string":
|
|
1697
|
+
return "string";
|
|
1698
|
+
case "true":
|
|
1699
|
+
case "false":
|
|
1700
|
+
return "boolean";
|
|
1701
|
+
case "null":
|
|
1702
|
+
return "null";
|
|
1703
|
+
case "undefined":
|
|
1704
|
+
return "undefined";
|
|
1705
|
+
case "array":
|
|
1706
|
+
return "array";
|
|
1707
|
+
case "object":
|
|
1708
|
+
return "object";
|
|
1709
|
+
case "arrow_function":
|
|
1710
|
+
case "function_expression":
|
|
1711
|
+
return "function";
|
|
1712
|
+
case "identifier":
|
|
1713
|
+
return `@var:${valueNode.text}`;
|
|
1714
|
+
case "call_expression":
|
|
1715
|
+
return "@call";
|
|
1716
|
+
default:
|
|
1717
|
+
return "unknown";
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
function extractPythonReturns(node, functionId, functionName, returns) {
|
|
1721
|
+
traverseNode2(node, (current) => {
|
|
1722
|
+
if (current.type === "return_statement") {
|
|
1723
|
+
const returnNode = current.children.find((c) => c.type !== "return");
|
|
1724
|
+
if (returnNode) {
|
|
1725
|
+
const returnInfo = parsePythonReturnValue(
|
|
1726
|
+
returnNode,
|
|
1727
|
+
functionId,
|
|
1728
|
+
functionName
|
|
1729
|
+
);
|
|
1730
|
+
if (returnInfo) {
|
|
1731
|
+
returns.push(returnInfo);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
function parsePythonReturnValue(returnNode, functionId, functionName) {
|
|
1738
|
+
const line = returnNode.startPosition.row + 1;
|
|
1739
|
+
const returnExpression = returnNode.text;
|
|
1740
|
+
let returnType = null;
|
|
1741
|
+
let objectShape = null;
|
|
1742
|
+
if (returnNode.type === "dictionary") {
|
|
1743
|
+
objectShape = extractPythonDictionaryShape(returnNode);
|
|
1744
|
+
returnType = "dict";
|
|
1745
|
+
} else if (returnNode.type === "list") {
|
|
1746
|
+
returnType = "list";
|
|
1747
|
+
objectShape = {
|
|
1748
|
+
properties: [],
|
|
1749
|
+
propertyTypes: /* @__PURE__ */ new Map(),
|
|
1750
|
+
nestedShapes: /* @__PURE__ */ new Map(),
|
|
1751
|
+
isArray: true,
|
|
1752
|
+
isDictionary: false,
|
|
1753
|
+
isOptional: false
|
|
1754
|
+
};
|
|
1755
|
+
} else if (returnNode.type === "call") {
|
|
1756
|
+
const funcNode = returnNode.childForFieldName("function");
|
|
1757
|
+
if (funcNode) {
|
|
1758
|
+
const funcName = funcNode.text;
|
|
1759
|
+
if (funcName[0] === funcName[0].toUpperCase()) {
|
|
1760
|
+
returnType = funcName;
|
|
1761
|
+
} else {
|
|
1762
|
+
returnType = `@call:${funcName}`;
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
} else if (returnNode.type === "identifier") {
|
|
1766
|
+
returnType = `@var:${returnNode.text}`;
|
|
1767
|
+
} else if (returnNode.type === "list_comprehension") {
|
|
1768
|
+
returnType = "list";
|
|
1769
|
+
objectShape = {
|
|
1770
|
+
properties: [],
|
|
1771
|
+
propertyTypes: /* @__PURE__ */ new Map(),
|
|
1772
|
+
nestedShapes: /* @__PURE__ */ new Map(),
|
|
1773
|
+
isArray: true,
|
|
1774
|
+
isDictionary: false,
|
|
1775
|
+
isOptional: false
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
return {
|
|
1779
|
+
functionId,
|
|
1780
|
+
functionName,
|
|
1781
|
+
returnType,
|
|
1782
|
+
objectShape,
|
|
1783
|
+
line,
|
|
1784
|
+
returnExpression
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
function extractPythonDictionaryShape(dictNode) {
|
|
1788
|
+
const properties = [];
|
|
1789
|
+
const propertyTypes = /* @__PURE__ */ new Map();
|
|
1790
|
+
const nestedShapes = /* @__PURE__ */ new Map();
|
|
1791
|
+
for (const child of dictNode.children) {
|
|
1792
|
+
if (child.type === "pair") {
|
|
1793
|
+
const keyNode = child.children.find((c) => c.type === "string");
|
|
1794
|
+
const valueNode = child.children.find(
|
|
1795
|
+
(c) => c.type !== "string" && c.type !== ":"
|
|
1796
|
+
);
|
|
1797
|
+
if (keyNode) {
|
|
1798
|
+
const stringContent = keyNode.children.find(
|
|
1799
|
+
(c) => c.type === "string_content"
|
|
1800
|
+
);
|
|
1801
|
+
const propertyName = stringContent ? stringContent.text : keyNode.text.replace(/['"]/g, "");
|
|
1802
|
+
properties.push(propertyName);
|
|
1803
|
+
if (valueNode) {
|
|
1804
|
+
const propertyType = inferPythonType(valueNode);
|
|
1805
|
+
propertyTypes.set(propertyName, propertyType);
|
|
1806
|
+
if (valueNode.type === "dictionary") {
|
|
1807
|
+
const nestedShape = extractPythonDictionaryShape(valueNode);
|
|
1808
|
+
nestedShapes.set(propertyName, nestedShape);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
return {
|
|
1815
|
+
properties,
|
|
1816
|
+
propertyTypes,
|
|
1817
|
+
nestedShapes,
|
|
1818
|
+
isArray: false,
|
|
1819
|
+
isDictionary: true,
|
|
1820
|
+
isOptional: false
|
|
1821
|
+
};
|
|
1822
|
+
}
|
|
1823
|
+
function inferPythonType(valueNode) {
|
|
1824
|
+
switch (valueNode.type) {
|
|
1825
|
+
case "integer":
|
|
1826
|
+
case "float":
|
|
1827
|
+
return "number";
|
|
1828
|
+
case "string":
|
|
1829
|
+
return "string";
|
|
1830
|
+
case "true":
|
|
1831
|
+
case "false":
|
|
1832
|
+
return "boolean";
|
|
1833
|
+
case "none":
|
|
1834
|
+
return "None";
|
|
1835
|
+
case "list":
|
|
1836
|
+
return "list";
|
|
1837
|
+
case "dictionary":
|
|
1838
|
+
return "dict";
|
|
1839
|
+
case "lambda":
|
|
1840
|
+
return "function";
|
|
1841
|
+
case "identifier":
|
|
1842
|
+
return `@var:${valueNode.text}`;
|
|
1843
|
+
case "call":
|
|
1844
|
+
return "@call";
|
|
1845
|
+
default:
|
|
1846
|
+
return "unknown";
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
function traverseNode2(node, visitor) {
|
|
1850
|
+
visitor(node);
|
|
1851
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1852
|
+
const child = node.child(i);
|
|
1853
|
+
if (child) {
|
|
1854
|
+
traverseNode2(child, visitor);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
// src/flow/storage-queries-phase3.ts
|
|
1860
|
+
import { randomUUID } from "crypto";
|
|
1861
|
+
function storePropertyAccesses(db, accesses) {
|
|
1862
|
+
const stmt = db.prepare(`
|
|
1863
|
+
INSERT OR REPLACE INTO property_accesses
|
|
1864
|
+
(id, scope_node_id, object_name, property_path, full_path, access_type, file_path, line)
|
|
1865
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1866
|
+
`);
|
|
1867
|
+
const insert = db.transaction((accesses2) => {
|
|
1868
|
+
for (const access of accesses2) {
|
|
1869
|
+
stmt.run(
|
|
1870
|
+
randomUUID(),
|
|
1871
|
+
access.scopeId,
|
|
1872
|
+
access.objectName,
|
|
1873
|
+
JSON.stringify(access.propertyPath),
|
|
1874
|
+
access.fullPath,
|
|
1875
|
+
access.accessType,
|
|
1876
|
+
access.filePath || "",
|
|
1877
|
+
access.line
|
|
1878
|
+
);
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
insert(accesses);
|
|
1882
|
+
}
|
|
1883
|
+
function storeReturnInfo(db, returns) {
|
|
1884
|
+
const stmt = db.prepare(`
|
|
1885
|
+
INSERT OR REPLACE INTO return_shapes
|
|
1886
|
+
(id, function_id, return_type, object_shape, line, return_expression)
|
|
1887
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
1888
|
+
`);
|
|
1889
|
+
const insert = db.transaction((returns2) => {
|
|
1890
|
+
for (const ret of returns2) {
|
|
1891
|
+
stmt.run(
|
|
1892
|
+
randomUUID(),
|
|
1893
|
+
ret.functionId,
|
|
1894
|
+
ret.returnType,
|
|
1895
|
+
ret.objectShape ? JSON.stringify(serializeObjectShape(ret.objectShape)) : null,
|
|
1896
|
+
ret.line,
|
|
1897
|
+
ret.returnExpression
|
|
1898
|
+
);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1901
|
+
insert(returns);
|
|
1902
|
+
}
|
|
1903
|
+
function serializeObjectShape(shape) {
|
|
1904
|
+
return {
|
|
1905
|
+
properties: shape.properties,
|
|
1906
|
+
propertyTypes: Array.from(shape.propertyTypes.entries()),
|
|
1907
|
+
nestedShapes: Array.from(shape.nestedShapes.entries()).map(([key, nested]) => [
|
|
1908
|
+
key,
|
|
1909
|
+
serializeObjectShape(nested)
|
|
1910
|
+
]),
|
|
1911
|
+
isArray: shape.isArray,
|
|
1912
|
+
isDictionary: shape.isDictionary,
|
|
1913
|
+
isOptional: shape.isOptional
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
// src/flow/phase3-analyzer.ts
|
|
1918
|
+
function analyzeFileForPhase3(filePath, functionNodes, db) {
|
|
1919
|
+
try {
|
|
1920
|
+
const language = getLanguageFromPath(filePath);
|
|
1921
|
+
if (!language) return;
|
|
1922
|
+
const parserInstance = parserRegistry.getByLanguage(language);
|
|
1923
|
+
if (!parserInstance || !parserInstance.treeSitterParser) return;
|
|
1924
|
+
const content = readFileSync(filePath, "utf-8");
|
|
1925
|
+
const tree = parserInstance.treeSitterParser.parse(content);
|
|
1926
|
+
const rootNode = tree.rootNode;
|
|
1927
|
+
for (const func of functionNodes) {
|
|
1928
|
+
const funcNode = findNodeAtLine(rootNode, func.startLine, func.endLine);
|
|
1929
|
+
if (!funcNode) continue;
|
|
1930
|
+
const accesses = extractPropertyAccesses(funcNode, func.id, func.name, filePath);
|
|
1931
|
+
if (accesses.length > 0) {
|
|
1932
|
+
const accessesWithFile = accesses.map((a) => ({ ...a, filePath }));
|
|
1933
|
+
storePropertyAccesses(db, accessesWithFile);
|
|
1934
|
+
}
|
|
1935
|
+
const returns = analyzeReturnStatements(funcNode, func.id, func.name, filePath);
|
|
1936
|
+
if (returns.length > 0) {
|
|
1937
|
+
storeReturnInfo(db, returns);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
} catch (error) {
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
function getLanguageFromPath(filePath) {
|
|
1944
|
+
if (filePath.endsWith(".py")) return "python";
|
|
1945
|
+
if (filePath.endsWith(".js") || filePath.endsWith(".jsx")) return "javascript";
|
|
1946
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
1947
|
+
return null;
|
|
1948
|
+
}
|
|
1949
|
+
function findNodeAtLine(node, startLine, endLine) {
|
|
1950
|
+
const nodeStart = node.startPosition.row + 1;
|
|
1951
|
+
const nodeEnd = node.endPosition.row + 1;
|
|
1952
|
+
if (nodeStart === startLine && nodeEnd === endLine) {
|
|
1953
|
+
return node;
|
|
1954
|
+
}
|
|
1955
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
1956
|
+
const child = node.child(i);
|
|
1957
|
+
if (child) {
|
|
1958
|
+
const result = findNodeAtLine(child, startLine, endLine);
|
|
1959
|
+
if (result) return result;
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
if (nodeStart <= startLine && nodeEnd >= endLine) {
|
|
1963
|
+
if (node.type === "function_definition" || node.type === "function_declaration" || node.type === "method_definition" || node.type === "arrow_function" || node.type === "function_expression") {
|
|
1964
|
+
return node;
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
return null;
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1355
1970
|
// src/flow/builder.ts
|
|
1356
1971
|
var FlowBuilder = class {
|
|
1357
1972
|
storage;
|
|
@@ -1379,7 +1994,7 @@ var FlowBuilder = class {
|
|
|
1379
1994
|
const filesToIndex = [];
|
|
1380
1995
|
let skipped = 0;
|
|
1381
1996
|
for (const filePath of allFiles) {
|
|
1382
|
-
const content =
|
|
1997
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1383
1998
|
const hash = this.hashContent(content);
|
|
1384
1999
|
if (!this.config.forceReindex) {
|
|
1385
2000
|
const existingHash = this.storage.getFileHash(filePath);
|
|
@@ -1463,7 +2078,7 @@ var FlowBuilder = class {
|
|
|
1463
2078
|
}
|
|
1464
2079
|
async parseAndStore(filePath, hash, rootPath) {
|
|
1465
2080
|
const language = this.getLanguage(filePath);
|
|
1466
|
-
const content =
|
|
2081
|
+
const content = readFileSync2(filePath, "utf-8");
|
|
1467
2082
|
const parser = parserRegistry.getByLanguage(language);
|
|
1468
2083
|
if (!parser) {
|
|
1469
2084
|
throw new Error(`No parser found for language: ${language}`);
|
|
@@ -1500,6 +2115,15 @@ var FlowBuilder = class {
|
|
|
1500
2115
|
);
|
|
1501
2116
|
}
|
|
1502
2117
|
}
|
|
2118
|
+
const functionNodes = analysis.nodes.filter((n) => n.type === "function" || n.type === "method").map((n) => ({
|
|
2119
|
+
id: n.id,
|
|
2120
|
+
name: n.name,
|
|
2121
|
+
startLine: n.startLine,
|
|
2122
|
+
endLine: n.endLine
|
|
2123
|
+
}));
|
|
2124
|
+
if (functionNodes.length > 0) {
|
|
2125
|
+
analyzeFileForPhase3(filePath, functionNodes, this.storage.db);
|
|
2126
|
+
}
|
|
1503
2127
|
if (analysis.databaseOperations) {
|
|
1504
2128
|
for (const dbOp of analysis.databaseOperations) {
|
|
1505
2129
|
this.storage.insertDatabaseOperation({
|