depwire-cli 1.0.0 → 1.0.4
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 +22 -3
- package/dist/{chunk-IYKS66CG.js → chunk-7HLVFIVW.js} +2087 -162
- package/dist/{chunk-5BQLGAUL.js → chunk-JPDK7SOI.js} +10 -7
- package/dist/index.js +2 -2
- package/dist/mcpb-entry.js +2 -2
- package/dist/parser/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/parser/grammars/tree-sitter-java.wasm +0 -0
- package/dist/sdk.js +1 -1
- package/package.json +3 -1
|
@@ -31,9 +31,12 @@ function scanDirectory(rootDir, baseDir = rootDir) {
|
|
|
31
31
|
const isPython = entry.endsWith(".py");
|
|
32
32
|
const isGo = entry.endsWith(".go") && !entry.endsWith("_test.go");
|
|
33
33
|
const isRust = entry.endsWith(".rs");
|
|
34
|
-
const isC = entry.endsWith(".c")
|
|
34
|
+
const isC = entry.endsWith(".c");
|
|
35
|
+
const isCpp = entry.endsWith(".cpp") || entry.endsWith(".cc") || entry.endsWith(".cxx") || entry.endsWith(".c++") || entry.endsWith(".hpp") || entry.endsWith(".hh") || entry.endsWith(".hxx") || entry.endsWith(".h++") || entry.endsWith(".h") || entry.endsWith(".inl") || entry.endsWith(".ipp");
|
|
35
36
|
const isCSharp = entry.endsWith(".cs") || entry.endsWith(".csx") || entry.endsWith(".csproj");
|
|
36
|
-
|
|
37
|
+
const isJava = entry.endsWith(".java") || entry === "pom.xml" || entry === "build.gradle" || entry === "build.gradle.kts";
|
|
38
|
+
const isCppBuild = entry === "CMakeLists.txt" || entry === "conanfile.txt" || entry === "vcpkg.json";
|
|
39
|
+
if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC || isCpp || isCSharp || isJava || isCppBuild) {
|
|
37
40
|
files.push(relative(rootDir, fullPath));
|
|
38
41
|
}
|
|
39
42
|
}
|
|
@@ -71,6 +74,10 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
71
74
|
// C/C++ (cmake-based)
|
|
72
75
|
"configure.ac",
|
|
73
76
|
// C/C++ (autotools)
|
|
77
|
+
"pom.xml",
|
|
78
|
+
// Java (Maven)
|
|
79
|
+
"build.gradle",
|
|
80
|
+
// Java (Gradle)
|
|
74
81
|
".git"
|
|
75
82
|
// Any git repo
|
|
76
83
|
];
|
|
@@ -104,11 +111,11 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
104
111
|
}
|
|
105
112
|
|
|
106
113
|
// src/parser/index.ts
|
|
107
|
-
import { readFileSync as
|
|
108
|
-
import { join as
|
|
114
|
+
import { readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
115
|
+
import { join as join11, resolve as resolve5 } from "path";
|
|
109
116
|
|
|
110
117
|
// src/parser/detect.ts
|
|
111
|
-
import { extname as
|
|
118
|
+
import { extname as extname6, basename as basename4 } from "path";
|
|
112
119
|
|
|
113
120
|
// src/parser/wasm-init.ts
|
|
114
121
|
import { Parser, Language } from "web-tree-sitter";
|
|
@@ -136,7 +143,9 @@ async function initParser() {
|
|
|
136
143
|
"go": "tree-sitter-go.wasm",
|
|
137
144
|
"rust": "tree-sitter-rust.wasm",
|
|
138
145
|
"c": "tree-sitter-c.wasm",
|
|
139
|
-
"c_sharp": "tree-sitter-c_sharp.wasm"
|
|
146
|
+
"c_sharp": "tree-sitter-c_sharp.wasm",
|
|
147
|
+
"java": "tree-sitter-java.wasm",
|
|
148
|
+
"cpp": "tree-sitter-cpp.wasm"
|
|
140
149
|
};
|
|
141
150
|
for (const [name, file] of Object.entries(grammarFiles)) {
|
|
142
151
|
const wasmPath = path.join(grammarsDir, file);
|
|
@@ -2780,7 +2789,7 @@ function getCurrentSymbolId6(context) {
|
|
|
2780
2789
|
}
|
|
2781
2790
|
var cParser = {
|
|
2782
2791
|
name: "c",
|
|
2783
|
-
extensions: [".c"
|
|
2792
|
+
extensions: [".c"],
|
|
2784
2793
|
parseFile: parseCFile
|
|
2785
2794
|
};
|
|
2786
2795
|
|
|
@@ -3342,6 +3351,1582 @@ var csharpParser = {
|
|
|
3342
3351
|
parseFile: parseCSharpFile
|
|
3343
3352
|
};
|
|
3344
3353
|
|
|
3354
|
+
// src/parser/java.ts
|
|
3355
|
+
import { dirname as dirname8, join as join9, resolve as resolve4, basename as basename2 } from "path";
|
|
3356
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
3357
|
+
function parseJavaFile(filePath, sourceCode, projectRoot) {
|
|
3358
|
+
if (filePath.endsWith("pom.xml")) {
|
|
3359
|
+
return parsePomXml(filePath, sourceCode, projectRoot);
|
|
3360
|
+
}
|
|
3361
|
+
if (filePath.endsWith("build.gradle") || filePath.endsWith("build.gradle.kts")) {
|
|
3362
|
+
return parseGradleBuild(filePath, sourceCode, projectRoot);
|
|
3363
|
+
}
|
|
3364
|
+
const parser = getParser("java");
|
|
3365
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
3366
|
+
const context = {
|
|
3367
|
+
filePath,
|
|
3368
|
+
projectRoot,
|
|
3369
|
+
sourceCode,
|
|
3370
|
+
symbols: [],
|
|
3371
|
+
edges: [],
|
|
3372
|
+
currentScope: [],
|
|
3373
|
+
currentClass: null,
|
|
3374
|
+
currentPackage: null,
|
|
3375
|
+
imports: /* @__PURE__ */ new Map(),
|
|
3376
|
+
isBuildFile: false
|
|
3377
|
+
};
|
|
3378
|
+
walkNode8(tree.rootNode, context);
|
|
3379
|
+
return {
|
|
3380
|
+
filePath,
|
|
3381
|
+
symbols: context.symbols,
|
|
3382
|
+
edges: context.edges
|
|
3383
|
+
};
|
|
3384
|
+
}
|
|
3385
|
+
function walkNode8(node, context) {
|
|
3386
|
+
processNode8(node, context);
|
|
3387
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3388
|
+
const child = node.child(i);
|
|
3389
|
+
if (child) {
|
|
3390
|
+
walkNode8(child, context);
|
|
3391
|
+
}
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
function processNode8(node, context) {
|
|
3395
|
+
switch (node.type) {
|
|
3396
|
+
case "package_declaration":
|
|
3397
|
+
processPackageDeclaration(node, context);
|
|
3398
|
+
break;
|
|
3399
|
+
case "import_declaration":
|
|
3400
|
+
processImportDeclaration2(node, context);
|
|
3401
|
+
break;
|
|
3402
|
+
case "class_declaration":
|
|
3403
|
+
processClassDeclaration4(node, context);
|
|
3404
|
+
break;
|
|
3405
|
+
case "interface_declaration":
|
|
3406
|
+
processInterfaceDeclaration3(node, context);
|
|
3407
|
+
break;
|
|
3408
|
+
case "enum_declaration":
|
|
3409
|
+
processEnumDeclaration3(node, context);
|
|
3410
|
+
break;
|
|
3411
|
+
case "annotation_type_declaration":
|
|
3412
|
+
processAnnotationTypeDeclaration(node, context);
|
|
3413
|
+
break;
|
|
3414
|
+
case "record_declaration":
|
|
3415
|
+
processRecordDeclaration2(node, context);
|
|
3416
|
+
break;
|
|
3417
|
+
case "method_declaration":
|
|
3418
|
+
processMethodDeclaration3(node, context);
|
|
3419
|
+
break;
|
|
3420
|
+
case "constructor_declaration":
|
|
3421
|
+
processConstructorDeclaration2(node, context);
|
|
3422
|
+
break;
|
|
3423
|
+
case "field_declaration":
|
|
3424
|
+
processFieldDeclaration(node, context);
|
|
3425
|
+
break;
|
|
3426
|
+
case "constant_declaration":
|
|
3427
|
+
processConstantDeclaration(node, context);
|
|
3428
|
+
break;
|
|
3429
|
+
case "annotation_type_element_declaration":
|
|
3430
|
+
processAnnotationElement(node, context);
|
|
3431
|
+
break;
|
|
3432
|
+
case "method_invocation":
|
|
3433
|
+
processCallExpression8(node, context);
|
|
3434
|
+
break;
|
|
3435
|
+
case "object_creation_expression":
|
|
3436
|
+
processObjectCreation(node, context);
|
|
3437
|
+
break;
|
|
3438
|
+
case "lambda_expression":
|
|
3439
|
+
processLambdaExpression(node, context);
|
|
3440
|
+
break;
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
function processPackageDeclaration(node, context) {
|
|
3444
|
+
const scopedIdent = findDescendantByTypes(node, ["scoped_identifier", "identifier"]);
|
|
3445
|
+
if (!scopedIdent) return;
|
|
3446
|
+
const name = nodeText7(scopedIdent, context);
|
|
3447
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
3448
|
+
context.symbols.push({
|
|
3449
|
+
id: symbolId,
|
|
3450
|
+
name,
|
|
3451
|
+
kind: "module",
|
|
3452
|
+
filePath: context.filePath,
|
|
3453
|
+
startLine: node.startPosition.row + 1,
|
|
3454
|
+
endLine: node.endPosition.row + 1,
|
|
3455
|
+
exported: true
|
|
3456
|
+
});
|
|
3457
|
+
context.currentPackage = name;
|
|
3458
|
+
}
|
|
3459
|
+
function processImportDeclaration2(node, context) {
|
|
3460
|
+
const text = nodeText7(node, context).trim();
|
|
3461
|
+
const isStatic = text.includes("import static");
|
|
3462
|
+
const isWildcard = text.includes(".*");
|
|
3463
|
+
const scopedIdent = findDescendantByTypes(node, ["scoped_identifier", "identifier"]);
|
|
3464
|
+
if (!scopedIdent) return;
|
|
3465
|
+
let importPath = nodeText7(scopedIdent, context);
|
|
3466
|
+
const asterisk = findChildByType8(node, "asterisk");
|
|
3467
|
+
if (asterisk) {
|
|
3468
|
+
importPath = importPath + ".*";
|
|
3469
|
+
}
|
|
3470
|
+
const resolvedPath = resolveJavaImport(importPath, context.filePath, context.projectRoot);
|
|
3471
|
+
if (resolvedPath) {
|
|
3472
|
+
const sourceId = `${context.filePath}::__file__`;
|
|
3473
|
+
const targetId = `${resolvedPath}::__file__`;
|
|
3474
|
+
context.edges.push({
|
|
3475
|
+
source: sourceId,
|
|
3476
|
+
target: targetId,
|
|
3477
|
+
kind: "imports",
|
|
3478
|
+
filePath: context.filePath,
|
|
3479
|
+
line: node.startPosition.row + 1
|
|
3480
|
+
});
|
|
3481
|
+
const parts = importPath.split(".");
|
|
3482
|
+
if (!isWildcard) {
|
|
3483
|
+
const simpleName = parts[parts.length - 1];
|
|
3484
|
+
context.imports.set(simpleName, `${resolvedPath}::${simpleName}`);
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
const symbolId = `${context.filePath}::import:${importPath}`;
|
|
3488
|
+
context.symbols.push({
|
|
3489
|
+
id: symbolId,
|
|
3490
|
+
name: importPath,
|
|
3491
|
+
kind: "import",
|
|
3492
|
+
filePath: context.filePath,
|
|
3493
|
+
startLine: node.startPosition.row + 1,
|
|
3494
|
+
endLine: node.endPosition.row + 1,
|
|
3495
|
+
exported: false
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
3498
|
+
function processClassDeclaration4(node, context) {
|
|
3499
|
+
processTypeDeclaration3(node, context, "class");
|
|
3500
|
+
}
|
|
3501
|
+
function processInterfaceDeclaration3(node, context) {
|
|
3502
|
+
processTypeDeclaration3(node, context, "interface");
|
|
3503
|
+
}
|
|
3504
|
+
function processRecordDeclaration2(node, context) {
|
|
3505
|
+
processTypeDeclaration3(node, context, "class");
|
|
3506
|
+
}
|
|
3507
|
+
function processAnnotationTypeDeclaration(node, context) {
|
|
3508
|
+
processTypeDeclaration3(node, context, "interface");
|
|
3509
|
+
}
|
|
3510
|
+
function processTypeDeclaration3(node, context, kind) {
|
|
3511
|
+
const nameNode = node.childForFieldName("name");
|
|
3512
|
+
if (!nameNode) return;
|
|
3513
|
+
let name = nodeText7(nameNode, context);
|
|
3514
|
+
const angleBracketIdx = name.indexOf("<");
|
|
3515
|
+
if (angleBracketIdx > 0) {
|
|
3516
|
+
name = name.substring(0, angleBracketIdx);
|
|
3517
|
+
}
|
|
3518
|
+
const exported = hasModifier2(node, context, "public");
|
|
3519
|
+
const scope = context.currentClass || void 0;
|
|
3520
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
3521
|
+
context.symbols.push({
|
|
3522
|
+
id: symbolId,
|
|
3523
|
+
name,
|
|
3524
|
+
kind,
|
|
3525
|
+
filePath: context.filePath,
|
|
3526
|
+
startLine: node.startPosition.row + 1,
|
|
3527
|
+
endLine: node.endPosition.row + 1,
|
|
3528
|
+
exported,
|
|
3529
|
+
scope
|
|
3530
|
+
});
|
|
3531
|
+
const superclass = node.childForFieldName("superclass");
|
|
3532
|
+
if (superclass) {
|
|
3533
|
+
let baseName = extractTypeName3(superclass, context);
|
|
3534
|
+
if (baseName) {
|
|
3535
|
+
const baseId = resolveSymbol7(baseName, context);
|
|
3536
|
+
if (baseId) {
|
|
3537
|
+
context.edges.push({
|
|
3538
|
+
source: symbolId,
|
|
3539
|
+
target: baseId,
|
|
3540
|
+
kind: "inherits",
|
|
3541
|
+
filePath: context.filePath,
|
|
3542
|
+
line: superclass.startPosition.row + 1
|
|
3543
|
+
});
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
const interfaces = node.childForFieldName("interfaces");
|
|
3548
|
+
if (interfaces) {
|
|
3549
|
+
processInterfaceList(interfaces, symbolId, context);
|
|
3550
|
+
}
|
|
3551
|
+
const extendsInterfaces = node.childForFieldName("extends_interfaces") || findChildByType8(node, "extends_interfaces");
|
|
3552
|
+
if (extendsInterfaces) {
|
|
3553
|
+
processInterfaceList(extendsInterfaces, symbolId, context);
|
|
3554
|
+
}
|
|
3555
|
+
const oldClass = context.currentClass;
|
|
3556
|
+
context.currentClass = name;
|
|
3557
|
+
context.currentScope.push(name);
|
|
3558
|
+
const body = node.childForFieldName("body") || findChildByType8(node, "class_body") || findChildByType8(node, "interface_body") || findChildByType8(node, "enum_body") || findChildByType8(node, "annotation_type_body") || findChildByType8(node, "record_declaration_body");
|
|
3559
|
+
if (body) {
|
|
3560
|
+
walkNode8(body, context);
|
|
3561
|
+
}
|
|
3562
|
+
context.currentScope.pop();
|
|
3563
|
+
context.currentClass = oldClass;
|
|
3564
|
+
}
|
|
3565
|
+
function processInterfaceList(node, sourceId, context) {
|
|
3566
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3567
|
+
const child = node.child(i);
|
|
3568
|
+
if (!child) continue;
|
|
3569
|
+
if (child.type === "type_identifier" || child.type === "generic_type" || child.type === "scoped_type_identifier") {
|
|
3570
|
+
const baseName = extractTypeName3(child, context);
|
|
3571
|
+
if (baseName) {
|
|
3572
|
+
const baseId = resolveSymbol7(baseName, context);
|
|
3573
|
+
if (baseId) {
|
|
3574
|
+
context.edges.push({
|
|
3575
|
+
source: sourceId,
|
|
3576
|
+
target: baseId,
|
|
3577
|
+
kind: "implements",
|
|
3578
|
+
filePath: context.filePath,
|
|
3579
|
+
line: child.startPosition.row + 1
|
|
3580
|
+
});
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
function processEnumDeclaration3(node, context) {
|
|
3587
|
+
const nameNode = node.childForFieldName("name");
|
|
3588
|
+
if (!nameNode) return;
|
|
3589
|
+
const name = nodeText7(nameNode, context);
|
|
3590
|
+
const exported = hasModifier2(node, context, "public");
|
|
3591
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
3592
|
+
context.symbols.push({
|
|
3593
|
+
id: symbolId,
|
|
3594
|
+
name,
|
|
3595
|
+
kind: "enum",
|
|
3596
|
+
filePath: context.filePath,
|
|
3597
|
+
startLine: node.startPosition.row + 1,
|
|
3598
|
+
endLine: node.endPosition.row + 1,
|
|
3599
|
+
exported
|
|
3600
|
+
});
|
|
3601
|
+
const body = node.childForFieldName("body") || findChildByType8(node, "enum_body");
|
|
3602
|
+
if (body) {
|
|
3603
|
+
const constants = findChildrenByType3(body, "enum_constant");
|
|
3604
|
+
for (const constant of constants) {
|
|
3605
|
+
const constNameNode = constant.childForFieldName("name");
|
|
3606
|
+
if (!constNameNode) continue;
|
|
3607
|
+
const constName = nodeText7(constNameNode, context);
|
|
3608
|
+
const constId = `${context.filePath}::${name}.${constName}`;
|
|
3609
|
+
context.symbols.push({
|
|
3610
|
+
id: constId,
|
|
3611
|
+
name: constName,
|
|
3612
|
+
kind: "constant",
|
|
3613
|
+
filePath: context.filePath,
|
|
3614
|
+
startLine: constant.startPosition.row + 1,
|
|
3615
|
+
endLine: constant.endPosition.row + 1,
|
|
3616
|
+
exported,
|
|
3617
|
+
scope: name
|
|
3618
|
+
});
|
|
3619
|
+
}
|
|
3620
|
+
const oldClass = context.currentClass;
|
|
3621
|
+
context.currentClass = name;
|
|
3622
|
+
context.currentScope.push(name);
|
|
3623
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
3624
|
+
const child = body.child(i);
|
|
3625
|
+
if (child && child.type !== "enum_constant") {
|
|
3626
|
+
walkNode8(child, context);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
context.currentScope.pop();
|
|
3630
|
+
context.currentClass = oldClass;
|
|
3631
|
+
}
|
|
3632
|
+
}
|
|
3633
|
+
function processMethodDeclaration3(node, context) {
|
|
3634
|
+
const nameNode = node.childForFieldName("name");
|
|
3635
|
+
if (!nameNode) return;
|
|
3636
|
+
const name = nodeText7(nameNode, context);
|
|
3637
|
+
const exported = hasModifier2(node, context, "public");
|
|
3638
|
+
const scope = context.currentClass || void 0;
|
|
3639
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3640
|
+
context.symbols.push({
|
|
3641
|
+
id: symbolId,
|
|
3642
|
+
name,
|
|
3643
|
+
kind: context.currentClass ? "method" : "function",
|
|
3644
|
+
filePath: context.filePath,
|
|
3645
|
+
startLine: node.startPosition.row + 1,
|
|
3646
|
+
endLine: node.endPosition.row + 1,
|
|
3647
|
+
exported,
|
|
3648
|
+
scope
|
|
3649
|
+
});
|
|
3650
|
+
const scopeName = scope ? `${scope}.${name}` : name;
|
|
3651
|
+
context.currentScope.push(scopeName);
|
|
3652
|
+
const body = node.childForFieldName("body");
|
|
3653
|
+
if (body) {
|
|
3654
|
+
walkNode8(body, context);
|
|
3655
|
+
}
|
|
3656
|
+
context.currentScope.pop();
|
|
3657
|
+
}
|
|
3658
|
+
function processConstructorDeclaration2(node, context) {
|
|
3659
|
+
const nameNode = node.childForFieldName("name");
|
|
3660
|
+
if (!nameNode) return;
|
|
3661
|
+
const name = nodeText7(nameNode, context);
|
|
3662
|
+
const exported = hasModifier2(node, context, "public");
|
|
3663
|
+
const scope = context.currentClass || void 0;
|
|
3664
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3665
|
+
context.symbols.push({
|
|
3666
|
+
id: symbolId,
|
|
3667
|
+
name,
|
|
3668
|
+
kind: "method",
|
|
3669
|
+
filePath: context.filePath,
|
|
3670
|
+
startLine: node.startPosition.row + 1,
|
|
3671
|
+
endLine: node.endPosition.row + 1,
|
|
3672
|
+
exported,
|
|
3673
|
+
scope
|
|
3674
|
+
});
|
|
3675
|
+
const scopeName = scope ? `${scope}.${name}` : name;
|
|
3676
|
+
context.currentScope.push(scopeName);
|
|
3677
|
+
const body = node.childForFieldName("body");
|
|
3678
|
+
if (body) {
|
|
3679
|
+
walkNode8(body, context);
|
|
3680
|
+
}
|
|
3681
|
+
context.currentScope.pop();
|
|
3682
|
+
}
|
|
3683
|
+
function processFieldDeclaration(node, context) {
|
|
3684
|
+
const declarator = findDescendantByTypes(node, ["variable_declarator"]);
|
|
3685
|
+
if (!declarator) return;
|
|
3686
|
+
const nameNode = declarator.childForFieldName("name");
|
|
3687
|
+
if (!nameNode) return;
|
|
3688
|
+
const name = nodeText7(nameNode, context);
|
|
3689
|
+
const exported = hasModifier2(node, context, "public");
|
|
3690
|
+
const scope = context.currentClass || void 0;
|
|
3691
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3692
|
+
const isConstant = hasModifier2(node, context, "static") && hasModifier2(node, context, "final");
|
|
3693
|
+
context.symbols.push({
|
|
3694
|
+
id: symbolId,
|
|
3695
|
+
name,
|
|
3696
|
+
kind: isConstant ? "constant" : "property",
|
|
3697
|
+
filePath: context.filePath,
|
|
3698
|
+
startLine: node.startPosition.row + 1,
|
|
3699
|
+
endLine: node.endPosition.row + 1,
|
|
3700
|
+
exported,
|
|
3701
|
+
scope
|
|
3702
|
+
});
|
|
3703
|
+
}
|
|
3704
|
+
function processConstantDeclaration(node, context) {
|
|
3705
|
+
const declarator = findDescendantByTypes(node, ["variable_declarator"]);
|
|
3706
|
+
if (!declarator) return;
|
|
3707
|
+
const nameNode = declarator.childForFieldName("name");
|
|
3708
|
+
if (!nameNode) return;
|
|
3709
|
+
const name = nodeText7(nameNode, context);
|
|
3710
|
+
const scope = context.currentClass || void 0;
|
|
3711
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3712
|
+
context.symbols.push({
|
|
3713
|
+
id: symbolId,
|
|
3714
|
+
name,
|
|
3715
|
+
kind: "constant",
|
|
3716
|
+
filePath: context.filePath,
|
|
3717
|
+
startLine: node.startPosition.row + 1,
|
|
3718
|
+
endLine: node.endPosition.row + 1,
|
|
3719
|
+
exported: true,
|
|
3720
|
+
// Interface constants are always public
|
|
3721
|
+
scope
|
|
3722
|
+
});
|
|
3723
|
+
}
|
|
3724
|
+
function processAnnotationElement(node, context) {
|
|
3725
|
+
const nameNode = node.childForFieldName("name");
|
|
3726
|
+
if (!nameNode) return;
|
|
3727
|
+
const name = nodeText7(nameNode, context);
|
|
3728
|
+
const scope = context.currentClass || void 0;
|
|
3729
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3730
|
+
context.symbols.push({
|
|
3731
|
+
id: symbolId,
|
|
3732
|
+
name,
|
|
3733
|
+
kind: "method",
|
|
3734
|
+
filePath: context.filePath,
|
|
3735
|
+
startLine: node.startPosition.row + 1,
|
|
3736
|
+
endLine: node.endPosition.row + 1,
|
|
3737
|
+
exported: true,
|
|
3738
|
+
scope
|
|
3739
|
+
});
|
|
3740
|
+
}
|
|
3741
|
+
function processCallExpression8(node, context) {
|
|
3742
|
+
const nameNode = node.childForFieldName("name");
|
|
3743
|
+
if (!nameNode) return;
|
|
3744
|
+
const calleeName = nodeText7(nameNode, context);
|
|
3745
|
+
const builtins = [
|
|
3746
|
+
"toString",
|
|
3747
|
+
"equals",
|
|
3748
|
+
"hashCode",
|
|
3749
|
+
"getClass",
|
|
3750
|
+
"println",
|
|
3751
|
+
"printf",
|
|
3752
|
+
"format",
|
|
3753
|
+
"parseInt",
|
|
3754
|
+
"valueOf",
|
|
3755
|
+
"length",
|
|
3756
|
+
"size",
|
|
3757
|
+
"get",
|
|
3758
|
+
"set",
|
|
3759
|
+
"add",
|
|
3760
|
+
"remove",
|
|
3761
|
+
"contains",
|
|
3762
|
+
"isEmpty",
|
|
3763
|
+
"stream",
|
|
3764
|
+
"collect",
|
|
3765
|
+
"map",
|
|
3766
|
+
"filter",
|
|
3767
|
+
"forEach",
|
|
3768
|
+
"of",
|
|
3769
|
+
"orElse",
|
|
3770
|
+
"orElseThrow",
|
|
3771
|
+
"isPresent",
|
|
3772
|
+
"ifPresent",
|
|
3773
|
+
"close",
|
|
3774
|
+
"flush",
|
|
3775
|
+
"write",
|
|
3776
|
+
"read"
|
|
3777
|
+
];
|
|
3778
|
+
if (builtins.includes(calleeName)) return;
|
|
3779
|
+
const callerId = getCurrentSymbolId8(context);
|
|
3780
|
+
if (!callerId) return;
|
|
3781
|
+
const calleeId = resolveSymbol7(calleeName, context);
|
|
3782
|
+
if (calleeId) {
|
|
3783
|
+
context.edges.push({
|
|
3784
|
+
source: callerId,
|
|
3785
|
+
target: calleeId,
|
|
3786
|
+
kind: "calls",
|
|
3787
|
+
filePath: context.filePath,
|
|
3788
|
+
line: node.startPosition.row + 1
|
|
3789
|
+
});
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
function processObjectCreation(node, context) {
|
|
3793
|
+
const typeNode = node.childForFieldName("type");
|
|
3794
|
+
if (!typeNode) return;
|
|
3795
|
+
const typeName = extractTypeName3(typeNode, context);
|
|
3796
|
+
if (!typeName) return;
|
|
3797
|
+
const callerId = getCurrentSymbolId8(context);
|
|
3798
|
+
if (!callerId) return;
|
|
3799
|
+
const targetId = resolveSymbol7(typeName, context);
|
|
3800
|
+
if (targetId) {
|
|
3801
|
+
context.edges.push({
|
|
3802
|
+
source: callerId,
|
|
3803
|
+
target: targetId,
|
|
3804
|
+
kind: "references",
|
|
3805
|
+
filePath: context.filePath,
|
|
3806
|
+
line: node.startPosition.row + 1
|
|
3807
|
+
});
|
|
3808
|
+
}
|
|
3809
|
+
const classBody = findChildByType8(node, "class_body");
|
|
3810
|
+
if (classBody) {
|
|
3811
|
+
const anonName = `<anonymous:${typeName}>`;
|
|
3812
|
+
const anonId = `${context.filePath}::${anonName}:${node.startPosition.row + 1}`;
|
|
3813
|
+
context.symbols.push({
|
|
3814
|
+
id: anonId,
|
|
3815
|
+
name: anonName,
|
|
3816
|
+
kind: "class",
|
|
3817
|
+
filePath: context.filePath,
|
|
3818
|
+
startLine: node.startPosition.row + 1,
|
|
3819
|
+
endLine: node.endPosition.row + 1,
|
|
3820
|
+
exported: false,
|
|
3821
|
+
scope: context.currentClass || void 0
|
|
3822
|
+
});
|
|
3823
|
+
const oldClass = context.currentClass;
|
|
3824
|
+
context.currentClass = anonName;
|
|
3825
|
+
context.currentScope.push(anonName);
|
|
3826
|
+
walkNode8(classBody, context);
|
|
3827
|
+
context.currentScope.pop();
|
|
3828
|
+
context.currentClass = oldClass;
|
|
3829
|
+
}
|
|
3830
|
+
}
|
|
3831
|
+
function processLambdaExpression(node, context) {
|
|
3832
|
+
const parent = node.parent;
|
|
3833
|
+
if (!parent) return;
|
|
3834
|
+
if (parent.type === "variable_declarator") {
|
|
3835
|
+
const nameNode = parent.childForFieldName("name");
|
|
3836
|
+
if (nameNode) {
|
|
3837
|
+
const name = nodeText7(nameNode, context);
|
|
3838
|
+
const scope = context.currentClass || void 0;
|
|
3839
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
3840
|
+
context.symbols.push({
|
|
3841
|
+
id: symbolId,
|
|
3842
|
+
name,
|
|
3843
|
+
kind: "function",
|
|
3844
|
+
filePath: context.filePath,
|
|
3845
|
+
startLine: node.startPosition.row + 1,
|
|
3846
|
+
endLine: node.endPosition.row + 1,
|
|
3847
|
+
exported: false,
|
|
3848
|
+
scope
|
|
3849
|
+
});
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
function parsePomXml(filePath, sourceCode, projectRoot) {
|
|
3854
|
+
const symbols = [];
|
|
3855
|
+
const edges = [];
|
|
3856
|
+
const lines = sourceCode.split("\n");
|
|
3857
|
+
const projectName = basename2(dirname8(join9(projectRoot, filePath)));
|
|
3858
|
+
symbols.push({
|
|
3859
|
+
id: `${filePath}::${projectName}`,
|
|
3860
|
+
name: projectName,
|
|
3861
|
+
kind: "module",
|
|
3862
|
+
filePath,
|
|
3863
|
+
startLine: 1,
|
|
3864
|
+
endLine: lines.length,
|
|
3865
|
+
exported: true
|
|
3866
|
+
});
|
|
3867
|
+
let inDependency = false;
|
|
3868
|
+
let groupId = "";
|
|
3869
|
+
let artifactId = "";
|
|
3870
|
+
let version = "";
|
|
3871
|
+
let depStartLine = 0;
|
|
3872
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3873
|
+
const line = lines[i];
|
|
3874
|
+
const lineNum = i + 1;
|
|
3875
|
+
if (/<dependency>/.test(line)) {
|
|
3876
|
+
inDependency = true;
|
|
3877
|
+
groupId = "";
|
|
3878
|
+
artifactId = "";
|
|
3879
|
+
version = "";
|
|
3880
|
+
depStartLine = lineNum;
|
|
3881
|
+
}
|
|
3882
|
+
if (inDependency) {
|
|
3883
|
+
const gMatch = line.match(/<groupId>([^<]+)<\/groupId>/);
|
|
3884
|
+
if (gMatch) groupId = gMatch[1];
|
|
3885
|
+
const aMatch = line.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
3886
|
+
if (aMatch) artifactId = aMatch[1];
|
|
3887
|
+
const vMatch = line.match(/<version>([^<]+)<\/version>/);
|
|
3888
|
+
if (vMatch) version = vMatch[1];
|
|
3889
|
+
}
|
|
3890
|
+
if (/<\/dependency>/.test(line) && inDependency) {
|
|
3891
|
+
inDependency = false;
|
|
3892
|
+
if (groupId && artifactId) {
|
|
3893
|
+
const depName = `${groupId}:${artifactId}`;
|
|
3894
|
+
const displayVersion = version || "managed";
|
|
3895
|
+
symbols.push({
|
|
3896
|
+
id: `${filePath}::dep:${depName}`,
|
|
3897
|
+
name: `${depName}@${displayVersion}`,
|
|
3898
|
+
kind: "import",
|
|
3899
|
+
filePath,
|
|
3900
|
+
startLine: depStartLine,
|
|
3901
|
+
endLine: lineNum,
|
|
3902
|
+
exported: false
|
|
3903
|
+
});
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
const moduleMatch = line.match(/<module>([^<]+)<\/module>/);
|
|
3907
|
+
if (moduleMatch) {
|
|
3908
|
+
const modulePath = moduleMatch[1];
|
|
3909
|
+
const pomDir = dirname8(join9(projectRoot, filePath));
|
|
3910
|
+
const resolvedModule = resolve4(pomDir, modulePath);
|
|
3911
|
+
const relativeModule = resolvedModule.startsWith(projectRoot + "/") ? resolvedModule.substring(projectRoot.length + 1) : null;
|
|
3912
|
+
if (relativeModule) {
|
|
3913
|
+
const modulePom = join9(relativeModule, "pom.xml");
|
|
3914
|
+
if (existsSync9(join9(projectRoot, modulePom))) {
|
|
3915
|
+
edges.push({
|
|
3916
|
+
source: `${filePath}::__file__`,
|
|
3917
|
+
target: `${modulePom}::__file__`,
|
|
3918
|
+
kind: "imports",
|
|
3919
|
+
filePath,
|
|
3920
|
+
line: lineNum
|
|
3921
|
+
});
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
}
|
|
3926
|
+
return { filePath, symbols, edges };
|
|
3927
|
+
}
|
|
3928
|
+
function parseGradleBuild(filePath, sourceCode, projectRoot) {
|
|
3929
|
+
const symbols = [];
|
|
3930
|
+
const edges = [];
|
|
3931
|
+
const lines = sourceCode.split("\n");
|
|
3932
|
+
const projectName = basename2(dirname8(join9(projectRoot, filePath)));
|
|
3933
|
+
symbols.push({
|
|
3934
|
+
id: `${filePath}::${projectName}`,
|
|
3935
|
+
name: projectName,
|
|
3936
|
+
kind: "module",
|
|
3937
|
+
filePath,
|
|
3938
|
+
startLine: 1,
|
|
3939
|
+
endLine: lines.length,
|
|
3940
|
+
exported: true
|
|
3941
|
+
});
|
|
3942
|
+
for (let i = 0; i < lines.length; i++) {
|
|
3943
|
+
const line = lines[i].trim();
|
|
3944
|
+
const lineNum = i + 1;
|
|
3945
|
+
const depMatch = line.match(
|
|
3946
|
+
/(?:implementation|api|compileOnly|runtimeOnly|testImplementation|testRuntimeOnly|annotationProcessor)\s*[\(]?\s*['"]([^'"]+)['"]\s*[\)]?/
|
|
3947
|
+
);
|
|
3948
|
+
if (depMatch) {
|
|
3949
|
+
const depCoord = depMatch[1];
|
|
3950
|
+
if (!depCoord.startsWith(":")) {
|
|
3951
|
+
symbols.push({
|
|
3952
|
+
id: `${filePath}::dep:${depCoord}`,
|
|
3953
|
+
name: depCoord,
|
|
3954
|
+
kind: "import",
|
|
3955
|
+
filePath,
|
|
3956
|
+
startLine: lineNum,
|
|
3957
|
+
endLine: lineNum,
|
|
3958
|
+
exported: false
|
|
3959
|
+
});
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
const projectMatch = line.match(/project\s*\(\s*['":]+([^'")\s]+)['"]*\s*\)/);
|
|
3963
|
+
if (projectMatch) {
|
|
3964
|
+
const moduleName = projectMatch[1].replace(/^:/, "");
|
|
3965
|
+
const candidates = [
|
|
3966
|
+
join9(moduleName, "build.gradle"),
|
|
3967
|
+
join9(moduleName, "build.gradle.kts")
|
|
3968
|
+
];
|
|
3969
|
+
for (const candidate of candidates) {
|
|
3970
|
+
if (existsSync9(join9(projectRoot, candidate))) {
|
|
3971
|
+
edges.push({
|
|
3972
|
+
source: `${filePath}::__file__`,
|
|
3973
|
+
target: `${candidate}::__file__`,
|
|
3974
|
+
kind: "imports",
|
|
3975
|
+
filePath,
|
|
3976
|
+
line: lineNum
|
|
3977
|
+
});
|
|
3978
|
+
break;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
return { filePath, symbols, edges };
|
|
3984
|
+
}
|
|
3985
|
+
function resolveJavaImport(importPath, currentFile, projectRoot) {
|
|
3986
|
+
const cleanPath2 = importPath.replace(/\.\*$/, "");
|
|
3987
|
+
const javaPath = cleanPath2.replace(/\./g, "/") + ".java";
|
|
3988
|
+
const sourceRoots = [
|
|
3989
|
+
"",
|
|
3990
|
+
"src/main/java",
|
|
3991
|
+
"src",
|
|
3992
|
+
"app/src/main/java"
|
|
3993
|
+
];
|
|
3994
|
+
for (const root of sourceRoots) {
|
|
3995
|
+
const candidate = root ? join9(root, javaPath) : javaPath;
|
|
3996
|
+
const fullPath = join9(projectRoot, candidate);
|
|
3997
|
+
if (existsSync9(fullPath)) {
|
|
3998
|
+
return candidate;
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
if (importPath.endsWith(".*")) {
|
|
4002
|
+
const packagePath = cleanPath2.replace(/\./g, "/");
|
|
4003
|
+
for (const root of sourceRoots) {
|
|
4004
|
+
const candidate = root ? join9(root, packagePath) : packagePath;
|
|
4005
|
+
const fullPath = join9(projectRoot, candidate);
|
|
4006
|
+
if (existsSync9(fullPath)) {
|
|
4007
|
+
try {
|
|
4008
|
+
const stats = statSync3(fullPath);
|
|
4009
|
+
if (stats.isDirectory()) {
|
|
4010
|
+
const javaFiles = readdirSync6(fullPath).filter((f) => f.endsWith(".java"));
|
|
4011
|
+
if (javaFiles.length > 0) {
|
|
4012
|
+
return join9(candidate, javaFiles[0]);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
} catch {
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
return null;
|
|
4021
|
+
}
|
|
4022
|
+
function resolveSymbol7(name, context) {
|
|
4023
|
+
if (context.imports.has(name)) {
|
|
4024
|
+
return context.imports.get(name) || null;
|
|
4025
|
+
}
|
|
4026
|
+
const currentFileId = `${context.filePath}::${name}`;
|
|
4027
|
+
if (context.symbols.find((s) => s.id === currentFileId)) {
|
|
4028
|
+
return currentFileId;
|
|
4029
|
+
}
|
|
4030
|
+
if (context.currentClass) {
|
|
4031
|
+
const classMethodId = `${context.filePath}::${context.currentClass}.${name}`;
|
|
4032
|
+
if (context.symbols.find((s) => s.id === classMethodId)) {
|
|
4033
|
+
return classMethodId;
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
return null;
|
|
4037
|
+
}
|
|
4038
|
+
function hasModifier2(node, context, modifier) {
|
|
4039
|
+
const modifiers = node.childForFieldName("modifiers") || findChildByType8(node, "modifiers");
|
|
4040
|
+
if (modifiers) {
|
|
4041
|
+
for (let i = 0; i < modifiers.childCount; i++) {
|
|
4042
|
+
const child = modifiers.child(i);
|
|
4043
|
+
if (child && nodeText7(child, context) === modifier) {
|
|
4044
|
+
return true;
|
|
4045
|
+
}
|
|
4046
|
+
}
|
|
4047
|
+
}
|
|
4048
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4049
|
+
const child = node.child(i);
|
|
4050
|
+
if (child && child.type === modifier) {
|
|
4051
|
+
return true;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
return false;
|
|
4055
|
+
}
|
|
4056
|
+
function extractTypeName3(node, context) {
|
|
4057
|
+
const text = nodeText7(node, context).trim();
|
|
4058
|
+
if (!text) return null;
|
|
4059
|
+
const angleBracketIdx = text.indexOf("<");
|
|
4060
|
+
const name = angleBracketIdx > 0 ? text.substring(0, angleBracketIdx) : text;
|
|
4061
|
+
const dotIdx = name.lastIndexOf(".");
|
|
4062
|
+
return dotIdx >= 0 ? name.substring(dotIdx + 1) : name;
|
|
4063
|
+
}
|
|
4064
|
+
function findChildByType8(node, type) {
|
|
4065
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4066
|
+
const child = node.child(i);
|
|
4067
|
+
if (child && child.type === type) return child;
|
|
4068
|
+
}
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
function findChildrenByType3(node, type) {
|
|
4072
|
+
const results = [];
|
|
4073
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4074
|
+
const child = node.child(i);
|
|
4075
|
+
if (child && child.type === type) results.push(child);
|
|
4076
|
+
}
|
|
4077
|
+
return results;
|
|
4078
|
+
}
|
|
4079
|
+
function findDescendantByTypes(node, types) {
|
|
4080
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4081
|
+
const child = node.child(i);
|
|
4082
|
+
if (!child) continue;
|
|
4083
|
+
if (types.includes(child.type)) return child;
|
|
4084
|
+
const found = findDescendantByTypes(child, types);
|
|
4085
|
+
if (found) return found;
|
|
4086
|
+
}
|
|
4087
|
+
return null;
|
|
4088
|
+
}
|
|
4089
|
+
function nodeText7(node, context) {
|
|
4090
|
+
return context.sourceCode.substring(node.startIndex, node.endIndex);
|
|
4091
|
+
}
|
|
4092
|
+
function getCurrentSymbolId8(context) {
|
|
4093
|
+
if (context.currentScope.length === 0) return null;
|
|
4094
|
+
return `${context.filePath}::${context.currentScope[context.currentScope.length - 1]}`;
|
|
4095
|
+
}
|
|
4096
|
+
var javaParser = {
|
|
4097
|
+
name: "java",
|
|
4098
|
+
extensions: [".java", "pom.xml", "build.gradle", "build.gradle.kts"],
|
|
4099
|
+
parseFile: parseJavaFile
|
|
4100
|
+
};
|
|
4101
|
+
|
|
4102
|
+
// src/parser/cpp.ts
|
|
4103
|
+
import { dirname as dirname9, join as join10, relative as relative5, basename as basename3 } from "path";
|
|
4104
|
+
import { existsSync as existsSync10 } from "fs";
|
|
4105
|
+
function parseCppFile(filePath, sourceCode, projectRoot) {
|
|
4106
|
+
if (basename3(filePath) === "CMakeLists.txt") {
|
|
4107
|
+
return parseCMakeLists(filePath, sourceCode, projectRoot);
|
|
4108
|
+
}
|
|
4109
|
+
if (basename3(filePath) === "conanfile.txt") {
|
|
4110
|
+
return parseConanfileTxt(filePath, sourceCode, projectRoot);
|
|
4111
|
+
}
|
|
4112
|
+
if (basename3(filePath) === "vcpkg.json") {
|
|
4113
|
+
return parseVcpkgJson(filePath, sourceCode, projectRoot);
|
|
4114
|
+
}
|
|
4115
|
+
const parser = getParser("cpp");
|
|
4116
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
4117
|
+
const context = {
|
|
4118
|
+
filePath,
|
|
4119
|
+
projectRoot,
|
|
4120
|
+
sourceCode,
|
|
4121
|
+
symbols: [],
|
|
4122
|
+
edges: [],
|
|
4123
|
+
currentScope: [],
|
|
4124
|
+
currentClass: null,
|
|
4125
|
+
currentNamespace: null,
|
|
4126
|
+
imports: /* @__PURE__ */ new Map(),
|
|
4127
|
+
isBuildFile: false
|
|
4128
|
+
};
|
|
4129
|
+
walkNode9(tree.rootNode, context);
|
|
4130
|
+
return {
|
|
4131
|
+
filePath,
|
|
4132
|
+
symbols: context.symbols,
|
|
4133
|
+
edges: context.edges
|
|
4134
|
+
};
|
|
4135
|
+
}
|
|
4136
|
+
function walkNode9(node, context) {
|
|
4137
|
+
processNode9(node, context);
|
|
4138
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4139
|
+
const child = node.child(i);
|
|
4140
|
+
if (child) {
|
|
4141
|
+
walkNode9(child, context);
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
function processNode9(node, context) {
|
|
4146
|
+
switch (node.type) {
|
|
4147
|
+
case "namespace_definition":
|
|
4148
|
+
processNamespaceDefinition(node, context);
|
|
4149
|
+
break;
|
|
4150
|
+
case "class_specifier":
|
|
4151
|
+
processClassSpecifier(node, context);
|
|
4152
|
+
break;
|
|
4153
|
+
case "struct_specifier":
|
|
4154
|
+
processStructSpecifier2(node, context);
|
|
4155
|
+
break;
|
|
4156
|
+
case "union_specifier":
|
|
4157
|
+
processUnionSpecifier(node, context);
|
|
4158
|
+
break;
|
|
4159
|
+
case "enum_specifier":
|
|
4160
|
+
processEnumSpecifier2(node, context);
|
|
4161
|
+
break;
|
|
4162
|
+
case "function_definition":
|
|
4163
|
+
processFunctionDefinition3(node, context);
|
|
4164
|
+
break;
|
|
4165
|
+
case "declaration":
|
|
4166
|
+
processDeclaration2(node, context);
|
|
4167
|
+
break;
|
|
4168
|
+
case "alias_declaration":
|
|
4169
|
+
processAliasDeclaration(node, context);
|
|
4170
|
+
break;
|
|
4171
|
+
case "type_definition":
|
|
4172
|
+
processTypeDefinition2(node, context);
|
|
4173
|
+
break;
|
|
4174
|
+
case "preproc_include":
|
|
4175
|
+
processIncludeDirective2(node, context);
|
|
4176
|
+
break;
|
|
4177
|
+
case "preproc_def":
|
|
4178
|
+
case "preproc_function_def":
|
|
4179
|
+
processMacroDefinition2(node, context);
|
|
4180
|
+
break;
|
|
4181
|
+
case "template_declaration":
|
|
4182
|
+
processTemplateDeclaration(node, context);
|
|
4183
|
+
break;
|
|
4184
|
+
case "call_expression":
|
|
4185
|
+
processCallExpression9(node, context);
|
|
4186
|
+
break;
|
|
4187
|
+
case "static_assert_declaration":
|
|
4188
|
+
processStaticAssert(node, context);
|
|
4189
|
+
break;
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
function processNamespaceDefinition(node, context) {
|
|
4193
|
+
const nameNode = node.childForFieldName("name");
|
|
4194
|
+
const name = nameNode ? nodeText8(nameNode, context) : "<anonymous>";
|
|
4195
|
+
if (name !== "<anonymous>") {
|
|
4196
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4197
|
+
context.symbols.push({
|
|
4198
|
+
id: symbolId,
|
|
4199
|
+
name,
|
|
4200
|
+
kind: "module",
|
|
4201
|
+
filePath: context.filePath,
|
|
4202
|
+
startLine: node.startPosition.row + 1,
|
|
4203
|
+
endLine: node.endPosition.row + 1,
|
|
4204
|
+
exported: true
|
|
4205
|
+
});
|
|
4206
|
+
}
|
|
4207
|
+
const oldNamespace = context.currentNamespace;
|
|
4208
|
+
context.currentNamespace = name !== "<anonymous>" ? name : oldNamespace;
|
|
4209
|
+
if (name !== "<anonymous>") context.currentScope.push(name);
|
|
4210
|
+
const body = node.childForFieldName("body") || findChildByType9(node, "declaration_list");
|
|
4211
|
+
if (body) {
|
|
4212
|
+
walkNode9(body, context);
|
|
4213
|
+
}
|
|
4214
|
+
if (name !== "<anonymous>") context.currentScope.pop();
|
|
4215
|
+
context.currentNamespace = oldNamespace;
|
|
4216
|
+
}
|
|
4217
|
+
function processClassSpecifier(node, context) {
|
|
4218
|
+
processTypeSpecifier(node, context, "class");
|
|
4219
|
+
}
|
|
4220
|
+
function processStructSpecifier2(node, context) {
|
|
4221
|
+
processTypeSpecifier(node, context, "class");
|
|
4222
|
+
}
|
|
4223
|
+
function processUnionSpecifier(node, context) {
|
|
4224
|
+
processTypeSpecifier(node, context, "class");
|
|
4225
|
+
}
|
|
4226
|
+
function processTypeSpecifier(node, context, kind) {
|
|
4227
|
+
let nameNode = node.childForFieldName("name");
|
|
4228
|
+
let name = null;
|
|
4229
|
+
if (nameNode) {
|
|
4230
|
+
name = nodeText8(nameNode, context);
|
|
4231
|
+
const angleBracketIdx = name.indexOf("<");
|
|
4232
|
+
if (angleBracketIdx > 0) {
|
|
4233
|
+
name = name.substring(0, angleBracketIdx);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
if (!name) {
|
|
4237
|
+
const parent = node.parent;
|
|
4238
|
+
if (parent && parent.type === "type_definition") {
|
|
4239
|
+
const typedefDecl = parent.childForFieldName("declarator");
|
|
4240
|
+
if (typedefDecl) {
|
|
4241
|
+
name = extractIdentifierFromDeclarator2(typedefDecl, context);
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
}
|
|
4245
|
+
if (!name) return;
|
|
4246
|
+
const exported = true;
|
|
4247
|
+
const scope = context.currentClass || void 0;
|
|
4248
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4249
|
+
context.symbols.push({
|
|
4250
|
+
id: symbolId,
|
|
4251
|
+
name,
|
|
4252
|
+
kind,
|
|
4253
|
+
filePath: context.filePath,
|
|
4254
|
+
startLine: node.startPosition.row + 1,
|
|
4255
|
+
endLine: node.endPosition.row + 1,
|
|
4256
|
+
exported,
|
|
4257
|
+
scope
|
|
4258
|
+
});
|
|
4259
|
+
const baseClause = findChildByType9(node, "base_class_clause");
|
|
4260
|
+
if (baseClause) {
|
|
4261
|
+
processBaseClassClause(baseClause, symbolId, context);
|
|
4262
|
+
}
|
|
4263
|
+
const oldClass = context.currentClass;
|
|
4264
|
+
context.currentClass = name;
|
|
4265
|
+
context.currentScope.push(name);
|
|
4266
|
+
const body = node.childForFieldName("body") || findChildByType9(node, "field_declaration_list");
|
|
4267
|
+
if (body) {
|
|
4268
|
+
walkNode9(body, context);
|
|
4269
|
+
}
|
|
4270
|
+
context.currentScope.pop();
|
|
4271
|
+
context.currentClass = oldClass;
|
|
4272
|
+
}
|
|
4273
|
+
function processBaseClassClause(node, sourceId, context) {
|
|
4274
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4275
|
+
const child = node.child(i);
|
|
4276
|
+
if (!child) continue;
|
|
4277
|
+
if (child.type === "base_class_specifier" || child.type === "type_identifier" || child.type === "qualified_identifier" || child.type === "template_type") {
|
|
4278
|
+
const baseName = extractTypeName4(child, context);
|
|
4279
|
+
if (baseName) {
|
|
4280
|
+
const baseId = resolveSymbol8(baseName, context);
|
|
4281
|
+
if (baseId) {
|
|
4282
|
+
context.edges.push({
|
|
4283
|
+
source: sourceId,
|
|
4284
|
+
target: baseId,
|
|
4285
|
+
kind: "inherits",
|
|
4286
|
+
filePath: context.filePath,
|
|
4287
|
+
line: child.startPosition.row + 1
|
|
4288
|
+
});
|
|
4289
|
+
}
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
function processEnumSpecifier2(node, context) {
|
|
4295
|
+
let nameNode = node.childForFieldName("name");
|
|
4296
|
+
let name = null;
|
|
4297
|
+
if (nameNode) {
|
|
4298
|
+
name = nodeText8(nameNode, context);
|
|
4299
|
+
}
|
|
4300
|
+
if (!name) {
|
|
4301
|
+
const parent = node.parent;
|
|
4302
|
+
if (parent && parent.type === "type_definition") {
|
|
4303
|
+
const typedefDecl = parent.childForFieldName("declarator");
|
|
4304
|
+
if (typedefDecl) {
|
|
4305
|
+
name = extractIdentifierFromDeclarator2(typedefDecl, context);
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
if (!name) return;
|
|
4310
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4311
|
+
context.symbols.push({
|
|
4312
|
+
id: symbolId,
|
|
4313
|
+
name,
|
|
4314
|
+
kind: "enum",
|
|
4315
|
+
filePath: context.filePath,
|
|
4316
|
+
startLine: node.startPosition.row + 1,
|
|
4317
|
+
endLine: node.endPosition.row + 1,
|
|
4318
|
+
exported: true
|
|
4319
|
+
});
|
|
4320
|
+
const body = node.childForFieldName("body") || findChildByType9(node, "enumerator_list");
|
|
4321
|
+
if (body) {
|
|
4322
|
+
for (let i = 0; i < body.childCount; i++) {
|
|
4323
|
+
const child = body.child(i);
|
|
4324
|
+
if (child && child.type === "enumerator") {
|
|
4325
|
+
const constNameNode = child.childForFieldName("name");
|
|
4326
|
+
if (!constNameNode) continue;
|
|
4327
|
+
const constName = nodeText8(constNameNode, context);
|
|
4328
|
+
const constId = `${context.filePath}::${name}.${constName}`;
|
|
4329
|
+
context.symbols.push({
|
|
4330
|
+
id: constId,
|
|
4331
|
+
name: constName,
|
|
4332
|
+
kind: "constant",
|
|
4333
|
+
filePath: context.filePath,
|
|
4334
|
+
startLine: child.startPosition.row + 1,
|
|
4335
|
+
endLine: child.endPosition.row + 1,
|
|
4336
|
+
exported: true,
|
|
4337
|
+
scope: name
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
}
|
|
4343
|
+
function processFunctionDefinition3(node, context) {
|
|
4344
|
+
const declarator = node.childForFieldName("declarator");
|
|
4345
|
+
if (!declarator) return;
|
|
4346
|
+
const nameNode = extractFunctionName2(declarator);
|
|
4347
|
+
if (!nameNode) return;
|
|
4348
|
+
let name = nodeText8(nameNode, context);
|
|
4349
|
+
if (name === "operator") {
|
|
4350
|
+
const fullText = nodeText8(declarator, context);
|
|
4351
|
+
const opMatch = fullText.match(/operator\s*([^\s(]+)/);
|
|
4352
|
+
if (opMatch) {
|
|
4353
|
+
name = `operator${opMatch[1]}`;
|
|
4354
|
+
}
|
|
4355
|
+
}
|
|
4356
|
+
const fullDeclText = nodeText8(declarator, context);
|
|
4357
|
+
if (fullDeclText.includes("~")) {
|
|
4358
|
+
const tildeMatch = fullDeclText.match(/~\s*(\w+)/);
|
|
4359
|
+
if (tildeMatch) {
|
|
4360
|
+
name = `~${tildeMatch[1]}`;
|
|
4361
|
+
}
|
|
4362
|
+
}
|
|
4363
|
+
const isConstructor = context.currentClass !== null && name === context.currentClass;
|
|
4364
|
+
const isDestructor = name.startsWith("~");
|
|
4365
|
+
const isStatic = hasStorageClass2(node, "static", context);
|
|
4366
|
+
const exported = !isStatic;
|
|
4367
|
+
const scope = context.currentClass || void 0;
|
|
4368
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
4369
|
+
context.symbols.push({
|
|
4370
|
+
id: symbolId,
|
|
4371
|
+
name,
|
|
4372
|
+
kind: context.currentClass ? "method" : "function",
|
|
4373
|
+
filePath: context.filePath,
|
|
4374
|
+
startLine: node.startPosition.row + 1,
|
|
4375
|
+
endLine: node.endPosition.row + 1,
|
|
4376
|
+
exported,
|
|
4377
|
+
scope
|
|
4378
|
+
});
|
|
4379
|
+
const scopeName = scope ? `${scope}.${name}` : name;
|
|
4380
|
+
context.currentScope.push(scopeName);
|
|
4381
|
+
const body = node.childForFieldName("body");
|
|
4382
|
+
if (body) {
|
|
4383
|
+
walkNode9(body, context);
|
|
4384
|
+
}
|
|
4385
|
+
context.currentScope.pop();
|
|
4386
|
+
}
|
|
4387
|
+
function processDeclaration2(node, context) {
|
|
4388
|
+
if (context.currentClass) {
|
|
4389
|
+
processFieldDeclaration2(node, context);
|
|
4390
|
+
return;
|
|
4391
|
+
}
|
|
4392
|
+
const parent = node.parent;
|
|
4393
|
+
if (!parent || parent.type !== "translation_unit" && parent.type !== "declaration_list") {
|
|
4394
|
+
return;
|
|
4395
|
+
}
|
|
4396
|
+
const declarator = node.childForFieldName("declarator");
|
|
4397
|
+
if (!declarator) return;
|
|
4398
|
+
if (containsType(declarator, "function_declarator")) {
|
|
4399
|
+
return;
|
|
4400
|
+
}
|
|
4401
|
+
const name = extractIdentifierFromDeclarator2(declarator, context);
|
|
4402
|
+
if (!name) return;
|
|
4403
|
+
const isStatic = hasStorageClass2(node, "static", context);
|
|
4404
|
+
const isConst = nodeText8(node, context).includes("const");
|
|
4405
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4406
|
+
context.symbols.push({
|
|
4407
|
+
id: symbolId,
|
|
4408
|
+
name,
|
|
4409
|
+
kind: isConst ? "constant" : "variable",
|
|
4410
|
+
filePath: context.filePath,
|
|
4411
|
+
startLine: node.startPosition.row + 1,
|
|
4412
|
+
endLine: node.endPosition.row + 1,
|
|
4413
|
+
exported: !isStatic
|
|
4414
|
+
});
|
|
4415
|
+
}
|
|
4416
|
+
function processFieldDeclaration2(node, context) {
|
|
4417
|
+
const declarator = node.childForFieldName("declarator");
|
|
4418
|
+
if (!declarator) return;
|
|
4419
|
+
if (containsType(declarator, "function_declarator")) {
|
|
4420
|
+
const fnName = extractFunctionName2(declarator);
|
|
4421
|
+
if (fnName) {
|
|
4422
|
+
let name2 = nodeText8(fnName, context);
|
|
4423
|
+
const scope2 = context.currentClass || void 0;
|
|
4424
|
+
const symbolId2 = scope2 ? `${context.filePath}::${scope2}.${name2}` : `${context.filePath}::${name2}`;
|
|
4425
|
+
if (!context.symbols.find((s) => s.id === symbolId2)) {
|
|
4426
|
+
const exported = !hasAccessSpecifier(node, "private", context);
|
|
4427
|
+
context.symbols.push({
|
|
4428
|
+
id: symbolId2,
|
|
4429
|
+
name: name2,
|
|
4430
|
+
kind: "method",
|
|
4431
|
+
filePath: context.filePath,
|
|
4432
|
+
startLine: node.startPosition.row + 1,
|
|
4433
|
+
endLine: node.endPosition.row + 1,
|
|
4434
|
+
exported,
|
|
4435
|
+
scope: scope2
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
}
|
|
4439
|
+
return;
|
|
4440
|
+
}
|
|
4441
|
+
const name = extractIdentifierFromDeclarator2(declarator, context);
|
|
4442
|
+
if (!name) return;
|
|
4443
|
+
const scope = context.currentClass || void 0;
|
|
4444
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
4445
|
+
const isConst = nodeText8(node, context).includes("const");
|
|
4446
|
+
const isStatic = nodeText8(node, context).includes("static");
|
|
4447
|
+
context.symbols.push({
|
|
4448
|
+
id: symbolId,
|
|
4449
|
+
name,
|
|
4450
|
+
kind: isConst && isStatic ? "constant" : "property",
|
|
4451
|
+
filePath: context.filePath,
|
|
4452
|
+
startLine: node.startPosition.row + 1,
|
|
4453
|
+
endLine: node.endPosition.row + 1,
|
|
4454
|
+
exported: !hasAccessSpecifier(node, "private", context),
|
|
4455
|
+
scope
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4458
|
+
function processAliasDeclaration(node, context) {
|
|
4459
|
+
const nameNode = node.childForFieldName("name");
|
|
4460
|
+
if (!nameNode) return;
|
|
4461
|
+
const name = nodeText8(nameNode, context);
|
|
4462
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4463
|
+
context.symbols.push({
|
|
4464
|
+
id: symbolId,
|
|
4465
|
+
name,
|
|
4466
|
+
kind: "type_alias",
|
|
4467
|
+
filePath: context.filePath,
|
|
4468
|
+
startLine: node.startPosition.row + 1,
|
|
4469
|
+
endLine: node.endPosition.row + 1,
|
|
4470
|
+
exported: true
|
|
4471
|
+
});
|
|
4472
|
+
}
|
|
4473
|
+
function processTypeDefinition2(node, context) {
|
|
4474
|
+
const typeNode = node.childForFieldName("type");
|
|
4475
|
+
if (!typeNode) return;
|
|
4476
|
+
if (typeNode.type === "struct_specifier" || typeNode.type === "enum_specifier" || typeNode.type === "union_specifier") {
|
|
4477
|
+
return;
|
|
4478
|
+
}
|
|
4479
|
+
const declarator = node.childForFieldName("declarator");
|
|
4480
|
+
if (!declarator) return;
|
|
4481
|
+
const name = extractIdentifierFromDeclarator2(declarator, context);
|
|
4482
|
+
if (!name) return;
|
|
4483
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4484
|
+
context.symbols.push({
|
|
4485
|
+
id: symbolId,
|
|
4486
|
+
name,
|
|
4487
|
+
kind: "type_alias",
|
|
4488
|
+
filePath: context.filePath,
|
|
4489
|
+
startLine: node.startPosition.row + 1,
|
|
4490
|
+
endLine: node.endPosition.row + 1,
|
|
4491
|
+
exported: true
|
|
4492
|
+
});
|
|
4493
|
+
}
|
|
4494
|
+
function processTemplateDeclaration(node, context) {
|
|
4495
|
+
}
|
|
4496
|
+
function processStaticAssert(node, context) {
|
|
4497
|
+
const symbolId = `${context.filePath}::static_assert:${node.startPosition.row + 1}`;
|
|
4498
|
+
context.symbols.push({
|
|
4499
|
+
id: symbolId,
|
|
4500
|
+
name: "static_assert",
|
|
4501
|
+
kind: "constant",
|
|
4502
|
+
filePath: context.filePath,
|
|
4503
|
+
startLine: node.startPosition.row + 1,
|
|
4504
|
+
endLine: node.endPosition.row + 1,
|
|
4505
|
+
exported: false
|
|
4506
|
+
});
|
|
4507
|
+
}
|
|
4508
|
+
function processMacroDefinition2(node, context) {
|
|
4509
|
+
const nameNode = node.childForFieldName("name");
|
|
4510
|
+
if (!nameNode) return;
|
|
4511
|
+
const name = nodeText8(nameNode, context);
|
|
4512
|
+
const kind = node.type === "preproc_function_def" ? "function" : "constant";
|
|
4513
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
4514
|
+
context.symbols.push({
|
|
4515
|
+
id: symbolId,
|
|
4516
|
+
name,
|
|
4517
|
+
kind,
|
|
4518
|
+
filePath: context.filePath,
|
|
4519
|
+
startLine: node.startPosition.row + 1,
|
|
4520
|
+
endLine: node.endPosition.row + 1,
|
|
4521
|
+
exported: true
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
function processIncludeDirective2(node, context) {
|
|
4525
|
+
const pathNode = node.childForFieldName("path");
|
|
4526
|
+
if (!pathNode) return;
|
|
4527
|
+
const pathText = nodeText8(pathNode, context);
|
|
4528
|
+
const isLocalInclude = pathText.startsWith('"') && pathText.endsWith('"');
|
|
4529
|
+
if (!isLocalInclude) {
|
|
4530
|
+
const includeName = pathText.replace(/[<>"]/g, "");
|
|
4531
|
+
const symbolId = `${context.filePath}::include:${includeName}`;
|
|
4532
|
+
context.symbols.push({
|
|
4533
|
+
id: symbolId,
|
|
4534
|
+
name: includeName,
|
|
4535
|
+
kind: "import",
|
|
4536
|
+
filePath: context.filePath,
|
|
4537
|
+
startLine: node.startPosition.row + 1,
|
|
4538
|
+
endLine: node.endPosition.row + 1,
|
|
4539
|
+
exported: false
|
|
4540
|
+
});
|
|
4541
|
+
return;
|
|
4542
|
+
}
|
|
4543
|
+
const includePath = pathText.slice(1, -1);
|
|
4544
|
+
const resolvedFiles = resolveIncludePath2(includePath, context.filePath, context.projectRoot);
|
|
4545
|
+
if (resolvedFiles.length === 0) return;
|
|
4546
|
+
const sourceId = `${context.filePath}::__file__`;
|
|
4547
|
+
for (const targetPath of resolvedFiles) {
|
|
4548
|
+
const targetId = `${targetPath}::__file__`;
|
|
4549
|
+
context.edges.push({
|
|
4550
|
+
source: sourceId,
|
|
4551
|
+
target: targetId,
|
|
4552
|
+
kind: "imports",
|
|
4553
|
+
filePath: context.filePath,
|
|
4554
|
+
line: node.startPosition.row + 1
|
|
4555
|
+
});
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
function processCallExpression9(node, context) {
|
|
4559
|
+
if (context.currentScope.length === 0) return;
|
|
4560
|
+
const functionNode = node.childForFieldName("function");
|
|
4561
|
+
if (!functionNode) return;
|
|
4562
|
+
let calleeName = null;
|
|
4563
|
+
if (functionNode.type === "identifier") {
|
|
4564
|
+
calleeName = nodeText8(functionNode, context);
|
|
4565
|
+
} else if (functionNode.type === "field_expression" || functionNode.type === "qualified_identifier") {
|
|
4566
|
+
const nameNode = functionNode.childForFieldName("name") || functionNode.childForFieldName("field");
|
|
4567
|
+
if (nameNode) {
|
|
4568
|
+
calleeName = nodeText8(nameNode, context);
|
|
4569
|
+
}
|
|
4570
|
+
} else if (functionNode.type === "template_function") {
|
|
4571
|
+
const nameNode = functionNode.childForFieldName("name");
|
|
4572
|
+
if (nameNode) {
|
|
4573
|
+
calleeName = nodeText8(nameNode, context);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
if (!calleeName) return;
|
|
4577
|
+
const builtins = /* @__PURE__ */ new Set([
|
|
4578
|
+
"printf",
|
|
4579
|
+
"scanf",
|
|
4580
|
+
"malloc",
|
|
4581
|
+
"free",
|
|
4582
|
+
"memcpy",
|
|
4583
|
+
"strlen",
|
|
4584
|
+
"strcmp",
|
|
4585
|
+
"strcpy",
|
|
4586
|
+
"strcat",
|
|
4587
|
+
"cout",
|
|
4588
|
+
"cin",
|
|
4589
|
+
"cerr",
|
|
4590
|
+
"endl",
|
|
4591
|
+
"make_shared",
|
|
4592
|
+
"make_unique",
|
|
4593
|
+
"make_pair",
|
|
4594
|
+
"make_tuple",
|
|
4595
|
+
"move",
|
|
4596
|
+
"forward",
|
|
4597
|
+
"swap",
|
|
4598
|
+
"begin",
|
|
4599
|
+
"end",
|
|
4600
|
+
"size",
|
|
4601
|
+
"empty",
|
|
4602
|
+
"push_back",
|
|
4603
|
+
"emplace_back",
|
|
4604
|
+
"insert",
|
|
4605
|
+
"erase",
|
|
4606
|
+
"find",
|
|
4607
|
+
"sort",
|
|
4608
|
+
"transform",
|
|
4609
|
+
"for_each",
|
|
4610
|
+
"accumulate",
|
|
4611
|
+
"static_cast",
|
|
4612
|
+
"dynamic_cast",
|
|
4613
|
+
"reinterpret_cast",
|
|
4614
|
+
"const_cast"
|
|
4615
|
+
]);
|
|
4616
|
+
if (builtins.has(calleeName)) return;
|
|
4617
|
+
const callerId = getCurrentSymbolId9(context);
|
|
4618
|
+
if (!callerId) return;
|
|
4619
|
+
const calleeId = resolveSymbol8(calleeName, context);
|
|
4620
|
+
if (calleeId) {
|
|
4621
|
+
context.edges.push({
|
|
4622
|
+
source: callerId,
|
|
4623
|
+
target: calleeId,
|
|
4624
|
+
kind: "calls",
|
|
4625
|
+
filePath: context.filePath,
|
|
4626
|
+
line: node.startPosition.row + 1
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
function parseCMakeLists(filePath, sourceCode, projectRoot) {
|
|
4631
|
+
const symbols = [];
|
|
4632
|
+
const edges = [];
|
|
4633
|
+
const lines = sourceCode.split("\n");
|
|
4634
|
+
const projectName = basename3(dirname9(join10(projectRoot, filePath)));
|
|
4635
|
+
symbols.push({
|
|
4636
|
+
id: `${filePath}::${projectName}`,
|
|
4637
|
+
name: projectName,
|
|
4638
|
+
kind: "module",
|
|
4639
|
+
filePath,
|
|
4640
|
+
startLine: 1,
|
|
4641
|
+
endLine: lines.length,
|
|
4642
|
+
exported: true
|
|
4643
|
+
});
|
|
4644
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4645
|
+
const line = lines[i].trim();
|
|
4646
|
+
const lineNum = i + 1;
|
|
4647
|
+
const findPkgMatch = line.match(/find_package\s*\(\s*(\w+)/i);
|
|
4648
|
+
if (findPkgMatch) {
|
|
4649
|
+
symbols.push({
|
|
4650
|
+
id: `${filePath}::dep:${findPkgMatch[1]}`,
|
|
4651
|
+
name: findPkgMatch[1],
|
|
4652
|
+
kind: "import",
|
|
4653
|
+
filePath,
|
|
4654
|
+
startLine: lineNum,
|
|
4655
|
+
endLine: lineNum,
|
|
4656
|
+
exported: false
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4659
|
+
const linkLibsMatch = line.match(/target_link_libraries\s*\(\s*\w+\s+(?:PRIVATE|PUBLIC|INTERFACE)?\s*(.*)\)/i);
|
|
4660
|
+
if (linkLibsMatch) {
|
|
4661
|
+
const libs = linkLibsMatch[1].trim().split(/\s+/).filter((l) => l && !["PRIVATE", "PUBLIC", "INTERFACE"].includes(l));
|
|
4662
|
+
for (const lib of libs) {
|
|
4663
|
+
symbols.push({
|
|
4664
|
+
id: `${filePath}::dep:${lib}`,
|
|
4665
|
+
name: lib,
|
|
4666
|
+
kind: "import",
|
|
4667
|
+
filePath,
|
|
4668
|
+
startLine: lineNum,
|
|
4669
|
+
endLine: lineNum,
|
|
4670
|
+
exported: false
|
|
4671
|
+
});
|
|
4672
|
+
}
|
|
4673
|
+
}
|
|
4674
|
+
const addSubdirMatch = line.match(/add_subdirectory\s*\(\s*([^\s)]+)/i);
|
|
4675
|
+
if (addSubdirMatch) {
|
|
4676
|
+
const subdir = addSubdirMatch[1];
|
|
4677
|
+
const cmakeDir = dirname9(join10(projectRoot, filePath));
|
|
4678
|
+
const subdirCMake = join10(relative5(projectRoot, cmakeDir), subdir, "CMakeLists.txt");
|
|
4679
|
+
if (existsSync10(join10(projectRoot, subdirCMake))) {
|
|
4680
|
+
edges.push({
|
|
4681
|
+
source: `${filePath}::__file__`,
|
|
4682
|
+
target: `${subdirCMake}::__file__`,
|
|
4683
|
+
kind: "imports",
|
|
4684
|
+
filePath,
|
|
4685
|
+
line: lineNum
|
|
4686
|
+
});
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
const projectMatch = line.match(/project\s*\(\s*(\w+)/i);
|
|
4690
|
+
if (projectMatch) {
|
|
4691
|
+
symbols.push({
|
|
4692
|
+
id: `${filePath}::project:${projectMatch[1]}`,
|
|
4693
|
+
name: projectMatch[1],
|
|
4694
|
+
kind: "module",
|
|
4695
|
+
filePath,
|
|
4696
|
+
startLine: lineNum,
|
|
4697
|
+
endLine: lineNum,
|
|
4698
|
+
exported: true
|
|
4699
|
+
});
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
return { filePath, symbols, edges };
|
|
4703
|
+
}
|
|
4704
|
+
function parseConanfileTxt(filePath, sourceCode, _projectRoot) {
|
|
4705
|
+
const symbols = [];
|
|
4706
|
+
const lines = sourceCode.split("\n");
|
|
4707
|
+
let inRequires = false;
|
|
4708
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4709
|
+
const line = lines[i].trim();
|
|
4710
|
+
const lineNum = i + 1;
|
|
4711
|
+
if (line === "[requires]") {
|
|
4712
|
+
inRequires = true;
|
|
4713
|
+
continue;
|
|
4714
|
+
}
|
|
4715
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
4716
|
+
inRequires = false;
|
|
4717
|
+
continue;
|
|
4718
|
+
}
|
|
4719
|
+
if (inRequires && line.length > 0) {
|
|
4720
|
+
symbols.push({
|
|
4721
|
+
id: `${filePath}::dep:${line}`,
|
|
4722
|
+
name: line,
|
|
4723
|
+
kind: "import",
|
|
4724
|
+
filePath,
|
|
4725
|
+
startLine: lineNum,
|
|
4726
|
+
endLine: lineNum,
|
|
4727
|
+
exported: false
|
|
4728
|
+
});
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
return { filePath, symbols, edges: [] };
|
|
4732
|
+
}
|
|
4733
|
+
function parseVcpkgJson(filePath, sourceCode, _projectRoot) {
|
|
4734
|
+
const symbols = [];
|
|
4735
|
+
try {
|
|
4736
|
+
const vcpkg = JSON.parse(sourceCode);
|
|
4737
|
+
if (vcpkg.dependencies && Array.isArray(vcpkg.dependencies)) {
|
|
4738
|
+
for (let i = 0; i < vcpkg.dependencies.length; i++) {
|
|
4739
|
+
const dep = vcpkg.dependencies[i];
|
|
4740
|
+
const name = typeof dep === "string" ? dep : dep.name || "";
|
|
4741
|
+
if (name) {
|
|
4742
|
+
symbols.push({
|
|
4743
|
+
id: `${filePath}::dep:${name}`,
|
|
4744
|
+
name,
|
|
4745
|
+
kind: "import",
|
|
4746
|
+
filePath,
|
|
4747
|
+
startLine: 1,
|
|
4748
|
+
endLine: 1,
|
|
4749
|
+
exported: false
|
|
4750
|
+
});
|
|
4751
|
+
}
|
|
4752
|
+
}
|
|
4753
|
+
}
|
|
4754
|
+
} catch {
|
|
4755
|
+
}
|
|
4756
|
+
return { filePath, symbols, edges: [] };
|
|
4757
|
+
}
|
|
4758
|
+
function resolveIncludePath2(includePath, currentFile, projectRoot) {
|
|
4759
|
+
const currentFileAbs = join10(projectRoot, currentFile);
|
|
4760
|
+
const currentDir = dirname9(currentFileAbs);
|
|
4761
|
+
const possibleFiles = [
|
|
4762
|
+
join10(currentDir, includePath),
|
|
4763
|
+
join10(projectRoot, includePath),
|
|
4764
|
+
join10(projectRoot, "include", includePath),
|
|
4765
|
+
join10(projectRoot, "src", includePath)
|
|
4766
|
+
];
|
|
4767
|
+
const resolvedFiles = [];
|
|
4768
|
+
for (const absPath of possibleFiles) {
|
|
4769
|
+
if (existsSync10(absPath)) {
|
|
4770
|
+
const relPath = relative5(projectRoot, absPath);
|
|
4771
|
+
if (!resolvedFiles.includes(relPath)) {
|
|
4772
|
+
resolvedFiles.push(relPath);
|
|
4773
|
+
}
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
return resolvedFiles;
|
|
4777
|
+
}
|
|
4778
|
+
function resolveSymbol8(name, context) {
|
|
4779
|
+
if (context.imports.has(name)) {
|
|
4780
|
+
return context.imports.get(name) || null;
|
|
4781
|
+
}
|
|
4782
|
+
const currentFileId = `${context.filePath}::${name}`;
|
|
4783
|
+
if (context.symbols.find((s) => s.id === currentFileId)) {
|
|
4784
|
+
return currentFileId;
|
|
4785
|
+
}
|
|
4786
|
+
if (context.currentClass) {
|
|
4787
|
+
const classMethodId = `${context.filePath}::${context.currentClass}.${name}`;
|
|
4788
|
+
if (context.symbols.find((s) => s.id === classMethodId)) {
|
|
4789
|
+
return classMethodId;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
return null;
|
|
4793
|
+
}
|
|
4794
|
+
function hasStorageClass2(node, className, context) {
|
|
4795
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4796
|
+
const child = node.child(i);
|
|
4797
|
+
if (child && child.type === "storage_class_specifier") {
|
|
4798
|
+
if (nodeText8(child, context) === className) {
|
|
4799
|
+
return true;
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
return false;
|
|
4804
|
+
}
|
|
4805
|
+
function hasAccessSpecifier(node, specifier, context) {
|
|
4806
|
+
const parent = node.parent;
|
|
4807
|
+
if (!parent) return false;
|
|
4808
|
+
let lastAccess = "";
|
|
4809
|
+
for (let i = 0; i < parent.childCount; i++) {
|
|
4810
|
+
const child = parent.child(i);
|
|
4811
|
+
if (!child) continue;
|
|
4812
|
+
if (child.type === "access_specifier") {
|
|
4813
|
+
lastAccess = nodeText8(child, context).replace(":", "").trim();
|
|
4814
|
+
}
|
|
4815
|
+
if (child === node) break;
|
|
4816
|
+
}
|
|
4817
|
+
return lastAccess === specifier;
|
|
4818
|
+
}
|
|
4819
|
+
function extractFunctionName2(declarator) {
|
|
4820
|
+
if (declarator.type === "identifier") {
|
|
4821
|
+
return declarator;
|
|
4822
|
+
}
|
|
4823
|
+
if (declarator.type === "function_declarator") {
|
|
4824
|
+
const innerDeclarator = declarator.childForFieldName("declarator");
|
|
4825
|
+
if (innerDeclarator) {
|
|
4826
|
+
return extractFunctionName2(innerDeclarator);
|
|
4827
|
+
}
|
|
4828
|
+
}
|
|
4829
|
+
if (declarator.type === "pointer_declarator" || declarator.type === "reference_declarator") {
|
|
4830
|
+
const innerDeclarator = declarator.childForFieldName("declarator");
|
|
4831
|
+
if (innerDeclarator) {
|
|
4832
|
+
return extractFunctionName2(innerDeclarator);
|
|
4833
|
+
}
|
|
4834
|
+
}
|
|
4835
|
+
if (declarator.type === "qualified_identifier" || declarator.type === "template_function") {
|
|
4836
|
+
const nameNode = declarator.childForFieldName("name");
|
|
4837
|
+
if (nameNode) {
|
|
4838
|
+
return extractFunctionName2(nameNode);
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4841
|
+
if (declarator.type === "destructor_name") {
|
|
4842
|
+
return declarator;
|
|
4843
|
+
}
|
|
4844
|
+
if (declarator.type === "operator_name") {
|
|
4845
|
+
return declarator;
|
|
4846
|
+
}
|
|
4847
|
+
for (let i = 0; i < declarator.childCount; i++) {
|
|
4848
|
+
const child = declarator.child(i);
|
|
4849
|
+
if (child && child.type === "identifier") {
|
|
4850
|
+
return child;
|
|
4851
|
+
}
|
|
4852
|
+
}
|
|
4853
|
+
return null;
|
|
4854
|
+
}
|
|
4855
|
+
function extractIdentifierFromDeclarator2(declarator, context) {
|
|
4856
|
+
if (declarator.type === "identifier") {
|
|
4857
|
+
return nodeText8(declarator, context);
|
|
4858
|
+
}
|
|
4859
|
+
if (declarator.type === "type_identifier") {
|
|
4860
|
+
return nodeText8(declarator, context);
|
|
4861
|
+
}
|
|
4862
|
+
const identifierNode = findChildByType9(declarator, "identifier");
|
|
4863
|
+
if (identifierNode) {
|
|
4864
|
+
return nodeText8(identifierNode, context);
|
|
4865
|
+
}
|
|
4866
|
+
const typeIdNode = findChildByType9(declarator, "type_identifier");
|
|
4867
|
+
if (typeIdNode) {
|
|
4868
|
+
return nodeText8(typeIdNode, context);
|
|
4869
|
+
}
|
|
4870
|
+
for (let i = 0; i < declarator.childCount; i++) {
|
|
4871
|
+
const child = declarator.child(i);
|
|
4872
|
+
if (child) {
|
|
4873
|
+
const name = extractIdentifierFromDeclarator2(child, context);
|
|
4874
|
+
if (name) return name;
|
|
4875
|
+
}
|
|
4876
|
+
}
|
|
4877
|
+
return null;
|
|
4878
|
+
}
|
|
4879
|
+
function extractTypeName4(node, context) {
|
|
4880
|
+
const text = nodeText8(node, context).trim();
|
|
4881
|
+
if (!text || text === ":" || text === ",") return null;
|
|
4882
|
+
const accessStripped = text.replace(/^(?:public|protected|private|virtual)\s+/g, "");
|
|
4883
|
+
const angleBracketIdx = accessStripped.indexOf("<");
|
|
4884
|
+
const name = angleBracketIdx > 0 ? accessStripped.substring(0, angleBracketIdx) : accessStripped;
|
|
4885
|
+
const colonIdx = name.lastIndexOf("::");
|
|
4886
|
+
return colonIdx >= 0 ? name.substring(colonIdx + 2) : name;
|
|
4887
|
+
}
|
|
4888
|
+
function containsType(node, type) {
|
|
4889
|
+
if (node.type === type) return true;
|
|
4890
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4891
|
+
const child = node.child(i);
|
|
4892
|
+
if (child && containsType(child, type)) return true;
|
|
4893
|
+
}
|
|
4894
|
+
return false;
|
|
4895
|
+
}
|
|
4896
|
+
function findChildByType9(node, type) {
|
|
4897
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
4898
|
+
const child = node.child(i);
|
|
4899
|
+
if (child && child.type === type) return child;
|
|
4900
|
+
}
|
|
4901
|
+
return null;
|
|
4902
|
+
}
|
|
4903
|
+
function nodeText8(node, context) {
|
|
4904
|
+
return context.sourceCode.substring(node.startIndex, node.endIndex);
|
|
4905
|
+
}
|
|
4906
|
+
function getCurrentSymbolId9(context) {
|
|
4907
|
+
if (context.currentScope.length === 0) return null;
|
|
4908
|
+
return `${context.filePath}::${context.currentScope[context.currentScope.length - 1]}`;
|
|
4909
|
+
}
|
|
4910
|
+
var cppParser = {
|
|
4911
|
+
name: "cpp",
|
|
4912
|
+
extensions: [
|
|
4913
|
+
".cpp",
|
|
4914
|
+
".cc",
|
|
4915
|
+
".cxx",
|
|
4916
|
+
".c++",
|
|
4917
|
+
".hpp",
|
|
4918
|
+
".hh",
|
|
4919
|
+
".hxx",
|
|
4920
|
+
".h++",
|
|
4921
|
+
".inl",
|
|
4922
|
+
".ipp",
|
|
4923
|
+
"CMakeLists.txt",
|
|
4924
|
+
"conanfile.txt",
|
|
4925
|
+
"vcpkg.json"
|
|
4926
|
+
],
|
|
4927
|
+
parseFile: parseCppFile
|
|
4928
|
+
};
|
|
4929
|
+
|
|
3345
4930
|
// src/parser/detect.ts
|
|
3346
4931
|
var parsers = [
|
|
3347
4932
|
typescriptParser,
|
|
@@ -3350,11 +4935,24 @@ var parsers = [
|
|
|
3350
4935
|
goParser,
|
|
3351
4936
|
rustParser,
|
|
3352
4937
|
cParser,
|
|
3353
|
-
csharpParser
|
|
4938
|
+
csharpParser,
|
|
4939
|
+
javaParser,
|
|
4940
|
+
cppParser
|
|
3354
4941
|
];
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
4942
|
+
var CPP_KEYWORDS = /\b(?:class|namespace|template|public:|private:|protected:|virtual|nullptr|constexpr|auto\s+\w+\s*=|using\s+\w+\s*=|static_cast|dynamic_cast|reinterpret_cast|const_cast|noexcept|override|final|decltype|concept|requires|co_await|co_yield|co_return|std::)\b/;
|
|
4943
|
+
function getParserForFile(filePath, content) {
|
|
4944
|
+
const ext = extname6(filePath).toLowerCase();
|
|
4945
|
+
const fileName = basename4(filePath);
|
|
4946
|
+
if (ext === ".h" && content) {
|
|
4947
|
+
if (CPP_KEYWORDS.test(content)) {
|
|
4948
|
+
return cppParser;
|
|
4949
|
+
}
|
|
4950
|
+
return cParser;
|
|
4951
|
+
}
|
|
4952
|
+
if (ext === ".h") {
|
|
4953
|
+
return cParser;
|
|
4954
|
+
}
|
|
4955
|
+
return parsers.find((p) => p.extensions.includes(ext) || p.extensions.includes(fileName)) || null;
|
|
3358
4956
|
}
|
|
3359
4957
|
|
|
3360
4958
|
// src/parser/index.ts
|
|
@@ -3362,7 +4960,7 @@ import { minimatch } from "minimatch";
|
|
|
3362
4960
|
var MAX_FILE_SIZE = 1e6;
|
|
3363
4961
|
function shouldParseFile(fullPath) {
|
|
3364
4962
|
try {
|
|
3365
|
-
const stats =
|
|
4963
|
+
const stats = statSync5(fullPath);
|
|
3366
4964
|
if (stats.size > MAX_FILE_SIZE) {
|
|
3367
4965
|
console.error(`[Parser] Skipping ${fullPath} \u2014 file too large (${(stats.size / 1024).toFixed(0)}KB)`);
|
|
3368
4966
|
return false;
|
|
@@ -3380,8 +4978,8 @@ async function parseProject(projectRoot, options) {
|
|
|
3380
4978
|
let errorFiles = 0;
|
|
3381
4979
|
for (const file of files) {
|
|
3382
4980
|
try {
|
|
3383
|
-
const fullPath =
|
|
3384
|
-
if (!
|
|
4981
|
+
const fullPath = join11(projectRoot, file);
|
|
4982
|
+
if (!resolve5(fullPath).startsWith(resolve5(projectRoot))) {
|
|
3385
4983
|
skippedFiles++;
|
|
3386
4984
|
continue;
|
|
3387
4985
|
}
|
|
@@ -3404,13 +5002,13 @@ async function parseProject(projectRoot, options) {
|
|
|
3404
5002
|
if (options?.verbose) {
|
|
3405
5003
|
console.error(`[Parser] Parsing: ${file}`);
|
|
3406
5004
|
}
|
|
3407
|
-
const
|
|
5005
|
+
const sourceCode = readFileSync8(fullPath, "utf-8");
|
|
5006
|
+
const parser = getParserForFile(file, sourceCode);
|
|
3408
5007
|
if (!parser) {
|
|
3409
5008
|
console.error(`No parser found for file: ${file}`);
|
|
3410
5009
|
skippedFiles++;
|
|
3411
5010
|
continue;
|
|
3412
5011
|
}
|
|
3413
|
-
const sourceCode = readFileSync6(fullPath, "utf-8");
|
|
3414
5012
|
const parsed = parser.parseFile(file, sourceCode, projectRoot);
|
|
3415
5013
|
parsedFiles.push(parsed);
|
|
3416
5014
|
} catch (err) {
|
|
@@ -3433,14 +5031,16 @@ async function parseProject(projectRoot, options) {
|
|
|
3433
5031
|
}
|
|
3434
5032
|
|
|
3435
5033
|
// src/cross-language/detectors/rest-api.ts
|
|
3436
|
-
import { readFileSync as
|
|
3437
|
-
import { join as
|
|
5034
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
5035
|
+
import { join as join12, resolve as resolve6 } from "path";
|
|
3438
5036
|
function getLanguage(filePath) {
|
|
3439
5037
|
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
3440
5038
|
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
3441
5039
|
if (filePath.endsWith(".py")) return "python";
|
|
3442
5040
|
if (filePath.endsWith(".go")) return "go";
|
|
3443
5041
|
if (filePath.endsWith(".cs") || filePath.endsWith(".csx")) return "csharp";
|
|
5042
|
+
if (filePath.endsWith(".java")) return "java";
|
|
5043
|
+
if (filePath.endsWith(".cpp") || filePath.endsWith(".cc") || filePath.endsWith(".cxx") || filePath.endsWith(".c++") || filePath.endsWith(".hpp") || filePath.endsWith(".hh") || filePath.endsWith(".hxx") || filePath.endsWith(".h++") || filePath.endsWith(".h") || filePath.endsWith(".inl") || filePath.endsWith(".ipp")) return "cpp";
|
|
3444
5044
|
return "unknown";
|
|
3445
5045
|
}
|
|
3446
5046
|
function normalizePath(routePath) {
|
|
@@ -3620,9 +5220,159 @@ function extractRouteDefinitions(source, filePath) {
|
|
|
3620
5220
|
}
|
|
3621
5221
|
}
|
|
3622
5222
|
}
|
|
5223
|
+
if (lang === "java") {
|
|
5224
|
+
const springMethodMatch = line.match(/@(Get|Post|Put|Delete|Patch)Mapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
|
|
5225
|
+
if (springMethodMatch) {
|
|
5226
|
+
const method = springMethodMatch[1].toUpperCase();
|
|
5227
|
+
let path6 = springMethodMatch[2];
|
|
5228
|
+
const classPrefix = findClassLevelPrefix(source);
|
|
5229
|
+
if (classPrefix) path6 = classPrefix + path6;
|
|
5230
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
5231
|
+
routes.push({
|
|
5232
|
+
method,
|
|
5233
|
+
path: path6,
|
|
5234
|
+
normalizedPath: normalizePath(path6),
|
|
5235
|
+
file: filePath,
|
|
5236
|
+
line: i + 1
|
|
5237
|
+
});
|
|
5238
|
+
}
|
|
5239
|
+
if (!springMethodMatch) {
|
|
5240
|
+
const springNoPathMatch = line.match(/@(Get|Post|Put|Delete|Patch)Mapping\s*$/);
|
|
5241
|
+
if (springNoPathMatch) {
|
|
5242
|
+
const method = springNoPathMatch[1].toUpperCase();
|
|
5243
|
+
const classPrefix = findClassLevelPrefix(source);
|
|
5244
|
+
if (classPrefix) {
|
|
5245
|
+
routes.push({
|
|
5246
|
+
method,
|
|
5247
|
+
path: classPrefix,
|
|
5248
|
+
normalizedPath: normalizePath(classPrefix),
|
|
5249
|
+
file: filePath,
|
|
5250
|
+
line: i + 1
|
|
5251
|
+
});
|
|
5252
|
+
}
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
const requestMappingMatch = line.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
|
|
5256
|
+
if (requestMappingMatch) {
|
|
5257
|
+
let path6 = requestMappingMatch[1];
|
|
5258
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
5259
|
+
const methodMatch = line.match(/method\s*=\s*RequestMethod\.(\w+)/);
|
|
5260
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "ANY";
|
|
5261
|
+
routes.push({
|
|
5262
|
+
method,
|
|
5263
|
+
path: path6,
|
|
5264
|
+
normalizedPath: normalizePath(path6),
|
|
5265
|
+
file: filePath,
|
|
5266
|
+
line: i + 1
|
|
5267
|
+
});
|
|
5268
|
+
}
|
|
5269
|
+
const jaxPathMatch = line.match(/@Path\s*\(\s*["']([^"']+)["']\s*\)/);
|
|
5270
|
+
if (jaxPathMatch) {
|
|
5271
|
+
let path6 = jaxPathMatch[1];
|
|
5272
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
5273
|
+
const nextLine = i + 1 < lines.length ? lines[i + 1] : "";
|
|
5274
|
+
const prevLine = i > 0 ? lines[i - 1] : "";
|
|
5275
|
+
const jaxMethodMatch = (nextLine + prevLine).match(/@(GET|POST|PUT|DELETE|PATCH)/);
|
|
5276
|
+
const method = jaxMethodMatch ? jaxMethodMatch[1] : "ANY";
|
|
5277
|
+
routes.push({
|
|
5278
|
+
method,
|
|
5279
|
+
path: path6,
|
|
5280
|
+
normalizedPath: normalizePath(path6),
|
|
5281
|
+
file: filePath,
|
|
5282
|
+
line: i + 1
|
|
5283
|
+
});
|
|
5284
|
+
}
|
|
5285
|
+
const webFluxMatch = line.match(/(?:route|andRoute)\s*\(\s*(GET|POST|PUT|DELETE|PATCH)\s*\(\s*["']([^"']+)["']\s*\)/);
|
|
5286
|
+
if (webFluxMatch) {
|
|
5287
|
+
const path6 = webFluxMatch[2].startsWith("/") ? webFluxMatch[2] : "/" + webFluxMatch[2];
|
|
5288
|
+
routes.push({
|
|
5289
|
+
method: webFluxMatch[1].toUpperCase(),
|
|
5290
|
+
path: path6,
|
|
5291
|
+
normalizedPath: normalizePath(path6),
|
|
5292
|
+
file: filePath,
|
|
5293
|
+
line: i + 1
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
}
|
|
5297
|
+
if (lang === "cpp") {
|
|
5298
|
+
const crowMatch = line.match(/CROW_ROUTE\s*\(\s*\w+\s*,\s*"([^"]+)"/);
|
|
5299
|
+
if (crowMatch) {
|
|
5300
|
+
const path6 = crowMatch[1];
|
|
5301
|
+
if (path6.startsWith("/")) {
|
|
5302
|
+
const methodsMatch = line.match(/methods\s*\(\s*"([^"]+)"_method/);
|
|
5303
|
+
const method = methodsMatch ? methodsMatch[1].toUpperCase() : "ANY";
|
|
5304
|
+
routes.push({
|
|
5305
|
+
method,
|
|
5306
|
+
path: path6,
|
|
5307
|
+
normalizedPath: normalizePath(path6),
|
|
5308
|
+
file: filePath,
|
|
5309
|
+
line: i + 1
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
const drogonMatch = line.match(/ADD_METHOD_TO\s*\(\s*[^,]+,\s*"([^"]+)"\s*,\s*(\w+)/);
|
|
5314
|
+
if (drogonMatch) {
|
|
5315
|
+
const path6 = drogonMatch[1].startsWith("/") ? drogonMatch[1] : "/" + drogonMatch[1];
|
|
5316
|
+
routes.push({
|
|
5317
|
+
method: drogonMatch[2].toUpperCase(),
|
|
5318
|
+
path: path6,
|
|
5319
|
+
normalizedPath: normalizePath(path6),
|
|
5320
|
+
file: filePath,
|
|
5321
|
+
line: i + 1
|
|
5322
|
+
});
|
|
5323
|
+
}
|
|
5324
|
+
const pathAddMatch = line.match(/PATH_ADD\s*\(\s*"([^"]+)"\s*,\s*(\w+)/);
|
|
5325
|
+
if (pathAddMatch) {
|
|
5326
|
+
const path6 = pathAddMatch[1].startsWith("/") ? pathAddMatch[1] : "/" + pathAddMatch[1];
|
|
5327
|
+
routes.push({
|
|
5328
|
+
method: pathAddMatch[2].toUpperCase(),
|
|
5329
|
+
path: path6,
|
|
5330
|
+
normalizedPath: normalizePath(path6),
|
|
5331
|
+
file: filePath,
|
|
5332
|
+
line: i + 1
|
|
5333
|
+
});
|
|
5334
|
+
}
|
|
5335
|
+
const pistacheMatch = line.match(/router\s*\.\s*(get|post|put|del|patch)\s*\(\s*"([^"]+)"/i);
|
|
5336
|
+
if (pistacheMatch) {
|
|
5337
|
+
const method = pistacheMatch[1].toUpperCase() === "DEL" ? "DELETE" : pistacheMatch[1].toUpperCase();
|
|
5338
|
+
const path6 = pistacheMatch[2];
|
|
5339
|
+
if (path6.startsWith("/")) {
|
|
5340
|
+
routes.push({
|
|
5341
|
+
method,
|
|
5342
|
+
path: path6,
|
|
5343
|
+
normalizedPath: normalizePath(path6),
|
|
5344
|
+
file: filePath,
|
|
5345
|
+
line: i + 1
|
|
5346
|
+
});
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
const httplibMatch = line.match(/(?:svr|server)\s*\.\s*(Get|Post|Put|Delete|Patch)\s*\(\s*"([^"]+)"/);
|
|
5350
|
+
if (httplibMatch) {
|
|
5351
|
+
const path6 = httplibMatch[2];
|
|
5352
|
+
if (path6.startsWith("/")) {
|
|
5353
|
+
routes.push({
|
|
5354
|
+
method: httplibMatch[1].toUpperCase(),
|
|
5355
|
+
path: path6,
|
|
5356
|
+
normalizedPath: normalizePath(path6),
|
|
5357
|
+
file: filePath,
|
|
5358
|
+
line: i + 1
|
|
5359
|
+
});
|
|
5360
|
+
}
|
|
5361
|
+
}
|
|
5362
|
+
}
|
|
3623
5363
|
}
|
|
3624
5364
|
return routes;
|
|
3625
5365
|
}
|
|
5366
|
+
function findClassLevelPrefix(source) {
|
|
5367
|
+
const match = source.match(/@RequestMapping\s*\(\s*(?:value\s*=\s*)?["']([^"']+)["']/);
|
|
5368
|
+
if (match) {
|
|
5369
|
+
let path6 = match[1];
|
|
5370
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
5371
|
+
if (path6.endsWith("/") && path6.length > 1) path6 = path6.slice(0, -1);
|
|
5372
|
+
return path6;
|
|
5373
|
+
}
|
|
5374
|
+
return null;
|
|
5375
|
+
}
|
|
3626
5376
|
function matchPaths(callPath, routeNormalized) {
|
|
3627
5377
|
const normalizedCall = normalizePath(stripTrailingSlash(callPath));
|
|
3628
5378
|
const normalizedRoute = stripTrailingSlash(routeNormalized);
|
|
@@ -3658,11 +5408,11 @@ function detectRestApiEdges(files, projectRoot) {
|
|
|
3658
5408
|
const allCalls = [];
|
|
3659
5409
|
const allRoutes = [];
|
|
3660
5410
|
for (const file of files) {
|
|
3661
|
-
const fullPath =
|
|
3662
|
-
if (!
|
|
5411
|
+
const fullPath = join12(projectRoot, file.filePath);
|
|
5412
|
+
if (!resolve6(fullPath).startsWith(resolve6(projectRoot))) continue;
|
|
3663
5413
|
let source;
|
|
3664
5414
|
try {
|
|
3665
|
-
source =
|
|
5415
|
+
source = readFileSync9(fullPath, "utf-8");
|
|
3666
5416
|
} catch {
|
|
3667
5417
|
continue;
|
|
3668
5418
|
}
|
|
@@ -3698,8 +5448,8 @@ function detectRestApiEdges(files, projectRoot) {
|
|
|
3698
5448
|
}
|
|
3699
5449
|
|
|
3700
5450
|
// src/cross-language/detectors/subprocess.ts
|
|
3701
|
-
import { readFileSync as
|
|
3702
|
-
import { join as
|
|
5451
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
5452
|
+
import { join as join13, resolve as resolve7, basename as basename5 } from "path";
|
|
3703
5453
|
var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
|
|
3704
5454
|
function getLanguage2(filePath) {
|
|
3705
5455
|
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
@@ -3798,16 +5548,16 @@ function detectSubprocessEdges(files, projectRoot) {
|
|
|
3798
5548
|
const knownFiles = new Set(files.map((f) => f.filePath));
|
|
3799
5549
|
const basenameMap = /* @__PURE__ */ new Map();
|
|
3800
5550
|
for (const f of files) {
|
|
3801
|
-
const base =
|
|
5551
|
+
const base = basename5(f.filePath);
|
|
3802
5552
|
if (!basenameMap.has(base)) basenameMap.set(base, []);
|
|
3803
5553
|
basenameMap.get(base).push(f.filePath);
|
|
3804
5554
|
}
|
|
3805
5555
|
for (const file of files) {
|
|
3806
|
-
const fullPath =
|
|
3807
|
-
if (!
|
|
5556
|
+
const fullPath = join13(projectRoot, file.filePath);
|
|
5557
|
+
if (!resolve7(fullPath).startsWith(resolve7(projectRoot))) continue;
|
|
3808
5558
|
let source;
|
|
3809
5559
|
try {
|
|
3810
|
-
source =
|
|
5560
|
+
source = readFileSync10(fullPath, "utf-8");
|
|
3811
5561
|
} catch {
|
|
3812
5562
|
continue;
|
|
3813
5563
|
}
|
|
@@ -3819,7 +5569,7 @@ function detectSubprocessEdges(files, projectRoot) {
|
|
|
3819
5569
|
targetFile = call.calledFile;
|
|
3820
5570
|
confidence = "high";
|
|
3821
5571
|
} else {
|
|
3822
|
-
const base =
|
|
5572
|
+
const base = basename5(call.calledFile);
|
|
3823
5573
|
const candidates = basenameMap.get(base);
|
|
3824
5574
|
if (candidates && candidates.length > 0) {
|
|
3825
5575
|
const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
|
|
@@ -4198,7 +5948,7 @@ function getArchitectureSummary(graph) {
|
|
|
4198
5948
|
}
|
|
4199
5949
|
|
|
4200
5950
|
// src/health/metrics.ts
|
|
4201
|
-
import { dirname as
|
|
5951
|
+
import { dirname as dirname10 } from "path";
|
|
4202
5952
|
function scoreToGrade(score) {
|
|
4203
5953
|
if (score >= 90) return "A";
|
|
4204
5954
|
if (score >= 80) return "B";
|
|
@@ -4231,8 +5981,8 @@ function calculateCouplingScore(graph) {
|
|
|
4231
5981
|
totalEdges++;
|
|
4232
5982
|
fileConnections.set(sourceAttrs.filePath, (fileConnections.get(sourceAttrs.filePath) || 0) + 1);
|
|
4233
5983
|
fileConnections.set(targetAttrs.filePath, (fileConnections.get(targetAttrs.filePath) || 0) + 1);
|
|
4234
|
-
const sourceDir =
|
|
4235
|
-
const targetDir =
|
|
5984
|
+
const sourceDir = dirname10(sourceAttrs.filePath).split("/")[0];
|
|
5985
|
+
const targetDir = dirname10(targetAttrs.filePath).split("/")[0];
|
|
4236
5986
|
if (sourceDir !== targetDir) {
|
|
4237
5987
|
crossDirEdges++;
|
|
4238
5988
|
}
|
|
@@ -4279,8 +6029,8 @@ function calculateCohesionScore(graph) {
|
|
|
4279
6029
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
4280
6030
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
4281
6031
|
if (sourceAttrs.filePath !== targetAttrs.filePath) {
|
|
4282
|
-
const sourceDir =
|
|
4283
|
-
const targetDir =
|
|
6032
|
+
const sourceDir = dirname10(sourceAttrs.filePath);
|
|
6033
|
+
const targetDir = dirname10(targetAttrs.filePath);
|
|
4284
6034
|
if (!dirEdges.has(sourceDir)) {
|
|
4285
6035
|
dirEdges.set(sourceDir, { internal: 0, total: 0 });
|
|
4286
6036
|
}
|
|
@@ -4562,8 +6312,8 @@ function calculateDepthScore(graph) {
|
|
|
4562
6312
|
}
|
|
4563
6313
|
|
|
4564
6314
|
// src/health/index.ts
|
|
4565
|
-
import { readFileSync as
|
|
4566
|
-
import { dirname as
|
|
6315
|
+
import { readFileSync as readFileSync11, writeFileSync, existsSync as existsSync11, mkdirSync } from "fs";
|
|
6316
|
+
import { dirname as dirname11, resolve as resolve8 } from "path";
|
|
4567
6317
|
function calculateHealthScore(graph, projectRoot) {
|
|
4568
6318
|
const coupling = calculateCouplingScore(graph);
|
|
4569
6319
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -4662,8 +6412,8 @@ function getHealthTrend(projectRoot, currentScore) {
|
|
|
4662
6412
|
}
|
|
4663
6413
|
}
|
|
4664
6414
|
function saveHealthHistory(projectRoot, report) {
|
|
4665
|
-
const resolvedRoot =
|
|
4666
|
-
const historyFile =
|
|
6415
|
+
const resolvedRoot = resolve8(projectRoot);
|
|
6416
|
+
const historyFile = resolve8(resolvedRoot, ".depwire", "health-history.json");
|
|
4667
6417
|
if (!historyFile.startsWith(resolvedRoot)) {
|
|
4668
6418
|
return;
|
|
4669
6419
|
}
|
|
@@ -4678,10 +6428,10 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
4678
6428
|
}))
|
|
4679
6429
|
};
|
|
4680
6430
|
let history = [];
|
|
4681
|
-
if (
|
|
6431
|
+
if (existsSync11(historyFile)) {
|
|
4682
6432
|
try {
|
|
4683
6433
|
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
4684
|
-
const content =
|
|
6434
|
+
const content = readFileSync11(historyFile, "utf-8");
|
|
4685
6435
|
history = JSON.parse(content);
|
|
4686
6436
|
} catch {
|
|
4687
6437
|
}
|
|
@@ -4690,19 +6440,19 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
4690
6440
|
if (history.length > 50) {
|
|
4691
6441
|
history = history.slice(-50);
|
|
4692
6442
|
}
|
|
4693
|
-
mkdirSync(
|
|
6443
|
+
mkdirSync(dirname11(historyFile), { recursive: true });
|
|
4694
6444
|
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
4695
6445
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
4696
6446
|
}
|
|
4697
6447
|
function loadHealthHistory(projectRoot) {
|
|
4698
|
-
const resolvedRoot =
|
|
4699
|
-
const historyFile =
|
|
4700
|
-
if (!historyFile.startsWith(resolvedRoot) || !
|
|
6448
|
+
const resolvedRoot = resolve8(projectRoot);
|
|
6449
|
+
const historyFile = resolve8(resolvedRoot, ".depwire", "health-history.json");
|
|
6450
|
+
if (!historyFile.startsWith(resolvedRoot) || !existsSync11(historyFile)) {
|
|
4701
6451
|
return [];
|
|
4702
6452
|
}
|
|
4703
6453
|
try {
|
|
4704
6454
|
if (!historyFile.startsWith(resolvedRoot)) return [];
|
|
4705
|
-
const content =
|
|
6455
|
+
const content = readFileSync11(historyFile, "utf-8");
|
|
4706
6456
|
return JSON.parse(content);
|
|
4707
6457
|
} catch {
|
|
4708
6458
|
return [];
|
|
@@ -4711,7 +6461,7 @@ function loadHealthHistory(projectRoot) {
|
|
|
4711
6461
|
|
|
4712
6462
|
// src/dead-code/detector.ts
|
|
4713
6463
|
import path2 from "path";
|
|
4714
|
-
import { readFileSync as
|
|
6464
|
+
import { readFileSync as readFileSync12, existsSync as existsSync12 } from "fs";
|
|
4715
6465
|
function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
|
|
4716
6466
|
const deadSymbols = [];
|
|
4717
6467
|
const context = { graph, projectRoot };
|
|
@@ -4844,11 +6594,11 @@ function getPackageEntryPoints(projectRoot) {
|
|
|
4844
6594
|
const entryPoints = /* @__PURE__ */ new Set();
|
|
4845
6595
|
const resolvedRoot = path2.resolve(projectRoot);
|
|
4846
6596
|
const packageJsonPath = path2.resolve(resolvedRoot, "package.json");
|
|
4847
|
-
if (!packageJsonPath.startsWith(resolvedRoot) || !
|
|
6597
|
+
if (!packageJsonPath.startsWith(resolvedRoot) || !existsSync12(packageJsonPath)) {
|
|
4848
6598
|
return entryPoints;
|
|
4849
6599
|
}
|
|
4850
6600
|
try {
|
|
4851
|
-
const packageJson = JSON.parse(
|
|
6601
|
+
const packageJson = JSON.parse(readFileSync12(packageJsonPath, "utf-8"));
|
|
4852
6602
|
if (packageJson.main) {
|
|
4853
6603
|
entryPoints.add(path2.resolve(projectRoot, packageJson.main));
|
|
4854
6604
|
}
|
|
@@ -4899,6 +6649,9 @@ function shouldExclude(attrs, context, includeTests, packageEntryPoints) {
|
|
|
4899
6649
|
if (isFrameworkAutoLoadedFile(relativePath)) {
|
|
4900
6650
|
return "framework";
|
|
4901
6651
|
}
|
|
6652
|
+
if (isCppExcluded(attrs)) {
|
|
6653
|
+
return "framework";
|
|
6654
|
+
}
|
|
4902
6655
|
return null;
|
|
4903
6656
|
}
|
|
4904
6657
|
function isRealPackageEntryPoint(filePath, packageEntryPoints) {
|
|
@@ -4921,7 +6674,21 @@ function isTypeDeclarationFile(filePath) {
|
|
|
4921
6674
|
return filePath.endsWith(".d.ts");
|
|
4922
6675
|
}
|
|
4923
6676
|
function isFrameworkAutoLoadedFile(filePath) {
|
|
4924
|
-
return filePath.includes("/pages/") || filePath.includes("/routes/") || filePath.includes("/middleware/") || filePath.includes("/commands/") || filePath.includes("/api/") || filePath.includes("/app/") || filePath.includes("/Controllers/") || filePath.includes("/Hubs/") || filePath.includes("/Migrations/")
|
|
6677
|
+
return filePath.includes("/pages/") || filePath.includes("/routes/") || filePath.includes("/middleware/") || filePath.includes("/commands/") || filePath.includes("/api/") || filePath.includes("/app/") || filePath.includes("/Controllers/") || filePath.includes("/Hubs/") || filePath.includes("/Migrations/") || // Java / Spring / Jakarta
|
|
6678
|
+
filePath.includes("/controller/") || filePath.includes("/controllers/") || filePath.includes("/service/") || filePath.includes("/repository/") || filePath.includes("/config/") || filePath.includes("/configuration/");
|
|
6679
|
+
}
|
|
6680
|
+
function isCppExcluded(attrs) {
|
|
6681
|
+
const filePath = attrs.file || attrs.filePath || "";
|
|
6682
|
+
const name = attrs.name || "";
|
|
6683
|
+
const kind = attrs.kind || "";
|
|
6684
|
+
if (/\.(?:h|hpp|hh|hxx|h\+\+|inl|ipp)$/.test(filePath)) {
|
|
6685
|
+
return true;
|
|
6686
|
+
}
|
|
6687
|
+
if (name === "main") return true;
|
|
6688
|
+
if (name.startsWith("operator")) return true;
|
|
6689
|
+
if (name.startsWith("~")) return true;
|
|
6690
|
+
if (kind === "constant" && /\.(?:h|hpp)$/.test(filePath)) return true;
|
|
6691
|
+
return false;
|
|
4925
6692
|
}
|
|
4926
6693
|
|
|
4927
6694
|
// src/dead-code/classifier.ts
|
|
@@ -4989,8 +6756,8 @@ function generateReason(symbol, confidence) {
|
|
|
4989
6756
|
return "Potentially unused";
|
|
4990
6757
|
}
|
|
4991
6758
|
function isBarrelFile(filePath) {
|
|
4992
|
-
const
|
|
4993
|
-
return
|
|
6759
|
+
const basename9 = path3.basename(filePath);
|
|
6760
|
+
return basename9 === "index.ts" || basename9 === "index.js";
|
|
4994
6761
|
}
|
|
4995
6762
|
function isTestFile2(filePath) {
|
|
4996
6763
|
return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
|
|
@@ -5169,11 +6936,11 @@ function filterByConfidence(symbols, minConfidence) {
|
|
|
5169
6936
|
}
|
|
5170
6937
|
|
|
5171
6938
|
// src/docs/generator.ts
|
|
5172
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as
|
|
5173
|
-
import { join as
|
|
6939
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync15 } from "fs";
|
|
6940
|
+
import { join as join17 } from "path";
|
|
5174
6941
|
|
|
5175
6942
|
// src/docs/architecture.ts
|
|
5176
|
-
import { dirname as
|
|
6943
|
+
import { dirname as dirname12 } from "path";
|
|
5177
6944
|
|
|
5178
6945
|
// src/docs/templates.ts
|
|
5179
6946
|
function header(text, level = 1) {
|
|
@@ -5324,7 +7091,7 @@ function generateModuleStructure(graph) {
|
|
|
5324
7091
|
function getDirectoryStats(graph) {
|
|
5325
7092
|
const dirMap = /* @__PURE__ */ new Map();
|
|
5326
7093
|
graph.forEachNode((node, attrs) => {
|
|
5327
|
-
const dir =
|
|
7094
|
+
const dir = dirname12(attrs.filePath);
|
|
5328
7095
|
if (dir === ".") return;
|
|
5329
7096
|
if (!dirMap.has(dir)) {
|
|
5330
7097
|
dirMap.set(dir, {
|
|
@@ -5349,7 +7116,7 @@ function getDirectoryStats(graph) {
|
|
|
5349
7116
|
});
|
|
5350
7117
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
5351
7118
|
graph.forEachNode((node, attrs) => {
|
|
5352
|
-
const dir =
|
|
7119
|
+
const dir = dirname12(attrs.filePath);
|
|
5353
7120
|
if (!filesPerDir.has(dir)) {
|
|
5354
7121
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
5355
7122
|
}
|
|
@@ -5364,8 +7131,8 @@ function getDirectoryStats(graph) {
|
|
|
5364
7131
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
5365
7132
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
5366
7133
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
5367
|
-
const sourceDir =
|
|
5368
|
-
const targetDir =
|
|
7134
|
+
const sourceDir = dirname12(sourceAttrs.filePath);
|
|
7135
|
+
const targetDir = dirname12(targetAttrs.filePath);
|
|
5369
7136
|
if (sourceDir !== targetDir) {
|
|
5370
7137
|
if (!dirEdges.has(sourceDir)) {
|
|
5371
7138
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -5572,7 +7339,7 @@ function detectCycles(graph) {
|
|
|
5572
7339
|
}
|
|
5573
7340
|
|
|
5574
7341
|
// src/docs/conventions.ts
|
|
5575
|
-
import { basename as
|
|
7342
|
+
import { basename as basename6, extname as extname7 } from "path";
|
|
5576
7343
|
function generateConventions(graph, projectRoot, version) {
|
|
5577
7344
|
let output = "";
|
|
5578
7345
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -5610,7 +7377,7 @@ function generateFileOrganization(graph) {
|
|
|
5610
7377
|
graph.forEachNode((node, attrs) => {
|
|
5611
7378
|
if (!files.has(attrs.filePath)) {
|
|
5612
7379
|
files.add(attrs.filePath);
|
|
5613
|
-
const fileName =
|
|
7380
|
+
const fileName = basename6(attrs.filePath);
|
|
5614
7381
|
if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
|
|
5615
7382
|
barrelFileCount++;
|
|
5616
7383
|
}
|
|
@@ -5669,7 +7436,7 @@ function generateNamingPatterns(graph) {
|
|
|
5669
7436
|
graph.forEachNode((node, attrs) => {
|
|
5670
7437
|
if (!files.has(attrs.filePath)) {
|
|
5671
7438
|
files.add(attrs.filePath);
|
|
5672
|
-
const fileName =
|
|
7439
|
+
const fileName = basename6(attrs.filePath, extname7(attrs.filePath));
|
|
5673
7440
|
if (isCamelCase(fileName)) patterns.files.camelCase++;
|
|
5674
7441
|
else if (isPascalCase(fileName)) patterns.files.PascalCase++;
|
|
5675
7442
|
else if (isKebabCase(fileName)) patterns.files.kebabCase++;
|
|
@@ -6346,7 +8113,7 @@ function detectCyclesDetailed(graph) {
|
|
|
6346
8113
|
}
|
|
6347
8114
|
|
|
6348
8115
|
// src/docs/onboarding.ts
|
|
6349
|
-
import { dirname as
|
|
8116
|
+
import { dirname as dirname13 } from "path";
|
|
6350
8117
|
function generateOnboarding(graph, projectRoot, version) {
|
|
6351
8118
|
let output = "";
|
|
6352
8119
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6405,7 +8172,7 @@ function generateQuickOrientation(graph) {
|
|
|
6405
8172
|
const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
|
|
6406
8173
|
const dirs = /* @__PURE__ */ new Set();
|
|
6407
8174
|
graph.forEachNode((node, attrs) => {
|
|
6408
|
-
const dir =
|
|
8175
|
+
const dir = dirname13(attrs.filePath);
|
|
6409
8176
|
if (dir !== ".") {
|
|
6410
8177
|
const topLevel = dir.split("/")[0];
|
|
6411
8178
|
dirs.add(topLevel);
|
|
@@ -6509,7 +8276,7 @@ function generateModuleMap(graph) {
|
|
|
6509
8276
|
function getDirectoryStats2(graph) {
|
|
6510
8277
|
const dirMap = /* @__PURE__ */ new Map();
|
|
6511
8278
|
graph.forEachNode((node, attrs) => {
|
|
6512
|
-
const dir =
|
|
8279
|
+
const dir = dirname13(attrs.filePath);
|
|
6513
8280
|
if (dir === ".") return;
|
|
6514
8281
|
if (!dirMap.has(dir)) {
|
|
6515
8282
|
dirMap.set(dir, {
|
|
@@ -6524,7 +8291,7 @@ function getDirectoryStats2(graph) {
|
|
|
6524
8291
|
});
|
|
6525
8292
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
6526
8293
|
graph.forEachNode((node, attrs) => {
|
|
6527
|
-
const dir =
|
|
8294
|
+
const dir = dirname13(attrs.filePath);
|
|
6528
8295
|
if (!filesPerDir.has(dir)) {
|
|
6529
8296
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
6530
8297
|
}
|
|
@@ -6539,8 +8306,8 @@ function getDirectoryStats2(graph) {
|
|
|
6539
8306
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
6540
8307
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
6541
8308
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
6542
|
-
const sourceDir =
|
|
6543
|
-
const targetDir =
|
|
8309
|
+
const sourceDir = dirname13(sourceAttrs.filePath);
|
|
8310
|
+
const targetDir = dirname13(targetAttrs.filePath);
|
|
6544
8311
|
if (sourceDir !== targetDir) {
|
|
6545
8312
|
if (!dirEdges.has(sourceDir)) {
|
|
6546
8313
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -6621,7 +8388,7 @@ function detectClusters(graph) {
|
|
|
6621
8388
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
6622
8389
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
6623
8390
|
graph.forEachNode((node, attrs) => {
|
|
6624
|
-
const dir =
|
|
8391
|
+
const dir = dirname13(attrs.filePath);
|
|
6625
8392
|
if (!dirFiles.has(dir)) {
|
|
6626
8393
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
6627
8394
|
}
|
|
@@ -6675,8 +8442,8 @@ function inferClusterName(files) {
|
|
|
6675
8442
|
if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
|
|
6676
8443
|
return capitalizeFirst2(sortedWords[0][0]);
|
|
6677
8444
|
}
|
|
6678
|
-
const commonDir =
|
|
6679
|
-
if (files.every((f) =>
|
|
8445
|
+
const commonDir = dirname13(files[0]);
|
|
8446
|
+
if (files.every((f) => dirname13(f) === commonDir)) {
|
|
6680
8447
|
return capitalizeFirst2(commonDir.split("/").pop() || "Core");
|
|
6681
8448
|
}
|
|
6682
8449
|
return "Core";
|
|
@@ -6734,7 +8501,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
6734
8501
|
}
|
|
6735
8502
|
|
|
6736
8503
|
// src/docs/files.ts
|
|
6737
|
-
import { dirname as
|
|
8504
|
+
import { dirname as dirname14, basename as basename7 } from "path";
|
|
6738
8505
|
function generateFiles(graph, projectRoot, version) {
|
|
6739
8506
|
let output = "";
|
|
6740
8507
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -6838,7 +8605,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
6838
8605
|
const fileStats = getFileStats2(graph);
|
|
6839
8606
|
const dirMap = /* @__PURE__ */ new Map();
|
|
6840
8607
|
for (const file of fileStats) {
|
|
6841
|
-
const dir =
|
|
8608
|
+
const dir = dirname14(file.filePath);
|
|
6842
8609
|
const topDir = dir === "." ? "." : dir.split("/")[0];
|
|
6843
8610
|
if (!dirMap.has(topDir)) {
|
|
6844
8611
|
dirMap.set(topDir, {
|
|
@@ -6853,7 +8620,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
6853
8620
|
dirStats.symbolCount += file.symbolCount;
|
|
6854
8621
|
if (file.totalConnections > dirStats.maxConnections) {
|
|
6855
8622
|
dirStats.maxConnections = file.totalConnections;
|
|
6856
|
-
dirStats.mostConnectedFile =
|
|
8623
|
+
dirStats.mostConnectedFile = basename7(file.filePath);
|
|
6857
8624
|
}
|
|
6858
8625
|
}
|
|
6859
8626
|
if (dirMap.size === 0) {
|
|
@@ -7481,7 +9248,7 @@ function generateRecommendations(graph) {
|
|
|
7481
9248
|
}
|
|
7482
9249
|
|
|
7483
9250
|
// src/docs/tests.ts
|
|
7484
|
-
import { basename as
|
|
9251
|
+
import { basename as basename8, dirname as dirname15 } from "path";
|
|
7485
9252
|
function generateTests(graph, projectRoot, version) {
|
|
7486
9253
|
let output = "";
|
|
7487
9254
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -7509,8 +9276,8 @@ function getFileCount8(graph) {
|
|
|
7509
9276
|
return files.size;
|
|
7510
9277
|
}
|
|
7511
9278
|
function isTestFile3(filePath) {
|
|
7512
|
-
const fileName =
|
|
7513
|
-
const dirPath =
|
|
9279
|
+
const fileName = basename8(filePath).toLowerCase();
|
|
9280
|
+
const dirPath = dirname15(filePath).toLowerCase();
|
|
7514
9281
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
7515
9282
|
return true;
|
|
7516
9283
|
}
|
|
@@ -7568,13 +9335,13 @@ function generateTestFileInventory(graph) {
|
|
|
7568
9335
|
return output;
|
|
7569
9336
|
}
|
|
7570
9337
|
function matchTestToSource(testFile) {
|
|
7571
|
-
const testFileName =
|
|
7572
|
-
const testDir =
|
|
9338
|
+
const testFileName = basename8(testFile);
|
|
9339
|
+
const testDir = dirname15(testFile);
|
|
7573
9340
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
7574
9341
|
const possiblePaths = [];
|
|
7575
9342
|
possiblePaths.push(testDir + "/" + sourceFileName);
|
|
7576
9343
|
if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
|
|
7577
|
-
const parentDir =
|
|
9344
|
+
const parentDir = dirname15(testDir);
|
|
7578
9345
|
possiblePaths.push(parentDir + "/" + sourceFileName);
|
|
7579
9346
|
}
|
|
7580
9347
|
if (testDir.includes("test")) {
|
|
@@ -7730,7 +9497,7 @@ function generateTestCoverageMap(graph) {
|
|
|
7730
9497
|
const rows = mappings.slice(0, 30).map((m) => [
|
|
7731
9498
|
`\`${m.sourceFile}\``,
|
|
7732
9499
|
m.hasTest ? "\u2705" : "\u274C",
|
|
7733
|
-
m.testFile ? `\`${
|
|
9500
|
+
m.testFile ? `\`${basename8(m.testFile)}\`` : "-",
|
|
7734
9501
|
formatNumber(m.symbolCount)
|
|
7735
9502
|
]);
|
|
7736
9503
|
let output = table(headers, rows);
|
|
@@ -7771,7 +9538,7 @@ function generateTestStatistics(graph) {
|
|
|
7771
9538
|
`;
|
|
7772
9539
|
const dirTestCoverage = /* @__PURE__ */ new Map();
|
|
7773
9540
|
for (const sourceFile of sourceFiles) {
|
|
7774
|
-
const dir =
|
|
9541
|
+
const dir = dirname15(sourceFile).split("/")[0];
|
|
7775
9542
|
if (!dirTestCoverage.has(dir)) {
|
|
7776
9543
|
dirTestCoverage.set(dir, { total: 0, tested: 0 });
|
|
7777
9544
|
}
|
|
@@ -7794,7 +9561,7 @@ function generateTestStatistics(graph) {
|
|
|
7794
9561
|
}
|
|
7795
9562
|
|
|
7796
9563
|
// src/docs/history.ts
|
|
7797
|
-
import { dirname as
|
|
9564
|
+
import { dirname as dirname16 } from "path";
|
|
7798
9565
|
import { execSync } from "child_process";
|
|
7799
9566
|
function generateHistory(graph, projectRoot, version) {
|
|
7800
9567
|
let output = "";
|
|
@@ -8075,7 +9842,7 @@ function generateFeatureClusters(graph) {
|
|
|
8075
9842
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
8076
9843
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
8077
9844
|
graph.forEachNode((node, attrs) => {
|
|
8078
|
-
const dir =
|
|
9845
|
+
const dir = dirname16(attrs.filePath);
|
|
8079
9846
|
if (!dirFiles.has(dir)) {
|
|
8080
9847
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
8081
9848
|
}
|
|
@@ -8157,7 +9924,7 @@ function capitalizeFirst3(str) {
|
|
|
8157
9924
|
}
|
|
8158
9925
|
|
|
8159
9926
|
// src/docs/current.ts
|
|
8160
|
-
import { dirname as
|
|
9927
|
+
import { dirname as dirname17 } from "path";
|
|
8161
9928
|
function generateCurrent(graph, projectRoot, version) {
|
|
8162
9929
|
let output = "";
|
|
8163
9930
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -8295,7 +10062,7 @@ function generateCompleteFileIndex(graph) {
|
|
|
8295
10062
|
fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
8296
10063
|
const dirGroups = /* @__PURE__ */ new Map();
|
|
8297
10064
|
for (const info of fileInfos) {
|
|
8298
|
-
const dir =
|
|
10065
|
+
const dir = dirname17(info.filePath);
|
|
8299
10066
|
const topDir = dir === "." ? "root" : dir.split("/")[0];
|
|
8300
10067
|
if (!dirGroups.has(topDir)) {
|
|
8301
10068
|
dirGroups.set(topDir, []);
|
|
@@ -8506,8 +10273,8 @@ function getTopLevelDir2(filePath) {
|
|
|
8506
10273
|
}
|
|
8507
10274
|
|
|
8508
10275
|
// src/docs/status.ts
|
|
8509
|
-
import { readFileSync as
|
|
8510
|
-
import { resolve as
|
|
10276
|
+
import { readFileSync as readFileSync13, existsSync as existsSync13 } from "fs";
|
|
10277
|
+
import { resolve as resolve9 } from "path";
|
|
8511
10278
|
function generateStatus(graph, projectRoot, version) {
|
|
8512
10279
|
let output = "";
|
|
8513
10280
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -8540,16 +10307,16 @@ function getFileCount11(graph) {
|
|
|
8540
10307
|
}
|
|
8541
10308
|
function extractComments(projectRoot, filePath) {
|
|
8542
10309
|
const comments = [];
|
|
8543
|
-
const resolvedRoot =
|
|
8544
|
-
const fullPath =
|
|
10310
|
+
const resolvedRoot = resolve9(projectRoot);
|
|
10311
|
+
const fullPath = resolve9(resolvedRoot, filePath);
|
|
8545
10312
|
if (!fullPath.startsWith(resolvedRoot)) {
|
|
8546
10313
|
return comments;
|
|
8547
10314
|
}
|
|
8548
|
-
if (!
|
|
10315
|
+
if (!existsSync13(fullPath)) {
|
|
8549
10316
|
return comments;
|
|
8550
10317
|
}
|
|
8551
10318
|
try {
|
|
8552
|
-
const content =
|
|
10319
|
+
const content = readFileSync13(fullPath, "utf-8");
|
|
8553
10320
|
const lines = content.split("\n");
|
|
8554
10321
|
const patterns = [
|
|
8555
10322
|
{ type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
|
|
@@ -9128,16 +10895,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
|
|
|
9128
10895
|
}
|
|
9129
10896
|
|
|
9130
10897
|
// src/docs/metadata.ts
|
|
9131
|
-
import { existsSync as
|
|
9132
|
-
import { resolve as
|
|
10898
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14, writeFileSync as writeFileSync2 } from "fs";
|
|
10899
|
+
import { resolve as resolve10 } from "path";
|
|
9133
10900
|
function loadMetadata(outputDir) {
|
|
9134
|
-
const resolvedDir =
|
|
9135
|
-
const metadataPath =
|
|
9136
|
-
if (!metadataPath.startsWith(resolvedDir) || !
|
|
10901
|
+
const resolvedDir = resolve10(outputDir);
|
|
10902
|
+
const metadataPath = resolve10(resolvedDir, "metadata.json");
|
|
10903
|
+
if (!metadataPath.startsWith(resolvedDir) || !existsSync14(metadataPath)) {
|
|
9137
10904
|
return null;
|
|
9138
10905
|
}
|
|
9139
10906
|
try {
|
|
9140
|
-
const content =
|
|
10907
|
+
const content = readFileSync14(metadataPath, "utf-8");
|
|
9141
10908
|
return JSON.parse(content);
|
|
9142
10909
|
} catch (err) {
|
|
9143
10910
|
console.error("Failed to load metadata:", err);
|
|
@@ -9145,8 +10912,8 @@ function loadMetadata(outputDir) {
|
|
|
9145
10912
|
}
|
|
9146
10913
|
}
|
|
9147
10914
|
function saveMetadata(outputDir, metadata) {
|
|
9148
|
-
const resolvedDir =
|
|
9149
|
-
const metadataPath =
|
|
10915
|
+
const resolvedDir = resolve10(outputDir);
|
|
10916
|
+
const metadataPath = resolve10(resolvedDir, "metadata.json");
|
|
9150
10917
|
if (!metadataPath.startsWith(resolvedDir)) {
|
|
9151
10918
|
throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
|
|
9152
10919
|
}
|
|
@@ -9192,7 +10959,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9192
10959
|
const generated = [];
|
|
9193
10960
|
const errors = [];
|
|
9194
10961
|
try {
|
|
9195
|
-
if (!
|
|
10962
|
+
if (!existsSync15(options.outputDir)) {
|
|
9196
10963
|
mkdirSync2(options.outputDir, { recursive: true });
|
|
9197
10964
|
if (options.verbose) {
|
|
9198
10965
|
console.log(`Created output directory: ${options.outputDir}`);
|
|
@@ -9231,7 +10998,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9231
10998
|
try {
|
|
9232
10999
|
if (options.verbose) console.log("Generating ARCHITECTURE.md...");
|
|
9233
11000
|
const content = generateArchitecture(graph, projectRoot, version, parseTime);
|
|
9234
|
-
const filePath =
|
|
11001
|
+
const filePath = join17(options.outputDir, "ARCHITECTURE.md");
|
|
9235
11002
|
writeFileSync3(filePath, content, "utf-8");
|
|
9236
11003
|
generated.push("ARCHITECTURE.md");
|
|
9237
11004
|
} catch (err) {
|
|
@@ -9242,7 +11009,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9242
11009
|
try {
|
|
9243
11010
|
if (options.verbose) console.log("Generating CONVENTIONS.md...");
|
|
9244
11011
|
const content = generateConventions(graph, projectRoot, version);
|
|
9245
|
-
const filePath =
|
|
11012
|
+
const filePath = join17(options.outputDir, "CONVENTIONS.md");
|
|
9246
11013
|
writeFileSync3(filePath, content, "utf-8");
|
|
9247
11014
|
generated.push("CONVENTIONS.md");
|
|
9248
11015
|
} catch (err) {
|
|
@@ -9253,7 +11020,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9253
11020
|
try {
|
|
9254
11021
|
if (options.verbose) console.log("Generating DEPENDENCIES.md...");
|
|
9255
11022
|
const content = generateDependencies(graph, projectRoot, version);
|
|
9256
|
-
const filePath =
|
|
11023
|
+
const filePath = join17(options.outputDir, "DEPENDENCIES.md");
|
|
9257
11024
|
writeFileSync3(filePath, content, "utf-8");
|
|
9258
11025
|
generated.push("DEPENDENCIES.md");
|
|
9259
11026
|
} catch (err) {
|
|
@@ -9264,7 +11031,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9264
11031
|
try {
|
|
9265
11032
|
if (options.verbose) console.log("Generating ONBOARDING.md...");
|
|
9266
11033
|
const content = generateOnboarding(graph, projectRoot, version);
|
|
9267
|
-
const filePath =
|
|
11034
|
+
const filePath = join17(options.outputDir, "ONBOARDING.md");
|
|
9268
11035
|
writeFileSync3(filePath, content, "utf-8");
|
|
9269
11036
|
generated.push("ONBOARDING.md");
|
|
9270
11037
|
} catch (err) {
|
|
@@ -9275,7 +11042,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9275
11042
|
try {
|
|
9276
11043
|
if (options.verbose) console.log("Generating FILES.md...");
|
|
9277
11044
|
const content = generateFiles(graph, projectRoot, version);
|
|
9278
|
-
const filePath =
|
|
11045
|
+
const filePath = join17(options.outputDir, "FILES.md");
|
|
9279
11046
|
writeFileSync3(filePath, content, "utf-8");
|
|
9280
11047
|
generated.push("FILES.md");
|
|
9281
11048
|
} catch (err) {
|
|
@@ -9286,7 +11053,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9286
11053
|
try {
|
|
9287
11054
|
if (options.verbose) console.log("Generating API_SURFACE.md...");
|
|
9288
11055
|
const content = generateApiSurface(graph, projectRoot, version);
|
|
9289
|
-
const filePath =
|
|
11056
|
+
const filePath = join17(options.outputDir, "API_SURFACE.md");
|
|
9290
11057
|
writeFileSync3(filePath, content, "utf-8");
|
|
9291
11058
|
generated.push("API_SURFACE.md");
|
|
9292
11059
|
} catch (err) {
|
|
@@ -9297,7 +11064,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9297
11064
|
try {
|
|
9298
11065
|
if (options.verbose) console.log("Generating ERRORS.md...");
|
|
9299
11066
|
const content = generateErrors(graph, projectRoot, version);
|
|
9300
|
-
const filePath =
|
|
11067
|
+
const filePath = join17(options.outputDir, "ERRORS.md");
|
|
9301
11068
|
writeFileSync3(filePath, content, "utf-8");
|
|
9302
11069
|
generated.push("ERRORS.md");
|
|
9303
11070
|
} catch (err) {
|
|
@@ -9308,7 +11075,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9308
11075
|
try {
|
|
9309
11076
|
if (options.verbose) console.log("Generating TESTS.md...");
|
|
9310
11077
|
const content = generateTests(graph, projectRoot, version);
|
|
9311
|
-
const filePath =
|
|
11078
|
+
const filePath = join17(options.outputDir, "TESTS.md");
|
|
9312
11079
|
writeFileSync3(filePath, content, "utf-8");
|
|
9313
11080
|
generated.push("TESTS.md");
|
|
9314
11081
|
} catch (err) {
|
|
@@ -9319,7 +11086,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9319
11086
|
try {
|
|
9320
11087
|
if (options.verbose) console.log("Generating HISTORY.md...");
|
|
9321
11088
|
const content = generateHistory(graph, projectRoot, version);
|
|
9322
|
-
const filePath =
|
|
11089
|
+
const filePath = join17(options.outputDir, "HISTORY.md");
|
|
9323
11090
|
writeFileSync3(filePath, content, "utf-8");
|
|
9324
11091
|
generated.push("HISTORY.md");
|
|
9325
11092
|
} catch (err) {
|
|
@@ -9330,7 +11097,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9330
11097
|
try {
|
|
9331
11098
|
if (options.verbose) console.log("Generating CURRENT.md...");
|
|
9332
11099
|
const content = generateCurrent(graph, projectRoot, version);
|
|
9333
|
-
const filePath =
|
|
11100
|
+
const filePath = join17(options.outputDir, "CURRENT.md");
|
|
9334
11101
|
writeFileSync3(filePath, content, "utf-8");
|
|
9335
11102
|
generated.push("CURRENT.md");
|
|
9336
11103
|
} catch (err) {
|
|
@@ -9341,7 +11108,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9341
11108
|
try {
|
|
9342
11109
|
if (options.verbose) console.log("Generating STATUS.md...");
|
|
9343
11110
|
const content = generateStatus(graph, projectRoot, version);
|
|
9344
|
-
const filePath =
|
|
11111
|
+
const filePath = join17(options.outputDir, "STATUS.md");
|
|
9345
11112
|
writeFileSync3(filePath, content, "utf-8");
|
|
9346
11113
|
generated.push("STATUS.md");
|
|
9347
11114
|
} catch (err) {
|
|
@@ -9352,7 +11119,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9352
11119
|
try {
|
|
9353
11120
|
if (options.verbose) console.log("Generating HEALTH.md...");
|
|
9354
11121
|
const content = generateHealth(graph, projectRoot, version);
|
|
9355
|
-
const filePath =
|
|
11122
|
+
const filePath = join17(options.outputDir, "HEALTH.md");
|
|
9356
11123
|
writeFileSync3(filePath, content, "utf-8");
|
|
9357
11124
|
generated.push("HEALTH.md");
|
|
9358
11125
|
} catch (err) {
|
|
@@ -9363,7 +11130,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
9363
11130
|
try {
|
|
9364
11131
|
if (options.verbose) console.log("Generating DEAD_CODE.md...");
|
|
9365
11132
|
const content = generateDeadCode(graph, projectRoot, version);
|
|
9366
|
-
const filePath =
|
|
11133
|
+
const filePath = join17(options.outputDir, "DEAD_CODE.md");
|
|
9367
11134
|
writeFileSync3(filePath, content, "utf-8");
|
|
9368
11135
|
generated.push("DEAD_CODE.md");
|
|
9369
11136
|
} catch (err) {
|
|
@@ -9407,7 +11174,7 @@ function getFileCount13(graph) {
|
|
|
9407
11174
|
}
|
|
9408
11175
|
|
|
9409
11176
|
// src/simulation/engine.ts
|
|
9410
|
-
import { dirname as
|
|
11177
|
+
import { dirname as dirname18, join as join18 } from "path";
|
|
9411
11178
|
function normalizePath2(p) {
|
|
9412
11179
|
return p.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
9413
11180
|
}
|
|
@@ -9538,7 +11305,7 @@ var SimulationEngine = class {
|
|
|
9538
11305
|
}
|
|
9539
11306
|
}
|
|
9540
11307
|
applyRename(clone, target, newName, brokenImports) {
|
|
9541
|
-
const destination =
|
|
11308
|
+
const destination = join18(dirname18(target), newName);
|
|
9542
11309
|
this.applyMove(clone, target, destination, brokenImports);
|
|
9543
11310
|
}
|
|
9544
11311
|
applySplit(clone, target, newFile, symbols, brokenImports) {
|
|
@@ -9738,13 +11505,13 @@ var SimulationEngine = class {
|
|
|
9738
11505
|
};
|
|
9739
11506
|
|
|
9740
11507
|
// src/security/scanner.ts
|
|
9741
|
-
import { existsSync as
|
|
9742
|
-
import { join as
|
|
11508
|
+
import { existsSync as existsSync17 } from "fs";
|
|
11509
|
+
import { join as join28 } from "path";
|
|
9743
11510
|
|
|
9744
11511
|
// src/security/checks/dependencies.ts
|
|
9745
11512
|
import { execSync as execSync2 } from "child_process";
|
|
9746
|
-
import { existsSync as
|
|
9747
|
-
import { join as
|
|
11513
|
+
import { existsSync as existsSync16, readFileSync as readFileSync15, readdirSync as readdirSync8 } from "fs";
|
|
11514
|
+
import { join as join19 } from "path";
|
|
9748
11515
|
function cvssToSeverity(score) {
|
|
9749
11516
|
if (score >= 9) return "critical";
|
|
9750
11517
|
if (score >= 7) return "high";
|
|
@@ -9754,18 +11521,18 @@ function cvssToSeverity(score) {
|
|
|
9754
11521
|
async function checkDependencies(_files, projectRoot) {
|
|
9755
11522
|
const findings = [];
|
|
9756
11523
|
try {
|
|
9757
|
-
if (
|
|
11524
|
+
if (existsSync16(join19(projectRoot, "package.json"))) {
|
|
9758
11525
|
findings.push(...checkNpmAudit(projectRoot));
|
|
9759
11526
|
findings.push(...checkPackageJsonPatterns(projectRoot));
|
|
9760
11527
|
findings.push(...checkPostinstallScripts(projectRoot));
|
|
9761
11528
|
}
|
|
9762
|
-
if (
|
|
11529
|
+
if (existsSync16(join19(projectRoot, "requirements.txt")) || existsSync16(join19(projectRoot, "pyproject.toml"))) {
|
|
9763
11530
|
findings.push(...checkPipAudit(projectRoot));
|
|
9764
11531
|
}
|
|
9765
|
-
if (
|
|
11532
|
+
if (existsSync16(join19(projectRoot, "Cargo.toml"))) {
|
|
9766
11533
|
findings.push(...checkCargoAudit(projectRoot));
|
|
9767
11534
|
}
|
|
9768
|
-
if (
|
|
11535
|
+
if (existsSync16(join19(projectRoot, "go.mod"))) {
|
|
9769
11536
|
findings.push(...checkGoVerify(projectRoot));
|
|
9770
11537
|
}
|
|
9771
11538
|
} catch (err) {
|
|
@@ -9854,8 +11621,8 @@ function checkNpmAudit(projectRoot) {
|
|
|
9854
11621
|
function checkPackageJsonPatterns(projectRoot) {
|
|
9855
11622
|
const findings = [];
|
|
9856
11623
|
try {
|
|
9857
|
-
const pkgPath =
|
|
9858
|
-
const pkg = JSON.parse(
|
|
11624
|
+
const pkgPath = join19(projectRoot, "package.json");
|
|
11625
|
+
const pkg = JSON.parse(readFileSync15(pkgPath, "utf-8"));
|
|
9859
11626
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
9860
11627
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
9861
11628
|
if (version.startsWith("^") || version.startsWith("~")) {
|
|
@@ -9877,15 +11644,15 @@ function checkPackageJsonPatterns(projectRoot) {
|
|
|
9877
11644
|
}
|
|
9878
11645
|
function checkPostinstallScripts(projectRoot) {
|
|
9879
11646
|
const findings = [];
|
|
9880
|
-
const nodeModules =
|
|
9881
|
-
if (!
|
|
11647
|
+
const nodeModules = join19(projectRoot, "node_modules");
|
|
11648
|
+
if (!existsSync16(nodeModules)) return findings;
|
|
9882
11649
|
try {
|
|
9883
|
-
const topLevelDeps =
|
|
11650
|
+
const topLevelDeps = readdirSync8(nodeModules).filter((d) => !d.startsWith("."));
|
|
9884
11651
|
for (const dep of topLevelDeps) {
|
|
9885
|
-
const depPkgPath =
|
|
9886
|
-
if (!
|
|
11652
|
+
const depPkgPath = join19(nodeModules, dep, "package.json");
|
|
11653
|
+
if (!existsSync16(depPkgPath)) continue;
|
|
9887
11654
|
try {
|
|
9888
|
-
const depPkg = JSON.parse(
|
|
11655
|
+
const depPkg = JSON.parse(readFileSync15(depPkgPath, "utf-8"));
|
|
9889
11656
|
const scripts = depPkg.scripts || {};
|
|
9890
11657
|
if (scripts.postinstall || scripts.preinstall || scripts.install) {
|
|
9891
11658
|
const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
|
|
@@ -9923,7 +11690,7 @@ function checkPipAudit(projectRoot) {
|
|
|
9923
11690
|
id: "",
|
|
9924
11691
|
severity: cvssToSeverity(vuln.cvss?.score || 5),
|
|
9925
11692
|
vulnerabilityClass: "dependency-cve",
|
|
9926
|
-
file:
|
|
11693
|
+
file: existsSync16(join19(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
|
|
9927
11694
|
title: `Vulnerable Python dependency: ${vuln.name}`,
|
|
9928
11695
|
description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
|
|
9929
11696
|
attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
|
|
@@ -10020,8 +11787,8 @@ function checkGoVerify(projectRoot) {
|
|
|
10020
11787
|
}
|
|
10021
11788
|
|
|
10022
11789
|
// src/security/checks/injection.ts
|
|
10023
|
-
import { readFileSync as
|
|
10024
|
-
import { join as
|
|
11790
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
11791
|
+
import { join as join20 } from "path";
|
|
10025
11792
|
var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10026
11793
|
var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
|
|
10027
11794
|
var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
|
|
@@ -10106,6 +11873,107 @@ var PATTERNS = [
|
|
|
10106
11873
|
description: "Database query built using fmt.Sprintf directly passed to db.Query.",
|
|
10107
11874
|
attackScenario: "An attacker could inject SQL through interpolated values.",
|
|
10108
11875
|
suggestedFix: 'Use parameterized queries: db.Query("SELECT ... WHERE id = ?", id)'
|
|
11876
|
+
},
|
|
11877
|
+
// Java-specific injection patterns
|
|
11878
|
+
{
|
|
11879
|
+
regex: /(?:executeQuery|executeUpdate|execute)\s*\(\s*["']?\s*(?:SELECT|INSERT|UPDATE|DELETE)\b[^"']*["']?\s*\+/i,
|
|
11880
|
+
title: "Java SQL injection via string concatenation",
|
|
11881
|
+
vulnClass: "code-injection",
|
|
11882
|
+
baseSeverity: "high",
|
|
11883
|
+
description: "SQL query built using string concatenation \u2014 vulnerable to SQL injection.",
|
|
11884
|
+
attackScenario: "An attacker could inject SQL through concatenated user input to read, modify, or delete database data.",
|
|
11885
|
+
suggestedFix: "Use PreparedStatement with parameterized queries: preparedStatement.setString(1, userInput)"
|
|
11886
|
+
},
|
|
11887
|
+
{
|
|
11888
|
+
regex: /Runtime\.getRuntime\(\)\.exec\s*\(/,
|
|
11889
|
+
title: "Java command injection via Runtime.exec",
|
|
11890
|
+
vulnClass: "shell-injection",
|
|
11891
|
+
baseSeverity: "high",
|
|
11892
|
+
description: "Runtime.exec() executes a system command \u2014 vulnerable if user input reaches the argument.",
|
|
11893
|
+
attackScenario: "An attacker could inject shell metacharacters to execute arbitrary commands on the server.",
|
|
11894
|
+
suggestedFix: "Use ProcessBuilder with an argument array. Validate all input against a strict allowlist."
|
|
11895
|
+
},
|
|
11896
|
+
{
|
|
11897
|
+
regex: /new\s+ProcessBuilder\s*\([^)]*(?:input|user|param|query|request|body|arg)/i,
|
|
11898
|
+
title: "Java command injection via ProcessBuilder with user input",
|
|
11899
|
+
vulnClass: "shell-injection",
|
|
11900
|
+
baseSeverity: "medium",
|
|
11901
|
+
description: "ProcessBuilder called with arguments that may originate from user input.",
|
|
11902
|
+
attackScenario: "An attacker could inject malicious arguments to the spawned process.",
|
|
11903
|
+
suggestedFix: "Validate all arguments against a strict allowlist before passing to ProcessBuilder."
|
|
11904
|
+
},
|
|
11905
|
+
{
|
|
11906
|
+
regex: /new\s+ObjectInputStream\s*\(/,
|
|
11907
|
+
title: "Java insecure deserialization via ObjectInputStream",
|
|
11908
|
+
vulnClass: "code-injection",
|
|
11909
|
+
baseSeverity: "high",
|
|
11910
|
+
description: "ObjectInputStream.readObject() deserializes arbitrary Java objects \u2014 potential RCE.",
|
|
11911
|
+
attackScenario: "An attacker could craft a malicious serialized object to achieve remote code execution.",
|
|
11912
|
+
suggestedFix: "Use a whitelist-based ObjectInputFilter, or switch to JSON/Protobuf for data exchange."
|
|
11913
|
+
},
|
|
11914
|
+
{
|
|
11915
|
+
regex: /DocumentBuilderFactory\.newInstance\(\)/,
|
|
11916
|
+
title: "Java XML External Entity (XXE) risk",
|
|
11917
|
+
vulnClass: "code-injection",
|
|
11918
|
+
baseSeverity: "medium",
|
|
11919
|
+
description: "DocumentBuilderFactory without FEATURE_SECURE_PROCESSING may allow XXE attacks.",
|
|
11920
|
+
attackScenario: "An attacker could inject external entity references in XML to read server files or perform SSRF.",
|
|
11921
|
+
suggestedFix: "Set FEATURE_SECURE_PROCESSING and disable external DTDs/entities: factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)"
|
|
11922
|
+
},
|
|
11923
|
+
{
|
|
11924
|
+
regex: /\.csrf\(\)\s*\.\s*disable\(\)/,
|
|
11925
|
+
title: "Spring Security CSRF protection disabled",
|
|
11926
|
+
vulnClass: "code-injection",
|
|
11927
|
+
baseSeverity: "medium",
|
|
11928
|
+
description: "CSRF protection has been explicitly disabled in Spring Security configuration.",
|
|
11929
|
+
attackScenario: "An attacker could forge cross-site requests to perform actions on behalf of authenticated users.",
|
|
11930
|
+
suggestedFix: "Only disable CSRF for stateless APIs using JWT. Keep CSRF enabled for session-based authentication."
|
|
11931
|
+
},
|
|
11932
|
+
{
|
|
11933
|
+
regex: /\.permitAll\(\).*(?:admin|manage|delete|config|setting)/i,
|
|
11934
|
+
title: "Spring Security permitAll on sensitive path",
|
|
11935
|
+
vulnClass: "code-injection",
|
|
11936
|
+
baseSeverity: "high",
|
|
11937
|
+
description: "permitAll() applied to a path that appears security-sensitive.",
|
|
11938
|
+
attackScenario: "An attacker could access administrative or destructive endpoints without authentication.",
|
|
11939
|
+
suggestedFix: 'Use .hasRole("ADMIN") or .authenticated() for sensitive endpoints.'
|
|
11940
|
+
},
|
|
11941
|
+
// C++ injection patterns
|
|
11942
|
+
{
|
|
11943
|
+
regex: /\b(?:strcpy|strcat|sprintf|gets)\s*\(/,
|
|
11944
|
+
title: "C++ buffer overflow risk: unsafe string function",
|
|
11945
|
+
vulnClass: "shell-injection",
|
|
11946
|
+
baseSeverity: "high",
|
|
11947
|
+
description: "Unsafe C string functions (strcpy, strcat, sprintf, gets) with no bounds checking \u2014 buffer overflow risk.",
|
|
11948
|
+
attackScenario: "An attacker could provide oversized input to overflow the buffer, enabling arbitrary code execution.",
|
|
11949
|
+
suggestedFix: "Use bounded alternatives: strncpy, strncat, snprintf, or C++ std::string."
|
|
11950
|
+
},
|
|
11951
|
+
{
|
|
11952
|
+
regex: /printf\s*\(\s*(?!")[a-zA-Z_]\w*/,
|
|
11953
|
+
title: "C++ format string vulnerability",
|
|
11954
|
+
vulnClass: "shell-injection",
|
|
11955
|
+
baseSeverity: "high",
|
|
11956
|
+
description: "printf called with a variable as the format string \u2014 format string attack risk.",
|
|
11957
|
+
attackScenario: "An attacker could inject format specifiers (%x, %n) to read/write arbitrary memory.",
|
|
11958
|
+
suggestedFix: 'Always use a literal format string: printf("%s", userInput).'
|
|
11959
|
+
},
|
|
11960
|
+
{
|
|
11961
|
+
regex: /\bsystem\s*\(\s*(?!")[a-zA-Z_]\w*/,
|
|
11962
|
+
title: "C++ command injection via system()",
|
|
11963
|
+
vulnClass: "shell-injection",
|
|
11964
|
+
baseSeverity: "high",
|
|
11965
|
+
description: "system() called with a variable argument \u2014 potential command injection.",
|
|
11966
|
+
attackScenario: "An attacker could inject shell metacharacters to execute arbitrary commands.",
|
|
11967
|
+
suggestedFix: "Avoid system(). Use execvp with an argument array, or validate input with a strict allowlist."
|
|
11968
|
+
},
|
|
11969
|
+
{
|
|
11970
|
+
regex: /\bpopen\s*\(\s*(?!")[a-zA-Z_]\w*/,
|
|
11971
|
+
title: "C++ command injection via popen()",
|
|
11972
|
+
vulnClass: "shell-injection",
|
|
11973
|
+
baseSeverity: "high",
|
|
11974
|
+
description: "popen() called with a variable argument \u2014 potential command injection.",
|
|
11975
|
+
attackScenario: "An attacker could inject shell metacharacters to execute arbitrary commands.",
|
|
11976
|
+
suggestedFix: "Avoid popen(). Use pipe/fork/exec with argument arrays instead."
|
|
10109
11977
|
}
|
|
10110
11978
|
];
|
|
10111
11979
|
function shouldSkip(filePath) {
|
|
@@ -10122,7 +11990,7 @@ async function checkInjection(files, projectRoot) {
|
|
|
10122
11990
|
if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
|
|
10123
11991
|
let content;
|
|
10124
11992
|
try {
|
|
10125
|
-
content =
|
|
11993
|
+
content = readFileSync16(join20(projectRoot, file.filePath), "utf-8");
|
|
10126
11994
|
} catch {
|
|
10127
11995
|
continue;
|
|
10128
11996
|
}
|
|
@@ -10160,8 +12028,8 @@ async function checkInjection(files, projectRoot) {
|
|
|
10160
12028
|
}
|
|
10161
12029
|
|
|
10162
12030
|
// src/security/checks/secrets.ts
|
|
10163
|
-
import { readFileSync as
|
|
10164
|
-
import { join as
|
|
12031
|
+
import { readFileSync as readFileSync17 } from "fs";
|
|
12032
|
+
import { join as join21 } from "path";
|
|
10165
12033
|
var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10166
12034
|
var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
|
|
10167
12035
|
var SECRET_PATTERNS = [
|
|
@@ -10194,7 +12062,7 @@ async function checkSecrets(files, projectRoot) {
|
|
|
10194
12062
|
if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
|
|
10195
12063
|
let content;
|
|
10196
12064
|
try {
|
|
10197
|
-
content =
|
|
12065
|
+
content = readFileSync17(join21(projectRoot, file.filePath), "utf-8");
|
|
10198
12066
|
} catch {
|
|
10199
12067
|
continue;
|
|
10200
12068
|
}
|
|
@@ -10227,8 +12095,8 @@ async function checkSecrets(files, projectRoot) {
|
|
|
10227
12095
|
}
|
|
10228
12096
|
|
|
10229
12097
|
// src/security/checks/path-traversal.ts
|
|
10230
|
-
import { readFileSync as
|
|
10231
|
-
import { join as
|
|
12098
|
+
import { readFileSync as readFileSync18 } from "fs";
|
|
12099
|
+
import { join as join22 } from "path";
|
|
10232
12100
|
var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10233
12101
|
var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
|
|
10234
12102
|
var PATTERNS2 = [
|
|
@@ -10275,7 +12143,7 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
10275
12143
|
if (shouldSkip3(file.filePath)) continue;
|
|
10276
12144
|
let content;
|
|
10277
12145
|
try {
|
|
10278
|
-
content =
|
|
12146
|
+
content = readFileSync18(join22(projectRoot, file.filePath), "utf-8");
|
|
10279
12147
|
} catch {
|
|
10280
12148
|
continue;
|
|
10281
12149
|
}
|
|
@@ -10317,8 +12185,8 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
10317
12185
|
}
|
|
10318
12186
|
|
|
10319
12187
|
// src/security/checks/auth.ts
|
|
10320
|
-
import { readFileSync as
|
|
10321
|
-
import { join as
|
|
12188
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
12189
|
+
import { join as join23 } from "path";
|
|
10322
12190
|
var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10323
12191
|
function shouldSkip4(filePath) {
|
|
10324
12192
|
return SKIP_DIRS4.some((d) => filePath.includes(d));
|
|
@@ -10334,7 +12202,7 @@ async function checkAuth(files, projectRoot) {
|
|
|
10334
12202
|
if (shouldSkip4(file.filePath)) continue;
|
|
10335
12203
|
let content;
|
|
10336
12204
|
try {
|
|
10337
|
-
content =
|
|
12205
|
+
content = readFileSync19(join23(projectRoot, file.filePath), "utf-8");
|
|
10338
12206
|
} catch {
|
|
10339
12207
|
continue;
|
|
10340
12208
|
}
|
|
@@ -10425,8 +12293,8 @@ async function checkAuth(files, projectRoot) {
|
|
|
10425
12293
|
}
|
|
10426
12294
|
|
|
10427
12295
|
// src/security/checks/input-validation.ts
|
|
10428
|
-
import { readFileSync as
|
|
10429
|
-
import { join as
|
|
12296
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
12297
|
+
import { join as join24 } from "path";
|
|
10430
12298
|
var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10431
12299
|
function shouldSkip5(filePath) {
|
|
10432
12300
|
return SKIP_DIRS5.some((d) => filePath.includes(d));
|
|
@@ -10438,7 +12306,7 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
10438
12306
|
if (shouldSkip5(file.filePath)) continue;
|
|
10439
12307
|
let content;
|
|
10440
12308
|
try {
|
|
10441
|
-
content =
|
|
12309
|
+
content = readFileSync20(join24(projectRoot, file.filePath), "utf-8");
|
|
10442
12310
|
} catch {
|
|
10443
12311
|
continue;
|
|
10444
12312
|
}
|
|
@@ -10515,8 +12383,8 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
10515
12383
|
}
|
|
10516
12384
|
|
|
10517
12385
|
// src/security/checks/information-disclosure.ts
|
|
10518
|
-
import { readFileSync as
|
|
10519
|
-
import { join as
|
|
12386
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
12387
|
+
import { join as join25 } from "path";
|
|
10520
12388
|
var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10521
12389
|
function shouldSkip6(filePath) {
|
|
10522
12390
|
return SKIP_DIRS6.some((d) => filePath.includes(d));
|
|
@@ -10528,7 +12396,7 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
10528
12396
|
if (shouldSkip6(file.filePath)) continue;
|
|
10529
12397
|
let content;
|
|
10530
12398
|
try {
|
|
10531
|
-
content =
|
|
12399
|
+
content = readFileSync21(join25(projectRoot, file.filePath), "utf-8");
|
|
10532
12400
|
} catch {
|
|
10533
12401
|
continue;
|
|
10534
12402
|
}
|
|
@@ -10598,9 +12466,10 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
10598
12466
|
}
|
|
10599
12467
|
|
|
10600
12468
|
// src/security/checks/cryptography.ts
|
|
10601
|
-
import { readFileSync as
|
|
10602
|
-
import { join as
|
|
12469
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
12470
|
+
import { join as join26 } from "path";
|
|
10603
12471
|
var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
12472
|
+
var USER_INPUT_NAMES2 = /(?:input|user|name|path|query|param|request|body|args|url)/i;
|
|
10604
12473
|
function shouldSkip7(filePath) {
|
|
10605
12474
|
return SKIP_DIRS7.some((d) => filePath.includes(d));
|
|
10606
12475
|
}
|
|
@@ -10615,7 +12484,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10615
12484
|
if (shouldSkip7(file.filePath)) continue;
|
|
10616
12485
|
let content;
|
|
10617
12486
|
try {
|
|
10618
|
-
content =
|
|
12487
|
+
content = readFileSync22(join26(projectRoot, file.filePath), "utf-8");
|
|
10619
12488
|
} catch {
|
|
10620
12489
|
continue;
|
|
10621
12490
|
}
|
|
@@ -10624,7 +12493,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10624
12493
|
for (let i = 0; i < lines.length; i++) {
|
|
10625
12494
|
const line = lines[i];
|
|
10626
12495
|
if (line.trimStart().startsWith("//") || line.trimStart().startsWith("#")) continue;
|
|
10627
|
-
if (/createHash\s*\(\s*['"]md5['"]\s*\)/.test(line) || /hashlib\.md5\s*\(/.test(line)) {
|
|
12496
|
+
if (/createHash\s*\(\s*['"]md5['"]\s*\)/.test(line) || /hashlib\.md5\s*\(/.test(line) || /MessageDigest\.getInstance\s*\(\s*["']MD5["']\s*\)/.test(line)) {
|
|
10628
12497
|
findings.push({
|
|
10629
12498
|
id: "",
|
|
10630
12499
|
severity: isCryptoFile ? "high" : "medium",
|
|
@@ -10637,7 +12506,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10637
12506
|
suggestedFix: "Use SHA-256 or SHA-3 for integrity checks. Use bcrypt, scrypt, or argon2 for password hashing."
|
|
10638
12507
|
});
|
|
10639
12508
|
}
|
|
10640
|
-
if (/createHash\s*\(\s*['"]sha1['"]\s*\)/.test(line) || /hashlib\.sha1\s*\(/.test(line)) {
|
|
12509
|
+
if (/createHash\s*\(\s*['"]sha1['"]\s*\)/.test(line) || /hashlib\.sha1\s*\(/.test(line) || /MessageDigest\.getInstance\s*\(\s*["']SHA-?1["']\s*\)/.test(line)) {
|
|
10641
12510
|
findings.push({
|
|
10642
12511
|
id: "",
|
|
10643
12512
|
severity: isCryptoFile ? "high" : "medium",
|
|
@@ -10650,6 +12519,34 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10650
12519
|
suggestedFix: "Use SHA-256 or SHA-3 for integrity checks. Use bcrypt, scrypt, or argon2 for password hashing."
|
|
10651
12520
|
});
|
|
10652
12521
|
}
|
|
12522
|
+
if (/Cipher\.getInstance\s*\(\s*["']DES/.test(line)) {
|
|
12523
|
+
findings.push({
|
|
12524
|
+
id: "",
|
|
12525
|
+
severity: "high",
|
|
12526
|
+
vulnerabilityClass: "cryptography",
|
|
12527
|
+
file: file.filePath,
|
|
12528
|
+
line: i + 1,
|
|
12529
|
+
title: "Weak cipher algorithm: DES",
|
|
12530
|
+
description: "DES uses a 56-bit key and can be brute-forced in hours.",
|
|
12531
|
+
attackScenario: "An attacker could brute-force DES-encrypted data to reveal plaintext.",
|
|
12532
|
+
suggestedFix: 'Use AES-256 with GCM mode: Cipher.getInstance("AES/GCM/NoPadding")'
|
|
12533
|
+
});
|
|
12534
|
+
}
|
|
12535
|
+
if (/(?:log|logger|LOG)\s*\.\s*(?:info|debug|warn|error|trace)\s*\([^)]*\+/.test(line)) {
|
|
12536
|
+
if (USER_INPUT_NAMES2.test(line)) {
|
|
12537
|
+
findings.push({
|
|
12538
|
+
id: "",
|
|
12539
|
+
severity: "medium",
|
|
12540
|
+
vulnerabilityClass: "cryptography",
|
|
12541
|
+
file: file.filePath,
|
|
12542
|
+
line: i + 1,
|
|
12543
|
+
title: "Potential log injection",
|
|
12544
|
+
description: "User-controlled input concatenated directly into log output.",
|
|
12545
|
+
attackScenario: "An attacker could inject newlines or control characters to forge log entries or hide malicious activity.",
|
|
12546
|
+
suggestedFix: 'Use parameterized logging: log.info("User: {}", userInput) instead of string concatenation.'
|
|
12547
|
+
});
|
|
12548
|
+
}
|
|
12549
|
+
}
|
|
10653
12550
|
if (/Math\.random\(\)/.test(line) && isCryptoFile) {
|
|
10654
12551
|
findings.push({
|
|
10655
12552
|
id: "",
|
|
@@ -10689,6 +12586,32 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10689
12586
|
suggestedFix: "Generate a unique random salt per user using crypto.randomBytes(16)."
|
|
10690
12587
|
});
|
|
10691
12588
|
}
|
|
12589
|
+
if (/\brand\s*\(\s*\)/.test(line) && isCryptoFile) {
|
|
12590
|
+
findings.push({
|
|
12591
|
+
id: "",
|
|
12592
|
+
severity: "medium",
|
|
12593
|
+
vulnerabilityClass: "cryptography",
|
|
12594
|
+
file: file.filePath,
|
|
12595
|
+
line: i + 1,
|
|
12596
|
+
title: "Weak random: rand() in security context",
|
|
12597
|
+
description: "rand() is not cryptographically secure \u2014 its output can be predicted.",
|
|
12598
|
+
attackScenario: "An attacker could predict rand() values to forge tokens or bypass security checks.",
|
|
12599
|
+
suggestedFix: "Use std::random_device or platform-specific CSPRNG (e.g., /dev/urandom, BCryptGenRandom)."
|
|
12600
|
+
});
|
|
12601
|
+
}
|
|
12602
|
+
if (/(?:const\s+(?:char|std::string)\s*\*?\s*(?:password|secret|api_key|apiKey|token)\s*=\s*["'])/i.test(line)) {
|
|
12603
|
+
findings.push({
|
|
12604
|
+
id: "",
|
|
12605
|
+
severity: "high",
|
|
12606
|
+
vulnerabilityClass: "cryptography",
|
|
12607
|
+
file: file.filePath,
|
|
12608
|
+
line: i + 1,
|
|
12609
|
+
title: "Hardcoded credentials in C++ source",
|
|
12610
|
+
description: "A password, secret, or API key is hardcoded as a string literal.",
|
|
12611
|
+
attackScenario: "An attacker with access to the binary or source could extract the credential.",
|
|
12612
|
+
suggestedFix: "Load credentials from environment variables or a secure vault at runtime."
|
|
12613
|
+
});
|
|
12614
|
+
}
|
|
10692
12615
|
}
|
|
10693
12616
|
}
|
|
10694
12617
|
} catch {
|
|
@@ -10697,8 +12620,8 @@ async function checkCryptography(files, projectRoot) {
|
|
|
10697
12620
|
}
|
|
10698
12621
|
|
|
10699
12622
|
// src/security/checks/frontend.ts
|
|
10700
|
-
import { readFileSync as
|
|
10701
|
-
import { join as
|
|
12623
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
12624
|
+
import { join as join27 } from "path";
|
|
10702
12625
|
var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
10703
12626
|
function shouldSkip8(filePath) {
|
|
10704
12627
|
return SKIP_DIRS8.some((d) => filePath.includes(d));
|
|
@@ -10714,7 +12637,7 @@ async function checkFrontend(files, projectRoot) {
|
|
|
10714
12637
|
if (!isFrontendFile(file.filePath)) continue;
|
|
10715
12638
|
let content;
|
|
10716
12639
|
try {
|
|
10717
|
-
content =
|
|
12640
|
+
content = readFileSync23(join27(projectRoot, file.filePath), "utf-8");
|
|
10718
12641
|
} catch {
|
|
10719
12642
|
continue;
|
|
10720
12643
|
}
|
|
@@ -11174,11 +13097,13 @@ async function scanSecurity(projectRoot, graph, options = {}) {
|
|
|
11174
13097
|
};
|
|
11175
13098
|
}
|
|
11176
13099
|
function detectPackageManager(projectRoot) {
|
|
11177
|
-
if (
|
|
11178
|
-
if (
|
|
11179
|
-
if (
|
|
11180
|
-
if (
|
|
11181
|
-
if (
|
|
13100
|
+
if (existsSync17(join28(projectRoot, "package.json"))) return "npm";
|
|
13101
|
+
if (existsSync17(join28(projectRoot, "requirements.txt"))) return "pip";
|
|
13102
|
+
if (existsSync17(join28(projectRoot, "pyproject.toml"))) return "pip";
|
|
13103
|
+
if (existsSync17(join28(projectRoot, "Cargo.toml"))) return "cargo";
|
|
13104
|
+
if (existsSync17(join28(projectRoot, "go.mod"))) return "go";
|
|
13105
|
+
if (existsSync17(join28(projectRoot, "pom.xml"))) return "maven";
|
|
13106
|
+
if (existsSync17(join28(projectRoot, "build.gradle")) || existsSync17(join28(projectRoot, "build.gradle.kts"))) return "gradle";
|
|
11182
13107
|
return "unknown";
|
|
11183
13108
|
}
|
|
11184
13109
|
|