depwire-cli 1.1.0 → 1.1.2
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 +34 -25
- package/dist/{chunk-W3ZVSDFL.js → chunk-M5BETAND.js} +8 -8
- package/dist/{chunk-WLKW7X7G.js → chunk-ZO3LWFDE.js} +1465 -157
- package/dist/index.js +2 -2
- package/dist/mcpb-entry.js +2 -2
- package/dist/parser/grammars/tree-sitter-swift.wasm +0 -0
- package/dist/sdk.js +1 -1
- package/package.json +4 -2
|
@@ -37,8 +37,10 @@ function scanDirectory(rootDir, baseDir = rootDir) {
|
|
|
37
37
|
const isJava = entry.endsWith(".java") || entry === "pom.xml" || entry === "build.gradle" || entry === "build.gradle.kts";
|
|
38
38
|
const isKotlin = entry.endsWith(".kt") || entry.endsWith(".kts") || entry === "settings.gradle.kts" || entry === "settings.gradle";
|
|
39
39
|
const isPhp = entry.endsWith(".php");
|
|
40
|
+
const isSwift = entry.endsWith(".swift");
|
|
41
|
+
const isMojo = entry.endsWith(".mojo") || entry.endsWith(".\u{1F525}");
|
|
40
42
|
const isCppBuild = entry === "CMakeLists.txt" || entry === "conanfile.txt" || entry === "vcpkg.json";
|
|
41
|
-
if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC || isCpp || isCSharp || isJava || isKotlin || isPhp || isCppBuild) {
|
|
43
|
+
if (isTypeScript || isJavaScript || isPython || isGo || isRust || isC || isCpp || isCSharp || isJava || isKotlin || isPhp || isSwift || isMojo || isCppBuild) {
|
|
42
44
|
files.push(relative(rootDir, fullPath));
|
|
43
45
|
}
|
|
44
46
|
}
|
|
@@ -84,6 +86,10 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
84
86
|
// Kotlin (Gradle KTS)
|
|
85
87
|
"composer.json",
|
|
86
88
|
// PHP
|
|
89
|
+
"Package.swift",
|
|
90
|
+
// Swift (SPM)
|
|
91
|
+
"mojoproject.toml",
|
|
92
|
+
// Mojo
|
|
87
93
|
".git"
|
|
88
94
|
// Any git repo
|
|
89
95
|
];
|
|
@@ -117,11 +123,11 @@ function findProjectRoot(startDir = process.cwd()) {
|
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
// src/parser/index.ts
|
|
120
|
-
import { readFileSync as
|
|
121
|
-
import { join as
|
|
126
|
+
import { readFileSync as readFileSync11, statSync as statSync8 } from "fs";
|
|
127
|
+
import { join as join15, resolve as resolve9 } from "path";
|
|
122
128
|
|
|
123
129
|
// src/parser/detect.ts
|
|
124
|
-
import { extname as
|
|
130
|
+
import { extname as extname10, basename as basename8 } from "path";
|
|
125
131
|
|
|
126
132
|
// src/parser/wasm-init.ts
|
|
127
133
|
import { Parser, Language } from "web-tree-sitter";
|
|
@@ -153,7 +159,9 @@ async function initParser() {
|
|
|
153
159
|
"java": "tree-sitter-java.wasm",
|
|
154
160
|
"cpp": "tree-sitter-cpp.wasm",
|
|
155
161
|
"kotlin": "tree-sitter-kotlin.wasm",
|
|
156
|
-
"php": "tree-sitter-php.wasm"
|
|
162
|
+
"php": "tree-sitter-php.wasm",
|
|
163
|
+
"swift": "tree-sitter-swift.wasm"
|
|
164
|
+
// Note: Mojo uses a pattern-based parser (no tree-sitter-mojo WASM available)
|
|
157
165
|
};
|
|
158
166
|
for (const [name, file] of Object.entries(grammarFiles)) {
|
|
159
167
|
const wasmPath = path.join(grammarsDir, file);
|
|
@@ -6231,6 +6239,921 @@ var phpParser = {
|
|
|
6231
6239
|
parseFile: parsePhpFile
|
|
6232
6240
|
};
|
|
6233
6241
|
|
|
6242
|
+
// src/parser/swift.ts
|
|
6243
|
+
import { dirname as dirname12, join as join13, basename as basename6 } from "path";
|
|
6244
|
+
import { existsSync as existsSync13, readdirSync as readdirSync10, statSync as statSync7 } from "fs";
|
|
6245
|
+
function parseSwiftFile(filePath, sourceCode, projectRoot) {
|
|
6246
|
+
if (filePath.endsWith("Package.swift")) {
|
|
6247
|
+
return parsePackageSwift(filePath, sourceCode, projectRoot);
|
|
6248
|
+
}
|
|
6249
|
+
const parser = getParser("swift");
|
|
6250
|
+
const tree = parser.parse(sourceCode, null, { bufferSize: 1024 * 1024 });
|
|
6251
|
+
const context = {
|
|
6252
|
+
filePath,
|
|
6253
|
+
projectRoot,
|
|
6254
|
+
sourceCode,
|
|
6255
|
+
symbols: [],
|
|
6256
|
+
edges: [],
|
|
6257
|
+
currentScope: [],
|
|
6258
|
+
currentClass: null,
|
|
6259
|
+
currentModule: null,
|
|
6260
|
+
imports: /* @__PURE__ */ new Map(),
|
|
6261
|
+
isPackageFile: false
|
|
6262
|
+
};
|
|
6263
|
+
walkNode12(tree.rootNode, context);
|
|
6264
|
+
return {
|
|
6265
|
+
filePath,
|
|
6266
|
+
symbols: context.symbols,
|
|
6267
|
+
edges: context.edges
|
|
6268
|
+
};
|
|
6269
|
+
}
|
|
6270
|
+
function walkNode12(node, context) {
|
|
6271
|
+
const handled = processNode12(node, context);
|
|
6272
|
+
if (handled) return;
|
|
6273
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
6274
|
+
const child = node.child(i);
|
|
6275
|
+
if (child) {
|
|
6276
|
+
walkNode12(child, context);
|
|
6277
|
+
}
|
|
6278
|
+
}
|
|
6279
|
+
}
|
|
6280
|
+
function processNode12(node, context) {
|
|
6281
|
+
switch (node.type) {
|
|
6282
|
+
case "import_declaration":
|
|
6283
|
+
processImportDeclaration3(node, context);
|
|
6284
|
+
return false;
|
|
6285
|
+
case "class_declaration":
|
|
6286
|
+
processClassDeclaration7(node, context);
|
|
6287
|
+
return true;
|
|
6288
|
+
// handles its own children
|
|
6289
|
+
case "protocol_declaration":
|
|
6290
|
+
processProtocolDeclaration(node, context);
|
|
6291
|
+
return true;
|
|
6292
|
+
// handles its own children
|
|
6293
|
+
case "function_declaration":
|
|
6294
|
+
processFunctionDeclaration5(node, context);
|
|
6295
|
+
return true;
|
|
6296
|
+
// handles its own children
|
|
6297
|
+
case "init_declaration":
|
|
6298
|
+
processInitDeclaration(node, context);
|
|
6299
|
+
return true;
|
|
6300
|
+
case "deinit_declaration":
|
|
6301
|
+
processDeinitDeclaration(node, context);
|
|
6302
|
+
return true;
|
|
6303
|
+
case "property_declaration":
|
|
6304
|
+
case "variable_declaration":
|
|
6305
|
+
processPropertyDeclaration4(node, context);
|
|
6306
|
+
return false;
|
|
6307
|
+
case "typealias_declaration":
|
|
6308
|
+
processTypealiasDeclaration(node, context);
|
|
6309
|
+
return false;
|
|
6310
|
+
case "associatedtype_declaration":
|
|
6311
|
+
processAssociatedTypeDeclaration(node, context);
|
|
6312
|
+
return false;
|
|
6313
|
+
case "call_expression":
|
|
6314
|
+
processCallExpression12(node, context);
|
|
6315
|
+
return false;
|
|
6316
|
+
default:
|
|
6317
|
+
return false;
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
function processImportDeclaration3(node, context) {
|
|
6321
|
+
const text = nodeText11(node, context).trim();
|
|
6322
|
+
const match = text.match(/^import\s+(?:(?:typealias|struct|class|enum|protocol|let|var|func)\s+)?(.+)$/);
|
|
6323
|
+
if (!match) return;
|
|
6324
|
+
const importPath = match[1].trim();
|
|
6325
|
+
const resolvedPath = resolveSwiftImport(importPath, context.filePath, context.projectRoot);
|
|
6326
|
+
if (resolvedPath) {
|
|
6327
|
+
const sourceId = `${context.filePath}::__file__`;
|
|
6328
|
+
const targetId = `${resolvedPath}::__file__`;
|
|
6329
|
+
context.edges.push({
|
|
6330
|
+
source: sourceId,
|
|
6331
|
+
target: targetId,
|
|
6332
|
+
kind: "imports",
|
|
6333
|
+
filePath: context.filePath,
|
|
6334
|
+
line: node.startPosition.row + 1
|
|
6335
|
+
});
|
|
6336
|
+
const parts = importPath.split(".");
|
|
6337
|
+
const simpleName = parts[parts.length - 1];
|
|
6338
|
+
context.imports.set(simpleName, `${resolvedPath}::${simpleName}`);
|
|
6339
|
+
}
|
|
6340
|
+
const symbolId = `${context.filePath}::import:${importPath}`;
|
|
6341
|
+
context.symbols.push({
|
|
6342
|
+
id: symbolId,
|
|
6343
|
+
name: importPath,
|
|
6344
|
+
kind: "import",
|
|
6345
|
+
filePath: context.filePath,
|
|
6346
|
+
startLine: node.startPosition.row + 1,
|
|
6347
|
+
endLine: node.endPosition.row + 1,
|
|
6348
|
+
exported: false
|
|
6349
|
+
});
|
|
6350
|
+
}
|
|
6351
|
+
function processClassDeclaration7(node, context) {
|
|
6352
|
+
let keyword = "class";
|
|
6353
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
6354
|
+
const child = node.child(i);
|
|
6355
|
+
if (child && ["class", "struct", "actor", "enum", "extension"].includes(child.type)) {
|
|
6356
|
+
keyword = child.type;
|
|
6357
|
+
break;
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
if (keyword === "extension") {
|
|
6361
|
+
const typeNode = findChildByType12(node, "user_type") || findChildByType12(node, "type_identifier");
|
|
6362
|
+
const extName = typeNode ? nodeText11(typeNode, context).trim() : "Unknown";
|
|
6363
|
+
const name2 = `${extName}+ext`;
|
|
6364
|
+
const symbolId2 = `${context.filePath}::${name2}`;
|
|
6365
|
+
context.symbols.push({
|
|
6366
|
+
id: symbolId2,
|
|
6367
|
+
name: name2,
|
|
6368
|
+
kind: "class",
|
|
6369
|
+
filePath: context.filePath,
|
|
6370
|
+
startLine: node.startPosition.row + 1,
|
|
6371
|
+
endLine: node.endPosition.row + 1,
|
|
6372
|
+
exported: true
|
|
6373
|
+
});
|
|
6374
|
+
const oldClass2 = context.currentClass;
|
|
6375
|
+
context.currentClass = extName;
|
|
6376
|
+
context.currentScope.push(extName);
|
|
6377
|
+
const body2 = findChildByType12(node, "class_body");
|
|
6378
|
+
if (body2) {
|
|
6379
|
+
walkNode12(body2, context);
|
|
6380
|
+
}
|
|
6381
|
+
context.currentScope.pop();
|
|
6382
|
+
context.currentClass = oldClass2;
|
|
6383
|
+
return;
|
|
6384
|
+
}
|
|
6385
|
+
const nameNode = findChildByType12(node, "type_identifier") || findChildByType12(node, "simple_identifier");
|
|
6386
|
+
if (!nameNode) return;
|
|
6387
|
+
const name = nodeText11(nameNode, context);
|
|
6388
|
+
const modifiers = getModifiers3(node, context);
|
|
6389
|
+
const exported = !modifiers.includes("private") && !modifiers.includes("fileprivate");
|
|
6390
|
+
const scope = context.currentClass || void 0;
|
|
6391
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
6392
|
+
let kind = "class";
|
|
6393
|
+
if (keyword === "enum") kind = "enum";
|
|
6394
|
+
else if (keyword === "struct" || keyword === "actor") kind = "class";
|
|
6395
|
+
context.symbols.push({
|
|
6396
|
+
id: symbolId,
|
|
6397
|
+
name,
|
|
6398
|
+
kind,
|
|
6399
|
+
filePath: context.filePath,
|
|
6400
|
+
startLine: node.startPosition.row + 1,
|
|
6401
|
+
endLine: node.endPosition.row + 1,
|
|
6402
|
+
exported,
|
|
6403
|
+
scope
|
|
6404
|
+
});
|
|
6405
|
+
processInheritance(node, symbolId, context);
|
|
6406
|
+
if (keyword === "enum") {
|
|
6407
|
+
processEnumCases(node, name, context);
|
|
6408
|
+
}
|
|
6409
|
+
const oldClass = context.currentClass;
|
|
6410
|
+
context.currentClass = name;
|
|
6411
|
+
context.currentScope.push(name);
|
|
6412
|
+
const body = findChildByType12(node, "class_body");
|
|
6413
|
+
if (body) {
|
|
6414
|
+
walkNode12(body, context);
|
|
6415
|
+
}
|
|
6416
|
+
context.currentScope.pop();
|
|
6417
|
+
context.currentClass = oldClass;
|
|
6418
|
+
}
|
|
6419
|
+
function processProtocolDeclaration(node, context) {
|
|
6420
|
+
const nameNode = findChildByType12(node, "type_identifier") || findChildByType12(node, "simple_identifier");
|
|
6421
|
+
if (!nameNode) return;
|
|
6422
|
+
const name = nodeText11(nameNode, context);
|
|
6423
|
+
const modifiers = getModifiers3(node, context);
|
|
6424
|
+
const exported = !modifiers.includes("private") && !modifiers.includes("fileprivate");
|
|
6425
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
6426
|
+
context.symbols.push({
|
|
6427
|
+
id: symbolId,
|
|
6428
|
+
name,
|
|
6429
|
+
kind: "interface",
|
|
6430
|
+
filePath: context.filePath,
|
|
6431
|
+
startLine: node.startPosition.row + 1,
|
|
6432
|
+
endLine: node.endPosition.row + 1,
|
|
6433
|
+
exported
|
|
6434
|
+
});
|
|
6435
|
+
processInheritance(node, symbolId, context);
|
|
6436
|
+
const oldClass = context.currentClass;
|
|
6437
|
+
context.currentClass = name;
|
|
6438
|
+
context.currentScope.push(name);
|
|
6439
|
+
const body = findChildByType12(node, "protocol_body");
|
|
6440
|
+
if (body) {
|
|
6441
|
+
walkNode12(body, context);
|
|
6442
|
+
}
|
|
6443
|
+
context.currentScope.pop();
|
|
6444
|
+
context.currentClass = oldClass;
|
|
6445
|
+
}
|
|
6446
|
+
function processFunctionDeclaration5(node, context) {
|
|
6447
|
+
const nameNode = findChildByType12(node, "simple_identifier");
|
|
6448
|
+
if (!nameNode) return;
|
|
6449
|
+
const name = nodeText11(nameNode, context);
|
|
6450
|
+
const modifiers = getModifiers3(node, context);
|
|
6451
|
+
const exported = !modifiers.includes("private") && !modifiers.includes("fileprivate");
|
|
6452
|
+
const scope = context.currentClass || void 0;
|
|
6453
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
6454
|
+
context.symbols.push({
|
|
6455
|
+
id: symbolId,
|
|
6456
|
+
name,
|
|
6457
|
+
kind: context.currentClass ? "method" : "function",
|
|
6458
|
+
filePath: context.filePath,
|
|
6459
|
+
startLine: node.startPosition.row + 1,
|
|
6460
|
+
endLine: node.endPosition.row + 1,
|
|
6461
|
+
exported,
|
|
6462
|
+
scope
|
|
6463
|
+
});
|
|
6464
|
+
const scopeName = scope ? `${scope}.${name}` : name;
|
|
6465
|
+
context.currentScope.push(scopeName);
|
|
6466
|
+
const body = findChildByType12(node, "function_body") || findChildByType12(node, "code_block");
|
|
6467
|
+
if (body) {
|
|
6468
|
+
walkNode12(body, context);
|
|
6469
|
+
}
|
|
6470
|
+
context.currentScope.pop();
|
|
6471
|
+
}
|
|
6472
|
+
function processInitDeclaration(node, context) {
|
|
6473
|
+
const scope = context.currentClass || void 0;
|
|
6474
|
+
if (!scope) return;
|
|
6475
|
+
const name = "init";
|
|
6476
|
+
const symbolId = `${context.filePath}::${scope}.${name}:${node.startPosition.row + 1}`;
|
|
6477
|
+
context.symbols.push({
|
|
6478
|
+
id: symbolId,
|
|
6479
|
+
name,
|
|
6480
|
+
kind: "method",
|
|
6481
|
+
filePath: context.filePath,
|
|
6482
|
+
startLine: node.startPosition.row + 1,
|
|
6483
|
+
endLine: node.endPosition.row + 1,
|
|
6484
|
+
exported: true,
|
|
6485
|
+
scope
|
|
6486
|
+
});
|
|
6487
|
+
const scopeName = `${scope}.${name}`;
|
|
6488
|
+
context.currentScope.push(scopeName);
|
|
6489
|
+
const body = findChildByType12(node, "function_body") || findChildByType12(node, "code_block");
|
|
6490
|
+
if (body) {
|
|
6491
|
+
walkNode12(body, context);
|
|
6492
|
+
}
|
|
6493
|
+
context.currentScope.pop();
|
|
6494
|
+
}
|
|
6495
|
+
function processDeinitDeclaration(node, context) {
|
|
6496
|
+
const scope = context.currentClass || void 0;
|
|
6497
|
+
if (!scope) return;
|
|
6498
|
+
const name = "deinit";
|
|
6499
|
+
const symbolId = `${context.filePath}::${scope}.${name}`;
|
|
6500
|
+
context.symbols.push({
|
|
6501
|
+
id: symbolId,
|
|
6502
|
+
name,
|
|
6503
|
+
kind: "method",
|
|
6504
|
+
filePath: context.filePath,
|
|
6505
|
+
startLine: node.startPosition.row + 1,
|
|
6506
|
+
endLine: node.endPosition.row + 1,
|
|
6507
|
+
exported: true,
|
|
6508
|
+
scope
|
|
6509
|
+
});
|
|
6510
|
+
}
|
|
6511
|
+
function processPropertyDeclaration4(node, context) {
|
|
6512
|
+
const nameNode = findChildByType12(node, "simple_identifier") || findChildByType12(node, "pattern");
|
|
6513
|
+
if (!nameNode) return;
|
|
6514
|
+
const name = nodeText11(nameNode, context).trim();
|
|
6515
|
+
if (!name || name.includes(" ")) return;
|
|
6516
|
+
const modifiers = getModifiers3(node, context);
|
|
6517
|
+
const exported = !modifiers.includes("private") && !modifiers.includes("fileprivate");
|
|
6518
|
+
const scope = context.currentClass || void 0;
|
|
6519
|
+
const text = nodeText11(node, context);
|
|
6520
|
+
const isConst = text.trimStart().startsWith("let");
|
|
6521
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
6522
|
+
context.symbols.push({
|
|
6523
|
+
id: symbolId,
|
|
6524
|
+
name,
|
|
6525
|
+
kind: isConst ? "constant" : "property",
|
|
6526
|
+
filePath: context.filePath,
|
|
6527
|
+
startLine: node.startPosition.row + 1,
|
|
6528
|
+
endLine: node.endPosition.row + 1,
|
|
6529
|
+
exported,
|
|
6530
|
+
scope
|
|
6531
|
+
});
|
|
6532
|
+
}
|
|
6533
|
+
function processTypealiasDeclaration(node, context) {
|
|
6534
|
+
const nameNode = findChildByType12(node, "type_identifier") || findChildByType12(node, "simple_identifier");
|
|
6535
|
+
if (!nameNode) return;
|
|
6536
|
+
const name = nodeText11(nameNode, context);
|
|
6537
|
+
const symbolId = `${context.filePath}::${name}`;
|
|
6538
|
+
context.symbols.push({
|
|
6539
|
+
id: symbolId,
|
|
6540
|
+
name,
|
|
6541
|
+
kind: "type_alias",
|
|
6542
|
+
filePath: context.filePath,
|
|
6543
|
+
startLine: node.startPosition.row + 1,
|
|
6544
|
+
endLine: node.endPosition.row + 1,
|
|
6545
|
+
exported: true
|
|
6546
|
+
});
|
|
6547
|
+
}
|
|
6548
|
+
function processAssociatedTypeDeclaration(node, context) {
|
|
6549
|
+
const nameNode = findChildByType12(node, "type_identifier") || findChildByType12(node, "simple_identifier");
|
|
6550
|
+
if (!nameNode) return;
|
|
6551
|
+
const name = nodeText11(nameNode, context);
|
|
6552
|
+
const scope = context.currentClass || void 0;
|
|
6553
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
6554
|
+
context.symbols.push({
|
|
6555
|
+
id: symbolId,
|
|
6556
|
+
name,
|
|
6557
|
+
kind: "type_alias",
|
|
6558
|
+
filePath: context.filePath,
|
|
6559
|
+
startLine: node.startPosition.row + 1,
|
|
6560
|
+
endLine: node.endPosition.row + 1,
|
|
6561
|
+
exported: true,
|
|
6562
|
+
scope
|
|
6563
|
+
});
|
|
6564
|
+
}
|
|
6565
|
+
function processCallExpression12(node, context) {
|
|
6566
|
+
if (context.currentScope.length === 0) return;
|
|
6567
|
+
const firstChild = node.child(0);
|
|
6568
|
+
if (!firstChild) return;
|
|
6569
|
+
let calleeName = null;
|
|
6570
|
+
if (firstChild.type === "simple_identifier") {
|
|
6571
|
+
calleeName = nodeText11(firstChild, context);
|
|
6572
|
+
} else if (firstChild.type === "navigation_expression" || firstChild.type === "member_access") {
|
|
6573
|
+
for (let i = firstChild.childCount - 1; i >= 0; i--) {
|
|
6574
|
+
const child = firstChild.child(i);
|
|
6575
|
+
if (child && (child.type === "simple_identifier" || child.type === "navigation_suffix")) {
|
|
6576
|
+
calleeName = nodeText11(child, context).replace(/^\./, "");
|
|
6577
|
+
break;
|
|
6578
|
+
}
|
|
6579
|
+
}
|
|
6580
|
+
}
|
|
6581
|
+
if (!calleeName) return;
|
|
6582
|
+
const builtins = /* @__PURE__ */ new Set([
|
|
6583
|
+
"print",
|
|
6584
|
+
"debugPrint",
|
|
6585
|
+
"dump",
|
|
6586
|
+
"fatalError",
|
|
6587
|
+
"precondition",
|
|
6588
|
+
"assert",
|
|
6589
|
+
"preconditionFailure",
|
|
6590
|
+
"assertionFailure",
|
|
6591
|
+
"map",
|
|
6592
|
+
"filter",
|
|
6593
|
+
"reduce",
|
|
6594
|
+
"forEach",
|
|
6595
|
+
"flatMap",
|
|
6596
|
+
"compactMap",
|
|
6597
|
+
"sorted",
|
|
6598
|
+
"contains",
|
|
6599
|
+
"first",
|
|
6600
|
+
"last",
|
|
6601
|
+
"count",
|
|
6602
|
+
"isEmpty",
|
|
6603
|
+
"append",
|
|
6604
|
+
"remove",
|
|
6605
|
+
"insert",
|
|
6606
|
+
"removeAll",
|
|
6607
|
+
"String",
|
|
6608
|
+
"Int",
|
|
6609
|
+
"Double",
|
|
6610
|
+
"Float",
|
|
6611
|
+
"Bool",
|
|
6612
|
+
"Array",
|
|
6613
|
+
"Dictionary",
|
|
6614
|
+
"Set",
|
|
6615
|
+
"DispatchQueue",
|
|
6616
|
+
"Task",
|
|
6617
|
+
"withCheckedContinuation",
|
|
6618
|
+
"withCheckedThrowingContinuation"
|
|
6619
|
+
]);
|
|
6620
|
+
if (builtins.has(calleeName)) return;
|
|
6621
|
+
const callerId = getCurrentSymbolId12(context);
|
|
6622
|
+
if (!callerId) return;
|
|
6623
|
+
const calleeId = resolveSymbol11(calleeName, context);
|
|
6624
|
+
if (calleeId) {
|
|
6625
|
+
context.edges.push({
|
|
6626
|
+
source: callerId,
|
|
6627
|
+
target: calleeId,
|
|
6628
|
+
kind: "calls",
|
|
6629
|
+
filePath: context.filePath,
|
|
6630
|
+
line: node.startPosition.row + 1
|
|
6631
|
+
});
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
function processInheritance(node, sourceId, context) {
|
|
6635
|
+
const inheritanceClause = findChildByType12(node, "inheritance_specifier") || findChildByType12(node, "type_inheritance_clause");
|
|
6636
|
+
if (!inheritanceClause) return;
|
|
6637
|
+
const text = nodeText11(node, context);
|
|
6638
|
+
const colonMatch = text.match(/:\s*([^{]+)/);
|
|
6639
|
+
if (!colonMatch) return;
|
|
6640
|
+
const types = colonMatch[1].split(",").map((t) => t.trim().split("<")[0].trim());
|
|
6641
|
+
for (const typeName of types) {
|
|
6642
|
+
if (!typeName || typeName.includes("{") || typeName.includes("where")) break;
|
|
6643
|
+
const baseId = resolveSymbol11(typeName, context);
|
|
6644
|
+
if (baseId) {
|
|
6645
|
+
context.edges.push({
|
|
6646
|
+
source: sourceId,
|
|
6647
|
+
target: baseId,
|
|
6648
|
+
kind: "implements",
|
|
6649
|
+
filePath: context.filePath,
|
|
6650
|
+
line: node.startPosition.row + 1
|
|
6651
|
+
});
|
|
6652
|
+
}
|
|
6653
|
+
}
|
|
6654
|
+
}
|
|
6655
|
+
function processEnumCases(node, enumName, context) {
|
|
6656
|
+
const text = nodeText11(node, context);
|
|
6657
|
+
const caseMatches = text.matchAll(/\bcase\s+(\w+)/g);
|
|
6658
|
+
for (const match of caseMatches) {
|
|
6659
|
+
const caseName = match[1];
|
|
6660
|
+
if (caseName === enumName) continue;
|
|
6661
|
+
const constId = `${context.filePath}::${enumName}.${caseName}`;
|
|
6662
|
+
context.symbols.push({
|
|
6663
|
+
id: constId,
|
|
6664
|
+
name: caseName,
|
|
6665
|
+
kind: "constant",
|
|
6666
|
+
filePath: context.filePath,
|
|
6667
|
+
startLine: node.startPosition.row + 1,
|
|
6668
|
+
endLine: node.endPosition.row + 1,
|
|
6669
|
+
exported: true,
|
|
6670
|
+
scope: enumName
|
|
6671
|
+
});
|
|
6672
|
+
}
|
|
6673
|
+
}
|
|
6674
|
+
function parsePackageSwift(filePath, sourceCode, projectRoot) {
|
|
6675
|
+
const symbols = [];
|
|
6676
|
+
const edges = [];
|
|
6677
|
+
const lines = sourceCode.split("\n");
|
|
6678
|
+
const nameMatch = sourceCode.match(/name\s*:\s*["']([^"']+)["']/);
|
|
6679
|
+
const packageName = nameMatch ? nameMatch[1] : basename6(dirname12(join13(projectRoot, filePath)));
|
|
6680
|
+
symbols.push({
|
|
6681
|
+
id: `${filePath}::${packageName}`,
|
|
6682
|
+
name: packageName,
|
|
6683
|
+
kind: "module",
|
|
6684
|
+
filePath,
|
|
6685
|
+
startLine: 1,
|
|
6686
|
+
endLine: lines.length,
|
|
6687
|
+
exported: true
|
|
6688
|
+
});
|
|
6689
|
+
for (let i = 0; i < lines.length; i++) {
|
|
6690
|
+
const line = lines[i].trim();
|
|
6691
|
+
const lineNum = i + 1;
|
|
6692
|
+
const depMatch = line.match(/\.package\s*\(\s*(?:url\s*:\s*)?["']([^"']+)["']/);
|
|
6693
|
+
if (depMatch) {
|
|
6694
|
+
const depUrl = depMatch[1];
|
|
6695
|
+
const depName = depUrl.split("/").pop()?.replace(/\.git$/, "") || depUrl;
|
|
6696
|
+
symbols.push({
|
|
6697
|
+
id: `${filePath}::dep:${depName}`,
|
|
6698
|
+
name: depName,
|
|
6699
|
+
kind: "import",
|
|
6700
|
+
filePath,
|
|
6701
|
+
startLine: lineNum,
|
|
6702
|
+
endLine: lineNum,
|
|
6703
|
+
exported: false
|
|
6704
|
+
});
|
|
6705
|
+
}
|
|
6706
|
+
const targetMatch = line.match(/\.(?:target|executableTarget|testTarget)\s*\(\s*name\s*:\s*["']([^"']+)["']/);
|
|
6707
|
+
if (targetMatch) {
|
|
6708
|
+
symbols.push({
|
|
6709
|
+
id: `${filePath}::target:${targetMatch[1]}`,
|
|
6710
|
+
name: targetMatch[1],
|
|
6711
|
+
kind: "module",
|
|
6712
|
+
filePath,
|
|
6713
|
+
startLine: lineNum,
|
|
6714
|
+
endLine: lineNum,
|
|
6715
|
+
exported: true
|
|
6716
|
+
});
|
|
6717
|
+
}
|
|
6718
|
+
}
|
|
6719
|
+
return { filePath, symbols, edges };
|
|
6720
|
+
}
|
|
6721
|
+
function resolveSwiftImport(importPath, currentFile, projectRoot) {
|
|
6722
|
+
const parts = importPath.split(".");
|
|
6723
|
+
const moduleName = parts[0];
|
|
6724
|
+
const sourceRoots = [
|
|
6725
|
+
"",
|
|
6726
|
+
"Sources",
|
|
6727
|
+
`Sources/${moduleName}`,
|
|
6728
|
+
"src",
|
|
6729
|
+
`src/${moduleName}`
|
|
6730
|
+
];
|
|
6731
|
+
for (const root of sourceRoots) {
|
|
6732
|
+
const candidate = root ? join13(root, moduleName + ".swift") : moduleName + ".swift";
|
|
6733
|
+
const fullPath = join13(projectRoot, candidate);
|
|
6734
|
+
if (existsSync13(fullPath)) {
|
|
6735
|
+
return candidate;
|
|
6736
|
+
}
|
|
6737
|
+
}
|
|
6738
|
+
for (const root of sourceRoots) {
|
|
6739
|
+
const dirCandidate = root || moduleName;
|
|
6740
|
+
const fullDir = join13(projectRoot, dirCandidate);
|
|
6741
|
+
if (existsSync13(fullDir)) {
|
|
6742
|
+
try {
|
|
6743
|
+
const stats = statSync7(fullDir);
|
|
6744
|
+
if (stats.isDirectory()) {
|
|
6745
|
+
const swiftFiles = readdirSync10(fullDir).filter((f) => f.endsWith(".swift"));
|
|
6746
|
+
if (swiftFiles.length > 0) {
|
|
6747
|
+
return join13(dirCandidate, swiftFiles[0]);
|
|
6748
|
+
}
|
|
6749
|
+
}
|
|
6750
|
+
} catch {
|
|
6751
|
+
}
|
|
6752
|
+
}
|
|
6753
|
+
}
|
|
6754
|
+
return null;
|
|
6755
|
+
}
|
|
6756
|
+
function resolveSymbol11(name, context) {
|
|
6757
|
+
if (context.imports.has(name)) {
|
|
6758
|
+
return context.imports.get(name) || null;
|
|
6759
|
+
}
|
|
6760
|
+
const currentFileId = `${context.filePath}::${name}`;
|
|
6761
|
+
if (context.symbols.find((s) => s.id === currentFileId)) {
|
|
6762
|
+
return currentFileId;
|
|
6763
|
+
}
|
|
6764
|
+
if (context.currentClass) {
|
|
6765
|
+
const classMethodId = `${context.filePath}::${context.currentClass}.${name}`;
|
|
6766
|
+
if (context.symbols.find((s) => s.id === classMethodId)) {
|
|
6767
|
+
return classMethodId;
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
return null;
|
|
6771
|
+
}
|
|
6772
|
+
function getModifiers3(node, context) {
|
|
6773
|
+
const modifiers = [];
|
|
6774
|
+
const modList = findChildByType12(node, "modifiers") || findChildByType12(node, "modifier");
|
|
6775
|
+
if (modList) {
|
|
6776
|
+
for (let i = 0; i < modList.childCount; i++) {
|
|
6777
|
+
const child = modList.child(i);
|
|
6778
|
+
if (child) {
|
|
6779
|
+
const text2 = nodeText11(child, context).trim();
|
|
6780
|
+
if (text2) modifiers.push(text2);
|
|
6781
|
+
}
|
|
6782
|
+
}
|
|
6783
|
+
}
|
|
6784
|
+
const text = nodeText11(node, context);
|
|
6785
|
+
if (text.match(/\bprivate\b/)) modifiers.push("private");
|
|
6786
|
+
if (text.match(/\bfileprivate\b/)) modifiers.push("fileprivate");
|
|
6787
|
+
if (text.match(/\binternal\b/)) modifiers.push("internal");
|
|
6788
|
+
if (text.match(/\bpublic\b/)) modifiers.push("public");
|
|
6789
|
+
if (text.match(/\bopen\b/)) modifiers.push("open");
|
|
6790
|
+
return modifiers;
|
|
6791
|
+
}
|
|
6792
|
+
function findChildByType12(node, type) {
|
|
6793
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
6794
|
+
const child = node.child(i);
|
|
6795
|
+
if (child && child.type === type) return child;
|
|
6796
|
+
}
|
|
6797
|
+
return null;
|
|
6798
|
+
}
|
|
6799
|
+
function nodeText11(node, context) {
|
|
6800
|
+
return context.sourceCode.substring(node.startIndex, node.endIndex);
|
|
6801
|
+
}
|
|
6802
|
+
function getCurrentSymbolId12(context) {
|
|
6803
|
+
if (context.currentScope.length === 0) return null;
|
|
6804
|
+
return `${context.filePath}::${context.currentScope[context.currentScope.length - 1]}`;
|
|
6805
|
+
}
|
|
6806
|
+
var swiftParser = {
|
|
6807
|
+
name: "swift",
|
|
6808
|
+
extensions: [".swift", "Package.swift"],
|
|
6809
|
+
parseFile: parseSwiftFile
|
|
6810
|
+
};
|
|
6811
|
+
|
|
6812
|
+
// src/parser/mojo.ts
|
|
6813
|
+
import { dirname as dirname13, join as join14, basename as basename7 } from "path";
|
|
6814
|
+
function parseMojoFile(filePath, sourceCode, projectRoot) {
|
|
6815
|
+
if (filePath.endsWith("mojoproject.toml")) {
|
|
6816
|
+
return parseMojoProject(filePath, sourceCode, projectRoot);
|
|
6817
|
+
}
|
|
6818
|
+
const context = {
|
|
6819
|
+
filePath,
|
|
6820
|
+
projectRoot,
|
|
6821
|
+
sourceCode,
|
|
6822
|
+
symbols: [],
|
|
6823
|
+
edges: [],
|
|
6824
|
+
currentScope: [],
|
|
6825
|
+
currentClass: null,
|
|
6826
|
+
imports: /* @__PURE__ */ new Map()
|
|
6827
|
+
};
|
|
6828
|
+
const lines = sourceCode.split("\n");
|
|
6829
|
+
parseLines(lines, context);
|
|
6830
|
+
return {
|
|
6831
|
+
filePath,
|
|
6832
|
+
symbols: context.symbols,
|
|
6833
|
+
edges: context.edges
|
|
6834
|
+
};
|
|
6835
|
+
}
|
|
6836
|
+
function parseLines(lines, context) {
|
|
6837
|
+
let i = 0;
|
|
6838
|
+
while (i < lines.length) {
|
|
6839
|
+
const line = lines[i];
|
|
6840
|
+
const trimmed = line.trimStart();
|
|
6841
|
+
const indent = line.length - trimmed.length;
|
|
6842
|
+
const lineNum = i + 1;
|
|
6843
|
+
if (!trimmed || trimmed.startsWith("#")) {
|
|
6844
|
+
i++;
|
|
6845
|
+
continue;
|
|
6846
|
+
}
|
|
6847
|
+
if (trimmed.startsWith("@")) {
|
|
6848
|
+
i++;
|
|
6849
|
+
continue;
|
|
6850
|
+
}
|
|
6851
|
+
if (trimmed.startsWith("import ") || trimmed.startsWith("from ")) {
|
|
6852
|
+
processImport(trimmed, lineNum, context);
|
|
6853
|
+
i++;
|
|
6854
|
+
continue;
|
|
6855
|
+
}
|
|
6856
|
+
const fnMatch = trimmed.match(/^fn\s+(\w+)\s*[\[(]/);
|
|
6857
|
+
if (fnMatch) {
|
|
6858
|
+
const name = fnMatch[1];
|
|
6859
|
+
const endLine = findBlockEnd(lines, i, indent);
|
|
6860
|
+
addFunction(name, lineNum, endLine, context);
|
|
6861
|
+
i = endLine;
|
|
6862
|
+
continue;
|
|
6863
|
+
}
|
|
6864
|
+
const defMatch = trimmed.match(/^def\s+(\w+)\s*[\[(]/);
|
|
6865
|
+
if (defMatch) {
|
|
6866
|
+
const name = defMatch[1];
|
|
6867
|
+
const endLine = findBlockEnd(lines, i, indent);
|
|
6868
|
+
addFunction(name, lineNum, endLine, context);
|
|
6869
|
+
i = endLine;
|
|
6870
|
+
continue;
|
|
6871
|
+
}
|
|
6872
|
+
const structMatch = trimmed.match(/^struct\s+(\w+)/);
|
|
6873
|
+
if (structMatch) {
|
|
6874
|
+
const name = structMatch[1];
|
|
6875
|
+
const endLine = findBlockEnd(lines, i, indent);
|
|
6876
|
+
addType(name, "class", lineNum, endLine, context);
|
|
6877
|
+
parseStructBody(lines, i + 1, endLine, indent, name, context);
|
|
6878
|
+
i = endLine;
|
|
6879
|
+
continue;
|
|
6880
|
+
}
|
|
6881
|
+
const classMatch = trimmed.match(/^class\s+(\w+)/);
|
|
6882
|
+
if (classMatch) {
|
|
6883
|
+
const name = classMatch[1];
|
|
6884
|
+
const endLine = findBlockEnd(lines, i, indent);
|
|
6885
|
+
addType(name, "class", lineNum, endLine, context);
|
|
6886
|
+
parseStructBody(lines, i + 1, endLine, indent, name, context);
|
|
6887
|
+
i = endLine;
|
|
6888
|
+
continue;
|
|
6889
|
+
}
|
|
6890
|
+
const traitMatch = trimmed.match(/^trait\s+(\w+)/);
|
|
6891
|
+
if (traitMatch) {
|
|
6892
|
+
const name = traitMatch[1];
|
|
6893
|
+
const endLine = findBlockEnd(lines, i, indent);
|
|
6894
|
+
addType(name, "interface", lineNum, endLine, context);
|
|
6895
|
+
parseStructBody(lines, i + 1, endLine, indent, name, context);
|
|
6896
|
+
i = endLine;
|
|
6897
|
+
continue;
|
|
6898
|
+
}
|
|
6899
|
+
const aliasMatch = trimmed.match(/^alias\s+(\w+)\s*[=:]/);
|
|
6900
|
+
if (aliasMatch) {
|
|
6901
|
+
const name = aliasMatch[1];
|
|
6902
|
+
context.symbols.push({
|
|
6903
|
+
id: `${context.filePath}::${name}`,
|
|
6904
|
+
name,
|
|
6905
|
+
kind: "type_alias",
|
|
6906
|
+
filePath: context.filePath,
|
|
6907
|
+
startLine: lineNum,
|
|
6908
|
+
endLine: lineNum,
|
|
6909
|
+
exported: true
|
|
6910
|
+
});
|
|
6911
|
+
i++;
|
|
6912
|
+
continue;
|
|
6913
|
+
}
|
|
6914
|
+
const varMatch = trimmed.match(/^(var|let)\s+(\w+)/);
|
|
6915
|
+
if (varMatch && indent === 0) {
|
|
6916
|
+
const name = varMatch[2];
|
|
6917
|
+
const kind = varMatch[1] === "let" ? "constant" : "var";
|
|
6918
|
+
context.symbols.push({
|
|
6919
|
+
id: `${context.filePath}::${name}`,
|
|
6920
|
+
name,
|
|
6921
|
+
kind,
|
|
6922
|
+
filePath: context.filePath,
|
|
6923
|
+
startLine: lineNum,
|
|
6924
|
+
endLine: lineNum,
|
|
6925
|
+
exported: true
|
|
6926
|
+
});
|
|
6927
|
+
i++;
|
|
6928
|
+
continue;
|
|
6929
|
+
}
|
|
6930
|
+
processCallsInLine(trimmed, lineNum, context);
|
|
6931
|
+
i++;
|
|
6932
|
+
}
|
|
6933
|
+
}
|
|
6934
|
+
function processImport(line, lineNum, context) {
|
|
6935
|
+
let importName = null;
|
|
6936
|
+
const fromMatch = line.match(/^from\s+([\w.]+)\s+import\s+(.+)/);
|
|
6937
|
+
if (fromMatch) {
|
|
6938
|
+
const module = fromMatch[1];
|
|
6939
|
+
const symbols = fromMatch[2].split(",").map((s) => s.trim());
|
|
6940
|
+
importName = module;
|
|
6941
|
+
for (const sym of symbols) {
|
|
6942
|
+
const cleanSym = sym.split(" as ")[0].trim();
|
|
6943
|
+
if (cleanSym && cleanSym !== "*") {
|
|
6944
|
+
context.imports.set(cleanSym, `${module}::${cleanSym}`);
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
} else {
|
|
6948
|
+
const importMatch = line.match(/^import\s+([\w.]+)(?:\s+as\s+(\w+))?/);
|
|
6949
|
+
if (importMatch) {
|
|
6950
|
+
importName = importMatch[1];
|
|
6951
|
+
const alias = importMatch[2] || importMatch[1].split(".").pop();
|
|
6952
|
+
context.imports.set(alias, `${importMatch[1]}::__module__`);
|
|
6953
|
+
}
|
|
6954
|
+
}
|
|
6955
|
+
if (importName) {
|
|
6956
|
+
context.symbols.push({
|
|
6957
|
+
id: `${context.filePath}::import:${importName}`,
|
|
6958
|
+
name: importName,
|
|
6959
|
+
kind: "import",
|
|
6960
|
+
filePath: context.filePath,
|
|
6961
|
+
startLine: lineNum,
|
|
6962
|
+
endLine: lineNum,
|
|
6963
|
+
exported: false
|
|
6964
|
+
});
|
|
6965
|
+
}
|
|
6966
|
+
}
|
|
6967
|
+
function addFunction(name, startLine, endLine, context) {
|
|
6968
|
+
const scope = context.currentClass || void 0;
|
|
6969
|
+
const symbolId = scope ? `${context.filePath}::${scope}.${name}` : `${context.filePath}::${name}`;
|
|
6970
|
+
context.symbols.push({
|
|
6971
|
+
id: symbolId,
|
|
6972
|
+
name,
|
|
6973
|
+
kind: context.currentClass ? "method" : "function",
|
|
6974
|
+
filePath: context.filePath,
|
|
6975
|
+
startLine,
|
|
6976
|
+
endLine,
|
|
6977
|
+
exported: true,
|
|
6978
|
+
scope
|
|
6979
|
+
});
|
|
6980
|
+
}
|
|
6981
|
+
function addType(name, kind, startLine, endLine, context) {
|
|
6982
|
+
context.symbols.push({
|
|
6983
|
+
id: `${context.filePath}::${name}`,
|
|
6984
|
+
name,
|
|
6985
|
+
kind,
|
|
6986
|
+
filePath: context.filePath,
|
|
6987
|
+
startLine,
|
|
6988
|
+
endLine,
|
|
6989
|
+
exported: true
|
|
6990
|
+
});
|
|
6991
|
+
}
|
|
6992
|
+
function parseStructBody(lines, start, end, baseIndent, className, context) {
|
|
6993
|
+
const oldClass = context.currentClass;
|
|
6994
|
+
context.currentClass = className;
|
|
6995
|
+
let i = start;
|
|
6996
|
+
while (i < end && i < lines.length) {
|
|
6997
|
+
const line = lines[i];
|
|
6998
|
+
const trimmed = line.trimStart();
|
|
6999
|
+
const indent = line.length - trimmed.length;
|
|
7000
|
+
const lineNum = i + 1;
|
|
7001
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("@")) {
|
|
7002
|
+
i++;
|
|
7003
|
+
continue;
|
|
7004
|
+
}
|
|
7005
|
+
if (indent <= baseIndent) break;
|
|
7006
|
+
const fnMatch = trimmed.match(/^(fn|def)\s+(\w+)\s*[\[(]/);
|
|
7007
|
+
if (fnMatch) {
|
|
7008
|
+
const name = fnMatch[2];
|
|
7009
|
+
const fnEnd = findBlockEnd(lines, i, indent);
|
|
7010
|
+
addFunction(name, lineNum, fnEnd, context);
|
|
7011
|
+
i = fnEnd;
|
|
7012
|
+
continue;
|
|
7013
|
+
}
|
|
7014
|
+
const varMatch = trimmed.match(/^(var|let)\s+(\w+)/);
|
|
7015
|
+
if (varMatch) {
|
|
7016
|
+
const name = varMatch[2];
|
|
7017
|
+
context.symbols.push({
|
|
7018
|
+
id: `${context.filePath}::${className}.${name}`,
|
|
7019
|
+
name,
|
|
7020
|
+
kind: "property",
|
|
7021
|
+
filePath: context.filePath,
|
|
7022
|
+
startLine: lineNum,
|
|
7023
|
+
endLine: lineNum,
|
|
7024
|
+
exported: true,
|
|
7025
|
+
scope: className
|
|
7026
|
+
});
|
|
7027
|
+
i++;
|
|
7028
|
+
continue;
|
|
7029
|
+
}
|
|
7030
|
+
const aliasMatch = trimmed.match(/^alias\s+(\w+)\s*[=:]/);
|
|
7031
|
+
if (aliasMatch) {
|
|
7032
|
+
const name = aliasMatch[1];
|
|
7033
|
+
context.symbols.push({
|
|
7034
|
+
id: `${context.filePath}::${className}.${name}`,
|
|
7035
|
+
name,
|
|
7036
|
+
kind: "type_alias",
|
|
7037
|
+
filePath: context.filePath,
|
|
7038
|
+
startLine: lineNum,
|
|
7039
|
+
endLine: lineNum,
|
|
7040
|
+
exported: true,
|
|
7041
|
+
scope: className
|
|
7042
|
+
});
|
|
7043
|
+
i++;
|
|
7044
|
+
continue;
|
|
7045
|
+
}
|
|
7046
|
+
i++;
|
|
7047
|
+
}
|
|
7048
|
+
context.currentClass = oldClass;
|
|
7049
|
+
}
|
|
7050
|
+
function processCallsInLine(line, lineNum, context) {
|
|
7051
|
+
const callRegex = /\b(\w+)\s*(?:\[[^\]]*\]\s*)?\(/g;
|
|
7052
|
+
let match;
|
|
7053
|
+
const builtins = /* @__PURE__ */ new Set([
|
|
7054
|
+
"print",
|
|
7055
|
+
"len",
|
|
7056
|
+
"range",
|
|
7057
|
+
"int",
|
|
7058
|
+
"str",
|
|
7059
|
+
"float",
|
|
7060
|
+
"bool",
|
|
7061
|
+
"type",
|
|
7062
|
+
"if",
|
|
7063
|
+
"elif",
|
|
7064
|
+
"while",
|
|
7065
|
+
"for",
|
|
7066
|
+
"return",
|
|
7067
|
+
"raise",
|
|
7068
|
+
"assert",
|
|
7069
|
+
"fn",
|
|
7070
|
+
"def",
|
|
7071
|
+
"struct",
|
|
7072
|
+
"class",
|
|
7073
|
+
"trait",
|
|
7074
|
+
"alias",
|
|
7075
|
+
"var",
|
|
7076
|
+
"let",
|
|
7077
|
+
"from",
|
|
7078
|
+
"import",
|
|
7079
|
+
"inout",
|
|
7080
|
+
"owned",
|
|
7081
|
+
"borrowed"
|
|
7082
|
+
]);
|
|
7083
|
+
while ((match = callRegex.exec(line)) !== null) {
|
|
7084
|
+
const name = match[1];
|
|
7085
|
+
if (builtins.has(name)) continue;
|
|
7086
|
+
if (name.startsWith("_") && name !== "__init__") continue;
|
|
7087
|
+
if (context.currentScope.length > 0 || context.currentClass) {
|
|
7088
|
+
const callerId = context.currentClass ? `${context.filePath}::${context.currentClass}` : `${context.filePath}::__file__`;
|
|
7089
|
+
const targetId = context.imports.get(name) || `${context.filePath}::${name}`;
|
|
7090
|
+
context.edges.push({
|
|
7091
|
+
source: callerId,
|
|
7092
|
+
target: targetId,
|
|
7093
|
+
kind: "calls",
|
|
7094
|
+
filePath: context.filePath,
|
|
7095
|
+
line: lineNum
|
|
7096
|
+
});
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
function findBlockEnd(lines, startIdx, baseIndent) {
|
|
7101
|
+
let i = startIdx + 1;
|
|
7102
|
+
while (i < lines.length) {
|
|
7103
|
+
const line = lines[i];
|
|
7104
|
+
if (line.trim() === "") {
|
|
7105
|
+
i++;
|
|
7106
|
+
continue;
|
|
7107
|
+
}
|
|
7108
|
+
const indent = line.length - line.trimStart().length;
|
|
7109
|
+
if (indent <= baseIndent) {
|
|
7110
|
+
return i;
|
|
7111
|
+
}
|
|
7112
|
+
i++;
|
|
7113
|
+
}
|
|
7114
|
+
return i;
|
|
7115
|
+
}
|
|
7116
|
+
function parseMojoProject(filePath, sourceCode, projectRoot) {
|
|
7117
|
+
const symbols = [];
|
|
7118
|
+
const edges = [];
|
|
7119
|
+
const lines = sourceCode.split("\n");
|
|
7120
|
+
let projectName = basename7(dirname13(join14(projectRoot, filePath)));
|
|
7121
|
+
const nameMatch = sourceCode.match(/name\s*=\s*["']([^"']+)["']/);
|
|
7122
|
+
if (nameMatch) {
|
|
7123
|
+
projectName = nameMatch[1];
|
|
7124
|
+
}
|
|
7125
|
+
symbols.push({
|
|
7126
|
+
id: `${filePath}::${projectName}`,
|
|
7127
|
+
name: projectName,
|
|
7128
|
+
kind: "module",
|
|
7129
|
+
filePath,
|
|
7130
|
+
startLine: 1,
|
|
7131
|
+
endLine: lines.length,
|
|
7132
|
+
exported: true
|
|
7133
|
+
});
|
|
7134
|
+
for (let i = 0; i < lines.length; i++) {
|
|
7135
|
+
const line = lines[i].trim();
|
|
7136
|
+
const depMatch = line.match(/^(\w[\w-]*)\s*=\s*["']([^"']+)["']/);
|
|
7137
|
+
if (depMatch) {
|
|
7138
|
+
symbols.push({
|
|
7139
|
+
id: `${filePath}::dep:${depMatch[1]}`,
|
|
7140
|
+
name: depMatch[1],
|
|
7141
|
+
kind: "import",
|
|
7142
|
+
filePath,
|
|
7143
|
+
startLine: i + 1,
|
|
7144
|
+
endLine: i + 1,
|
|
7145
|
+
exported: false
|
|
7146
|
+
});
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
7149
|
+
return { filePath, symbols, edges };
|
|
7150
|
+
}
|
|
7151
|
+
var mojoParser = {
|
|
7152
|
+
name: "mojo",
|
|
7153
|
+
extensions: [".mojo", ".\u{1F525}", "mojoproject.toml"],
|
|
7154
|
+
parseFile: parseMojoFile
|
|
7155
|
+
};
|
|
7156
|
+
|
|
6234
7157
|
// src/parser/detect.ts
|
|
6235
7158
|
var parsers = [
|
|
6236
7159
|
typescriptParser,
|
|
@@ -6243,12 +7166,14 @@ var parsers = [
|
|
|
6243
7166
|
javaParser,
|
|
6244
7167
|
cppParser,
|
|
6245
7168
|
kotlinParser,
|
|
6246
|
-
phpParser
|
|
7169
|
+
phpParser,
|
|
7170
|
+
swiftParser,
|
|
7171
|
+
mojoParser
|
|
6247
7172
|
];
|
|
6248
7173
|
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/;
|
|
6249
7174
|
function getParserForFile(filePath, content) {
|
|
6250
|
-
const ext =
|
|
6251
|
-
const fileName =
|
|
7175
|
+
const ext = extname10(filePath).toLowerCase();
|
|
7176
|
+
const fileName = basename8(filePath);
|
|
6252
7177
|
if (ext === ".h" && content) {
|
|
6253
7178
|
if (CPP_KEYWORDS.test(content)) {
|
|
6254
7179
|
return cppParser;
|
|
@@ -6266,7 +7191,7 @@ import { minimatch } from "minimatch";
|
|
|
6266
7191
|
var MAX_FILE_SIZE = 1e6;
|
|
6267
7192
|
function shouldParseFile(fullPath) {
|
|
6268
7193
|
try {
|
|
6269
|
-
const stats =
|
|
7194
|
+
const stats = statSync8(fullPath);
|
|
6270
7195
|
if (stats.size > MAX_FILE_SIZE) {
|
|
6271
7196
|
console.error(`[Parser] Skipping ${fullPath} \u2014 file too large (${(stats.size / 1024).toFixed(0)}KB)`);
|
|
6272
7197
|
return false;
|
|
@@ -6284,8 +7209,8 @@ async function parseProject(projectRoot, options) {
|
|
|
6284
7209
|
let errorFiles = 0;
|
|
6285
7210
|
for (const file of files) {
|
|
6286
7211
|
try {
|
|
6287
|
-
const fullPath =
|
|
6288
|
-
if (!
|
|
7212
|
+
const fullPath = join15(projectRoot, file);
|
|
7213
|
+
if (!resolve9(fullPath).startsWith(resolve9(projectRoot))) {
|
|
6289
7214
|
skippedFiles++;
|
|
6290
7215
|
continue;
|
|
6291
7216
|
}
|
|
@@ -6308,7 +7233,7 @@ async function parseProject(projectRoot, options) {
|
|
|
6308
7233
|
if (options?.verbose) {
|
|
6309
7234
|
console.error(`[Parser] Parsing: ${file}`);
|
|
6310
7235
|
}
|
|
6311
|
-
const sourceCode =
|
|
7236
|
+
const sourceCode = readFileSync11(fullPath, "utf-8");
|
|
6312
7237
|
const parser = getParserForFile(file, sourceCode);
|
|
6313
7238
|
if (!parser) {
|
|
6314
7239
|
console.error(`No parser found for file: ${file}`);
|
|
@@ -6337,8 +7262,8 @@ async function parseProject(projectRoot, options) {
|
|
|
6337
7262
|
}
|
|
6338
7263
|
|
|
6339
7264
|
// src/cross-language/detectors/rest-api.ts
|
|
6340
|
-
import { readFileSync as
|
|
6341
|
-
import { join as
|
|
7265
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
7266
|
+
import { join as join16, resolve as resolve10 } from "path";
|
|
6342
7267
|
function getLanguage(filePath) {
|
|
6343
7268
|
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
6344
7269
|
if (filePath.endsWith(".js") || filePath.endsWith(".jsx") || filePath.endsWith(".mjs") || filePath.endsWith(".cjs")) return "javascript";
|
|
@@ -6348,6 +7273,8 @@ function getLanguage(filePath) {
|
|
|
6348
7273
|
if (filePath.endsWith(".java")) return "java";
|
|
6349
7274
|
if (filePath.endsWith(".kt") || filePath.endsWith(".kts")) return "kotlin";
|
|
6350
7275
|
if (filePath.endsWith(".php")) return "php";
|
|
7276
|
+
if (filePath.endsWith(".swift")) return "swift";
|
|
7277
|
+
if (filePath.endsWith(".mojo") || filePath.endsWith(".\u{1F525}")) return "mojo";
|
|
6351
7278
|
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";
|
|
6352
7279
|
return "unknown";
|
|
6353
7280
|
}
|
|
@@ -6767,6 +7694,73 @@ function extractRouteDefinitions(source, filePath) {
|
|
|
6767
7694
|
});
|
|
6768
7695
|
}
|
|
6769
7696
|
}
|
|
7697
|
+
if (lang === "swift") {
|
|
7698
|
+
const vaporMatch = line.match(/(?:app|router|routes)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*["']([^"']+)["']/i);
|
|
7699
|
+
if (vaporMatch) {
|
|
7700
|
+
let path6 = vaporMatch[2];
|
|
7701
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
7702
|
+
routes.push({
|
|
7703
|
+
method: vaporMatch[1].toUpperCase(),
|
|
7704
|
+
path: path6,
|
|
7705
|
+
normalizedPath: normalizePath(path6),
|
|
7706
|
+
file: filePath,
|
|
7707
|
+
line: i + 1
|
|
7708
|
+
});
|
|
7709
|
+
}
|
|
7710
|
+
const hbMatch = line.match(/router\s*\.\s*(get|post|put|delete|patch)\s*\(\s*["']([^"']+)["']/i);
|
|
7711
|
+
if (hbMatch && !vaporMatch) {
|
|
7712
|
+
let path6 = hbMatch[2];
|
|
7713
|
+
if (!path6.startsWith("/")) path6 = "/" + path6;
|
|
7714
|
+
routes.push({
|
|
7715
|
+
method: hbMatch[1].toUpperCase(),
|
|
7716
|
+
path: path6,
|
|
7717
|
+
normalizedPath: normalizePath(path6),
|
|
7718
|
+
file: filePath,
|
|
7719
|
+
line: i + 1
|
|
7720
|
+
});
|
|
7721
|
+
}
|
|
7722
|
+
const perfectMatch = line.match(/routes\s*\.\s*add\s*\([^)]*uri\s*:\s*["']([^"']+)["']/);
|
|
7723
|
+
if (perfectMatch) {
|
|
7724
|
+
const path6 = perfectMatch[1].startsWith("/") ? perfectMatch[1] : "/" + perfectMatch[1];
|
|
7725
|
+
const methodMatch = line.match(/method\s*:\s*\.(\w+)/);
|
|
7726
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "ANY";
|
|
7727
|
+
routes.push({
|
|
7728
|
+
method,
|
|
7729
|
+
path: path6,
|
|
7730
|
+
normalizedPath: normalizePath(path6),
|
|
7731
|
+
file: filePath,
|
|
7732
|
+
line: i + 1
|
|
7733
|
+
});
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
if (lang === "mojo") {
|
|
7737
|
+
const pythonMatch = line.match(/@(?:app|router)\s*\.\s*(get|post|put|delete|patch)\s*\(\s*(['"])([^'"]+)\2/i);
|
|
7738
|
+
if (pythonMatch) {
|
|
7739
|
+
const path6 = pythonMatch[3];
|
|
7740
|
+
if (path6.startsWith("/")) {
|
|
7741
|
+
routes.push({
|
|
7742
|
+
method: pythonMatch[1].toUpperCase(),
|
|
7743
|
+
path: path6,
|
|
7744
|
+
normalizedPath: normalizePath(path6),
|
|
7745
|
+
file: filePath,
|
|
7746
|
+
line: i + 1
|
|
7747
|
+
});
|
|
7748
|
+
}
|
|
7749
|
+
}
|
|
7750
|
+
const mojoHttpMatch = line.match(/(?:server|app)\s*\.\s*(?:route|handle)\s*\(\s*['"]([^'"]+)['"]\s*,\s*['"]?(GET|POST|PUT|DELETE|PATCH)['"]?/i);
|
|
7751
|
+
if (mojoHttpMatch) {
|
|
7752
|
+
const path6 = mojoHttpMatch[1];
|
|
7753
|
+
if (path6.startsWith("/")) {
|
|
7754
|
+
routes.push({
|
|
7755
|
+
method: mojoHttpMatch[2].toUpperCase(),
|
|
7756
|
+
path: path6,
|
|
7757
|
+
normalizedPath: normalizePath(path6),
|
|
7758
|
+
file: filePath,
|
|
7759
|
+
line: i + 1
|
|
7760
|
+
});
|
|
7761
|
+
}
|
|
7762
|
+
}
|
|
7763
|
+
}
|
|
6770
7764
|
if (lang === "cpp") {
|
|
6771
7765
|
const crowMatch = line.match(/CROW_ROUTE\s*\(\s*\w+\s*,\s*"([^"]+)"/);
|
|
6772
7766
|
if (crowMatch) {
|
|
@@ -6881,11 +7875,11 @@ function detectRestApiEdges(files, projectRoot) {
|
|
|
6881
7875
|
const allCalls = [];
|
|
6882
7876
|
const allRoutes = [];
|
|
6883
7877
|
for (const file of files) {
|
|
6884
|
-
const fullPath =
|
|
6885
|
-
if (!
|
|
7878
|
+
const fullPath = join16(projectRoot, file.filePath);
|
|
7879
|
+
if (!resolve10(fullPath).startsWith(resolve10(projectRoot))) continue;
|
|
6886
7880
|
let source;
|
|
6887
7881
|
try {
|
|
6888
|
-
source =
|
|
7882
|
+
source = readFileSync12(fullPath, "utf-8");
|
|
6889
7883
|
} catch {
|
|
6890
7884
|
continue;
|
|
6891
7885
|
}
|
|
@@ -6905,6 +7899,30 @@ function detectRestApiEdges(files, projectRoot) {
|
|
|
6905
7899
|
}
|
|
6906
7900
|
}
|
|
6907
7901
|
}
|
|
7902
|
+
if (lang === "swift") {
|
|
7903
|
+
const swiftLines = source.split("\n");
|
|
7904
|
+
for (let i = 0; i < swiftLines.length; i++) {
|
|
7905
|
+
const line = swiftLines[i];
|
|
7906
|
+
const urlMatch = line.match(/URL\s*\(\s*string\s*:\s*["']([^"']+)["']/);
|
|
7907
|
+
if (urlMatch) {
|
|
7908
|
+
const path6 = urlMatch[1];
|
|
7909
|
+
if (isLocalApiPath(path6)) {
|
|
7910
|
+
const methodMatch = line.match(/httpMethod\s*=\s*["'](\w+)["']/);
|
|
7911
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
7912
|
+
allCalls.push({ method, path: cleanPath(path6), file: file.filePath, line: i + 1 });
|
|
7913
|
+
}
|
|
7914
|
+
}
|
|
7915
|
+
const afMatch = line.match(/AF\s*\.\s*(?:request|upload|download)\s*\(\s*["']([^"']+)["']/);
|
|
7916
|
+
if (afMatch) {
|
|
7917
|
+
const path6 = afMatch[1];
|
|
7918
|
+
if (isLocalApiPath(path6)) {
|
|
7919
|
+
const methodMatch = line.match(/method\s*:\s*\.(\w+)/);
|
|
7920
|
+
const method = methodMatch ? methodMatch[1].toUpperCase() : "GET";
|
|
7921
|
+
allCalls.push({ method, path: cleanPath(path6), file: file.filePath, line: i + 1 });
|
|
7922
|
+
}
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
6908
7926
|
if (lang === "php") {
|
|
6909
7927
|
const phpLines = source.split("\n");
|
|
6910
7928
|
for (let i = 0; i < phpLines.length; i++) {
|
|
@@ -6961,8 +7979,8 @@ function detectRestApiEdges(files, projectRoot) {
|
|
|
6961
7979
|
}
|
|
6962
7980
|
|
|
6963
7981
|
// src/cross-language/detectors/subprocess.ts
|
|
6964
|
-
import { readFileSync as
|
|
6965
|
-
import { join as
|
|
7982
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
7983
|
+
import { join as join17, resolve as resolve11, basename as basename9 } from "path";
|
|
6966
7984
|
var SCRIPT_EXTENSIONS = [".py", ".js", ".ts", ".go", ".rs"];
|
|
6967
7985
|
function getLanguage2(filePath) {
|
|
6968
7986
|
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) return "typescript";
|
|
@@ -7061,16 +8079,16 @@ function detectSubprocessEdges(files, projectRoot) {
|
|
|
7061
8079
|
const knownFiles = new Set(files.map((f) => f.filePath));
|
|
7062
8080
|
const basenameMap = /* @__PURE__ */ new Map();
|
|
7063
8081
|
for (const f of files) {
|
|
7064
|
-
const base =
|
|
8082
|
+
const base = basename9(f.filePath);
|
|
7065
8083
|
if (!basenameMap.has(base)) basenameMap.set(base, []);
|
|
7066
8084
|
basenameMap.get(base).push(f.filePath);
|
|
7067
8085
|
}
|
|
7068
8086
|
for (const file of files) {
|
|
7069
|
-
const fullPath =
|
|
7070
|
-
if (!
|
|
8087
|
+
const fullPath = join17(projectRoot, file.filePath);
|
|
8088
|
+
if (!resolve11(fullPath).startsWith(resolve11(projectRoot))) continue;
|
|
7071
8089
|
let source;
|
|
7072
8090
|
try {
|
|
7073
|
-
source =
|
|
8091
|
+
source = readFileSync13(fullPath, "utf-8");
|
|
7074
8092
|
} catch {
|
|
7075
8093
|
continue;
|
|
7076
8094
|
}
|
|
@@ -7082,7 +8100,7 @@ function detectSubprocessEdges(files, projectRoot) {
|
|
|
7082
8100
|
targetFile = call.calledFile;
|
|
7083
8101
|
confidence = "high";
|
|
7084
8102
|
} else {
|
|
7085
|
-
const base =
|
|
8103
|
+
const base = basename9(call.calledFile);
|
|
7086
8104
|
const candidates = basenameMap.get(base);
|
|
7087
8105
|
if (candidates && candidates.length > 0) {
|
|
7088
8106
|
const exactCandidate = candidates.find((c) => c.endsWith(call.calledFile));
|
|
@@ -7461,7 +8479,7 @@ function getArchitectureSummary(graph) {
|
|
|
7461
8479
|
}
|
|
7462
8480
|
|
|
7463
8481
|
// src/health/metrics.ts
|
|
7464
|
-
import { dirname as
|
|
8482
|
+
import { dirname as dirname14 } from "path";
|
|
7465
8483
|
function scoreToGrade(score) {
|
|
7466
8484
|
if (score >= 90) return "A";
|
|
7467
8485
|
if (score >= 80) return "B";
|
|
@@ -7494,8 +8512,8 @@ function calculateCouplingScore(graph) {
|
|
|
7494
8512
|
totalEdges++;
|
|
7495
8513
|
fileConnections.set(sourceAttrs.filePath, (fileConnections.get(sourceAttrs.filePath) || 0) + 1);
|
|
7496
8514
|
fileConnections.set(targetAttrs.filePath, (fileConnections.get(targetAttrs.filePath) || 0) + 1);
|
|
7497
|
-
const sourceDir =
|
|
7498
|
-
const targetDir =
|
|
8515
|
+
const sourceDir = dirname14(sourceAttrs.filePath).split("/")[0];
|
|
8516
|
+
const targetDir = dirname14(targetAttrs.filePath).split("/")[0];
|
|
7499
8517
|
if (sourceDir !== targetDir) {
|
|
7500
8518
|
crossDirEdges++;
|
|
7501
8519
|
}
|
|
@@ -7542,8 +8560,8 @@ function calculateCohesionScore(graph) {
|
|
|
7542
8560
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
7543
8561
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
7544
8562
|
if (sourceAttrs.filePath !== targetAttrs.filePath) {
|
|
7545
|
-
const sourceDir =
|
|
7546
|
-
const targetDir =
|
|
8563
|
+
const sourceDir = dirname14(sourceAttrs.filePath);
|
|
8564
|
+
const targetDir = dirname14(targetAttrs.filePath);
|
|
7547
8565
|
if (!dirEdges.has(sourceDir)) {
|
|
7548
8566
|
dirEdges.set(sourceDir, { internal: 0, total: 0 });
|
|
7549
8567
|
}
|
|
@@ -7825,8 +8843,8 @@ function calculateDepthScore(graph) {
|
|
|
7825
8843
|
}
|
|
7826
8844
|
|
|
7827
8845
|
// src/health/index.ts
|
|
7828
|
-
import { readFileSync as
|
|
7829
|
-
import { dirname as
|
|
8846
|
+
import { readFileSync as readFileSync14, writeFileSync, existsSync as existsSync14, mkdirSync } from "fs";
|
|
8847
|
+
import { dirname as dirname15, resolve as resolve12 } from "path";
|
|
7830
8848
|
function calculateHealthScore(graph, projectRoot) {
|
|
7831
8849
|
const coupling = calculateCouplingScore(graph);
|
|
7832
8850
|
const cohesion = calculateCohesionScore(graph);
|
|
@@ -7925,8 +8943,8 @@ function getHealthTrend(projectRoot, currentScore) {
|
|
|
7925
8943
|
}
|
|
7926
8944
|
}
|
|
7927
8945
|
function saveHealthHistory(projectRoot, report) {
|
|
7928
|
-
const resolvedRoot =
|
|
7929
|
-
const historyFile =
|
|
8946
|
+
const resolvedRoot = resolve12(projectRoot);
|
|
8947
|
+
const historyFile = resolve12(resolvedRoot, ".depwire", "health-history.json");
|
|
7930
8948
|
if (!historyFile.startsWith(resolvedRoot)) {
|
|
7931
8949
|
return;
|
|
7932
8950
|
}
|
|
@@ -7941,10 +8959,10 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
7941
8959
|
}))
|
|
7942
8960
|
};
|
|
7943
8961
|
let history = [];
|
|
7944
|
-
if (
|
|
8962
|
+
if (existsSync14(historyFile)) {
|
|
7945
8963
|
try {
|
|
7946
8964
|
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
7947
|
-
const content =
|
|
8965
|
+
const content = readFileSync14(historyFile, "utf-8");
|
|
7948
8966
|
history = JSON.parse(content);
|
|
7949
8967
|
} catch {
|
|
7950
8968
|
}
|
|
@@ -7953,19 +8971,19 @@ function saveHealthHistory(projectRoot, report) {
|
|
|
7953
8971
|
if (history.length > 50) {
|
|
7954
8972
|
history = history.slice(-50);
|
|
7955
8973
|
}
|
|
7956
|
-
mkdirSync(
|
|
8974
|
+
mkdirSync(dirname15(historyFile), { recursive: true });
|
|
7957
8975
|
if (!historyFile.startsWith(resolvedRoot)) return;
|
|
7958
8976
|
writeFileSync(historyFile, JSON.stringify(history, null, 2), "utf-8");
|
|
7959
8977
|
}
|
|
7960
8978
|
function loadHealthHistory(projectRoot) {
|
|
7961
|
-
const resolvedRoot =
|
|
7962
|
-
const historyFile =
|
|
7963
|
-
if (!historyFile.startsWith(resolvedRoot) || !
|
|
8979
|
+
const resolvedRoot = resolve12(projectRoot);
|
|
8980
|
+
const historyFile = resolve12(resolvedRoot, ".depwire", "health-history.json");
|
|
8981
|
+
if (!historyFile.startsWith(resolvedRoot) || !existsSync14(historyFile)) {
|
|
7964
8982
|
return [];
|
|
7965
8983
|
}
|
|
7966
8984
|
try {
|
|
7967
8985
|
if (!historyFile.startsWith(resolvedRoot)) return [];
|
|
7968
|
-
const content =
|
|
8986
|
+
const content = readFileSync14(historyFile, "utf-8");
|
|
7969
8987
|
return JSON.parse(content);
|
|
7970
8988
|
} catch {
|
|
7971
8989
|
return [];
|
|
@@ -7974,7 +8992,7 @@ function loadHealthHistory(projectRoot) {
|
|
|
7974
8992
|
|
|
7975
8993
|
// src/dead-code/detector.ts
|
|
7976
8994
|
import path2 from "path";
|
|
7977
|
-
import { readFileSync as
|
|
8995
|
+
import { readFileSync as readFileSync15, existsSync as existsSync15 } from "fs";
|
|
7978
8996
|
function findDeadSymbols(graph, projectRoot, includeTests = false, debug = false) {
|
|
7979
8997
|
const deadSymbols = [];
|
|
7980
8998
|
const context = { graph, projectRoot };
|
|
@@ -8107,11 +9125,11 @@ function getPackageEntryPoints(projectRoot) {
|
|
|
8107
9125
|
const entryPoints = /* @__PURE__ */ new Set();
|
|
8108
9126
|
const resolvedRoot = path2.resolve(projectRoot);
|
|
8109
9127
|
const packageJsonPath = path2.resolve(resolvedRoot, "package.json");
|
|
8110
|
-
if (!packageJsonPath.startsWith(resolvedRoot) || !
|
|
9128
|
+
if (!packageJsonPath.startsWith(resolvedRoot) || !existsSync15(packageJsonPath)) {
|
|
8111
9129
|
return entryPoints;
|
|
8112
9130
|
}
|
|
8113
9131
|
try {
|
|
8114
|
-
const packageJson = JSON.parse(
|
|
9132
|
+
const packageJson = JSON.parse(readFileSync15(packageJsonPath, "utf-8"));
|
|
8115
9133
|
if (packageJson.main) {
|
|
8116
9134
|
entryPoints.add(path2.resolve(projectRoot, packageJson.main));
|
|
8117
9135
|
}
|
|
@@ -8171,6 +9189,12 @@ function shouldExclude(attrs, context, includeTests, packageEntryPoints) {
|
|
|
8171
9189
|
if (isPhpExcluded(attrs)) {
|
|
8172
9190
|
return "framework";
|
|
8173
9191
|
}
|
|
9192
|
+
if (isSwiftExcluded(attrs)) {
|
|
9193
|
+
return "framework";
|
|
9194
|
+
}
|
|
9195
|
+
if (isMojoExcluded(attrs)) {
|
|
9196
|
+
return "framework";
|
|
9197
|
+
}
|
|
8174
9198
|
return null;
|
|
8175
9199
|
}
|
|
8176
9200
|
function isRealPackageEntryPoint(filePath, packageEntryPoints) {
|
|
@@ -8301,6 +9325,88 @@ function isPhpExcluded(attrs) {
|
|
|
8301
9325
|
if (name.startsWith("test") || name === "setUp" || name === "tearDown" || name === "setUpBeforeClass" || name === "tearDownAfterClass") return true;
|
|
8302
9326
|
return false;
|
|
8303
9327
|
}
|
|
9328
|
+
function isSwiftExcluded(attrs) {
|
|
9329
|
+
const filePath = attrs.file || attrs.filePath || "";
|
|
9330
|
+
const name = attrs.name || "";
|
|
9331
|
+
if (!filePath.endsWith(".swift")) return false;
|
|
9332
|
+
if (name === "main") return true;
|
|
9333
|
+
const appLifecycle = [
|
|
9334
|
+
"application",
|
|
9335
|
+
"applicationDidFinishLaunching",
|
|
9336
|
+
"applicationWillTerminate",
|
|
9337
|
+
"applicationDidBecomeActive",
|
|
9338
|
+
"applicationWillResignActive",
|
|
9339
|
+
"applicationDidEnterBackground",
|
|
9340
|
+
"applicationWillEnterForeground",
|
|
9341
|
+
"scene",
|
|
9342
|
+
"sceneDidDisconnect",
|
|
9343
|
+
"sceneDidBecomeActive",
|
|
9344
|
+
"sceneWillResignActive",
|
|
9345
|
+
"sceneWillEnterForeground",
|
|
9346
|
+
"sceneDidEnterBackground"
|
|
9347
|
+
];
|
|
9348
|
+
if (appLifecycle.includes(name)) return true;
|
|
9349
|
+
if (name === "body" || name === "previews") return true;
|
|
9350
|
+
const protocolMethods = [
|
|
9351
|
+
"hash",
|
|
9352
|
+
"encode",
|
|
9353
|
+
"init",
|
|
9354
|
+
"deinit",
|
|
9355
|
+
"tableView",
|
|
9356
|
+
"collectionView",
|
|
9357
|
+
"numberOfSections",
|
|
9358
|
+
"numberOfRowsInSection",
|
|
9359
|
+
"cellForRowAt",
|
|
9360
|
+
"didSelectRowAt"
|
|
9361
|
+
];
|
|
9362
|
+
if (protocolMethods.includes(name)) return true;
|
|
9363
|
+
if (["encode", "decode", "init(from:)"].includes(name)) return true;
|
|
9364
|
+
if (name.startsWith("test") || name === "setUp" || name === "tearDown" || name === "setUpWithError" || name === "tearDownWithError") return true;
|
|
9365
|
+
return false;
|
|
9366
|
+
}
|
|
9367
|
+
function isMojoExcluded(attrs) {
|
|
9368
|
+
const filePath = attrs.file || attrs.filePath || "";
|
|
9369
|
+
const name = attrs.name || "";
|
|
9370
|
+
if (!filePath.endsWith(".mojo") && !filePath.endsWith(".\u{1F525}")) return false;
|
|
9371
|
+
const lifecycleMethods = [
|
|
9372
|
+
"__init__",
|
|
9373
|
+
"__copyinit__",
|
|
9374
|
+
"__moveinit__",
|
|
9375
|
+
"__del__",
|
|
9376
|
+
"__enter__",
|
|
9377
|
+
"__exit__"
|
|
9378
|
+
];
|
|
9379
|
+
if (lifecycleMethods.includes(name)) return true;
|
|
9380
|
+
const traitMethods = [
|
|
9381
|
+
"__str__",
|
|
9382
|
+
"__repr__",
|
|
9383
|
+
"__len__",
|
|
9384
|
+
"__getitem__",
|
|
9385
|
+
"__setitem__",
|
|
9386
|
+
"__eq__",
|
|
9387
|
+
"__ne__",
|
|
9388
|
+
"__lt__",
|
|
9389
|
+
"__le__",
|
|
9390
|
+
"__gt__",
|
|
9391
|
+
"__ge__",
|
|
9392
|
+
"__add__",
|
|
9393
|
+
"__sub__",
|
|
9394
|
+
"__mul__",
|
|
9395
|
+
"__truediv__",
|
|
9396
|
+
"__floordiv__",
|
|
9397
|
+
"__hash__",
|
|
9398
|
+
"__bool__",
|
|
9399
|
+
"__int__",
|
|
9400
|
+
"__float__",
|
|
9401
|
+
"__iter__",
|
|
9402
|
+
"__next__",
|
|
9403
|
+
"__contains__"
|
|
9404
|
+
];
|
|
9405
|
+
if (traitMethods.includes(name)) return true;
|
|
9406
|
+
if (name.startsWith("__mlir_") || name.startsWith("_mlir_")) return true;
|
|
9407
|
+
if (name === "main") return true;
|
|
9408
|
+
return false;
|
|
9409
|
+
}
|
|
8304
9410
|
|
|
8305
9411
|
// src/dead-code/classifier.ts
|
|
8306
9412
|
import path3 from "path";
|
|
@@ -8367,8 +9473,8 @@ function generateReason(symbol, confidence) {
|
|
|
8367
9473
|
return "Potentially unused";
|
|
8368
9474
|
}
|
|
8369
9475
|
function isBarrelFile(filePath) {
|
|
8370
|
-
const
|
|
8371
|
-
return
|
|
9476
|
+
const basename13 = path3.basename(filePath);
|
|
9477
|
+
return basename13 === "index.ts" || basename13 === "index.js";
|
|
8372
9478
|
}
|
|
8373
9479
|
function isTestFile2(filePath) {
|
|
8374
9480
|
return filePath.includes("__tests__/") || filePath.includes(".test.") || filePath.includes(".spec.") || filePath.includes("/test/") || filePath.includes("/tests/");
|
|
@@ -8547,11 +9653,11 @@ function filterByConfidence(symbols, minConfidence) {
|
|
|
8547
9653
|
}
|
|
8548
9654
|
|
|
8549
9655
|
// src/docs/generator.ts
|
|
8550
|
-
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as
|
|
8551
|
-
import { join as
|
|
9656
|
+
import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync18 } from "fs";
|
|
9657
|
+
import { join as join21 } from "path";
|
|
8552
9658
|
|
|
8553
9659
|
// src/docs/architecture.ts
|
|
8554
|
-
import { dirname as
|
|
9660
|
+
import { dirname as dirname16 } from "path";
|
|
8555
9661
|
|
|
8556
9662
|
// src/docs/templates.ts
|
|
8557
9663
|
function header(text, level = 1) {
|
|
@@ -8702,7 +9808,7 @@ function generateModuleStructure(graph) {
|
|
|
8702
9808
|
function getDirectoryStats(graph) {
|
|
8703
9809
|
const dirMap = /* @__PURE__ */ new Map();
|
|
8704
9810
|
graph.forEachNode((node, attrs) => {
|
|
8705
|
-
const dir =
|
|
9811
|
+
const dir = dirname16(attrs.filePath);
|
|
8706
9812
|
if (dir === ".") return;
|
|
8707
9813
|
if (!dirMap.has(dir)) {
|
|
8708
9814
|
dirMap.set(dir, {
|
|
@@ -8727,7 +9833,7 @@ function getDirectoryStats(graph) {
|
|
|
8727
9833
|
});
|
|
8728
9834
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
8729
9835
|
graph.forEachNode((node, attrs) => {
|
|
8730
|
-
const dir =
|
|
9836
|
+
const dir = dirname16(attrs.filePath);
|
|
8731
9837
|
if (!filesPerDir.has(dir)) {
|
|
8732
9838
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
8733
9839
|
}
|
|
@@ -8742,8 +9848,8 @@ function getDirectoryStats(graph) {
|
|
|
8742
9848
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
8743
9849
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
8744
9850
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
8745
|
-
const sourceDir =
|
|
8746
|
-
const targetDir =
|
|
9851
|
+
const sourceDir = dirname16(sourceAttrs.filePath);
|
|
9852
|
+
const targetDir = dirname16(targetAttrs.filePath);
|
|
8747
9853
|
if (sourceDir !== targetDir) {
|
|
8748
9854
|
if (!dirEdges.has(sourceDir)) {
|
|
8749
9855
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -8950,7 +10056,7 @@ function detectCycles(graph) {
|
|
|
8950
10056
|
}
|
|
8951
10057
|
|
|
8952
10058
|
// src/docs/conventions.ts
|
|
8953
|
-
import { basename as
|
|
10059
|
+
import { basename as basename10, extname as extname11 } from "path";
|
|
8954
10060
|
function generateConventions(graph, projectRoot, version) {
|
|
8955
10061
|
let output = "";
|
|
8956
10062
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -8988,7 +10094,7 @@ function generateFileOrganization(graph) {
|
|
|
8988
10094
|
graph.forEachNode((node, attrs) => {
|
|
8989
10095
|
if (!files.has(attrs.filePath)) {
|
|
8990
10096
|
files.add(attrs.filePath);
|
|
8991
|
-
const fileName =
|
|
10097
|
+
const fileName = basename10(attrs.filePath);
|
|
8992
10098
|
if (fileName === "index.ts" || fileName === "index.js" || fileName === "index.tsx" || fileName === "index.jsx") {
|
|
8993
10099
|
barrelFileCount++;
|
|
8994
10100
|
}
|
|
@@ -9047,7 +10153,7 @@ function generateNamingPatterns(graph) {
|
|
|
9047
10153
|
graph.forEachNode((node, attrs) => {
|
|
9048
10154
|
if (!files.has(attrs.filePath)) {
|
|
9049
10155
|
files.add(attrs.filePath);
|
|
9050
|
-
const fileName =
|
|
10156
|
+
const fileName = basename10(attrs.filePath, extname11(attrs.filePath));
|
|
9051
10157
|
if (isCamelCase(fileName)) patterns.files.camelCase++;
|
|
9052
10158
|
else if (isPascalCase(fileName)) patterns.files.PascalCase++;
|
|
9053
10159
|
else if (isKebabCase(fileName)) patterns.files.kebabCase++;
|
|
@@ -9724,7 +10830,7 @@ function detectCyclesDetailed(graph) {
|
|
|
9724
10830
|
}
|
|
9725
10831
|
|
|
9726
10832
|
// src/docs/onboarding.ts
|
|
9727
|
-
import { dirname as
|
|
10833
|
+
import { dirname as dirname17 } from "path";
|
|
9728
10834
|
function generateOnboarding(graph, projectRoot, version) {
|
|
9729
10835
|
let output = "";
|
|
9730
10836
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -9783,7 +10889,7 @@ function generateQuickOrientation(graph) {
|
|
|
9783
10889
|
const primaryLang = Object.entries(languages2).sort((a, b) => b[1] - a[1])[0];
|
|
9784
10890
|
const dirs = /* @__PURE__ */ new Set();
|
|
9785
10891
|
graph.forEachNode((node, attrs) => {
|
|
9786
|
-
const dir =
|
|
10892
|
+
const dir = dirname17(attrs.filePath);
|
|
9787
10893
|
if (dir !== ".") {
|
|
9788
10894
|
const topLevel = dir.split("/")[0];
|
|
9789
10895
|
dirs.add(topLevel);
|
|
@@ -9887,7 +10993,7 @@ function generateModuleMap(graph) {
|
|
|
9887
10993
|
function getDirectoryStats2(graph) {
|
|
9888
10994
|
const dirMap = /* @__PURE__ */ new Map();
|
|
9889
10995
|
graph.forEachNode((node, attrs) => {
|
|
9890
|
-
const dir =
|
|
10996
|
+
const dir = dirname17(attrs.filePath);
|
|
9891
10997
|
if (dir === ".") return;
|
|
9892
10998
|
if (!dirMap.has(dir)) {
|
|
9893
10999
|
dirMap.set(dir, {
|
|
@@ -9902,7 +11008,7 @@ function getDirectoryStats2(graph) {
|
|
|
9902
11008
|
});
|
|
9903
11009
|
const filesPerDir = /* @__PURE__ */ new Map();
|
|
9904
11010
|
graph.forEachNode((node, attrs) => {
|
|
9905
|
-
const dir =
|
|
11011
|
+
const dir = dirname17(attrs.filePath);
|
|
9906
11012
|
if (!filesPerDir.has(dir)) {
|
|
9907
11013
|
filesPerDir.set(dir, /* @__PURE__ */ new Set());
|
|
9908
11014
|
}
|
|
@@ -9917,8 +11023,8 @@ function getDirectoryStats2(graph) {
|
|
|
9917
11023
|
graph.forEachEdge((edge, attrs, source, target) => {
|
|
9918
11024
|
const sourceAttrs = graph.getNodeAttributes(source);
|
|
9919
11025
|
const targetAttrs = graph.getNodeAttributes(target);
|
|
9920
|
-
const sourceDir =
|
|
9921
|
-
const targetDir =
|
|
11026
|
+
const sourceDir = dirname17(sourceAttrs.filePath);
|
|
11027
|
+
const targetDir = dirname17(targetAttrs.filePath);
|
|
9922
11028
|
if (sourceDir !== targetDir) {
|
|
9923
11029
|
if (!dirEdges.has(sourceDir)) {
|
|
9924
11030
|
dirEdges.set(sourceDir, { in: 0, out: 0 });
|
|
@@ -9999,7 +11105,7 @@ function detectClusters(graph) {
|
|
|
9999
11105
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
10000
11106
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
10001
11107
|
graph.forEachNode((node, attrs) => {
|
|
10002
|
-
const dir =
|
|
11108
|
+
const dir = dirname17(attrs.filePath);
|
|
10003
11109
|
if (!dirFiles.has(dir)) {
|
|
10004
11110
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
10005
11111
|
}
|
|
@@ -10053,8 +11159,8 @@ function inferClusterName(files) {
|
|
|
10053
11159
|
if (sortedWords.length > 0 && sortedWords[0][1] > 1) {
|
|
10054
11160
|
return capitalizeFirst2(sortedWords[0][0]);
|
|
10055
11161
|
}
|
|
10056
|
-
const commonDir =
|
|
10057
|
-
if (files.every((f) =>
|
|
11162
|
+
const commonDir = dirname17(files[0]);
|
|
11163
|
+
if (files.every((f) => dirname17(f) === commonDir)) {
|
|
10058
11164
|
return capitalizeFirst2(commonDir.split("/").pop() || "Core");
|
|
10059
11165
|
}
|
|
10060
11166
|
return "Core";
|
|
@@ -10112,7 +11218,7 @@ function generateDepwireUsage(projectRoot) {
|
|
|
10112
11218
|
}
|
|
10113
11219
|
|
|
10114
11220
|
// src/docs/files.ts
|
|
10115
|
-
import { dirname as
|
|
11221
|
+
import { dirname as dirname18, basename as basename11 } from "path";
|
|
10116
11222
|
function generateFiles(graph, projectRoot, version) {
|
|
10117
11223
|
let output = "";
|
|
10118
11224
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -10216,7 +11322,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
10216
11322
|
const fileStats = getFileStats2(graph);
|
|
10217
11323
|
const dirMap = /* @__PURE__ */ new Map();
|
|
10218
11324
|
for (const file of fileStats) {
|
|
10219
|
-
const dir =
|
|
11325
|
+
const dir = dirname18(file.filePath);
|
|
10220
11326
|
const topDir = dir === "." ? "." : dir.split("/")[0];
|
|
10221
11327
|
if (!dirMap.has(topDir)) {
|
|
10222
11328
|
dirMap.set(topDir, {
|
|
@@ -10231,7 +11337,7 @@ function generateDirectoryBreakdown(graph) {
|
|
|
10231
11337
|
dirStats.symbolCount += file.symbolCount;
|
|
10232
11338
|
if (file.totalConnections > dirStats.maxConnections) {
|
|
10233
11339
|
dirStats.maxConnections = file.totalConnections;
|
|
10234
|
-
dirStats.mostConnectedFile =
|
|
11340
|
+
dirStats.mostConnectedFile = basename11(file.filePath);
|
|
10235
11341
|
}
|
|
10236
11342
|
}
|
|
10237
11343
|
if (dirMap.size === 0) {
|
|
@@ -10859,7 +11965,7 @@ function generateRecommendations(graph) {
|
|
|
10859
11965
|
}
|
|
10860
11966
|
|
|
10861
11967
|
// src/docs/tests.ts
|
|
10862
|
-
import { basename as
|
|
11968
|
+
import { basename as basename12, dirname as dirname19 } from "path";
|
|
10863
11969
|
function generateTests(graph, projectRoot, version) {
|
|
10864
11970
|
let output = "";
|
|
10865
11971
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -10887,8 +11993,8 @@ function getFileCount8(graph) {
|
|
|
10887
11993
|
return files.size;
|
|
10888
11994
|
}
|
|
10889
11995
|
function isTestFile3(filePath) {
|
|
10890
|
-
const fileName =
|
|
10891
|
-
const dirPath =
|
|
11996
|
+
const fileName = basename12(filePath).toLowerCase();
|
|
11997
|
+
const dirPath = dirname19(filePath).toLowerCase();
|
|
10892
11998
|
if (dirPath.includes("test") || dirPath.includes("spec") || dirPath.includes("__tests__")) {
|
|
10893
11999
|
return true;
|
|
10894
12000
|
}
|
|
@@ -10946,13 +12052,13 @@ function generateTestFileInventory(graph) {
|
|
|
10946
12052
|
return output;
|
|
10947
12053
|
}
|
|
10948
12054
|
function matchTestToSource(testFile) {
|
|
10949
|
-
const testFileName =
|
|
10950
|
-
const testDir =
|
|
12055
|
+
const testFileName = basename12(testFile);
|
|
12056
|
+
const testDir = dirname19(testFile);
|
|
10951
12057
|
let sourceFileName = testFileName.replace(/\.test\./g, ".").replace(/\.spec\./g, ".").replace(/_test\./g, ".").replace(/_spec\./g, ".");
|
|
10952
12058
|
const possiblePaths = [];
|
|
10953
12059
|
possiblePaths.push(testDir + "/" + sourceFileName);
|
|
10954
12060
|
if (testDir.endsWith("/test") || testDir.endsWith("/tests") || testDir.endsWith("/__tests__")) {
|
|
10955
|
-
const parentDir =
|
|
12061
|
+
const parentDir = dirname19(testDir);
|
|
10956
12062
|
possiblePaths.push(parentDir + "/" + sourceFileName);
|
|
10957
12063
|
}
|
|
10958
12064
|
if (testDir.includes("test")) {
|
|
@@ -11108,7 +12214,7 @@ function generateTestCoverageMap(graph) {
|
|
|
11108
12214
|
const rows = mappings.slice(0, 30).map((m) => [
|
|
11109
12215
|
`\`${m.sourceFile}\``,
|
|
11110
12216
|
m.hasTest ? "\u2705" : "\u274C",
|
|
11111
|
-
m.testFile ? `\`${
|
|
12217
|
+
m.testFile ? `\`${basename12(m.testFile)}\`` : "-",
|
|
11112
12218
|
formatNumber(m.symbolCount)
|
|
11113
12219
|
]);
|
|
11114
12220
|
let output = table(headers, rows);
|
|
@@ -11149,7 +12255,7 @@ function generateTestStatistics(graph) {
|
|
|
11149
12255
|
`;
|
|
11150
12256
|
const dirTestCoverage = /* @__PURE__ */ new Map();
|
|
11151
12257
|
for (const sourceFile of sourceFiles) {
|
|
11152
|
-
const dir =
|
|
12258
|
+
const dir = dirname19(sourceFile).split("/")[0];
|
|
11153
12259
|
if (!dirTestCoverage.has(dir)) {
|
|
11154
12260
|
dirTestCoverage.set(dir, { total: 0, tested: 0 });
|
|
11155
12261
|
}
|
|
@@ -11172,7 +12278,7 @@ function generateTestStatistics(graph) {
|
|
|
11172
12278
|
}
|
|
11173
12279
|
|
|
11174
12280
|
// src/docs/history.ts
|
|
11175
|
-
import { dirname as
|
|
12281
|
+
import { dirname as dirname20 } from "path";
|
|
11176
12282
|
import { execSync } from "child_process";
|
|
11177
12283
|
function generateHistory(graph, projectRoot, version) {
|
|
11178
12284
|
let output = "";
|
|
@@ -11453,7 +12559,7 @@ function generateFeatureClusters(graph) {
|
|
|
11453
12559
|
const dirFiles = /* @__PURE__ */ new Map();
|
|
11454
12560
|
const fileEdges = /* @__PURE__ */ new Map();
|
|
11455
12561
|
graph.forEachNode((node, attrs) => {
|
|
11456
|
-
const dir =
|
|
12562
|
+
const dir = dirname20(attrs.filePath);
|
|
11457
12563
|
if (!dirFiles.has(dir)) {
|
|
11458
12564
|
dirFiles.set(dir, /* @__PURE__ */ new Set());
|
|
11459
12565
|
}
|
|
@@ -11535,7 +12641,7 @@ function capitalizeFirst3(str) {
|
|
|
11535
12641
|
}
|
|
11536
12642
|
|
|
11537
12643
|
// src/docs/current.ts
|
|
11538
|
-
import { dirname as
|
|
12644
|
+
import { dirname as dirname21 } from "path";
|
|
11539
12645
|
function generateCurrent(graph, projectRoot, version) {
|
|
11540
12646
|
let output = "";
|
|
11541
12647
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -11673,7 +12779,7 @@ function generateCompleteFileIndex(graph) {
|
|
|
11673
12779
|
fileInfos.sort((a, b) => a.filePath.localeCompare(b.filePath));
|
|
11674
12780
|
const dirGroups = /* @__PURE__ */ new Map();
|
|
11675
12781
|
for (const info of fileInfos) {
|
|
11676
|
-
const dir =
|
|
12782
|
+
const dir = dirname21(info.filePath);
|
|
11677
12783
|
const topDir = dir === "." ? "root" : dir.split("/")[0];
|
|
11678
12784
|
if (!dirGroups.has(topDir)) {
|
|
11679
12785
|
dirGroups.set(topDir, []);
|
|
@@ -11884,8 +12990,8 @@ function getTopLevelDir2(filePath) {
|
|
|
11884
12990
|
}
|
|
11885
12991
|
|
|
11886
12992
|
// src/docs/status.ts
|
|
11887
|
-
import { readFileSync as
|
|
11888
|
-
import { resolve as
|
|
12993
|
+
import { readFileSync as readFileSync16, existsSync as existsSync16 } from "fs";
|
|
12994
|
+
import { resolve as resolve13 } from "path";
|
|
11889
12995
|
function generateStatus(graph, projectRoot, version) {
|
|
11890
12996
|
let output = "";
|
|
11891
12997
|
const now = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -11918,16 +13024,16 @@ function getFileCount11(graph) {
|
|
|
11918
13024
|
}
|
|
11919
13025
|
function extractComments(projectRoot, filePath) {
|
|
11920
13026
|
const comments = [];
|
|
11921
|
-
const resolvedRoot =
|
|
11922
|
-
const fullPath =
|
|
13027
|
+
const resolvedRoot = resolve13(projectRoot);
|
|
13028
|
+
const fullPath = resolve13(resolvedRoot, filePath);
|
|
11923
13029
|
if (!fullPath.startsWith(resolvedRoot)) {
|
|
11924
13030
|
return comments;
|
|
11925
13031
|
}
|
|
11926
|
-
if (!
|
|
13032
|
+
if (!existsSync16(fullPath)) {
|
|
11927
13033
|
return comments;
|
|
11928
13034
|
}
|
|
11929
13035
|
try {
|
|
11930
|
-
const content =
|
|
13036
|
+
const content = readFileSync16(fullPath, "utf-8");
|
|
11931
13037
|
const lines = content.split("\n");
|
|
11932
13038
|
const patterns = [
|
|
11933
13039
|
{ type: "TODO", regex: /(?:\/\/|#|\/\*)\s*TODO:?\s*(.+)/i },
|
|
@@ -12506,16 +13612,16 @@ function generateConfidenceSection(title, description, symbols, projectRoot) {
|
|
|
12506
13612
|
}
|
|
12507
13613
|
|
|
12508
13614
|
// src/docs/metadata.ts
|
|
12509
|
-
import { existsSync as
|
|
12510
|
-
import { resolve as
|
|
13615
|
+
import { existsSync as existsSync17, readFileSync as readFileSync17, writeFileSync as writeFileSync2 } from "fs";
|
|
13616
|
+
import { resolve as resolve14 } from "path";
|
|
12511
13617
|
function loadMetadata(outputDir) {
|
|
12512
|
-
const resolvedDir =
|
|
12513
|
-
const metadataPath =
|
|
12514
|
-
if (!metadataPath.startsWith(resolvedDir) || !
|
|
13618
|
+
const resolvedDir = resolve14(outputDir);
|
|
13619
|
+
const metadataPath = resolve14(resolvedDir, "metadata.json");
|
|
13620
|
+
if (!metadataPath.startsWith(resolvedDir) || !existsSync17(metadataPath)) {
|
|
12515
13621
|
return null;
|
|
12516
13622
|
}
|
|
12517
13623
|
try {
|
|
12518
|
-
const content =
|
|
13624
|
+
const content = readFileSync17(metadataPath, "utf-8");
|
|
12519
13625
|
return JSON.parse(content);
|
|
12520
13626
|
} catch (err) {
|
|
12521
13627
|
console.error("Failed to load metadata:", err);
|
|
@@ -12523,8 +13629,8 @@ function loadMetadata(outputDir) {
|
|
|
12523
13629
|
}
|
|
12524
13630
|
}
|
|
12525
13631
|
function saveMetadata(outputDir, metadata) {
|
|
12526
|
-
const resolvedDir =
|
|
12527
|
-
const metadataPath =
|
|
13632
|
+
const resolvedDir = resolve14(outputDir);
|
|
13633
|
+
const metadataPath = resolve14(resolvedDir, "metadata.json");
|
|
12528
13634
|
if (!metadataPath.startsWith(resolvedDir)) {
|
|
12529
13635
|
throw new Error(`Path traversal attempt blocked: ${metadataPath}`);
|
|
12530
13636
|
}
|
|
@@ -12570,7 +13676,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12570
13676
|
const generated = [];
|
|
12571
13677
|
const errors = [];
|
|
12572
13678
|
try {
|
|
12573
|
-
if (!
|
|
13679
|
+
if (!existsSync18(options.outputDir)) {
|
|
12574
13680
|
mkdirSync2(options.outputDir, { recursive: true });
|
|
12575
13681
|
if (options.verbose) {
|
|
12576
13682
|
console.log(`Created output directory: ${options.outputDir}`);
|
|
@@ -12609,7 +13715,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12609
13715
|
try {
|
|
12610
13716
|
if (options.verbose) console.log("Generating ARCHITECTURE.md...");
|
|
12611
13717
|
const content = generateArchitecture(graph, projectRoot, version, parseTime);
|
|
12612
|
-
const filePath =
|
|
13718
|
+
const filePath = join21(options.outputDir, "ARCHITECTURE.md");
|
|
12613
13719
|
writeFileSync3(filePath, content, "utf-8");
|
|
12614
13720
|
generated.push("ARCHITECTURE.md");
|
|
12615
13721
|
} catch (err) {
|
|
@@ -12620,7 +13726,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12620
13726
|
try {
|
|
12621
13727
|
if (options.verbose) console.log("Generating CONVENTIONS.md...");
|
|
12622
13728
|
const content = generateConventions(graph, projectRoot, version);
|
|
12623
|
-
const filePath =
|
|
13729
|
+
const filePath = join21(options.outputDir, "CONVENTIONS.md");
|
|
12624
13730
|
writeFileSync3(filePath, content, "utf-8");
|
|
12625
13731
|
generated.push("CONVENTIONS.md");
|
|
12626
13732
|
} catch (err) {
|
|
@@ -12631,7 +13737,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12631
13737
|
try {
|
|
12632
13738
|
if (options.verbose) console.log("Generating DEPENDENCIES.md...");
|
|
12633
13739
|
const content = generateDependencies(graph, projectRoot, version);
|
|
12634
|
-
const filePath =
|
|
13740
|
+
const filePath = join21(options.outputDir, "DEPENDENCIES.md");
|
|
12635
13741
|
writeFileSync3(filePath, content, "utf-8");
|
|
12636
13742
|
generated.push("DEPENDENCIES.md");
|
|
12637
13743
|
} catch (err) {
|
|
@@ -12642,7 +13748,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12642
13748
|
try {
|
|
12643
13749
|
if (options.verbose) console.log("Generating ONBOARDING.md...");
|
|
12644
13750
|
const content = generateOnboarding(graph, projectRoot, version);
|
|
12645
|
-
const filePath =
|
|
13751
|
+
const filePath = join21(options.outputDir, "ONBOARDING.md");
|
|
12646
13752
|
writeFileSync3(filePath, content, "utf-8");
|
|
12647
13753
|
generated.push("ONBOARDING.md");
|
|
12648
13754
|
} catch (err) {
|
|
@@ -12653,7 +13759,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12653
13759
|
try {
|
|
12654
13760
|
if (options.verbose) console.log("Generating FILES.md...");
|
|
12655
13761
|
const content = generateFiles(graph, projectRoot, version);
|
|
12656
|
-
const filePath =
|
|
13762
|
+
const filePath = join21(options.outputDir, "FILES.md");
|
|
12657
13763
|
writeFileSync3(filePath, content, "utf-8");
|
|
12658
13764
|
generated.push("FILES.md");
|
|
12659
13765
|
} catch (err) {
|
|
@@ -12664,7 +13770,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12664
13770
|
try {
|
|
12665
13771
|
if (options.verbose) console.log("Generating API_SURFACE.md...");
|
|
12666
13772
|
const content = generateApiSurface(graph, projectRoot, version);
|
|
12667
|
-
const filePath =
|
|
13773
|
+
const filePath = join21(options.outputDir, "API_SURFACE.md");
|
|
12668
13774
|
writeFileSync3(filePath, content, "utf-8");
|
|
12669
13775
|
generated.push("API_SURFACE.md");
|
|
12670
13776
|
} catch (err) {
|
|
@@ -12675,7 +13781,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12675
13781
|
try {
|
|
12676
13782
|
if (options.verbose) console.log("Generating ERRORS.md...");
|
|
12677
13783
|
const content = generateErrors(graph, projectRoot, version);
|
|
12678
|
-
const filePath =
|
|
13784
|
+
const filePath = join21(options.outputDir, "ERRORS.md");
|
|
12679
13785
|
writeFileSync3(filePath, content, "utf-8");
|
|
12680
13786
|
generated.push("ERRORS.md");
|
|
12681
13787
|
} catch (err) {
|
|
@@ -12686,7 +13792,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12686
13792
|
try {
|
|
12687
13793
|
if (options.verbose) console.log("Generating TESTS.md...");
|
|
12688
13794
|
const content = generateTests(graph, projectRoot, version);
|
|
12689
|
-
const filePath =
|
|
13795
|
+
const filePath = join21(options.outputDir, "TESTS.md");
|
|
12690
13796
|
writeFileSync3(filePath, content, "utf-8");
|
|
12691
13797
|
generated.push("TESTS.md");
|
|
12692
13798
|
} catch (err) {
|
|
@@ -12697,7 +13803,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12697
13803
|
try {
|
|
12698
13804
|
if (options.verbose) console.log("Generating HISTORY.md...");
|
|
12699
13805
|
const content = generateHistory(graph, projectRoot, version);
|
|
12700
|
-
const filePath =
|
|
13806
|
+
const filePath = join21(options.outputDir, "HISTORY.md");
|
|
12701
13807
|
writeFileSync3(filePath, content, "utf-8");
|
|
12702
13808
|
generated.push("HISTORY.md");
|
|
12703
13809
|
} catch (err) {
|
|
@@ -12708,7 +13814,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12708
13814
|
try {
|
|
12709
13815
|
if (options.verbose) console.log("Generating CURRENT.md...");
|
|
12710
13816
|
const content = generateCurrent(graph, projectRoot, version);
|
|
12711
|
-
const filePath =
|
|
13817
|
+
const filePath = join21(options.outputDir, "CURRENT.md");
|
|
12712
13818
|
writeFileSync3(filePath, content, "utf-8");
|
|
12713
13819
|
generated.push("CURRENT.md");
|
|
12714
13820
|
} catch (err) {
|
|
@@ -12719,7 +13825,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12719
13825
|
try {
|
|
12720
13826
|
if (options.verbose) console.log("Generating STATUS.md...");
|
|
12721
13827
|
const content = generateStatus(graph, projectRoot, version);
|
|
12722
|
-
const filePath =
|
|
13828
|
+
const filePath = join21(options.outputDir, "STATUS.md");
|
|
12723
13829
|
writeFileSync3(filePath, content, "utf-8");
|
|
12724
13830
|
generated.push("STATUS.md");
|
|
12725
13831
|
} catch (err) {
|
|
@@ -12730,7 +13836,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12730
13836
|
try {
|
|
12731
13837
|
if (options.verbose) console.log("Generating HEALTH.md...");
|
|
12732
13838
|
const content = generateHealth(graph, projectRoot, version);
|
|
12733
|
-
const filePath =
|
|
13839
|
+
const filePath = join21(options.outputDir, "HEALTH.md");
|
|
12734
13840
|
writeFileSync3(filePath, content, "utf-8");
|
|
12735
13841
|
generated.push("HEALTH.md");
|
|
12736
13842
|
} catch (err) {
|
|
@@ -12741,7 +13847,7 @@ async function generateDocs(graph, projectRoot, version, parseTime, options) {
|
|
|
12741
13847
|
try {
|
|
12742
13848
|
if (options.verbose) console.log("Generating DEAD_CODE.md...");
|
|
12743
13849
|
const content = generateDeadCode(graph, projectRoot, version);
|
|
12744
|
-
const filePath =
|
|
13850
|
+
const filePath = join21(options.outputDir, "DEAD_CODE.md");
|
|
12745
13851
|
writeFileSync3(filePath, content, "utf-8");
|
|
12746
13852
|
generated.push("DEAD_CODE.md");
|
|
12747
13853
|
} catch (err) {
|
|
@@ -12785,7 +13891,7 @@ function getFileCount13(graph) {
|
|
|
12785
13891
|
}
|
|
12786
13892
|
|
|
12787
13893
|
// src/simulation/engine.ts
|
|
12788
|
-
import { dirname as
|
|
13894
|
+
import { dirname as dirname22, join as join22 } from "path";
|
|
12789
13895
|
function normalizePath2(p) {
|
|
12790
13896
|
return p.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
12791
13897
|
}
|
|
@@ -12917,7 +14023,7 @@ var SimulationEngine = class {
|
|
|
12917
14023
|
}
|
|
12918
14024
|
}
|
|
12919
14025
|
applyRename(clone, target, newName, brokenImports) {
|
|
12920
|
-
const destination =
|
|
14026
|
+
const destination = join22(dirname22(target), newName);
|
|
12921
14027
|
this.applyMove(clone, target, destination, brokenImports);
|
|
12922
14028
|
}
|
|
12923
14029
|
applySplit(clone, target, newFile, symbols, brokenImports) {
|
|
@@ -13117,13 +14223,13 @@ var SimulationEngine = class {
|
|
|
13117
14223
|
};
|
|
13118
14224
|
|
|
13119
14225
|
// src/security/scanner.ts
|
|
13120
|
-
import { existsSync as
|
|
13121
|
-
import { join as
|
|
14226
|
+
import { existsSync as existsSync20 } from "fs";
|
|
14227
|
+
import { join as join32 } from "path";
|
|
13122
14228
|
|
|
13123
14229
|
// src/security/checks/dependencies.ts
|
|
13124
14230
|
import { execSync as execSync2 } from "child_process";
|
|
13125
|
-
import { existsSync as
|
|
13126
|
-
import { join as
|
|
14231
|
+
import { existsSync as existsSync19, readFileSync as readFileSync18, readdirSync as readdirSync11 } from "fs";
|
|
14232
|
+
import { join as join23 } from "path";
|
|
13127
14233
|
function cvssToSeverity(score) {
|
|
13128
14234
|
if (score >= 9) return "critical";
|
|
13129
14235
|
if (score >= 7) return "high";
|
|
@@ -13133,18 +14239,18 @@ function cvssToSeverity(score) {
|
|
|
13133
14239
|
async function checkDependencies(_files, projectRoot) {
|
|
13134
14240
|
const findings = [];
|
|
13135
14241
|
try {
|
|
13136
|
-
if (
|
|
14242
|
+
if (existsSync19(join23(projectRoot, "package.json"))) {
|
|
13137
14243
|
findings.push(...checkNpmAudit(projectRoot));
|
|
13138
14244
|
findings.push(...checkPackageJsonPatterns(projectRoot));
|
|
13139
14245
|
findings.push(...checkPostinstallScripts(projectRoot));
|
|
13140
14246
|
}
|
|
13141
|
-
if (
|
|
14247
|
+
if (existsSync19(join23(projectRoot, "requirements.txt")) || existsSync19(join23(projectRoot, "pyproject.toml"))) {
|
|
13142
14248
|
findings.push(...checkPipAudit(projectRoot));
|
|
13143
14249
|
}
|
|
13144
|
-
if (
|
|
14250
|
+
if (existsSync19(join23(projectRoot, "Cargo.toml"))) {
|
|
13145
14251
|
findings.push(...checkCargoAudit(projectRoot));
|
|
13146
14252
|
}
|
|
13147
|
-
if (
|
|
14253
|
+
if (existsSync19(join23(projectRoot, "go.mod"))) {
|
|
13148
14254
|
findings.push(...checkGoVerify(projectRoot));
|
|
13149
14255
|
}
|
|
13150
14256
|
} catch (err) {
|
|
@@ -13233,8 +14339,8 @@ function checkNpmAudit(projectRoot) {
|
|
|
13233
14339
|
function checkPackageJsonPatterns(projectRoot) {
|
|
13234
14340
|
const findings = [];
|
|
13235
14341
|
try {
|
|
13236
|
-
const pkgPath =
|
|
13237
|
-
const pkg = JSON.parse(
|
|
14342
|
+
const pkgPath = join23(projectRoot, "package.json");
|
|
14343
|
+
const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
|
|
13238
14344
|
const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13239
14345
|
for (const [name, version] of Object.entries(allDeps)) {
|
|
13240
14346
|
if (version.startsWith("^") || version.startsWith("~")) {
|
|
@@ -13256,15 +14362,15 @@ function checkPackageJsonPatterns(projectRoot) {
|
|
|
13256
14362
|
}
|
|
13257
14363
|
function checkPostinstallScripts(projectRoot) {
|
|
13258
14364
|
const findings = [];
|
|
13259
|
-
const nodeModules =
|
|
13260
|
-
if (!
|
|
14365
|
+
const nodeModules = join23(projectRoot, "node_modules");
|
|
14366
|
+
if (!existsSync19(nodeModules)) return findings;
|
|
13261
14367
|
try {
|
|
13262
|
-
const topLevelDeps =
|
|
14368
|
+
const topLevelDeps = readdirSync11(nodeModules).filter((d) => !d.startsWith("."));
|
|
13263
14369
|
for (const dep of topLevelDeps) {
|
|
13264
|
-
const depPkgPath =
|
|
13265
|
-
if (!
|
|
14370
|
+
const depPkgPath = join23(nodeModules, dep, "package.json");
|
|
14371
|
+
if (!existsSync19(depPkgPath)) continue;
|
|
13266
14372
|
try {
|
|
13267
|
-
const depPkg = JSON.parse(
|
|
14373
|
+
const depPkg = JSON.parse(readFileSync18(depPkgPath, "utf-8"));
|
|
13268
14374
|
const scripts = depPkg.scripts || {};
|
|
13269
14375
|
if (scripts.postinstall || scripts.preinstall || scripts.install) {
|
|
13270
14376
|
const scriptName = scripts.postinstall ? "postinstall" : scripts.preinstall ? "preinstall" : "install";
|
|
@@ -13302,7 +14408,7 @@ function checkPipAudit(projectRoot) {
|
|
|
13302
14408
|
id: "",
|
|
13303
14409
|
severity: cvssToSeverity(vuln.cvss?.score || 5),
|
|
13304
14410
|
vulnerabilityClass: "dependency-cve",
|
|
13305
|
-
file:
|
|
14411
|
+
file: existsSync19(join23(projectRoot, "requirements.txt")) ? "requirements.txt" : "pyproject.toml",
|
|
13306
14412
|
title: `Vulnerable Python dependency: ${vuln.name}`,
|
|
13307
14413
|
description: `${vuln.name}@${vuln.version} \u2014 ${vuln.id}: ${vuln.description || "Known vulnerability"}`,
|
|
13308
14414
|
attackScenario: `An attacker could exploit the vulnerability in ${vuln.name}.`,
|
|
@@ -13399,8 +14505,8 @@ function checkGoVerify(projectRoot) {
|
|
|
13399
14505
|
}
|
|
13400
14506
|
|
|
13401
14507
|
// src/security/checks/injection.ts
|
|
13402
|
-
import { readFileSync as
|
|
13403
|
-
import { join as
|
|
14508
|
+
import { readFileSync as readFileSync19 } from "fs";
|
|
14509
|
+
import { join as join24 } from "path";
|
|
13404
14510
|
var SKIP_DIRS = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
13405
14511
|
var TEST_PATTERNS = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__"];
|
|
13406
14512
|
var USER_INPUT_NAMES = /(?:input|user|name|path|query|branch|hash|cmd|command|req\.|params|body|args|url|dir|file|subdirectory)/i;
|
|
@@ -13687,6 +14793,89 @@ var PATTERNS = [
|
|
|
13687
14793
|
description: "extract() on $_GET/$_POST/$_REQUEST overwrites local variables \u2014 can bypass security checks.",
|
|
13688
14794
|
attackScenario: "An attacker could set arbitrary variables by crafting request parameters, potentially overwriting auth flags or config values.",
|
|
13689
14795
|
suggestedFix: "Avoid extract() on user input. Access superglobals directly or use a whitelist of expected keys."
|
|
14796
|
+
},
|
|
14797
|
+
// Swift injection patterns
|
|
14798
|
+
{
|
|
14799
|
+
regex: /["'](?:SELECT|INSERT|UPDATE|DELETE)\b[^"']*\\\(/,
|
|
14800
|
+
title: "Swift SQL injection via string interpolation",
|
|
14801
|
+
vulnClass: "code-injection",
|
|
14802
|
+
baseSeverity: "high",
|
|
14803
|
+
description: "SQL query built using Swift string interpolation \u2014 vulnerable to SQL injection.",
|
|
14804
|
+
attackScenario: "An attacker could inject SQL through interpolated variables to read, modify, or delete database data.",
|
|
14805
|
+
suggestedFix: "Use parameterized queries with your database library (e.g., Fluent ORM, SQLite.swift bindings)."
|
|
14806
|
+
},
|
|
14807
|
+
{
|
|
14808
|
+
regex: /Process\s*\(\s*\)/,
|
|
14809
|
+
title: "Swift command injection via Process class",
|
|
14810
|
+
vulnClass: "shell-injection",
|
|
14811
|
+
baseSeverity: "medium",
|
|
14812
|
+
description: "Process() class executes external commands \u2014 vulnerable if user input reaches arguments.",
|
|
14813
|
+
attackScenario: "An attacker could inject shell metacharacters to execute arbitrary commands on the server.",
|
|
14814
|
+
suggestedFix: "Validate all arguments against a strict allowlist before passing to Process. Avoid shell execution."
|
|
14815
|
+
},
|
|
14816
|
+
{
|
|
14817
|
+
regex: /Unsafe(?:Raw|Mutable|Buffer)?Pointer/,
|
|
14818
|
+
title: "Swift unsafe pointer usage",
|
|
14819
|
+
vulnClass: "code-injection",
|
|
14820
|
+
baseSeverity: "medium",
|
|
14821
|
+
description: "Unsafe pointer usage bypasses Swift memory safety \u2014 potential for memory corruption.",
|
|
14822
|
+
attackScenario: "An attacker could exploit unsafe pointer operations to corrupt memory or execute arbitrary code.",
|
|
14823
|
+
suggestedFix: "Prefer safe Swift alternatives. If unsafe pointers are necessary, validate all bounds and lifetimes."
|
|
14824
|
+
},
|
|
14825
|
+
{
|
|
14826
|
+
regex: /UserDefaults\s*\.\s*(?:standard\s*\.\s*)?set\s*\([^)]*(?:password|secret|token|apiKey|api_key)/i,
|
|
14827
|
+
title: "Swift sensitive data in UserDefaults (unencrypted)",
|
|
14828
|
+
vulnClass: "code-injection",
|
|
14829
|
+
baseSeverity: "high",
|
|
14830
|
+
description: "Sensitive data stored in UserDefaults without encryption \u2014 easily accessible on jailbroken devices.",
|
|
14831
|
+
attackScenario: "An attacker with device access could read UserDefaults plist to extract credentials.",
|
|
14832
|
+
suggestedFix: "Use Keychain Services for storing sensitive data. Never store passwords or tokens in UserDefaults."
|
|
14833
|
+
},
|
|
14834
|
+
// Mojo injection patterns
|
|
14835
|
+
{
|
|
14836
|
+
regex: /\bPointer\s*\[\s*\w+\s*\]/,
|
|
14837
|
+
title: "Mojo unsafe Pointer[T] usage",
|
|
14838
|
+
vulnClass: "code-injection",
|
|
14839
|
+
baseSeverity: "medium",
|
|
14840
|
+
description: "Mojo Pointer[T] bypasses memory safety \u2014 potential for memory corruption or buffer overflow.",
|
|
14841
|
+
attackScenario: "An attacker could exploit unsafe pointer operations to corrupt memory or execute arbitrary code.",
|
|
14842
|
+
suggestedFix: "Use safe Mojo abstractions (SIMD, Tensor) instead of raw pointers where possible. Validate bounds."
|
|
14843
|
+
},
|
|
14844
|
+
{
|
|
14845
|
+
regex: /\bDTypePointer\b/,
|
|
14846
|
+
title: "Mojo unsafe DTypePointer usage",
|
|
14847
|
+
vulnClass: "code-injection",
|
|
14848
|
+
baseSeverity: "medium",
|
|
14849
|
+
description: "DTypePointer provides raw memory access without bounds checking.",
|
|
14850
|
+
attackScenario: "An attacker could exploit unvalidated pointer operations for buffer overflow or memory corruption.",
|
|
14851
|
+
suggestedFix: "Use Tensor or SIMD types with bounds checking instead of raw DTypePointer."
|
|
14852
|
+
},
|
|
14853
|
+
{
|
|
14854
|
+
regex: /from\s+python\s+import.*\beval\b/,
|
|
14855
|
+
title: "Mojo Python interop: eval() imported",
|
|
14856
|
+
vulnClass: "code-injection",
|
|
14857
|
+
baseSeverity: "high",
|
|
14858
|
+
description: "Python eval() imported via Mojo Python interop \u2014 can execute arbitrary code.",
|
|
14859
|
+
attackScenario: "An attacker could inject malicious code strings executed through the Python bridge.",
|
|
14860
|
+
suggestedFix: "Avoid importing eval. Use safe parsing alternatives or validate all input strictly."
|
|
14861
|
+
},
|
|
14862
|
+
{
|
|
14863
|
+
regex: /\b__get_address_as_lvalue\b/,
|
|
14864
|
+
title: "Mojo uninitialized memory access pattern",
|
|
14865
|
+
vulnClass: "code-injection",
|
|
14866
|
+
baseSeverity: "medium",
|
|
14867
|
+
description: "Low-level memory access without initialization \u2014 potential for use of uninitialized data.",
|
|
14868
|
+
attackScenario: "An attacker could exploit uninitialized memory to leak data or corrupt program state.",
|
|
14869
|
+
suggestedFix: "Always initialize memory before use. Use safe constructors and value semantics."
|
|
14870
|
+
},
|
|
14871
|
+
{
|
|
14872
|
+
regex: /SIMD\s*\[[^\]]*\]\s*\.\s*(?:store|load)\s*\(/,
|
|
14873
|
+
title: "Mojo SIMD store/load without bounds check",
|
|
14874
|
+
vulnClass: "code-injection",
|
|
14875
|
+
baseSeverity: "medium",
|
|
14876
|
+
description: "SIMD store/load operations without explicit bounds checking \u2014 buffer overflow risk.",
|
|
14877
|
+
attackScenario: "An attacker could trigger out-of-bounds SIMD operations to corrupt memory.",
|
|
14878
|
+
suggestedFix: "Validate buffer size against SIMD width before store/load operations."
|
|
13690
14879
|
}
|
|
13691
14880
|
];
|
|
13692
14881
|
function shouldSkip(filePath) {
|
|
@@ -13703,7 +14892,7 @@ async function checkInjection(files, projectRoot) {
|
|
|
13703
14892
|
if (shouldSkip(file.filePath) || isTestFile4(file.filePath)) continue;
|
|
13704
14893
|
let content;
|
|
13705
14894
|
try {
|
|
13706
|
-
content =
|
|
14895
|
+
content = readFileSync19(join24(projectRoot, file.filePath), "utf-8");
|
|
13707
14896
|
} catch {
|
|
13708
14897
|
continue;
|
|
13709
14898
|
}
|
|
@@ -13741,8 +14930,8 @@ async function checkInjection(files, projectRoot) {
|
|
|
13741
14930
|
}
|
|
13742
14931
|
|
|
13743
14932
|
// src/security/checks/secrets.ts
|
|
13744
|
-
import { readFileSync as
|
|
13745
|
-
import { join as
|
|
14933
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
14934
|
+
import { join as join25 } from "path";
|
|
13746
14935
|
var SKIP_DIRS2 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
13747
14936
|
var TEST_PATTERNS2 = ["test", "spec", "fixture", "mock", "__tests__", "__mocks__", ".example", ".sample"];
|
|
13748
14937
|
var SECRET_PATTERNS = [
|
|
@@ -13775,7 +14964,7 @@ async function checkSecrets(files, projectRoot) {
|
|
|
13775
14964
|
if (shouldSkip2(file.filePath) || isTestFile5(file.filePath)) continue;
|
|
13776
14965
|
let content;
|
|
13777
14966
|
try {
|
|
13778
|
-
content =
|
|
14967
|
+
content = readFileSync20(join25(projectRoot, file.filePath), "utf-8");
|
|
13779
14968
|
} catch {
|
|
13780
14969
|
continue;
|
|
13781
14970
|
}
|
|
@@ -13808,8 +14997,8 @@ async function checkSecrets(files, projectRoot) {
|
|
|
13808
14997
|
}
|
|
13809
14998
|
|
|
13810
14999
|
// src/security/checks/path-traversal.ts
|
|
13811
|
-
import { readFileSync as
|
|
13812
|
-
import { join as
|
|
15000
|
+
import { readFileSync as readFileSync21 } from "fs";
|
|
15001
|
+
import { join as join26 } from "path";
|
|
13813
15002
|
var SKIP_DIRS3 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
13814
15003
|
var USER_INPUT_VARS = /(?:req\.|params|query|body|input|path|dir|subdirectory|file|userInput|fileName|filePath)/i;
|
|
13815
15004
|
var PATTERNS2 = [
|
|
@@ -13856,7 +15045,7 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
13856
15045
|
if (shouldSkip3(file.filePath)) continue;
|
|
13857
15046
|
let content;
|
|
13858
15047
|
try {
|
|
13859
|
-
content =
|
|
15048
|
+
content = readFileSync21(join26(projectRoot, file.filePath), "utf-8");
|
|
13860
15049
|
} catch {
|
|
13861
15050
|
continue;
|
|
13862
15051
|
}
|
|
@@ -13898,8 +15087,8 @@ async function checkPathTraversal(files, projectRoot) {
|
|
|
13898
15087
|
}
|
|
13899
15088
|
|
|
13900
15089
|
// src/security/checks/auth.ts
|
|
13901
|
-
import { readFileSync as
|
|
13902
|
-
import { join as
|
|
15090
|
+
import { readFileSync as readFileSync22 } from "fs";
|
|
15091
|
+
import { join as join27 } from "path";
|
|
13903
15092
|
var SKIP_DIRS4 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
13904
15093
|
function shouldSkip4(filePath) {
|
|
13905
15094
|
return SKIP_DIRS4.some((d) => filePath.includes(d));
|
|
@@ -13915,7 +15104,7 @@ async function checkAuth(files, projectRoot) {
|
|
|
13915
15104
|
if (shouldSkip4(file.filePath)) continue;
|
|
13916
15105
|
let content;
|
|
13917
15106
|
try {
|
|
13918
|
-
content =
|
|
15107
|
+
content = readFileSync22(join27(projectRoot, file.filePath), "utf-8");
|
|
13919
15108
|
} catch {
|
|
13920
15109
|
continue;
|
|
13921
15110
|
}
|
|
@@ -14006,8 +15195,8 @@ async function checkAuth(files, projectRoot) {
|
|
|
14006
15195
|
}
|
|
14007
15196
|
|
|
14008
15197
|
// src/security/checks/input-validation.ts
|
|
14009
|
-
import { readFileSync as
|
|
14010
|
-
import { join as
|
|
15198
|
+
import { readFileSync as readFileSync23 } from "fs";
|
|
15199
|
+
import { join as join28 } from "path";
|
|
14011
15200
|
var SKIP_DIRS5 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
14012
15201
|
function shouldSkip5(filePath) {
|
|
14013
15202
|
return SKIP_DIRS5.some((d) => filePath.includes(d));
|
|
@@ -14019,7 +15208,7 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
14019
15208
|
if (shouldSkip5(file.filePath)) continue;
|
|
14020
15209
|
let content;
|
|
14021
15210
|
try {
|
|
14022
|
-
content =
|
|
15211
|
+
content = readFileSync23(join28(projectRoot, file.filePath), "utf-8");
|
|
14023
15212
|
} catch {
|
|
14024
15213
|
continue;
|
|
14025
15214
|
}
|
|
@@ -14096,8 +15285,8 @@ async function checkInputValidation(files, projectRoot) {
|
|
|
14096
15285
|
}
|
|
14097
15286
|
|
|
14098
15287
|
// src/security/checks/information-disclosure.ts
|
|
14099
|
-
import { readFileSync as
|
|
14100
|
-
import { join as
|
|
15288
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
15289
|
+
import { join as join29 } from "path";
|
|
14101
15290
|
var SKIP_DIRS6 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
14102
15291
|
function shouldSkip6(filePath) {
|
|
14103
15292
|
return SKIP_DIRS6.some((d) => filePath.includes(d));
|
|
@@ -14109,7 +15298,7 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
14109
15298
|
if (shouldSkip6(file.filePath)) continue;
|
|
14110
15299
|
let content;
|
|
14111
15300
|
try {
|
|
14112
|
-
content =
|
|
15301
|
+
content = readFileSync24(join29(projectRoot, file.filePath), "utf-8");
|
|
14113
15302
|
} catch {
|
|
14114
15303
|
continue;
|
|
14115
15304
|
}
|
|
@@ -14179,8 +15368,8 @@ async function checkInformationDisclosure(files, projectRoot) {
|
|
|
14179
15368
|
}
|
|
14180
15369
|
|
|
14181
15370
|
// src/security/checks/cryptography.ts
|
|
14182
|
-
import { readFileSync as
|
|
14183
|
-
import { join as
|
|
15371
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
15372
|
+
import { join as join30 } from "path";
|
|
14184
15373
|
var SKIP_DIRS7 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
14185
15374
|
var USER_INPUT_NAMES2 = /(?:input|user|name|path|query|param|request|body|args|url)/i;
|
|
14186
15375
|
function shouldSkip7(filePath) {
|
|
@@ -14197,7 +15386,7 @@ async function checkCryptography(files, projectRoot) {
|
|
|
14197
15386
|
if (shouldSkip7(file.filePath)) continue;
|
|
14198
15387
|
let content;
|
|
14199
15388
|
try {
|
|
14200
|
-
content =
|
|
15389
|
+
content = readFileSync25(join30(projectRoot, file.filePath), "utf-8");
|
|
14201
15390
|
} catch {
|
|
14202
15391
|
continue;
|
|
14203
15392
|
}
|
|
@@ -14455,6 +15644,125 @@ async function checkCryptography(files, projectRoot) {
|
|
|
14455
15644
|
suggestedFix: "Load credentials from environment variables using getenv() or $_ENV."
|
|
14456
15645
|
});
|
|
14457
15646
|
}
|
|
15647
|
+
if (/\bCC_MD5\b/.test(line) || /Insecure\s*\.\s*MD5/.test(line)) {
|
|
15648
|
+
findings.push({
|
|
15649
|
+
id: "",
|
|
15650
|
+
severity: isCryptoFile ? "high" : "medium",
|
|
15651
|
+
vulnerabilityClass: "cryptography",
|
|
15652
|
+
file: file.filePath,
|
|
15653
|
+
line: i + 1,
|
|
15654
|
+
title: "Weak hash algorithm: MD5 in Swift",
|
|
15655
|
+
description: "MD5 is cryptographically broken \u2014 collisions can be generated in seconds.",
|
|
15656
|
+
attackScenario: "An attacker could generate MD5 collisions to bypass integrity checks or forge hashes.",
|
|
15657
|
+
suggestedFix: "Use SHA256 from CryptoKit: SHA256.hash(data: data)"
|
|
15658
|
+
});
|
|
15659
|
+
}
|
|
15660
|
+
if (/\bCC_SHA1\b/.test(line) || /Insecure\s*\.\s*SHA1/.test(line)) {
|
|
15661
|
+
findings.push({
|
|
15662
|
+
id: "",
|
|
15663
|
+
severity: isCryptoFile ? "high" : "medium",
|
|
15664
|
+
vulnerabilityClass: "cryptography",
|
|
15665
|
+
file: file.filePath,
|
|
15666
|
+
line: i + 1,
|
|
15667
|
+
title: "Weak hash algorithm: SHA-1 in Swift",
|
|
15668
|
+
description: "SHA-1 has known collision attacks \u2014 should not be used for security purposes.",
|
|
15669
|
+
attackScenario: "An attacker could generate SHA-1 collisions to bypass integrity checks.",
|
|
15670
|
+
suggestedFix: "Use SHA256 from CryptoKit: SHA256.hash(data: data)"
|
|
15671
|
+
});
|
|
15672
|
+
}
|
|
15673
|
+
if (/(?:let|var)\s+(?:password|secret|apiKey|api_key|token)\s*(?::\s*String\s*)?=\s*["']/i.test(line)) {
|
|
15674
|
+
findings.push({
|
|
15675
|
+
id: "",
|
|
15676
|
+
severity: "high",
|
|
15677
|
+
vulnerabilityClass: "cryptography",
|
|
15678
|
+
file: file.filePath,
|
|
15679
|
+
line: i + 1,
|
|
15680
|
+
title: "Hardcoded credentials in Swift source",
|
|
15681
|
+
description: "A password, secret, or API key is hardcoded as a string literal.",
|
|
15682
|
+
attackScenario: "An attacker with access to the binary or source could extract the credential.",
|
|
15683
|
+
suggestedFix: "Load credentials from Keychain, environment variables, or a secure configuration service."
|
|
15684
|
+
});
|
|
15685
|
+
}
|
|
15686
|
+
if (/\barc4random\b/.test(line) && isCryptoFile) {
|
|
15687
|
+
findings.push({
|
|
15688
|
+
id: "",
|
|
15689
|
+
severity: "medium",
|
|
15690
|
+
vulnerabilityClass: "cryptography",
|
|
15691
|
+
file: file.filePath,
|
|
15692
|
+
line: i + 1,
|
|
15693
|
+
title: "arc4random in Swift security context",
|
|
15694
|
+
description: "arc4random is not suitable for cryptographic key generation.",
|
|
15695
|
+
attackScenario: "An attacker could predict random values if used for cryptographic purposes.",
|
|
15696
|
+
suggestedFix: "Use SecRandomCopyBytes or SystemRandomNumberGenerator for security-sensitive randomness."
|
|
15697
|
+
});
|
|
15698
|
+
}
|
|
15699
|
+
if (/allowsArbitraryLoads\s*:\s*true/.test(line) || /NSAllowsArbitraryLoads.*true/.test(line)) {
|
|
15700
|
+
findings.push({
|
|
15701
|
+
id: "",
|
|
15702
|
+
severity: "medium",
|
|
15703
|
+
vulnerabilityClass: "cryptography",
|
|
15704
|
+
file: file.filePath,
|
|
15705
|
+
line: i + 1,
|
|
15706
|
+
title: "Swift App Transport Security disabled",
|
|
15707
|
+
description: "allowsArbitraryLoads is set to true \u2014 all HTTP traffic is permitted without encryption.",
|
|
15708
|
+
attackScenario: "An attacker on the network path could intercept, read, or modify data in transit.",
|
|
15709
|
+
suggestedFix: "Remove allowsArbitraryLoads or set to false. Add specific exceptions only for domains that require HTTP."
|
|
15710
|
+
});
|
|
15711
|
+
}
|
|
15712
|
+
if (/(?:let|var)\s+\w*[Uu]rl\w*\s*=\s*["']http:\/\/(?!(?:localhost|127\.))/.test(line)) {
|
|
15713
|
+
findings.push({
|
|
15714
|
+
id: "",
|
|
15715
|
+
severity: "medium",
|
|
15716
|
+
vulnerabilityClass: "cryptography",
|
|
15717
|
+
file: file.filePath,
|
|
15718
|
+
line: i + 1,
|
|
15719
|
+
title: "Hardcoded HTTP URL in Swift source",
|
|
15720
|
+
description: "An HTTP (not HTTPS) URL is hardcoded \u2014 data is transmitted unencrypted.",
|
|
15721
|
+
attackScenario: "An attacker on the network path could intercept, read, or modify data in transit.",
|
|
15722
|
+
suggestedFix: "Use HTTPS for all external URLs to ensure data confidentiality and integrity."
|
|
15723
|
+
});
|
|
15724
|
+
}
|
|
15725
|
+
if (/from\s+python\s+import\s+random/.test(line)) {
|
|
15726
|
+
findings.push({
|
|
15727
|
+
id: "",
|
|
15728
|
+
severity: "medium",
|
|
15729
|
+
vulnerabilityClass: "cryptography",
|
|
15730
|
+
file: file.filePath,
|
|
15731
|
+
line: i + 1,
|
|
15732
|
+
title: "Mojo weak random via Python random module",
|
|
15733
|
+
description: "Python random module imported via Mojo interop \u2014 not cryptographically secure.",
|
|
15734
|
+
attackScenario: "An attacker could predict random values to forge tokens or bypass security checks.",
|
|
15735
|
+
suggestedFix: "Use Python secrets module through interop, or implement CSPRNG natively in Mojo."
|
|
15736
|
+
});
|
|
15737
|
+
}
|
|
15738
|
+
if (/\balias\s+(?:key|secret|password|token|api_key)\s*[=:]\s*["'][^"']{4,}["']/i.test(line)) {
|
|
15739
|
+
findings.push({
|
|
15740
|
+
id: "",
|
|
15741
|
+
severity: "high",
|
|
15742
|
+
vulnerabilityClass: "cryptography",
|
|
15743
|
+
file: file.filePath,
|
|
15744
|
+
line: i + 1,
|
|
15745
|
+
title: "Hardcoded credentials in Mojo alias declaration",
|
|
15746
|
+
description: "A secret, key, or password is hardcoded in a compile-time alias \u2014 visible in source.",
|
|
15747
|
+
attackScenario: "An attacker with access to source or compiled binary could extract the credential.",
|
|
15748
|
+
suggestedFix: "Load credentials from environment variables at runtime instead of alias declarations."
|
|
15749
|
+
});
|
|
15750
|
+
}
|
|
15751
|
+
if (/from\s+python\s+import\s+hashlib/.test(line)) {
|
|
15752
|
+
if (isCryptoFile) {
|
|
15753
|
+
findings.push({
|
|
15754
|
+
id: "",
|
|
15755
|
+
severity: "medium",
|
|
15756
|
+
vulnerabilityClass: "cryptography",
|
|
15757
|
+
file: file.filePath,
|
|
15758
|
+
line: i + 1,
|
|
15759
|
+
title: "Mojo Python hashlib imported in security context",
|
|
15760
|
+
description: "Python hashlib imported via interop \u2014 ensure only strong algorithms (SHA-256+) are used.",
|
|
15761
|
+
attackScenario: "If MD5 or SHA-1 from hashlib is used, an attacker could exploit weak hash collisions.",
|
|
15762
|
+
suggestedFix: "Only use hashlib.sha256() or stronger. Avoid md5() and sha1() for security purposes."
|
|
15763
|
+
});
|
|
15764
|
+
}
|
|
15765
|
+
}
|
|
14458
15766
|
}
|
|
14459
15767
|
}
|
|
14460
15768
|
} catch {
|
|
@@ -14463,8 +15771,8 @@ async function checkCryptography(files, projectRoot) {
|
|
|
14463
15771
|
}
|
|
14464
15772
|
|
|
14465
15773
|
// src/security/checks/frontend.ts
|
|
14466
|
-
import { readFileSync as
|
|
14467
|
-
import { join as
|
|
15774
|
+
import { readFileSync as readFileSync26 } from "fs";
|
|
15775
|
+
import { join as join31 } from "path";
|
|
14468
15776
|
var SKIP_DIRS8 = ["node_modules/", "dist/", ".git/", ".wrangler/", "src/security/checks/"];
|
|
14469
15777
|
function shouldSkip8(filePath) {
|
|
14470
15778
|
return SKIP_DIRS8.some((d) => filePath.includes(d));
|
|
@@ -14480,7 +15788,7 @@ async function checkFrontend(files, projectRoot) {
|
|
|
14480
15788
|
if (!isFrontendFile(file.filePath)) continue;
|
|
14481
15789
|
let content;
|
|
14482
15790
|
try {
|
|
14483
|
-
content =
|
|
15791
|
+
content = readFileSync26(join31(projectRoot, file.filePath), "utf-8");
|
|
14484
15792
|
} catch {
|
|
14485
15793
|
continue;
|
|
14486
15794
|
}
|
|
@@ -14940,13 +16248,13 @@ async function scanSecurity(projectRoot, graph, options = {}) {
|
|
|
14940
16248
|
};
|
|
14941
16249
|
}
|
|
14942
16250
|
function detectPackageManager(projectRoot) {
|
|
14943
|
-
if (
|
|
14944
|
-
if (
|
|
14945
|
-
if (
|
|
14946
|
-
if (
|
|
14947
|
-
if (
|
|
14948
|
-
if (
|
|
14949
|
-
if (
|
|
16251
|
+
if (existsSync20(join32(projectRoot, "package.json"))) return "npm";
|
|
16252
|
+
if (existsSync20(join32(projectRoot, "requirements.txt"))) return "pip";
|
|
16253
|
+
if (existsSync20(join32(projectRoot, "pyproject.toml"))) return "pip";
|
|
16254
|
+
if (existsSync20(join32(projectRoot, "Cargo.toml"))) return "cargo";
|
|
16255
|
+
if (existsSync20(join32(projectRoot, "go.mod"))) return "go";
|
|
16256
|
+
if (existsSync20(join32(projectRoot, "pom.xml"))) return "maven";
|
|
16257
|
+
if (existsSync20(join32(projectRoot, "build.gradle")) || existsSync20(join32(projectRoot, "build.gradle.kts"))) return "gradle";
|
|
14950
16258
|
return "unknown";
|
|
14951
16259
|
}
|
|
14952
16260
|
|