@aiready/context-analyzer 0.9.23 → 0.9.26
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/.turbo/turbo-build.log +11 -11
- package/.turbo/turbo-test.log +21 -24
- package/dist/chunk-HOUDVRG2.mjs +1422 -0
- package/dist/chunk-PJD4VCIH.mjs +1722 -0
- package/dist/chunk-XZ645X5U.mjs +1425 -0
- package/dist/cli.js +368 -8
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +368 -8
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/src/__tests__/file-classification.test.ts +596 -10
- package/src/analyzer.ts +651 -8
- package/src/index.ts +11 -3
- package/src/types.ts +6 -0
package/dist/index.js
CHANGED
|
@@ -966,24 +966,50 @@ function calculateDomainCohesion(exports2) {
|
|
|
966
966
|
return maxEntropy > 0 ? 1 - entropy / maxEntropy : 1;
|
|
967
967
|
}
|
|
968
968
|
function classifyFile(node, cohesionScore, domains) {
|
|
969
|
-
const { exports: exports2, imports, linesOfCode } = node;
|
|
969
|
+
const { exports: exports2, imports, linesOfCode, file } = node;
|
|
970
970
|
if (isBarrelExport(node)) {
|
|
971
971
|
return "barrel-export";
|
|
972
972
|
}
|
|
973
973
|
if (isTypeDefinitionFile(node)) {
|
|
974
974
|
return "type-definition";
|
|
975
975
|
}
|
|
976
|
+
if (isConfigOrSchemaFile(node)) {
|
|
977
|
+
return "cohesive-module";
|
|
978
|
+
}
|
|
979
|
+
if (isLambdaHandler(node)) {
|
|
980
|
+
return "lambda-handler";
|
|
981
|
+
}
|
|
982
|
+
if (isEmailTemplate(node)) {
|
|
983
|
+
return "email-template";
|
|
984
|
+
}
|
|
985
|
+
if (isParserFile(node)) {
|
|
986
|
+
return "parser-file";
|
|
987
|
+
}
|
|
988
|
+
if (isServiceFile(node)) {
|
|
989
|
+
return "service-file";
|
|
990
|
+
}
|
|
991
|
+
if (isSessionFile(node)) {
|
|
992
|
+
return "cohesive-module";
|
|
993
|
+
}
|
|
994
|
+
if (isNextJsPage(node)) {
|
|
995
|
+
return "nextjs-page";
|
|
996
|
+
}
|
|
997
|
+
if (isUtilityFile(node)) {
|
|
998
|
+
return "utility-module";
|
|
999
|
+
}
|
|
976
1000
|
const uniqueDomains = domains.filter((d) => d !== "unknown");
|
|
977
1001
|
const hasSingleDomain = uniqueDomains.length <= 1;
|
|
978
|
-
|
|
979
|
-
if (hasSingleDomain && hasHighCohesion) {
|
|
1002
|
+
if (hasSingleDomain) {
|
|
980
1003
|
return "cohesive-module";
|
|
981
1004
|
}
|
|
982
1005
|
const hasMultipleDomains = uniqueDomains.length > 1;
|
|
983
|
-
const hasLowCohesion = cohesionScore < 0.
|
|
984
|
-
if (hasMultipleDomains
|
|
1006
|
+
const hasLowCohesion = cohesionScore < 0.4;
|
|
1007
|
+
if (hasMultipleDomains && hasLowCohesion) {
|
|
985
1008
|
return "mixed-concerns";
|
|
986
1009
|
}
|
|
1010
|
+
if (cohesionScore >= 0.5) {
|
|
1011
|
+
return "cohesive-module";
|
|
1012
|
+
}
|
|
987
1013
|
return "unknown";
|
|
988
1014
|
}
|
|
989
1015
|
function isBarrelExport(node) {
|
|
@@ -1008,10 +1034,300 @@ function isTypeDefinitionFile(node) {
|
|
|
1008
1034
|
const { file, exports: exports2 } = node;
|
|
1009
1035
|
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1010
1036
|
const isTypesFile = fileName?.includes("types") || fileName?.includes(".d.ts") || fileName === "types.ts" || fileName === "interfaces.ts";
|
|
1037
|
+
const lowerPath = file.toLowerCase();
|
|
1038
|
+
const isTypesPath = lowerPath.includes("/types/") || lowerPath.includes("/typings/") || lowerPath.includes("/@types/") || lowerPath.startsWith("types/") || lowerPath.startsWith("typings/");
|
|
1011
1039
|
const typeExports = exports2.filter((e) => e.type === "type" || e.type === "interface");
|
|
1012
1040
|
const runtimeExports = exports2.filter((e) => e.type === "function" || e.type === "class" || e.type === "const");
|
|
1013
1041
|
const mostlyTypes = exports2.length > 0 && typeExports.length > runtimeExports.length && typeExports.length / exports2.length > 0.7;
|
|
1014
|
-
|
|
1042
|
+
const pureTypeFile = exports2.length > 0 && typeExports.length === exports2.length;
|
|
1043
|
+
const emptyOrReExportInTypesDir = isTypesPath && exports2.length === 0;
|
|
1044
|
+
return isTypesFile || isTypesPath || mostlyTypes || pureTypeFile || emptyOrReExportInTypesDir;
|
|
1045
|
+
}
|
|
1046
|
+
function isConfigOrSchemaFile(node) {
|
|
1047
|
+
const { file, exports: exports2 } = node;
|
|
1048
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1049
|
+
const configPatterns = [
|
|
1050
|
+
"config",
|
|
1051
|
+
"schema",
|
|
1052
|
+
"settings",
|
|
1053
|
+
"options",
|
|
1054
|
+
"constants",
|
|
1055
|
+
"env",
|
|
1056
|
+
"environment",
|
|
1057
|
+
".config.",
|
|
1058
|
+
"-config.",
|
|
1059
|
+
"_config."
|
|
1060
|
+
];
|
|
1061
|
+
const isConfigName = configPatterns.some(
|
|
1062
|
+
(pattern) => fileName?.includes(pattern) || fileName?.startsWith(pattern) || fileName?.endsWith(`${pattern}.ts`)
|
|
1063
|
+
);
|
|
1064
|
+
const isConfigPath = file.toLowerCase().includes("/config/") || file.toLowerCase().includes("/schemas/") || file.toLowerCase().includes("/settings/");
|
|
1065
|
+
const hasSchemaExports = exports2.some(
|
|
1066
|
+
(e) => e.name.toLowerCase().includes("table") || e.name.toLowerCase().includes("schema") || e.name.toLowerCase().includes("config") || e.name.toLowerCase().includes("setting")
|
|
1067
|
+
);
|
|
1068
|
+
return isConfigName || isConfigPath || hasSchemaExports;
|
|
1069
|
+
}
|
|
1070
|
+
function isUtilityFile(node) {
|
|
1071
|
+
const { file, exports: exports2 } = node;
|
|
1072
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1073
|
+
const utilityPatterns = [
|
|
1074
|
+
"util",
|
|
1075
|
+
"utility",
|
|
1076
|
+
"utilities",
|
|
1077
|
+
"helper",
|
|
1078
|
+
"helpers",
|
|
1079
|
+
"common",
|
|
1080
|
+
"shared",
|
|
1081
|
+
"toolbox",
|
|
1082
|
+
"toolkit",
|
|
1083
|
+
".util.",
|
|
1084
|
+
"-util.",
|
|
1085
|
+
"_util.",
|
|
1086
|
+
"-utils.",
|
|
1087
|
+
".utils."
|
|
1088
|
+
];
|
|
1089
|
+
const isUtilityName = utilityPatterns.some(
|
|
1090
|
+
(pattern) => fileName?.includes(pattern)
|
|
1091
|
+
);
|
|
1092
|
+
const isUtilityPath = file.toLowerCase().includes("/utils/") || file.toLowerCase().includes("/helpers/") || file.toLowerCase().includes("/common/") || file.toLowerCase().endsWith("-utils.ts") || file.toLowerCase().endsWith("-util.ts") || file.toLowerCase().endsWith("-helper.ts") || file.toLowerCase().endsWith("-helpers.ts");
|
|
1093
|
+
const hasManySmallExportsInUtilityContext = exports2.length >= 3 && exports2.every((e) => e.type === "function" || e.type === "const") && (isUtilityName || isUtilityPath);
|
|
1094
|
+
return isUtilityName || isUtilityPath || hasManySmallExportsInUtilityContext;
|
|
1095
|
+
}
|
|
1096
|
+
function isLambdaHandler(node) {
|
|
1097
|
+
const { file, exports: exports2 } = node;
|
|
1098
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1099
|
+
const handlerPatterns = [
|
|
1100
|
+
"handler",
|
|
1101
|
+
".handler.",
|
|
1102
|
+
"-handler.",
|
|
1103
|
+
"lambda",
|
|
1104
|
+
".lambda.",
|
|
1105
|
+
"-lambda."
|
|
1106
|
+
];
|
|
1107
|
+
const isHandlerName = handlerPatterns.some(
|
|
1108
|
+
(pattern) => fileName?.includes(pattern)
|
|
1109
|
+
);
|
|
1110
|
+
const isHandlerPath = file.toLowerCase().includes("/handlers/") || file.toLowerCase().includes("/lambdas/") || file.toLowerCase().includes("/functions/");
|
|
1111
|
+
const hasHandlerExport = exports2.some(
|
|
1112
|
+
(e) => e.name.toLowerCase() === "handler" || e.name.toLowerCase() === "main" || e.name.toLowerCase() === "lambdahandler" || e.name.toLowerCase().endsWith("handler")
|
|
1113
|
+
);
|
|
1114
|
+
const hasSingleEntryInHandlerContext = exports2.length === 1 && (exports2[0].type === "function" || exports2[0].name === "default") && (isHandlerPath || isHandlerName);
|
|
1115
|
+
return isHandlerName || isHandlerPath || hasHandlerExport || hasSingleEntryInHandlerContext;
|
|
1116
|
+
}
|
|
1117
|
+
function isServiceFile(node) {
|
|
1118
|
+
const { file, exports: exports2 } = node;
|
|
1119
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1120
|
+
const servicePatterns = [
|
|
1121
|
+
"service",
|
|
1122
|
+
".service.",
|
|
1123
|
+
"-service.",
|
|
1124
|
+
"_service."
|
|
1125
|
+
];
|
|
1126
|
+
const isServiceName = servicePatterns.some(
|
|
1127
|
+
(pattern) => fileName?.includes(pattern)
|
|
1128
|
+
);
|
|
1129
|
+
const isServicePath = file.toLowerCase().includes("/services/");
|
|
1130
|
+
const hasServiceNamedExport = exports2.some(
|
|
1131
|
+
(e) => e.name.toLowerCase().includes("service") || e.name.toLowerCase().endsWith("service")
|
|
1132
|
+
);
|
|
1133
|
+
const hasClassExport = exports2.some((e) => e.type === "class");
|
|
1134
|
+
return isServiceName || isServicePath || hasServiceNamedExport && hasClassExport;
|
|
1135
|
+
}
|
|
1136
|
+
function isEmailTemplate(node) {
|
|
1137
|
+
const { file, exports: exports2 } = node;
|
|
1138
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1139
|
+
const emailTemplatePatterns = [
|
|
1140
|
+
"-email-",
|
|
1141
|
+
".email.",
|
|
1142
|
+
"_email_",
|
|
1143
|
+
"-template",
|
|
1144
|
+
".template.",
|
|
1145
|
+
"_template",
|
|
1146
|
+
"-mail.",
|
|
1147
|
+
".mail."
|
|
1148
|
+
];
|
|
1149
|
+
const isEmailTemplateName = emailTemplatePatterns.some(
|
|
1150
|
+
(pattern) => fileName?.includes(pattern)
|
|
1151
|
+
);
|
|
1152
|
+
const isSpecificTemplateName = fileName?.includes("receipt") || fileName?.includes("invoice-email") || fileName?.includes("welcome-email") || fileName?.includes("notification-email") || fileName?.includes("writer") && fileName.includes("receipt");
|
|
1153
|
+
const isEmailPath = file.toLowerCase().includes("/emails/") || file.toLowerCase().includes("/mail/") || file.toLowerCase().includes("/notifications/");
|
|
1154
|
+
const hasTemplateFunction = exports2.some(
|
|
1155
|
+
(e) => e.type === "function" && (e.name.toLowerCase().startsWith("render") || e.name.toLowerCase().startsWith("generate") || e.name.toLowerCase().includes("template") && e.name.toLowerCase().includes("email"))
|
|
1156
|
+
);
|
|
1157
|
+
const hasEmailExport = exports2.some(
|
|
1158
|
+
(e) => e.name.toLowerCase().includes("template") && e.type === "function" || e.name.toLowerCase().includes("render") && e.type === "function" || e.name.toLowerCase().includes("email") && e.type !== "class"
|
|
1159
|
+
);
|
|
1160
|
+
return isEmailPath || isEmailTemplateName || isSpecificTemplateName || hasTemplateFunction && hasEmailExport;
|
|
1161
|
+
}
|
|
1162
|
+
function isParserFile(node) {
|
|
1163
|
+
const { file, exports: exports2 } = node;
|
|
1164
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1165
|
+
const parserPatterns = [
|
|
1166
|
+
"parser",
|
|
1167
|
+
".parser.",
|
|
1168
|
+
"-parser.",
|
|
1169
|
+
"_parser.",
|
|
1170
|
+
"transform",
|
|
1171
|
+
".transform.",
|
|
1172
|
+
"-transform.",
|
|
1173
|
+
"converter",
|
|
1174
|
+
".converter.",
|
|
1175
|
+
"-converter.",
|
|
1176
|
+
"mapper",
|
|
1177
|
+
".mapper.",
|
|
1178
|
+
"-mapper.",
|
|
1179
|
+
"serializer",
|
|
1180
|
+
".serializer.",
|
|
1181
|
+
"deterministic"
|
|
1182
|
+
// For base-parser-deterministic.ts pattern
|
|
1183
|
+
];
|
|
1184
|
+
const isParserName = parserPatterns.some(
|
|
1185
|
+
(pattern) => fileName?.includes(pattern)
|
|
1186
|
+
);
|
|
1187
|
+
const isParserPath = file.toLowerCase().includes("/parsers/") || file.toLowerCase().includes("/transformers/") || file.toLowerCase().includes("/converters/") || file.toLowerCase().includes("/mappers/");
|
|
1188
|
+
const hasParserExport = exports2.some(
|
|
1189
|
+
(e) => e.name.toLowerCase().includes("parse") || e.name.toLowerCase().includes("transform") || e.name.toLowerCase().includes("convert") || e.name.toLowerCase().includes("map") || e.name.toLowerCase().includes("serialize") || e.name.toLowerCase().includes("deserialize")
|
|
1190
|
+
);
|
|
1191
|
+
const hasParseFunction = exports2.some(
|
|
1192
|
+
(e) => e.type === "function" && (e.name.toLowerCase().startsWith("parse") || e.name.toLowerCase().startsWith("transform") || e.name.toLowerCase().startsWith("convert") || e.name.toLowerCase().startsWith("map") || e.name.toLowerCase().startsWith("extract"))
|
|
1193
|
+
);
|
|
1194
|
+
return isParserName || isParserPath || hasParserExport || hasParseFunction;
|
|
1195
|
+
}
|
|
1196
|
+
function isSessionFile(node) {
|
|
1197
|
+
const { file, exports: exports2 } = node;
|
|
1198
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1199
|
+
const sessionPatterns = [
|
|
1200
|
+
"session",
|
|
1201
|
+
".session.",
|
|
1202
|
+
"-session.",
|
|
1203
|
+
"state",
|
|
1204
|
+
".state.",
|
|
1205
|
+
"-state.",
|
|
1206
|
+
"context",
|
|
1207
|
+
".context.",
|
|
1208
|
+
"-context.",
|
|
1209
|
+
"store",
|
|
1210
|
+
".store.",
|
|
1211
|
+
"-store."
|
|
1212
|
+
];
|
|
1213
|
+
const isSessionName = sessionPatterns.some(
|
|
1214
|
+
(pattern) => fileName?.includes(pattern)
|
|
1215
|
+
);
|
|
1216
|
+
const isSessionPath = file.toLowerCase().includes("/sessions/") || file.toLowerCase().includes("/state/") || file.toLowerCase().includes("/context/") || file.toLowerCase().includes("/store/");
|
|
1217
|
+
const hasSessionExport = exports2.some(
|
|
1218
|
+
(e) => e.name.toLowerCase().includes("session") || e.name.toLowerCase().includes("state") || e.name.toLowerCase().includes("context") || e.name.toLowerCase().includes("manager") || e.name.toLowerCase().includes("store")
|
|
1219
|
+
);
|
|
1220
|
+
return isSessionName || isSessionPath || hasSessionExport;
|
|
1221
|
+
}
|
|
1222
|
+
function isNextJsPage(node) {
|
|
1223
|
+
const { file, exports: exports2 } = node;
|
|
1224
|
+
const lowerPath = file.toLowerCase();
|
|
1225
|
+
const fileName = file.split("/").pop()?.toLowerCase();
|
|
1226
|
+
const isInAppDir = lowerPath.includes("/app/") || lowerPath.startsWith("app/");
|
|
1227
|
+
const isPageFile = fileName === "page.tsx" || fileName === "page.ts";
|
|
1228
|
+
if (!isInAppDir || !isPageFile) {
|
|
1229
|
+
return false;
|
|
1230
|
+
}
|
|
1231
|
+
const exportNames = exports2.map((e) => e.name.toLowerCase());
|
|
1232
|
+
const hasDefaultExport = exports2.some((e) => e.type === "default");
|
|
1233
|
+
const nextJsExports = ["metadata", "generatemetadata", "faqjsonld", "jsonld", "icon", "viewport", "dynamic"];
|
|
1234
|
+
const hasNextJsExports = exportNames.some(
|
|
1235
|
+
(name) => nextJsExports.includes(name) || name.includes("jsonld")
|
|
1236
|
+
);
|
|
1237
|
+
return hasDefaultExport || hasNextJsExports;
|
|
1238
|
+
}
|
|
1239
|
+
function adjustCohesionForClassification(baseCohesion, classification, node) {
|
|
1240
|
+
switch (classification) {
|
|
1241
|
+
case "barrel-export":
|
|
1242
|
+
return 1;
|
|
1243
|
+
case "type-definition":
|
|
1244
|
+
return 1;
|
|
1245
|
+
case "utility-module": {
|
|
1246
|
+
if (node) {
|
|
1247
|
+
const exportNames = node.exports.map((e) => e.name.toLowerCase());
|
|
1248
|
+
const hasRelatedNames = hasRelatedExportNames(exportNames);
|
|
1249
|
+
if (hasRelatedNames) {
|
|
1250
|
+
return Math.min(1, baseCohesion + 0.45);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return Math.min(1, baseCohesion + 0.35);
|
|
1254
|
+
}
|
|
1255
|
+
case "service-file": {
|
|
1256
|
+
if (node?.exports.some((e) => e.type === "class")) {
|
|
1257
|
+
return Math.min(1, baseCohesion + 0.4);
|
|
1258
|
+
}
|
|
1259
|
+
return Math.min(1, baseCohesion + 0.3);
|
|
1260
|
+
}
|
|
1261
|
+
case "lambda-handler": {
|
|
1262
|
+
if (node) {
|
|
1263
|
+
const hasSingleEntry = node.exports.length === 1 || node.exports.some((e) => e.name.toLowerCase() === "handler");
|
|
1264
|
+
if (hasSingleEntry) {
|
|
1265
|
+
return Math.min(1, baseCohesion + 0.45);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return Math.min(1, baseCohesion + 0.35);
|
|
1269
|
+
}
|
|
1270
|
+
case "email-template": {
|
|
1271
|
+
if (node) {
|
|
1272
|
+
const hasTemplateFunc = node.exports.some(
|
|
1273
|
+
(e) => e.name.toLowerCase().includes("render") || e.name.toLowerCase().includes("generate") || e.name.toLowerCase().includes("template")
|
|
1274
|
+
);
|
|
1275
|
+
if (hasTemplateFunc) {
|
|
1276
|
+
return Math.min(1, baseCohesion + 0.4);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return Math.min(1, baseCohesion + 0.3);
|
|
1280
|
+
}
|
|
1281
|
+
case "parser-file": {
|
|
1282
|
+
if (node) {
|
|
1283
|
+
const hasParseFunc = node.exports.some(
|
|
1284
|
+
(e) => e.name.toLowerCase().startsWith("parse") || e.name.toLowerCase().startsWith("transform") || e.name.toLowerCase().startsWith("convert")
|
|
1285
|
+
);
|
|
1286
|
+
if (hasParseFunc) {
|
|
1287
|
+
return Math.min(1, baseCohesion + 0.4);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
return Math.min(1, baseCohesion + 0.3);
|
|
1291
|
+
}
|
|
1292
|
+
case "nextjs-page":
|
|
1293
|
+
return 1;
|
|
1294
|
+
case "cohesive-module":
|
|
1295
|
+
return Math.max(baseCohesion, 0.7);
|
|
1296
|
+
case "mixed-concerns":
|
|
1297
|
+
return baseCohesion;
|
|
1298
|
+
default:
|
|
1299
|
+
return Math.min(1, baseCohesion + 0.1);
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
function hasRelatedExportNames(exportNames) {
|
|
1303
|
+
if (exportNames.length < 2) return true;
|
|
1304
|
+
const stems = /* @__PURE__ */ new Set();
|
|
1305
|
+
const domains = /* @__PURE__ */ new Set();
|
|
1306
|
+
for (const name of exportNames) {
|
|
1307
|
+
const verbs = ["get", "set", "create", "update", "delete", "fetch", "save", "load", "parse", "format", "validate", "convert", "transform", "build", "generate", "render", "send", "receive"];
|
|
1308
|
+
for (const verb of verbs) {
|
|
1309
|
+
if (name.startsWith(verb) && name.length > verb.length) {
|
|
1310
|
+
stems.add(name.slice(verb.length).toLowerCase());
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
const domainPatterns = ["user", "order", "product", "session", "email", "file", "db", "s3", "dynamo", "api", "config"];
|
|
1314
|
+
for (const domain of domainPatterns) {
|
|
1315
|
+
if (name.includes(domain)) {
|
|
1316
|
+
domains.add(domain);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
if (stems.size === 1 && exportNames.length >= 2) return true;
|
|
1321
|
+
if (domains.size === 1 && exportNames.length >= 2) return true;
|
|
1322
|
+
const prefixes = exportNames.map((name) => {
|
|
1323
|
+
const match = name.match(/^([a-z]+)/);
|
|
1324
|
+
return match ? match[1] : "";
|
|
1325
|
+
}).filter((p) => p.length >= 3);
|
|
1326
|
+
if (prefixes.length >= 2) {
|
|
1327
|
+
const uniquePrefixes = new Set(prefixes);
|
|
1328
|
+
if (uniquePrefixes.size === 1) return true;
|
|
1329
|
+
}
|
|
1330
|
+
return false;
|
|
1015
1331
|
}
|
|
1016
1332
|
function adjustFragmentationForClassification(baseFragmentation, classification) {
|
|
1017
1333
|
switch (classification) {
|
|
@@ -1019,6 +1335,13 @@ function adjustFragmentationForClassification(baseFragmentation, classification)
|
|
|
1019
1335
|
return 0;
|
|
1020
1336
|
case "type-definition":
|
|
1021
1337
|
return 0;
|
|
1338
|
+
case "utility-module":
|
|
1339
|
+
case "service-file":
|
|
1340
|
+
case "lambda-handler":
|
|
1341
|
+
case "email-template":
|
|
1342
|
+
case "parser-file":
|
|
1343
|
+
case "nextjs-page":
|
|
1344
|
+
return baseFragmentation * 0.2;
|
|
1022
1345
|
case "cohesive-module":
|
|
1023
1346
|
return baseFragmentation * 0.3;
|
|
1024
1347
|
case "mixed-concerns":
|
|
@@ -1044,6 +1367,36 @@ function getClassificationRecommendations(classification, file, issues) {
|
|
|
1044
1367
|
"Module has good cohesion despite its size",
|
|
1045
1368
|
"Consider documenting the module boundaries for AI assistants"
|
|
1046
1369
|
];
|
|
1370
|
+
case "utility-module":
|
|
1371
|
+
return [
|
|
1372
|
+
"Utility module detected - multiple domains are acceptable here",
|
|
1373
|
+
"Consider grouping related utilities by prefix or domain for better discoverability"
|
|
1374
|
+
];
|
|
1375
|
+
case "service-file":
|
|
1376
|
+
return [
|
|
1377
|
+
"Service file detected - orchestration of multiple dependencies is expected",
|
|
1378
|
+
"Consider documenting service boundaries and dependencies"
|
|
1379
|
+
];
|
|
1380
|
+
case "lambda-handler":
|
|
1381
|
+
return [
|
|
1382
|
+
"Lambda handler detected - coordination of services is expected",
|
|
1383
|
+
"Ensure handler has clear single responsibility"
|
|
1384
|
+
];
|
|
1385
|
+
case "email-template":
|
|
1386
|
+
return [
|
|
1387
|
+
"Email template detected - references multiple domains for rendering",
|
|
1388
|
+
"Template structure is cohesive by design"
|
|
1389
|
+
];
|
|
1390
|
+
case "parser-file":
|
|
1391
|
+
return [
|
|
1392
|
+
"Parser/transformer file detected - handles multiple data sources",
|
|
1393
|
+
"Consider documenting input/output schemas"
|
|
1394
|
+
];
|
|
1395
|
+
case "nextjs-page":
|
|
1396
|
+
return [
|
|
1397
|
+
"Next.js App Router page detected - metadata/JSON-LD/component pattern is cohesive",
|
|
1398
|
+
"Multiple exports (metadata, faqJsonLd, default) serve single page purpose"
|
|
1399
|
+
];
|
|
1047
1400
|
case "mixed-concerns":
|
|
1048
1401
|
return [
|
|
1049
1402
|
"Consider splitting this file by domain",
|
|
@@ -1317,6 +1670,11 @@ async function analyzeContext(options) {
|
|
|
1317
1670
|
...new Set(node.exports.map((e) => e.inferredDomain || "unknown"))
|
|
1318
1671
|
];
|
|
1319
1672
|
const fileClassification = classifyFile(node, cohesionScore, domains);
|
|
1673
|
+
const adjustedCohesionScore = adjustCohesionForClassification(
|
|
1674
|
+
cohesionScore,
|
|
1675
|
+
fileClassification,
|
|
1676
|
+
node
|
|
1677
|
+
);
|
|
1320
1678
|
const adjustedFragmentationScore = adjustFragmentationForClassification(
|
|
1321
1679
|
fragmentationScore,
|
|
1322
1680
|
fileClassification
|
|
@@ -1335,7 +1693,8 @@ async function analyzeContext(options) {
|
|
|
1335
1693
|
file,
|
|
1336
1694
|
importDepth,
|
|
1337
1695
|
contextBudget,
|
|
1338
|
-
cohesionScore,
|
|
1696
|
+
cohesionScore: adjustedCohesionScore,
|
|
1697
|
+
// Use adjusted cohesion
|
|
1339
1698
|
fragmentationScore: adjustedFragmentationScore,
|
|
1340
1699
|
maxDepth,
|
|
1341
1700
|
maxContextBudget,
|
|
@@ -1351,7 +1710,8 @@ async function analyzeContext(options) {
|
|
|
1351
1710
|
dependencyCount: dependencyList.length,
|
|
1352
1711
|
dependencyList,
|
|
1353
1712
|
circularDeps: circularDeps.filter((cycle) => cycle.includes(file)),
|
|
1354
|
-
cohesionScore,
|
|
1713
|
+
cohesionScore: adjustedCohesionScore,
|
|
1714
|
+
// Report adjusted cohesion
|
|
1355
1715
|
domains,
|
|
1356
1716
|
exportCount: node.exports.length,
|
|
1357
1717
|
contextBudget,
|
package/dist/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/context-analyzer",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.26",
|
|
4
4
|
"description": "AI context window cost analysis - detect fragmented code, deep import chains, and expensive context budgets",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|