@aiready/core 0.23.1 → 0.23.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/parser-factory.test.d.ts +1 -1
- package/dist/__tests__/parser-factory.test.js +62 -50
- package/dist/__tests__/python-parser.test.d.ts +1 -1
- package/dist/__tests__/python-parser.test.js +111 -109
- package/dist/__tests__/scoring.test.d.ts +1 -1
- package/dist/__tests__/scoring.test.js +193 -176
- package/dist/chunk-3YI4IS3D.mjs +191 -173
- package/dist/chunk-5HIXDC3X.mjs +273 -251
- package/dist/chunk-5V3L53AE.mjs +805 -0
- package/dist/chunk-CKVKHN3G.mjs +228 -211
- package/dist/chunk-COHIBX3Q.mjs +213 -195
- package/dist/chunk-CWRCDSKZ.mjs +91 -82
- package/dist/chunk-D3D3NCRR.mjs +147 -129
- package/dist/chunk-HCFYP7UD.mjs +805 -0
- package/dist/chunk-HFLFBA6F.mjs +79 -72
- package/dist/chunk-HKSARRCD.mjs +66 -58
- package/dist/chunk-JJ5JL5FX.mjs +91 -82
- package/dist/chunk-KDSTXVLQ.mjs +724 -0
- package/dist/chunk-KI7XORTN.mjs +91 -82
- package/dist/chunk-LTMHFNFK.mjs +690 -0
- package/dist/chunk-LTNXTXRI.mjs +228 -211
- package/dist/chunk-M22BXHBR.mjs +805 -0
- package/dist/chunk-MH3A3LX6.mjs +200 -182
- package/dist/chunk-NGHT7JOG.mjs +697 -0
- package/dist/chunk-OQ6IGDXG.mjs +147 -129
- package/dist/chunk-QAFB3HXQ.mjs +181 -165
- package/dist/chunk-QQBKXHLU.mjs +678 -0
- package/dist/chunk-RDHYGES7.mjs +678 -0
- package/dist/chunk-SWTDBVYJ.mjs +228 -213
- package/dist/chunk-UIWL5JQB.mjs +79 -72
- package/dist/chunk-UQGI67WR.mjs +79 -72
- package/dist/chunk-UTZOO4XO.mjs +147 -131
- package/dist/chunk-X4F46I5L.mjs +213 -195
- package/dist/chunk-XKK7YHPX.mjs +204 -186
- package/dist/chunk-YCA4FTEK.mjs +190 -172
- package/dist/chunk-ZSZRRTJM.mjs +719 -0
- package/dist/client-BgmiMoil.d.mts +1344 -0
- package/dist/client-BgmiMoil.d.ts +1344 -0
- package/dist/client-BxGrPuuN.d.mts +1191 -0
- package/dist/client-BxGrPuuN.d.ts +1191 -0
- package/dist/client-D-cn9ydj.d.mts +1136 -0
- package/dist/client-D-cn9ydj.d.ts +1136 -0
- package/dist/client-D9seCH4K.d.mts +1334 -0
- package/dist/client-D9seCH4K.d.ts +1334 -0
- package/dist/client-DIXIh7rw.d.mts +1193 -0
- package/dist/client-DIXIh7rw.d.ts +1193 -0
- package/dist/client-DVHXWOHw.d.mts +1245 -0
- package/dist/client-DVHXWOHw.d.ts +1245 -0
- package/dist/client.d.mts +2 -1094
- package/dist/client.d.ts +2 -1094
- package/dist/client.js +23 -43
- package/dist/client.mjs +3 -25
- package/dist/index.d.mts +380 -108
- package/dist/index.d.ts +380 -108
- package/dist/index.js +609 -445
- package/dist/index.mjs +587 -429
- package/dist/parsers/parser-factory.d.ts +45 -45
- package/dist/parsers/parser-factory.js +86 -84
- package/dist/parsers/python-parser.d.ts +33 -28
- package/dist/parsers/python-parser.js +224 -222
- package/dist/parsers/typescript-parser.d.ts +15 -10
- package/dist/parsers/typescript-parser.js +223 -197
- package/dist/scoring.d.ts +59 -49
- package/dist/scoring.js +129 -127
- package/dist/types/language.d.ts +104 -93
- package/dist/types/language.js +23 -23
- package/dist/types.d.ts +105 -87
- package/dist/types.js +1 -1
- package/dist/utils/ast-parser.d.ts +42 -33
- package/dist/utils/ast-parser.js +159 -162
- package/dist/utils/cli-helpers.d.ts +27 -10
- package/dist/utils/cli-helpers.js +45 -43
- package/dist/utils/config.d.ts +8 -3
- package/dist/utils/config.js +67 -69
- package/dist/utils/file-scanner.d.ts +1 -1
- package/dist/utils/file-scanner.js +80 -76
- package/dist/utils/metrics.d.ts +1 -1
- package/dist/utils/metrics.js +2 -2
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -38,12 +38,13 @@ import {
|
|
|
38
38
|
getProjectSizeTier,
|
|
39
39
|
getRating,
|
|
40
40
|
getRatingDisplay,
|
|
41
|
+
getRatingSlug,
|
|
41
42
|
getRatingWithContext,
|
|
42
43
|
getRecommendedThreshold,
|
|
43
44
|
getToolWeight,
|
|
44
45
|
normalizeToolName,
|
|
45
46
|
parseWeightString
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-NGHT7JOG.mjs";
|
|
47
48
|
|
|
48
49
|
// src/types/contract.ts
|
|
49
50
|
function validateSpokeOutput(toolName, output) {
|
|
@@ -439,8 +440,15 @@ function isSourceFile(filePath) {
|
|
|
439
440
|
}
|
|
440
441
|
|
|
441
442
|
// src/utils/cli-helpers.ts
|
|
442
|
-
import {
|
|
443
|
-
|
|
443
|
+
import {
|
|
444
|
+
writeFileSync,
|
|
445
|
+
mkdirSync,
|
|
446
|
+
existsSync as existsSync2,
|
|
447
|
+
readdirSync,
|
|
448
|
+
statSync
|
|
449
|
+
} from "fs";
|
|
450
|
+
import { join as join2, dirname as dirname2, resolve as resolvePath } from "path";
|
|
451
|
+
import chalk from "chalk";
|
|
444
452
|
function resolveOutputPath(userPath, defaultFilename, workingDir = process.cwd()) {
|
|
445
453
|
let outputPath;
|
|
446
454
|
if (userPath) {
|
|
@@ -508,25 +516,180 @@ function emitProgress(processed, total, toolId, message, onProgress, throttleCou
|
|
|
508
516
|
onProgress(processed, total, `${message} (${processed}/${total})`);
|
|
509
517
|
}
|
|
510
518
|
}
|
|
511
|
-
function getSeverityColor(severity, chalk) {
|
|
519
|
+
function getSeverityColor(severity, chalkInstance = chalk) {
|
|
512
520
|
switch (severity.toLowerCase()) {
|
|
513
521
|
case "critical":
|
|
514
522
|
case "high-risk":
|
|
515
523
|
case "blind-risk":
|
|
516
|
-
return
|
|
524
|
+
return chalkInstance.red;
|
|
517
525
|
case "major":
|
|
518
526
|
case "moderate-risk":
|
|
519
|
-
return
|
|
527
|
+
return chalkInstance.yellow;
|
|
520
528
|
case "minor":
|
|
521
529
|
case "safe":
|
|
522
|
-
return
|
|
530
|
+
return chalkInstance.green;
|
|
531
|
+
case "info":
|
|
532
|
+
return chalkInstance.blue;
|
|
533
|
+
default:
|
|
534
|
+
return chalkInstance.white;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function getSeverityValue(s) {
|
|
538
|
+
if (!s) return 0;
|
|
539
|
+
switch (s.toLowerCase()) {
|
|
540
|
+
case "critical":
|
|
541
|
+
return 4;
|
|
542
|
+
case "major":
|
|
543
|
+
return 3;
|
|
544
|
+
case "minor":
|
|
545
|
+
return 2;
|
|
523
546
|
case "info":
|
|
524
|
-
return
|
|
547
|
+
return 1;
|
|
548
|
+
default:
|
|
549
|
+
return 0;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
function getSeverityLevel(s) {
|
|
553
|
+
return getSeverityValue(s);
|
|
554
|
+
}
|
|
555
|
+
function getSeverityBadge(severity, chalkInstance = chalk) {
|
|
556
|
+
const val = getSeverityValue(
|
|
557
|
+
typeof severity === "string" ? severity : severity
|
|
558
|
+
);
|
|
559
|
+
switch (val) {
|
|
560
|
+
case 4:
|
|
561
|
+
return chalkInstance.bgRed.white.bold(" CRITICAL ");
|
|
562
|
+
case 3:
|
|
563
|
+
return chalkInstance.bgYellow.black.bold(" MAJOR ");
|
|
564
|
+
case 2:
|
|
565
|
+
return chalkInstance.bgBlue.white.bold(" MINOR ");
|
|
566
|
+
case 1:
|
|
567
|
+
return chalkInstance.bgCyan.black(" INFO ");
|
|
568
|
+
default:
|
|
569
|
+
return chalkInstance.bgCyan.black(" INFO ");
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function getSeverityEnum(s) {
|
|
573
|
+
const level = getSeverityLevel(s);
|
|
574
|
+
switch (level) {
|
|
575
|
+
case 4:
|
|
576
|
+
return "critical";
|
|
577
|
+
case 3:
|
|
578
|
+
return "major";
|
|
579
|
+
case 2:
|
|
580
|
+
return "minor";
|
|
581
|
+
case 1:
|
|
582
|
+
return "info";
|
|
525
583
|
default:
|
|
526
|
-
return
|
|
584
|
+
return "info";
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function findLatestReport(dirPath) {
|
|
588
|
+
const aireadyDir = resolvePath(dirPath, ".aiready");
|
|
589
|
+
if (!existsSync2(aireadyDir)) {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
let files = readdirSync(aireadyDir).filter(
|
|
593
|
+
(f) => f.startsWith("aiready-report-") && f.endsWith(".json")
|
|
594
|
+
);
|
|
595
|
+
if (files.length === 0) {
|
|
596
|
+
files = readdirSync(aireadyDir).filter(
|
|
597
|
+
(f) => f.startsWith("aiready-scan-") && f.endsWith(".json")
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
if (files.length === 0) {
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
const sortedFiles = files.map((f) => ({
|
|
604
|
+
name: f,
|
|
605
|
+
path: resolvePath(aireadyDir, f),
|
|
606
|
+
mtime: statSync(resolvePath(aireadyDir, f)).mtime
|
|
607
|
+
})).sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
608
|
+
return sortedFiles[0].path;
|
|
609
|
+
}
|
|
610
|
+
function findLatestScanReport(scanReportsDir, reportFilePrefix) {
|
|
611
|
+
try {
|
|
612
|
+
let reportFiles = [];
|
|
613
|
+
if (existsSync2(scanReportsDir)) {
|
|
614
|
+
const files = readdirSync(scanReportsDir);
|
|
615
|
+
if (files.length > 0) {
|
|
616
|
+
const prefixRegex = new RegExp(`^${reportFilePrefix}\\d+\\.json$`);
|
|
617
|
+
reportFiles = files.filter((file) => prefixRegex.test(file));
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (reportFiles.length === 0) return null;
|
|
621
|
+
reportFiles.sort((a, b) => {
|
|
622
|
+
const idA = parseInt(a.match(/\d+/)?.[0] || "0", 10);
|
|
623
|
+
const idB = parseInt(b.match(/\d+/)?.[0] || "0", 10);
|
|
624
|
+
return idB - idA;
|
|
625
|
+
});
|
|
626
|
+
return join2(scanReportsDir, reportFiles[0]);
|
|
627
|
+
} catch (e) {
|
|
628
|
+
console.error("Error while finding latest scan report:", e);
|
|
629
|
+
return null;
|
|
527
630
|
}
|
|
528
631
|
}
|
|
529
632
|
|
|
633
|
+
// src/utils/provider-utils.ts
|
|
634
|
+
function groupIssuesByFile(issues) {
|
|
635
|
+
const fileIssuesMap = /* @__PURE__ */ new Map();
|
|
636
|
+
for (const issue of issues) {
|
|
637
|
+
const file = issue.location?.file ?? "unknown";
|
|
638
|
+
if (!fileIssuesMap.has(file)) fileIssuesMap.set(file, []);
|
|
639
|
+
fileIssuesMap.get(file).push(issue);
|
|
640
|
+
}
|
|
641
|
+
return Array.from(fileIssuesMap.entries()).map(([fileName, issueList]) => ({
|
|
642
|
+
fileName,
|
|
643
|
+
issues: issueList,
|
|
644
|
+
metrics: {}
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
function buildSimpleProviderScore(toolName, summary, rawData = {}) {
|
|
648
|
+
return {
|
|
649
|
+
toolName,
|
|
650
|
+
score: summary.score ?? 0,
|
|
651
|
+
rawMetrics: { ...summary, ...rawData },
|
|
652
|
+
factors: [],
|
|
653
|
+
recommendations: (summary.recommendations ?? []).map((action) => ({
|
|
654
|
+
action,
|
|
655
|
+
estimatedImpact: 5,
|
|
656
|
+
priority: "medium"
|
|
657
|
+
}))
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
function buildSpokeOutput(toolName, version, summary, results, metadata = {}) {
|
|
661
|
+
return SpokeOutputSchema.parse({
|
|
662
|
+
results,
|
|
663
|
+
summary,
|
|
664
|
+
metadata: {
|
|
665
|
+
toolName,
|
|
666
|
+
version,
|
|
667
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
668
|
+
...metadata
|
|
669
|
+
}
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
function createProvider(config) {
|
|
673
|
+
return {
|
|
674
|
+
id: config.id,
|
|
675
|
+
alias: config.alias,
|
|
676
|
+
defaultWeight: config.defaultWeight,
|
|
677
|
+
async analyze(options) {
|
|
678
|
+
const report = await config.analyzeReport(options);
|
|
679
|
+
return buildSpokeOutput(
|
|
680
|
+
config.id,
|
|
681
|
+
config.version,
|
|
682
|
+
config.getSummary(report),
|
|
683
|
+
config.getResults(report),
|
|
684
|
+
config.getMetadata?.(report) ?? {}
|
|
685
|
+
);
|
|
686
|
+
},
|
|
687
|
+
score(output, options) {
|
|
688
|
+
return config.score(output, options);
|
|
689
|
+
}
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
530
693
|
// src/utils/ast-parser.ts
|
|
531
694
|
import { parse as parse2 } from "@typescript-eslint/typescript-estree";
|
|
532
695
|
|
|
@@ -653,8 +816,10 @@ var TypeScriptParser = class {
|
|
|
653
816
|
// camelCase for variables and functions
|
|
654
817
|
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
655
818
|
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
656
|
-
// PascalCase for classes
|
|
819
|
+
// PascalCase for classes, types and interfaces
|
|
657
820
|
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
821
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
822
|
+
interfacePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
658
823
|
// UPPER_CASE for constants
|
|
659
824
|
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
660
825
|
// Common exceptions (React hooks, etc.)
|
|
@@ -865,6 +1030,101 @@ var TypeScriptParser = class {
|
|
|
865
1030
|
}
|
|
866
1031
|
};
|
|
867
1032
|
|
|
1033
|
+
// src/parsers/metadata-utils.ts
|
|
1034
|
+
function analyzeNodeMetadata(node, code, options) {
|
|
1035
|
+
const metadata = {
|
|
1036
|
+
isPure: true,
|
|
1037
|
+
hasSideEffects: false
|
|
1038
|
+
};
|
|
1039
|
+
try {
|
|
1040
|
+
let prev = node.previousSibling || null;
|
|
1041
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1042
|
+
const text = prev.text || "";
|
|
1043
|
+
if (text.trim().startsWith("/**") || text.trim().startsWith("/*")) {
|
|
1044
|
+
metadata.documentation = {
|
|
1045
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1046
|
+
type: "comment"
|
|
1047
|
+
};
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1050
|
+
if (text.trim().startsWith("///")) {
|
|
1051
|
+
metadata.documentation = {
|
|
1052
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1053
|
+
type: "xml-doc"
|
|
1054
|
+
};
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
1057
|
+
if (text.trim().startsWith("//")) {
|
|
1058
|
+
metadata.documentation = {
|
|
1059
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1060
|
+
type: "comment"
|
|
1061
|
+
};
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
prev = prev.previousSibling;
|
|
1065
|
+
}
|
|
1066
|
+
if (node.type === "function_definition") {
|
|
1067
|
+
const body2 = node.childForFieldName ? node.childForFieldName("body") : node.children.find((c) => c.type === "block");
|
|
1068
|
+
if (body2 && body2.children.length > 0) {
|
|
1069
|
+
const firstStmt = body2.children[0];
|
|
1070
|
+
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
1071
|
+
metadata.documentation = {
|
|
1072
|
+
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
1073
|
+
type: "docstring"
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
} catch {
|
|
1079
|
+
}
|
|
1080
|
+
const defaultSignatures = [
|
|
1081
|
+
"console.",
|
|
1082
|
+
"fmt.",
|
|
1083
|
+
"panic(",
|
|
1084
|
+
"os.Exit",
|
|
1085
|
+
"log.",
|
|
1086
|
+
"Console.Write",
|
|
1087
|
+
"File.Write",
|
|
1088
|
+
"System.out",
|
|
1089
|
+
"System.err",
|
|
1090
|
+
"Files.write",
|
|
1091
|
+
"process.exit",
|
|
1092
|
+
"exit("
|
|
1093
|
+
];
|
|
1094
|
+
const signatures = Array.from(
|
|
1095
|
+
/* @__PURE__ */ new Set([...options?.sideEffectSignatures || [], ...defaultSignatures])
|
|
1096
|
+
);
|
|
1097
|
+
const walk = (n) => {
|
|
1098
|
+
try {
|
|
1099
|
+
const t = n.type || "";
|
|
1100
|
+
if (/assign|assignment|assignment_statement|assignment_expression|throw|throw_statement|send_statement|global_statement|nonlocal_statement/i.test(
|
|
1101
|
+
t
|
|
1102
|
+
)) {
|
|
1103
|
+
metadata.isPure = false;
|
|
1104
|
+
metadata.hasSideEffects = true;
|
|
1105
|
+
}
|
|
1106
|
+
const text = n.text || "";
|
|
1107
|
+
for (const s of signatures) {
|
|
1108
|
+
if (text.includes(s)) {
|
|
1109
|
+
metadata.isPure = false;
|
|
1110
|
+
metadata.hasSideEffects = true;
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1115
|
+
const c = n.child(i);
|
|
1116
|
+
if (c) walk(c);
|
|
1117
|
+
}
|
|
1118
|
+
} catch {
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
const body = node.childForFieldName?.("body") || node.children.find(
|
|
1122
|
+
(c) => /body|block|class_body|declaration_list|function_body/.test(c.type)
|
|
1123
|
+
);
|
|
1124
|
+
if (body) walk(body);
|
|
1125
|
+
return metadata;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
868
1128
|
// src/parsers/tree-sitter-utils.ts
|
|
869
1129
|
import * as Parser from "web-tree-sitter";
|
|
870
1130
|
import * as path from "path";
|
|
@@ -953,11 +1213,9 @@ async function setupParser(language) {
|
|
|
953
1213
|
}
|
|
954
1214
|
}
|
|
955
1215
|
|
|
956
|
-
// src/parsers/
|
|
957
|
-
var
|
|
1216
|
+
// src/parsers/base-parser.ts
|
|
1217
|
+
var BaseLanguageParser = class {
|
|
958
1218
|
constructor() {
|
|
959
|
-
this.language = "python" /* Python */;
|
|
960
|
-
this.extensions = [".py"];
|
|
961
1219
|
this.parser = null;
|
|
962
1220
|
this.initialized = false;
|
|
963
1221
|
}
|
|
@@ -966,48 +1224,18 @@ var PythonParser = class {
|
|
|
966
1224
|
*/
|
|
967
1225
|
async initialize() {
|
|
968
1226
|
if (this.initialized) return;
|
|
969
|
-
|
|
970
|
-
|
|
1227
|
+
try {
|
|
1228
|
+
this.parser = await setupParser(this.getParserName());
|
|
1229
|
+
this.initialized = true;
|
|
1230
|
+
} catch (error) {
|
|
1231
|
+
console.warn(`Failed to initialize ${this.language} parser:`, error);
|
|
1232
|
+
}
|
|
971
1233
|
}
|
|
972
1234
|
async getAST(code, filePath) {
|
|
973
1235
|
if (!this.initialized) await this.initialize();
|
|
974
1236
|
if (!this.parser) return null;
|
|
975
1237
|
return this.parser.parse(code);
|
|
976
1238
|
}
|
|
977
|
-
analyzeMetadata(node, code) {
|
|
978
|
-
const metadata = {
|
|
979
|
-
isPure: true,
|
|
980
|
-
hasSideEffects: false
|
|
981
|
-
};
|
|
982
|
-
const body = node.childForFieldName("body");
|
|
983
|
-
if (body && body.children.length > 0) {
|
|
984
|
-
const firstStmt = body.children[0];
|
|
985
|
-
if (firstStmt.type === "expression_statement" && firstStmt.firstChild?.type === "string") {
|
|
986
|
-
metadata.documentation = {
|
|
987
|
-
content: firstStmt.firstChild.text.replace(/['"`]/g, "").trim(),
|
|
988
|
-
type: "docstring"
|
|
989
|
-
};
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const walk = (n) => {
|
|
993
|
-
if (n.type === "global_statement" || n.type === "nonlocal_statement") {
|
|
994
|
-
metadata.isPure = false;
|
|
995
|
-
metadata.hasSideEffects = true;
|
|
996
|
-
}
|
|
997
|
-
if (n.type === "call") {
|
|
998
|
-
const functionNode = n.childForFieldName("function");
|
|
999
|
-
if (functionNode && ["print", "input", "open"].includes(functionNode.text)) {
|
|
1000
|
-
metadata.isPure = false;
|
|
1001
|
-
metadata.hasSideEffects = true;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
for (const child of n.children) {
|
|
1005
|
-
walk(child);
|
|
1006
|
-
}
|
|
1007
|
-
};
|
|
1008
|
-
if (body) walk(body);
|
|
1009
|
-
return metadata;
|
|
1010
|
-
}
|
|
1011
1239
|
parse(code, filePath) {
|
|
1012
1240
|
if (!this.initialized || !this.parser) {
|
|
1013
1241
|
return this.parseRegex(code, filePath);
|
|
@@ -1017,19 +1245,42 @@ var PythonParser = class {
|
|
|
1017
1245
|
if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
|
|
1018
1246
|
return this.parseRegex(code, filePath);
|
|
1019
1247
|
}
|
|
1020
|
-
const
|
|
1021
|
-
const
|
|
1022
|
-
const exports = this.extractExportsAST(rootNode, code);
|
|
1248
|
+
const imports = this.extractImportsAST(tree.rootNode);
|
|
1249
|
+
const exports = this.extractExportsAST(tree.rootNode, code);
|
|
1023
1250
|
return {
|
|
1024
1251
|
exports,
|
|
1025
1252
|
imports,
|
|
1026
|
-
language:
|
|
1253
|
+
language: this.language,
|
|
1027
1254
|
warnings: []
|
|
1028
1255
|
};
|
|
1029
1256
|
} catch (error) {
|
|
1257
|
+
console.warn(
|
|
1258
|
+
`AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
|
|
1259
|
+
);
|
|
1030
1260
|
return this.parseRegex(code, filePath);
|
|
1031
1261
|
}
|
|
1032
1262
|
}
|
|
1263
|
+
canHandle(filePath) {
|
|
1264
|
+
const lowerPath = filePath.toLowerCase();
|
|
1265
|
+
return this.extensions.some((ext) => lowerPath.endsWith(ext));
|
|
1266
|
+
}
|
|
1267
|
+
};
|
|
1268
|
+
|
|
1269
|
+
// src/parsers/python-parser.ts
|
|
1270
|
+
var PythonParser = class extends BaseLanguageParser {
|
|
1271
|
+
constructor() {
|
|
1272
|
+
super(...arguments);
|
|
1273
|
+
this.language = "python" /* Python */;
|
|
1274
|
+
this.extensions = [".py"];
|
|
1275
|
+
}
|
|
1276
|
+
getParserName() {
|
|
1277
|
+
return "python";
|
|
1278
|
+
}
|
|
1279
|
+
analyzeMetadata(node, code) {
|
|
1280
|
+
return analyzeNodeMetadata(node, code, {
|
|
1281
|
+
sideEffectSignatures: ["print(", "input(", "open("]
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1033
1284
|
extractImportsAST(rootNode) {
|
|
1034
1285
|
const imports = [];
|
|
1035
1286
|
const processImportNode = (node) => {
|
|
@@ -1355,94 +1606,132 @@ var PythonParser = class {
|
|
|
1355
1606
|
}
|
|
1356
1607
|
};
|
|
1357
1608
|
|
|
1358
|
-
// src/parsers/
|
|
1359
|
-
var
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1609
|
+
// src/parsers/shared-parser-utils.ts
|
|
1610
|
+
var SIDE_EFFECT_KEYWORDS = [
|
|
1611
|
+
"print(",
|
|
1612
|
+
"console.",
|
|
1613
|
+
"System.out",
|
|
1614
|
+
"System.err",
|
|
1615
|
+
"fmt.",
|
|
1616
|
+
"File.Write",
|
|
1617
|
+
"Files.write",
|
|
1618
|
+
"os.Exit",
|
|
1619
|
+
"panic(",
|
|
1620
|
+
"throw ",
|
|
1621
|
+
"Logging.",
|
|
1622
|
+
"log."
|
|
1623
|
+
];
|
|
1624
|
+
function analyzeGeneralMetadata(node, code, options = {}) {
|
|
1625
|
+
const metadata = {
|
|
1626
|
+
isPure: true,
|
|
1627
|
+
hasSideEffects: false
|
|
1628
|
+
};
|
|
1629
|
+
try {
|
|
1630
|
+
let prev = node.previousSibling || null;
|
|
1631
|
+
while (prev && /comment/i.test(prev.type)) {
|
|
1632
|
+
const text = prev.text || "";
|
|
1633
|
+
if (text.trim().startsWith("/**")) {
|
|
1634
|
+
metadata.documentation = {
|
|
1635
|
+
content: text.replace(/^[/*]+|[/*]+$/g, "").trim(),
|
|
1636
|
+
type: "jsdoc"
|
|
1637
|
+
};
|
|
1638
|
+
break;
|
|
1639
|
+
}
|
|
1640
|
+
if (text.trim().startsWith("///")) {
|
|
1387
1641
|
metadata.documentation = {
|
|
1388
|
-
content:
|
|
1642
|
+
content: text.replace(/^\/\/\//, "").trim(),
|
|
1389
1643
|
type: "xml-doc"
|
|
1390
|
-
|
|
1644
|
+
};
|
|
1645
|
+
break;
|
|
1646
|
+
}
|
|
1647
|
+
if (text.trim().startsWith("//")) {
|
|
1648
|
+
metadata.documentation = {
|
|
1649
|
+
content: text.replace(/^\/\//, "").trim(),
|
|
1650
|
+
type: "comment"
|
|
1391
1651
|
};
|
|
1392
1652
|
break;
|
|
1393
1653
|
}
|
|
1394
1654
|
prev = prev.previousSibling;
|
|
1395
1655
|
}
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1656
|
+
} catch {
|
|
1657
|
+
}
|
|
1658
|
+
const signatures = [
|
|
1659
|
+
...SIDE_EFFECT_KEYWORDS,
|
|
1660
|
+
...options.sideEffectSignatures || []
|
|
1661
|
+
];
|
|
1662
|
+
const walk = (n) => {
|
|
1663
|
+
if (/assign|assignment|assignment_statement|assignment_expression/i.test(
|
|
1664
|
+
n.type
|
|
1665
|
+
)) {
|
|
1666
|
+
metadata.isPure = false;
|
|
1667
|
+
metadata.hasSideEffects = true;
|
|
1668
|
+
}
|
|
1669
|
+
const text = n.text;
|
|
1670
|
+
for (const sig of signatures) {
|
|
1671
|
+
if (text.includes(sig)) {
|
|
1409
1672
|
metadata.isPure = false;
|
|
1410
1673
|
metadata.hasSideEffects = true;
|
|
1674
|
+
break;
|
|
1411
1675
|
}
|
|
1412
|
-
for (const child of n.children) {
|
|
1413
|
-
walk(child);
|
|
1414
|
-
}
|
|
1415
|
-
};
|
|
1416
|
-
const body = node.children.find(
|
|
1417
|
-
(c) => c.type === "block" || c.type === "class_body"
|
|
1418
|
-
);
|
|
1419
|
-
if (body) walk(body);
|
|
1420
|
-
return metadata;
|
|
1421
|
-
}
|
|
1422
|
-
parse(code, filePath) {
|
|
1423
|
-
if (!this.initialized || !this.parser) {
|
|
1424
|
-
return this.parseRegex(code, filePath);
|
|
1425
1676
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1677
|
+
if (!metadata.hasSideEffects) {
|
|
1678
|
+
for (let i = 0; i < n.childCount; i++) {
|
|
1679
|
+
const child = n.child(i);
|
|
1680
|
+
if (child) walk(child);
|
|
1430
1681
|
}
|
|
1431
|
-
const rootNode = tree.rootNode;
|
|
1432
|
-
const imports = this.extractImportsAST(rootNode);
|
|
1433
|
-
const exports = this.extractExportsAST(rootNode, code);
|
|
1434
|
-
return {
|
|
1435
|
-
exports,
|
|
1436
|
-
imports,
|
|
1437
|
-
language: "java" /* Java */,
|
|
1438
|
-
warnings: []
|
|
1439
|
-
};
|
|
1440
|
-
} catch (error) {
|
|
1441
|
-
console.warn(
|
|
1442
|
-
`AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
|
|
1443
|
-
);
|
|
1444
|
-
return this.parseRegex(code, filePath);
|
|
1445
1682
|
}
|
|
1683
|
+
};
|
|
1684
|
+
walk(node);
|
|
1685
|
+
return metadata;
|
|
1686
|
+
}
|
|
1687
|
+
function extractParameterNames(node) {
|
|
1688
|
+
const params = [];
|
|
1689
|
+
const candidates = [
|
|
1690
|
+
// common field name
|
|
1691
|
+
node.childForFieldName ? node.childForFieldName("parameters") : null,
|
|
1692
|
+
node.childForFieldName ? node.childForFieldName("parameter_list") : null,
|
|
1693
|
+
node.children.find((c) => c.type === "parameter_list") || null,
|
|
1694
|
+
node.children.find((c) => c.type === "parameters") || null,
|
|
1695
|
+
node.children.find((c) => c.type === "formal_parameters") || null,
|
|
1696
|
+
node.children.find((c) => c.type === "formal_parameter") || null
|
|
1697
|
+
];
|
|
1698
|
+
const list = candidates.find(Boolean);
|
|
1699
|
+
if (!list) return params;
|
|
1700
|
+
for (const child of list.children) {
|
|
1701
|
+
if (!child) continue;
|
|
1702
|
+
const id = child.childForFieldName?.("name") || child.children.find(
|
|
1703
|
+
(c) => [
|
|
1704
|
+
"identifier",
|
|
1705
|
+
"variable_name",
|
|
1706
|
+
"name",
|
|
1707
|
+
"parameter",
|
|
1708
|
+
"formal_parameter"
|
|
1709
|
+
].includes(c.type)
|
|
1710
|
+
) || (child.type === "identifier" ? child : void 0);
|
|
1711
|
+
if (id && typeof id.text === "string") params.push(id.text);
|
|
1712
|
+
}
|
|
1713
|
+
return params;
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1716
|
+
// src/parsers/java-parser.ts
|
|
1717
|
+
var JavaParser = class extends BaseLanguageParser {
|
|
1718
|
+
constructor() {
|
|
1719
|
+
super(...arguments);
|
|
1720
|
+
this.language = "java" /* Java */;
|
|
1721
|
+
this.extensions = [".java"];
|
|
1722
|
+
}
|
|
1723
|
+
getParserName() {
|
|
1724
|
+
return "java";
|
|
1725
|
+
}
|
|
1726
|
+
analyzeMetadata(node, code) {
|
|
1727
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1728
|
+
sideEffectSignatures: [
|
|
1729
|
+
"System.out",
|
|
1730
|
+
"System.err",
|
|
1731
|
+
"Files.write",
|
|
1732
|
+
"Logging."
|
|
1733
|
+
]
|
|
1734
|
+
});
|
|
1446
1735
|
}
|
|
1447
1736
|
parseRegex(code, filePath) {
|
|
1448
1737
|
const lines = code.split("\n");
|
|
@@ -1517,7 +1806,7 @@ var JavaParser = class {
|
|
|
1517
1806
|
const imports = [];
|
|
1518
1807
|
for (const node of rootNode.children) {
|
|
1519
1808
|
if (node.type === "import_declaration") {
|
|
1520
|
-
|
|
1809
|
+
const sourceArr = [];
|
|
1521
1810
|
let isStatic = false;
|
|
1522
1811
|
let isWildcard = false;
|
|
1523
1812
|
for (const child of node.children) {
|
|
@@ -1615,14 +1904,7 @@ var JavaParser = class {
|
|
|
1615
1904
|
}
|
|
1616
1905
|
}
|
|
1617
1906
|
extractParameters(node) {
|
|
1618
|
-
|
|
1619
|
-
(c) => c.type === "formal_parameters"
|
|
1620
|
-
);
|
|
1621
|
-
if (!paramsNode) return [];
|
|
1622
|
-
return paramsNode.children.filter((c) => c.type === "formal_parameter").map((c) => {
|
|
1623
|
-
const idNode = c.children.find((child) => child.type === "identifier");
|
|
1624
|
-
return idNode ? idNode.text : "unknown";
|
|
1625
|
-
});
|
|
1907
|
+
return extractParameterNames(node);
|
|
1626
1908
|
}
|
|
1627
1909
|
getNamingConventions() {
|
|
1628
1910
|
return {
|
|
@@ -1639,91 +1921,19 @@ var JavaParser = class {
|
|
|
1639
1921
|
};
|
|
1640
1922
|
|
|
1641
1923
|
// src/parsers/csharp-parser.ts
|
|
1642
|
-
var CSharpParser = class {
|
|
1924
|
+
var CSharpParser = class extends BaseLanguageParser {
|
|
1643
1925
|
constructor() {
|
|
1926
|
+
super(...arguments);
|
|
1644
1927
|
this.language = "csharp" /* CSharp */;
|
|
1645
1928
|
this.extensions = [".cs"];
|
|
1646
|
-
this.parser = null;
|
|
1647
|
-
this.initialized = false;
|
|
1648
1929
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
*/
|
|
1652
|
-
async initialize() {
|
|
1653
|
-
if (this.initialized) return;
|
|
1654
|
-
this.parser = await setupParser("c_sharp");
|
|
1655
|
-
this.initialized = true;
|
|
1656
|
-
}
|
|
1657
|
-
async getAST(code, filePath) {
|
|
1658
|
-
if (!this.initialized) await this.initialize();
|
|
1659
|
-
if (!this.parser) return null;
|
|
1660
|
-
return this.parser.parse(code);
|
|
1930
|
+
getParserName() {
|
|
1931
|
+
return "c_sharp";
|
|
1661
1932
|
}
|
|
1662
1933
|
analyzeMetadata(node, code) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
};
|
|
1667
|
-
let prev = node.previousSibling;
|
|
1668
|
-
while (prev && (prev.type === "comment" || prev.type === "triple_slash_comment")) {
|
|
1669
|
-
if (prev.text.trim().startsWith("///") || prev.type === "triple_slash_comment") {
|
|
1670
|
-
metadata.documentation = {
|
|
1671
|
-
content: prev.text.replace("///", "").trim(),
|
|
1672
|
-
type: "xml-doc"
|
|
1673
|
-
};
|
|
1674
|
-
break;
|
|
1675
|
-
}
|
|
1676
|
-
prev = prev.previousSibling;
|
|
1677
|
-
}
|
|
1678
|
-
const walk = (n) => {
|
|
1679
|
-
if (n.type === "assignment_expression") {
|
|
1680
|
-
metadata.isPure = false;
|
|
1681
|
-
metadata.hasSideEffects = true;
|
|
1682
|
-
}
|
|
1683
|
-
if (n.type === "invocation_expression") {
|
|
1684
|
-
const text = n.text;
|
|
1685
|
-
if (text.includes("Console.Write") || text.includes("File.Write") || text.includes("Log.")) {
|
|
1686
|
-
metadata.isPure = false;
|
|
1687
|
-
metadata.hasSideEffects = true;
|
|
1688
|
-
}
|
|
1689
|
-
}
|
|
1690
|
-
if (n.type === "throw_statement") {
|
|
1691
|
-
metadata.isPure = false;
|
|
1692
|
-
metadata.hasSideEffects = true;
|
|
1693
|
-
}
|
|
1694
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1695
|
-
const child = n.child(i);
|
|
1696
|
-
if (child) walk(child);
|
|
1697
|
-
}
|
|
1698
|
-
};
|
|
1699
|
-
const body = node.children.find(
|
|
1700
|
-
(c) => c.type === "block" || c.type === "declaration_list"
|
|
1701
|
-
);
|
|
1702
|
-
if (body) walk(body);
|
|
1703
|
-
return metadata;
|
|
1704
|
-
}
|
|
1705
|
-
parse(code, filePath) {
|
|
1706
|
-
if (!this.initialized || !this.parser) {
|
|
1707
|
-
return this.parseRegex(code, filePath);
|
|
1708
|
-
}
|
|
1709
|
-
try {
|
|
1710
|
-
const tree = this.parser.parse(code);
|
|
1711
|
-
if (!tree) throw new Error("Parser.parse(code) returned null");
|
|
1712
|
-
const rootNode = tree.rootNode;
|
|
1713
|
-
const imports = this.extractImportsAST(rootNode);
|
|
1714
|
-
const exports = this.extractExportsAST(rootNode, code);
|
|
1715
|
-
return {
|
|
1716
|
-
exports,
|
|
1717
|
-
imports,
|
|
1718
|
-
language: "csharp" /* CSharp */,
|
|
1719
|
-
warnings: []
|
|
1720
|
-
};
|
|
1721
|
-
} catch (error) {
|
|
1722
|
-
console.warn(
|
|
1723
|
-
`AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
|
|
1724
|
-
);
|
|
1725
|
-
return this.parseRegex(code, filePath);
|
|
1726
|
-
}
|
|
1934
|
+
return analyzeGeneralMetadata(node, code, {
|
|
1935
|
+
sideEffectSignatures: ["Console.Write", "File.Write", "Logging."]
|
|
1936
|
+
});
|
|
1727
1937
|
}
|
|
1728
1938
|
parseRegex(code, filePath) {
|
|
1729
1939
|
const lines = code.split("\n");
|
|
@@ -1905,19 +2115,7 @@ var CSharpParser = class {
|
|
|
1905
2115
|
return modifiers;
|
|
1906
2116
|
}
|
|
1907
2117
|
extractParameters(node) {
|
|
1908
|
-
|
|
1909
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
1910
|
-
if (parameterList) {
|
|
1911
|
-
for (const param of parameterList.children) {
|
|
1912
|
-
if (param.type === "parameter") {
|
|
1913
|
-
const nameNode = param.childForFieldName("name") || param.children.find((c) => c.type === "identifier");
|
|
1914
|
-
if (nameNode) {
|
|
1915
|
-
params.push(nameNode.text);
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
}
|
|
1920
|
-
return params;
|
|
2118
|
+
return extractParameterNames(node);
|
|
1921
2119
|
}
|
|
1922
2120
|
getNamingConventions() {
|
|
1923
2121
|
return {
|
|
@@ -1933,86 +2131,19 @@ var CSharpParser = class {
|
|
|
1933
2131
|
};
|
|
1934
2132
|
|
|
1935
2133
|
// src/parsers/go-parser.ts
|
|
1936
|
-
var GoParser = class {
|
|
2134
|
+
var GoParser = class extends BaseLanguageParser {
|
|
1937
2135
|
constructor() {
|
|
2136
|
+
super(...arguments);
|
|
1938
2137
|
this.language = "go" /* Go */;
|
|
1939
2138
|
this.extensions = [".go"];
|
|
1940
|
-
this.parser = null;
|
|
1941
|
-
this.initialized = false;
|
|
1942
2139
|
}
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
*/
|
|
1946
|
-
async initialize() {
|
|
1947
|
-
if (this.initialized) return;
|
|
1948
|
-
this.parser = await setupParser("go");
|
|
1949
|
-
this.initialized = true;
|
|
1950
|
-
}
|
|
1951
|
-
async getAST(code, filePath) {
|
|
1952
|
-
if (!this.initialized) await this.initialize();
|
|
1953
|
-
if (!this.parser) return null;
|
|
1954
|
-
return this.parser.parse(code);
|
|
2140
|
+
getParserName() {
|
|
2141
|
+
return "go";
|
|
1955
2142
|
}
|
|
1956
2143
|
analyzeMetadata(node, code) {
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
};
|
|
1961
|
-
let prev = node.previousSibling;
|
|
1962
|
-
while (prev && prev.type === "comment") {
|
|
1963
|
-
metadata.documentation = {
|
|
1964
|
-
content: prev.text.replace(/\/\/|\/\*|\*\//g, "").trim(),
|
|
1965
|
-
type: "comment"
|
|
1966
|
-
};
|
|
1967
|
-
break;
|
|
1968
|
-
}
|
|
1969
|
-
const walk = (n) => {
|
|
1970
|
-
if (n.type === "send_statement" || n.type === "expression_statement" && n.text.includes("<-")) {
|
|
1971
|
-
metadata.isPure = false;
|
|
1972
|
-
metadata.hasSideEffects = true;
|
|
1973
|
-
}
|
|
1974
|
-
if (n.type === "assignment_statement" || n.type === "short_var_declaration") {
|
|
1975
|
-
}
|
|
1976
|
-
if (n.type === "call_expression") {
|
|
1977
|
-
const text = n.text;
|
|
1978
|
-
if (text.includes("fmt.Print") || text.includes("os.Exit") || text.includes("panic(") || text.includes("log.")) {
|
|
1979
|
-
metadata.isPure = false;
|
|
1980
|
-
metadata.hasSideEffects = true;
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
for (let i = 0; i < n.childCount; i++) {
|
|
1984
|
-
const child = n.child(i);
|
|
1985
|
-
if (child) walk(child);
|
|
1986
|
-
}
|
|
1987
|
-
};
|
|
1988
|
-
const body = node.childForFieldName("body");
|
|
1989
|
-
if (body) walk(body);
|
|
1990
|
-
return metadata;
|
|
1991
|
-
}
|
|
1992
|
-
parse(code, filePath) {
|
|
1993
|
-
if (!this.initialized || !this.parser) {
|
|
1994
|
-
return this.parseRegex(code, filePath);
|
|
1995
|
-
}
|
|
1996
|
-
try {
|
|
1997
|
-
const tree = this.parser.parse(code);
|
|
1998
|
-
if (!tree || tree.rootNode.type === "ERROR" || tree.rootNode.hasError) {
|
|
1999
|
-
return this.parseRegex(code, filePath);
|
|
2000
|
-
}
|
|
2001
|
-
const rootNode = tree.rootNode;
|
|
2002
|
-
const imports = this.extractImportsAST(rootNode);
|
|
2003
|
-
const exports = this.extractExportsAST(rootNode, code);
|
|
2004
|
-
return {
|
|
2005
|
-
exports,
|
|
2006
|
-
imports,
|
|
2007
|
-
language: "go" /* Go */,
|
|
2008
|
-
warnings: []
|
|
2009
|
-
};
|
|
2010
|
-
} catch (error) {
|
|
2011
|
-
console.warn(
|
|
2012
|
-
`AST parsing failed for ${filePath}, falling back to regex: ${error.message}`
|
|
2013
|
-
);
|
|
2014
|
-
return this.parseRegex(code, filePath);
|
|
2015
|
-
}
|
|
2144
|
+
return analyzeGeneralMetadata(node, code, {
|
|
2145
|
+
sideEffectSignatures: ["<-", "fmt.Print", "fmt.Fprintf", "os.Exit"]
|
|
2146
|
+
});
|
|
2016
2147
|
}
|
|
2017
2148
|
parseRegex(code, filePath) {
|
|
2018
2149
|
const lines = code.split("\n");
|
|
@@ -2209,17 +2340,7 @@ var GoParser = class {
|
|
|
2209
2340
|
return exports;
|
|
2210
2341
|
}
|
|
2211
2342
|
extractParameters(node) {
|
|
2212
|
-
|
|
2213
|
-
const parameterList = node.childForFieldName("parameters") || node.children.find((c) => c.type === "parameter_list");
|
|
2214
|
-
if (parameterList) {
|
|
2215
|
-
for (const param of parameterList.children) {
|
|
2216
|
-
if (param.type === "parameter_declaration") {
|
|
2217
|
-
const names = param.children.filter((c) => c.type === "identifier");
|
|
2218
|
-
names.forEach((n) => params.push(n.text));
|
|
2219
|
-
}
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
return params;
|
|
2343
|
+
return extractParameterNames(node);
|
|
2223
2344
|
}
|
|
2224
2345
|
getNamingConventions() {
|
|
2225
2346
|
return {
|
|
@@ -2350,48 +2471,7 @@ function getSupportedLanguages() {
|
|
|
2350
2471
|
return ParserFactory.getInstance().getSupportedLanguages();
|
|
2351
2472
|
}
|
|
2352
2473
|
|
|
2353
|
-
// src/utils/ast-
|
|
2354
|
-
function parseFileExports(code, filePath) {
|
|
2355
|
-
const parser = getParser(filePath);
|
|
2356
|
-
if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
|
|
2357
|
-
try {
|
|
2358
|
-
const result = parser.parse(code, filePath);
|
|
2359
|
-
return {
|
|
2360
|
-
exports: result.exports.map((e) => ({
|
|
2361
|
-
name: e.name,
|
|
2362
|
-
type: e.type,
|
|
2363
|
-
imports: e.imports || [],
|
|
2364
|
-
dependencies: e.dependencies || [],
|
|
2365
|
-
typeReferences: e.typeReferences || [],
|
|
2366
|
-
loc: e.loc ? {
|
|
2367
|
-
start: { line: e.loc.start.line, column: e.loc.start.column },
|
|
2368
|
-
end: { line: e.loc.end.line, column: e.loc.end.column }
|
|
2369
|
-
} : void 0
|
|
2370
|
-
})),
|
|
2371
|
-
imports: result.imports.map((i) => ({
|
|
2372
|
-
source: i.source,
|
|
2373
|
-
specifiers: i.specifiers,
|
|
2374
|
-
isTypeOnly: i.isTypeOnly || false
|
|
2375
|
-
}))
|
|
2376
|
-
};
|
|
2377
|
-
} catch (e) {
|
|
2378
|
-
return { exports: [], imports: [] };
|
|
2379
|
-
}
|
|
2380
|
-
}
|
|
2381
|
-
try {
|
|
2382
|
-
const ast = parse2(code, {
|
|
2383
|
-
loc: true,
|
|
2384
|
-
range: true,
|
|
2385
|
-
jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
|
|
2386
|
-
filePath
|
|
2387
|
-
});
|
|
2388
|
-
const imports = extractFileImports(ast);
|
|
2389
|
-
const exports = extractExportsWithDependencies(ast, imports);
|
|
2390
|
-
return { exports, imports };
|
|
2391
|
-
} catch (error) {
|
|
2392
|
-
return { exports: [], imports: [] };
|
|
2393
|
-
}
|
|
2394
|
-
}
|
|
2474
|
+
// src/utils/ast-visitor.ts
|
|
2395
2475
|
function extractFileImports(ast) {
|
|
2396
2476
|
const imports = [];
|
|
2397
2477
|
for (const node of ast.body) {
|
|
@@ -2449,22 +2529,23 @@ function extractExportsWithDependencies(ast, fileImports) {
|
|
|
2449
2529
|
}
|
|
2450
2530
|
return exports;
|
|
2451
2531
|
}
|
|
2452
|
-
function extractFromDeclaration(
|
|
2532
|
+
function extractFromDeclaration(node) {
|
|
2533
|
+
if (!node) return [];
|
|
2453
2534
|
const results = [];
|
|
2454
|
-
if (
|
|
2455
|
-
results.push({ name:
|
|
2456
|
-
} else if (
|
|
2457
|
-
results.push({ name:
|
|
2458
|
-
} else if (
|
|
2459
|
-
for (const
|
|
2460
|
-
if (
|
|
2461
|
-
results.push({ name:
|
|
2535
|
+
if (node.type === "FunctionDeclaration" && node.id) {
|
|
2536
|
+
results.push({ name: node.id.name, type: "function" });
|
|
2537
|
+
} else if (node.type === "ClassDeclaration" && node.id) {
|
|
2538
|
+
results.push({ name: node.id.name, type: "class" });
|
|
2539
|
+
} else if (node.type === "VariableDeclaration") {
|
|
2540
|
+
for (const decl of node.declarations) {
|
|
2541
|
+
if (decl.id.type === "Identifier") {
|
|
2542
|
+
results.push({ name: decl.id.name, type: "const" });
|
|
2462
2543
|
}
|
|
2463
2544
|
}
|
|
2464
|
-
} else if (
|
|
2465
|
-
results.push({ name:
|
|
2466
|
-
} else if (
|
|
2467
|
-
results.push({ name:
|
|
2545
|
+
} else if (node.type === "TSInterfaceDeclaration" && node.id) {
|
|
2546
|
+
results.push({ name: node.id.name, type: "interface" });
|
|
2547
|
+
} else if (node.type === "TSTypeAliasDeclaration" && node.id) {
|
|
2548
|
+
results.push({ name: node.id.name, type: "type" });
|
|
2468
2549
|
}
|
|
2469
2550
|
return results;
|
|
2470
2551
|
}
|
|
@@ -2492,16 +2573,6 @@ function findUsedImports(node, importedNames) {
|
|
|
2492
2573
|
visit(node);
|
|
2493
2574
|
return Array.from(usedImports);
|
|
2494
2575
|
}
|
|
2495
|
-
function calculateImportSimilarity(export1, export2) {
|
|
2496
|
-
if (export1.imports.length === 0 && export2.imports.length === 0) {
|
|
2497
|
-
return 1;
|
|
2498
|
-
}
|
|
2499
|
-
const set1 = new Set(export1.imports);
|
|
2500
|
-
const set2 = new Set(export2.imports);
|
|
2501
|
-
const intersection = new Set([...set1].filter((x) => set2.has(x)));
|
|
2502
|
-
const union = /* @__PURE__ */ new Set([...set1, ...set2]);
|
|
2503
|
-
return intersection.size / union.size;
|
|
2504
|
-
}
|
|
2505
2576
|
function extractTypeReferences(node) {
|
|
2506
2577
|
const types = /* @__PURE__ */ new Set();
|
|
2507
2578
|
function visit(n) {
|
|
@@ -2539,6 +2610,59 @@ function extractTypeReferences(node) {
|
|
|
2539
2610
|
visit(node);
|
|
2540
2611
|
return Array.from(types);
|
|
2541
2612
|
}
|
|
2613
|
+
|
|
2614
|
+
// src/utils/ast-parser.ts
|
|
2615
|
+
function parseFileExports(code, filePath) {
|
|
2616
|
+
const parser = getParser(filePath);
|
|
2617
|
+
if (parser && parser.language !== "typescript" /* TypeScript */ && parser.language !== "javascript" /* JavaScript */) {
|
|
2618
|
+
try {
|
|
2619
|
+
const result = parser.parse(code, filePath);
|
|
2620
|
+
return {
|
|
2621
|
+
exports: result.exports.map((e) => ({
|
|
2622
|
+
name: e.name,
|
|
2623
|
+
type: e.type,
|
|
2624
|
+
imports: e.imports || [],
|
|
2625
|
+
dependencies: e.dependencies || [],
|
|
2626
|
+
typeReferences: e.typeReferences || [],
|
|
2627
|
+
loc: e.loc ? {
|
|
2628
|
+
start: { line: e.loc.start.line, column: e.loc.start.column },
|
|
2629
|
+
end: { line: e.loc.end.line, column: e.loc.end.column }
|
|
2630
|
+
} : void 0
|
|
2631
|
+
})),
|
|
2632
|
+
imports: result.imports.map((i) => ({
|
|
2633
|
+
source: i.source,
|
|
2634
|
+
specifiers: i.specifiers,
|
|
2635
|
+
isTypeOnly: i.isTypeOnly || false
|
|
2636
|
+
}))
|
|
2637
|
+
};
|
|
2638
|
+
} catch (e) {
|
|
2639
|
+
return { exports: [], imports: [] };
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
try {
|
|
2643
|
+
const ast = parse2(code, {
|
|
2644
|
+
loc: true,
|
|
2645
|
+
range: true,
|
|
2646
|
+
jsx: filePath.endsWith(".tsx") || filePath.endsWith(".jsx"),
|
|
2647
|
+
filePath
|
|
2648
|
+
});
|
|
2649
|
+
const imports = extractFileImports(ast);
|
|
2650
|
+
const exports = extractExportsWithDependencies(ast, imports);
|
|
2651
|
+
return { exports, imports };
|
|
2652
|
+
} catch (error) {
|
|
2653
|
+
return { exports: [], imports: [] };
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
function calculateImportSimilarity(export1, export2) {
|
|
2657
|
+
if (export1.imports.length === 0 && export2.imports.length === 0) {
|
|
2658
|
+
return 1;
|
|
2659
|
+
}
|
|
2660
|
+
const set1 = new Set(export1.imports);
|
|
2661
|
+
const set2 = new Set(export2.imports);
|
|
2662
|
+
const intersection = new Set([...set1].filter((x) => set2.has(x)));
|
|
2663
|
+
const union = /* @__PURE__ */ new Set([...set1, ...set2]);
|
|
2664
|
+
return intersection.size / union.size;
|
|
2665
|
+
}
|
|
2542
2666
|
function parseCode(code, language) {
|
|
2543
2667
|
return null;
|
|
2544
2668
|
}
|
|
@@ -2569,34 +2693,47 @@ var CONFIG_FILES = [
|
|
|
2569
2693
|
async function loadConfig(rootDir) {
|
|
2570
2694
|
let currentDir = resolve(rootDir);
|
|
2571
2695
|
while (true) {
|
|
2696
|
+
const foundConfigs = [];
|
|
2572
2697
|
for (const configFile of CONFIG_FILES) {
|
|
2698
|
+
if (existsSync4(join4(currentDir, configFile))) {
|
|
2699
|
+
foundConfigs.push(configFile);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (foundConfigs.length > 0) {
|
|
2703
|
+
if (foundConfigs.length > 1) {
|
|
2704
|
+
console.warn(
|
|
2705
|
+
`\u26A0\uFE0F Multiple configuration files found in ${currentDir}: ${foundConfigs.join(
|
|
2706
|
+
", "
|
|
2707
|
+
)}. Using ${foundConfigs[0]}.`
|
|
2708
|
+
);
|
|
2709
|
+
} else {
|
|
2710
|
+
}
|
|
2711
|
+
const configFile = foundConfigs[0];
|
|
2573
2712
|
const configPath = join4(currentDir, configFile);
|
|
2574
|
-
|
|
2713
|
+
try {
|
|
2714
|
+
let config;
|
|
2715
|
+
if (configFile.endsWith(".js")) {
|
|
2716
|
+
const fileUrl = pathToFileURL(configPath).href;
|
|
2717
|
+
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2718
|
+
config = module.default || module;
|
|
2719
|
+
} else {
|
|
2720
|
+
const content = readFileSync(configPath, "utf-8");
|
|
2721
|
+
config = JSON.parse(content);
|
|
2722
|
+
}
|
|
2723
|
+
if (typeof config !== "object" || config === null) {
|
|
2724
|
+
throw new Error("Config must be an object");
|
|
2725
|
+
}
|
|
2726
|
+
return config;
|
|
2727
|
+
} catch (error) {
|
|
2728
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2729
|
+
const configError = new Error(
|
|
2730
|
+
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2731
|
+
);
|
|
2575
2732
|
try {
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
const fileUrl = pathToFileURL(configPath).href;
|
|
2579
|
-
const module = await import(`${fileUrl}?t=${Date.now()}`);
|
|
2580
|
-
config = module.default || module;
|
|
2581
|
-
} else {
|
|
2582
|
-
const content = readFileSync(configPath, "utf-8");
|
|
2583
|
-
config = JSON.parse(content);
|
|
2584
|
-
}
|
|
2585
|
-
if (typeof config !== "object" || config === null) {
|
|
2586
|
-
throw new Error("Config must be an object");
|
|
2587
|
-
}
|
|
2588
|
-
return config;
|
|
2589
|
-
} catch (error) {
|
|
2590
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2591
|
-
const e = new Error(
|
|
2592
|
-
`Failed to load config from ${configPath}: ${errorMessage}`
|
|
2593
|
-
);
|
|
2594
|
-
try {
|
|
2595
|
-
e.cause = error instanceof Error ? error : void 0;
|
|
2596
|
-
} catch {
|
|
2597
|
-
}
|
|
2598
|
-
throw e;
|
|
2733
|
+
configError.cause = error instanceof Error ? error : void 0;
|
|
2734
|
+
} catch {
|
|
2599
2735
|
}
|
|
2736
|
+
throw configError;
|
|
2600
2737
|
}
|
|
2601
2738
|
}
|
|
2602
2739
|
const parent = dirname4(currentDir);
|
|
@@ -2609,28 +2746,28 @@ async function loadConfig(rootDir) {
|
|
|
2609
2746
|
}
|
|
2610
2747
|
function mergeConfigWithDefaults(userConfig, defaults) {
|
|
2611
2748
|
if (!userConfig) return defaults;
|
|
2612
|
-
const
|
|
2749
|
+
const mergedConfig = { ...defaults };
|
|
2613
2750
|
if (userConfig.scan) {
|
|
2614
|
-
if (userConfig.scan.include)
|
|
2615
|
-
if (userConfig.scan.exclude)
|
|
2751
|
+
if (userConfig.scan.include) mergedConfig.include = userConfig.scan.include;
|
|
2752
|
+
if (userConfig.scan.exclude) mergedConfig.exclude = userConfig.scan.exclude;
|
|
2616
2753
|
}
|
|
2617
2754
|
const toolOverrides = userConfig.tools && !Array.isArray(userConfig.tools) && typeof userConfig.tools === "object" ? userConfig.tools : userConfig.toolConfigs;
|
|
2618
2755
|
if (toolOverrides) {
|
|
2619
|
-
if (!
|
|
2756
|
+
if (!mergedConfig.toolConfigs) mergedConfig.toolConfigs = {};
|
|
2620
2757
|
for (const [toolName, toolConfig] of Object.entries(toolOverrides)) {
|
|
2621
2758
|
if (typeof toolConfig === "object" && toolConfig !== null) {
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
...
|
|
2759
|
+
mergedConfig[toolName] = { ...mergedConfig[toolName], ...toolConfig };
|
|
2760
|
+
mergedConfig.toolConfigs[toolName] = {
|
|
2761
|
+
...mergedConfig.toolConfigs[toolName],
|
|
2625
2762
|
...toolConfig
|
|
2626
2763
|
};
|
|
2627
2764
|
}
|
|
2628
2765
|
}
|
|
2629
2766
|
}
|
|
2630
2767
|
if (userConfig.output) {
|
|
2631
|
-
|
|
2768
|
+
mergedConfig.output = { ...mergedConfig.output, ...userConfig.output };
|
|
2632
2769
|
}
|
|
2633
|
-
return
|
|
2770
|
+
return mergedConfig;
|
|
2634
2771
|
}
|
|
2635
2772
|
|
|
2636
2773
|
// src/business/pricing-models.ts
|
|
@@ -2843,7 +2980,10 @@ function predictAcceptanceRate(toolOutputs) {
|
|
|
2843
2980
|
impact: Math.round((50 - aiSignalClarity.score) * 2e-3 * 100)
|
|
2844
2981
|
});
|
|
2845
2982
|
}
|
|
2846
|
-
const totalImpact = factors.reduce(
|
|
2983
|
+
const totalImpact = factors.reduce(
|
|
2984
|
+
(sum, f) => sum + f.impact / 100,
|
|
2985
|
+
0
|
|
2986
|
+
);
|
|
2847
2987
|
const rate = Math.max(0.05, Math.min(0.8, baseRate + totalImpact));
|
|
2848
2988
|
let confidence = 0.35;
|
|
2849
2989
|
if (toolOutputs.size >= 4) confidence = 0.75;
|
|
@@ -3086,6 +3226,24 @@ function collectFutureProofRecommendations(params) {
|
|
|
3086
3226
|
}
|
|
3087
3227
|
return recommendations;
|
|
3088
3228
|
}
|
|
3229
|
+
function collectBaseFutureProofRecommendations(params) {
|
|
3230
|
+
const recommendations = [];
|
|
3231
|
+
for (const rec of params.patternEntropy.recommendations) {
|
|
3232
|
+
recommendations.push({
|
|
3233
|
+
action: rec,
|
|
3234
|
+
estimatedImpact: 5,
|
|
3235
|
+
priority: "medium"
|
|
3236
|
+
});
|
|
3237
|
+
}
|
|
3238
|
+
if (params.conceptCohesion.rating === "poor") {
|
|
3239
|
+
recommendations.push({
|
|
3240
|
+
action: "Improve concept cohesion by grouping related exports",
|
|
3241
|
+
estimatedImpact: 8,
|
|
3242
|
+
priority: "high"
|
|
3243
|
+
});
|
|
3244
|
+
}
|
|
3245
|
+
return recommendations;
|
|
3246
|
+
}
|
|
3089
3247
|
|
|
3090
3248
|
// src/metrics/cognitive-load.ts
|
|
3091
3249
|
function calculateCognitiveLoad(params) {
|
|
@@ -3776,21 +3934,10 @@ function calculateFutureProofScore(params) {
|
|
|
3776
3934
|
description: params.conceptCohesion.rating
|
|
3777
3935
|
}
|
|
3778
3936
|
];
|
|
3779
|
-
const recommendations =
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
estimatedImpact: 5,
|
|
3784
|
-
priority: "medium"
|
|
3785
|
-
});
|
|
3786
|
-
}
|
|
3787
|
-
if (params.conceptCohesion.rating === "poor") {
|
|
3788
|
-
recommendations.push({
|
|
3789
|
-
action: "Improve concept cohesion by grouping related exports",
|
|
3790
|
-
estimatedImpact: 8,
|
|
3791
|
-
priority: "high"
|
|
3792
|
-
});
|
|
3793
|
-
}
|
|
3937
|
+
const recommendations = collectBaseFutureProofRecommendations({
|
|
3938
|
+
patternEntropy: params.patternEntropy,
|
|
3939
|
+
conceptCohesion: params.conceptCohesion
|
|
3940
|
+
});
|
|
3794
3941
|
const semanticDistanceAvg = params.semanticDistances?.length ? params.semanticDistances.reduce((s, d) => s + d.distance, 0) / params.semanticDistances.length : 0;
|
|
3795
3942
|
return {
|
|
3796
3943
|
toolName: "future-proof",
|
|
@@ -4122,6 +4269,8 @@ export {
|
|
|
4122
4269
|
TypeScriptParser,
|
|
4123
4270
|
UnifiedReportSchema,
|
|
4124
4271
|
VAGUE_FILE_NAMES,
|
|
4272
|
+
buildSimpleProviderScore,
|
|
4273
|
+
buildSpokeOutput,
|
|
4125
4274
|
calculateAgentGrounding,
|
|
4126
4275
|
calculateAiSignalClarity,
|
|
4127
4276
|
calculateBusinessROI,
|
|
@@ -4145,6 +4294,7 @@ export {
|
|
|
4145
4294
|
calculateTestabilityIndex,
|
|
4146
4295
|
calculateTokenBudget,
|
|
4147
4296
|
clearHistory,
|
|
4297
|
+
createProvider,
|
|
4148
4298
|
emitAnnotation,
|
|
4149
4299
|
emitIssuesAsAnnotations,
|
|
4150
4300
|
emitProgress,
|
|
@@ -4153,6 +4303,8 @@ export {
|
|
|
4153
4303
|
exportHistory,
|
|
4154
4304
|
extractFunctions,
|
|
4155
4305
|
extractImports,
|
|
4306
|
+
findLatestReport,
|
|
4307
|
+
findLatestScanReport,
|
|
4156
4308
|
formatAcceptanceRate,
|
|
4157
4309
|
formatCost,
|
|
4158
4310
|
formatHours,
|
|
@@ -4170,15 +4322,21 @@ export {
|
|
|
4170
4322
|
getProjectSizeTier,
|
|
4171
4323
|
getRating,
|
|
4172
4324
|
getRatingDisplay,
|
|
4325
|
+
getRatingSlug,
|
|
4173
4326
|
getRatingWithContext,
|
|
4174
4327
|
getRecommendedThreshold,
|
|
4175
4328
|
getRepoMetadata,
|
|
4176
4329
|
getSafetyIcon,
|
|
4177
4330
|
getScoreBar,
|
|
4331
|
+
getSeverityBadge,
|
|
4178
4332
|
getSeverityColor,
|
|
4333
|
+
getSeverityEnum,
|
|
4334
|
+
getSeverityLevel,
|
|
4335
|
+
getSeverityValue,
|
|
4179
4336
|
getSupportedLanguages,
|
|
4180
4337
|
getToolWeight,
|
|
4181
4338
|
getWasmPath,
|
|
4339
|
+
groupIssuesByFile,
|
|
4182
4340
|
handleCLIError,
|
|
4183
4341
|
handleJSONOutput,
|
|
4184
4342
|
initTreeSitter,
|