@aiready/consistency 0.8.31 → 0.8.32
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/.github/FUNDING.yml +2 -2
- package/.turbo/turbo-build.log +9 -9
- package/.turbo/turbo-test.log +12 -15
- package/CONTRIBUTING.md +10 -3
- package/dist/chunk-EIQ5K6OO.mjs +1579 -0
- package/dist/chunk-J5IFYDVU.mjs +1579 -0
- package/dist/cli.js +136 -37
- package/dist/cli.mjs +28 -5
- package/dist/index.js +191 -50
- package/dist/index.mjs +76 -19
- package/package.json +2 -2
- package/src/__tests__/analyzer.test.ts +63 -8
- package/src/__tests__/language-filter.test.ts +6 -9
- package/src/__tests__/scoring.test.ts +35 -10
- package/src/analyzer.ts +60 -29
- package/src/analyzers/naming-ast.ts +134 -46
- package/src/analyzers/naming-constants.ts +356 -42
- package/src/analyzers/naming-python.ts +27 -9
- package/src/analyzers/naming.ts +160 -68
- package/src/analyzers/patterns.ts +59 -35
- package/src/cli.ts +42 -18
- package/src/scoring.ts +35 -31
- package/src/types.ts +1 -1
- package/src/utils/ast-parser.ts +32 -14
- package/src/utils/config-loader.ts +8 -3
- package/src/utils/context-detector.ts +76 -43
- package/src/utils/scope-tracker.ts +11 -11
package/dist/index.js
CHANGED
|
@@ -62,6 +62,7 @@ function parseFile(filePath, content) {
|
|
|
62
62
|
filePath: isTypeScript ? filePath : void 0
|
|
63
63
|
});
|
|
64
64
|
} catch (error) {
|
|
65
|
+
void error;
|
|
65
66
|
return null;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -300,6 +301,7 @@ var ScopeTracker = class {
|
|
|
300
301
|
|
|
301
302
|
// src/utils/context-detector.ts
|
|
302
303
|
function detectFileType(filePath, ast) {
|
|
304
|
+
void ast;
|
|
303
305
|
const path = filePath.toLowerCase();
|
|
304
306
|
if (path.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/) || path.includes("__tests__")) {
|
|
305
307
|
return "test";
|
|
@@ -330,7 +332,9 @@ function detectCodeLayer(ast) {
|
|
|
330
332
|
}
|
|
331
333
|
if (node.type === "FunctionDeclaration" && node.id) {
|
|
332
334
|
const name = node.id.name;
|
|
333
|
-
if (name.match(
|
|
335
|
+
if (name.match(
|
|
336
|
+
/^(get|post|put|delete|patch|handle|api|route|controller)/i
|
|
337
|
+
)) {
|
|
334
338
|
hasAPIIndicators++;
|
|
335
339
|
}
|
|
336
340
|
if (name.match(/^(calculate|process|validate|transform|compute|analyze)/i)) {
|
|
@@ -339,7 +343,9 @@ function detectCodeLayer(ast) {
|
|
|
339
343
|
if (name.match(/^(find|create|update|delete|save|fetch|query|insert)/i)) {
|
|
340
344
|
hasDataIndicators++;
|
|
341
345
|
}
|
|
342
|
-
if (name.match(
|
|
346
|
+
if (name.match(
|
|
347
|
+
/^(format|parse|convert|normalize|sanitize|encode|decode)/i
|
|
348
|
+
)) {
|
|
343
349
|
hasUtilityIndicators++;
|
|
344
350
|
}
|
|
345
351
|
}
|
|
@@ -856,7 +862,9 @@ function snakeCaseToCamelCase(str) {
|
|
|
856
862
|
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
857
863
|
}
|
|
858
864
|
function detectNamingConventions(files, allIssues) {
|
|
859
|
-
const camelCaseCount = allIssues.filter(
|
|
865
|
+
const camelCaseCount = allIssues.filter(
|
|
866
|
+
(i) => i.type === "convention-mix"
|
|
867
|
+
).length;
|
|
860
868
|
const totalChecks = files.length * 10;
|
|
861
869
|
if (camelCaseCount / totalChecks > 0.3) {
|
|
862
870
|
return { dominantConvention: "mixed", conventionScore: 0.5 };
|
|
@@ -869,10 +877,15 @@ async function loadNamingConfig(files) {
|
|
|
869
877
|
const rootDir = files.length > 0 ? (0, import_path.dirname)(files[0]) : process.cwd();
|
|
870
878
|
const config = await (0, import_core.loadConfig)(rootDir);
|
|
871
879
|
const consistencyConfig = config?.tools?.["consistency"];
|
|
872
|
-
const customAbbreviations = new Set(
|
|
880
|
+
const customAbbreviations = new Set(
|
|
881
|
+
consistencyConfig?.acceptedAbbreviations || []
|
|
882
|
+
);
|
|
873
883
|
const customShortWords = new Set(consistencyConfig?.shortWords || []);
|
|
874
884
|
const disabledChecks = new Set(consistencyConfig?.disableChecks || []);
|
|
875
|
-
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
885
|
+
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
886
|
+
...ACCEPTABLE_ABBREVIATIONS,
|
|
887
|
+
...customAbbreviations
|
|
888
|
+
]);
|
|
876
889
|
const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
|
|
877
890
|
return {
|
|
878
891
|
customAbbreviations,
|
|
@@ -921,9 +934,14 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
921
934
|
if ("params" in node) {
|
|
922
935
|
for (const param of node.params) {
|
|
923
936
|
if (param.type === "Identifier") {
|
|
924
|
-
scopeTracker.declareVariable(
|
|
925
|
-
|
|
926
|
-
|
|
937
|
+
scopeTracker.declareVariable(
|
|
938
|
+
param.name,
|
|
939
|
+
param,
|
|
940
|
+
getLineNumber(param),
|
|
941
|
+
{
|
|
942
|
+
isParameter: true
|
|
943
|
+
}
|
|
944
|
+
);
|
|
927
945
|
} else if (param.type === "ObjectPattern" || param.type === "ArrayPattern") {
|
|
928
946
|
extractIdentifiersFromPattern(param, scopeTracker, true);
|
|
929
947
|
}
|
|
@@ -938,7 +956,7 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
938
956
|
}
|
|
939
957
|
if (node.type === "VariableDeclarator") {
|
|
940
958
|
if (node.id.type === "Identifier") {
|
|
941
|
-
|
|
959
|
+
void isCoverageContext(node, ancestors);
|
|
942
960
|
scopeTracker.declareVariable(
|
|
943
961
|
node.id.name,
|
|
944
962
|
node.id,
|
|
@@ -950,7 +968,12 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
950
968
|
}
|
|
951
969
|
);
|
|
952
970
|
} else if (node.id.type === "ObjectPattern" || node.id.type === "ArrayPattern") {
|
|
953
|
-
extractIdentifiersFromPattern(
|
|
971
|
+
extractIdentifiersFromPattern(
|
|
972
|
+
node.id,
|
|
973
|
+
scopeTracker,
|
|
974
|
+
false,
|
|
975
|
+
ancestors
|
|
976
|
+
);
|
|
954
977
|
}
|
|
955
978
|
}
|
|
956
979
|
if (node.type === "Identifier" && parent) {
|
|
@@ -1017,7 +1040,10 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
1017
1040
|
}
|
|
1018
1041
|
if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
1019
1042
|
if (name.includes("_") && !name.startsWith("_") && name.toLowerCase() === name) {
|
|
1020
|
-
const camelCase = name.replace(
|
|
1043
|
+
const camelCase = name.replace(
|
|
1044
|
+
/_([a-z])/g,
|
|
1045
|
+
(_, letter) => letter.toUpperCase()
|
|
1046
|
+
);
|
|
1021
1047
|
issues.push({
|
|
1022
1048
|
file,
|
|
1023
1049
|
line,
|
|
@@ -1037,16 +1063,37 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
1037
1063
|
if (!name) return;
|
|
1038
1064
|
const line = getLineNumber(node);
|
|
1039
1065
|
if (["main", "init", "setup", "bootstrap"].includes(name)) return;
|
|
1040
|
-
const hasActionVerb = name.match(
|
|
1041
|
-
|
|
1066
|
+
const hasActionVerb = name.match(
|
|
1067
|
+
/^(get|set|is|has|can|should|create|update|delete|fetch|load|save|process|handle|validate|check|find|search|filter|map|reduce|make|do|run|start|stop|build|parse|format|render|calculate|compute|generate|transform|convert|normalize|sanitize|encode|decode|compress|extract|merge|split|join|sort|compare|test|verify|ensure|apply|execute|invoke|call|emit|dispatch|trigger|listen|subscribe|unsubscribe|add|remove|clear|reset|toggle|enable|disable|open|close|connect|disconnect|send|receive|read|write|import|export|register|unregister|mount|unmount|track|store|persist|upsert|derive|classify|combine|discover|activate|require|assert|expect|mask|escape|sign|put|list|complete|page|safe|mock|pick|pluralize|text|count|detect|select)/
|
|
1068
|
+
);
|
|
1069
|
+
const isFactoryPattern = name.match(
|
|
1070
|
+
/(Factory|Builder|Creator|Generator|Provider|Adapter|Mock)$/
|
|
1071
|
+
);
|
|
1042
1072
|
const isEventHandler = name.match(/^on[A-Z]/);
|
|
1043
1073
|
const isDescriptiveLong = name.length > 15;
|
|
1044
1074
|
const isReactHook = name.match(/^use[A-Z]/);
|
|
1045
1075
|
const isHelperPattern = name.match(/^(to|from|with|without|for|as|into)\w+/) || name.match(/^\w+(To|From|With|Without|For|As|Into)\w*$/);
|
|
1046
|
-
const isUtilityName = [
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1076
|
+
const isUtilityName = [
|
|
1077
|
+
"cn",
|
|
1078
|
+
"proxy",
|
|
1079
|
+
"sitemap",
|
|
1080
|
+
"robots",
|
|
1081
|
+
"gtag"
|
|
1082
|
+
].includes(name);
|
|
1083
|
+
const isLanguageKeyword = [
|
|
1084
|
+
"constructor",
|
|
1085
|
+
"toString",
|
|
1086
|
+
"valueOf",
|
|
1087
|
+
"toJSON"
|
|
1088
|
+
].includes(name);
|
|
1089
|
+
const isFrameworkPattern = name.match(
|
|
1090
|
+
/^(goto|fill|click|select|submit|wait|expect)\w*/
|
|
1091
|
+
);
|
|
1092
|
+
const isDescriptivePattern = name.match(
|
|
1093
|
+
/^(default|total|count|sum|avg|max|min|initial|current|previous|next)\w+/
|
|
1094
|
+
) || name.match(
|
|
1095
|
+
/\w+(Count|Total|Sum|Average|List|Map|Set|Config|Settings|Options|Props|Data|Info|Details|State|Status|Response|Result)$/
|
|
1096
|
+
);
|
|
1050
1097
|
const capitalCount = (name.match(/[A-Z]/g) || []).length;
|
|
1051
1098
|
const isCompoundWord = capitalCount >= 3;
|
|
1052
1099
|
if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isReactHook && !isHelperPattern && !isUtilityName && !isDescriptivePattern && !isCompoundWord && !isLanguageKeyword && !isFrameworkPattern) {
|
|
@@ -1066,6 +1113,7 @@ function analyzeFileNamingAST(file, ast, allAbbreviations, allShortWords, disabl
|
|
|
1066
1113
|
return issues;
|
|
1067
1114
|
}
|
|
1068
1115
|
function extractIdentifiersFromPattern(pattern, scopeTracker, isParameter, ancestors) {
|
|
1116
|
+
void ancestors;
|
|
1069
1117
|
if (pattern.type === "ObjectPattern") {
|
|
1070
1118
|
for (const prop of pattern.properties) {
|
|
1071
1119
|
if (prop.type === "Property" && prop.value.type === "Identifier") {
|
|
@@ -1123,7 +1171,12 @@ async function analyzePythonNaming(files) {
|
|
|
1123
1171
|
const code = await fs.promises.readFile(file, "utf-8");
|
|
1124
1172
|
const result = parser.parse(code, file);
|
|
1125
1173
|
for (const exp of result.exports) {
|
|
1126
|
-
const nameIssue = checkPythonNaming(
|
|
1174
|
+
const nameIssue = checkPythonNaming(
|
|
1175
|
+
exp.name,
|
|
1176
|
+
exp.type,
|
|
1177
|
+
file,
|
|
1178
|
+
exp.loc?.start.line || 0
|
|
1179
|
+
);
|
|
1127
1180
|
if (nameIssue) {
|
|
1128
1181
|
issues.push(nameIssue);
|
|
1129
1182
|
}
|
|
@@ -1131,7 +1184,12 @@ async function analyzePythonNaming(files) {
|
|
|
1131
1184
|
for (const imp of result.imports) {
|
|
1132
1185
|
for (const spec of imp.specifiers) {
|
|
1133
1186
|
if (spec !== "*" && spec !== "default") {
|
|
1134
|
-
const nameIssue = checkPythonNaming(
|
|
1187
|
+
const nameIssue = checkPythonNaming(
|
|
1188
|
+
spec,
|
|
1189
|
+
"variable",
|
|
1190
|
+
file,
|
|
1191
|
+
imp.loc?.start.line || 0
|
|
1192
|
+
);
|
|
1135
1193
|
if (nameIssue) {
|
|
1136
1194
|
issues.push(nameIssue);
|
|
1137
1195
|
}
|
|
@@ -1254,15 +1312,19 @@ async function analyzeErrorHandling(files) {
|
|
|
1254
1312
|
}
|
|
1255
1313
|
}
|
|
1256
1314
|
const issues = [];
|
|
1257
|
-
const strategiesUsed = Object.values(patterns).filter(
|
|
1315
|
+
const strategiesUsed = Object.values(patterns).filter(
|
|
1316
|
+
(p) => p.length > 0
|
|
1317
|
+
).length;
|
|
1258
1318
|
if (strategiesUsed > 2) {
|
|
1259
1319
|
issues.push({
|
|
1260
|
-
files: [
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1320
|
+
files: [
|
|
1321
|
+
.../* @__PURE__ */ new Set([
|
|
1322
|
+
...patterns.tryCatch,
|
|
1323
|
+
...patterns.throwsError,
|
|
1324
|
+
...patterns.returnsNull,
|
|
1325
|
+
...patterns.returnsError
|
|
1326
|
+
])
|
|
1327
|
+
],
|
|
1266
1328
|
type: "error-handling",
|
|
1267
1329
|
description: "Inconsistent error handling strategies across codebase",
|
|
1268
1330
|
examples: [
|
|
@@ -1383,6 +1445,7 @@ async function analyzeConsistency(options) {
|
|
|
1383
1445
|
minSeverity = "info",
|
|
1384
1446
|
...scanOptions
|
|
1385
1447
|
} = options;
|
|
1448
|
+
void checkArchitecture;
|
|
1386
1449
|
const filePaths = await (0, import_core4.scanFiles)(scanOptions);
|
|
1387
1450
|
const tsJsFiles = filePaths.filter((f) => /\.(ts|tsx|js|jsx)$/i.test(f));
|
|
1388
1451
|
const pythonFiles = filePaths.filter((f) => /\.py$/i.test(f));
|
|
@@ -1452,10 +1515,14 @@ async function analyzeConsistency(options) {
|
|
|
1452
1515
|
results.sort((fileResultA, fileResultB) => {
|
|
1453
1516
|
const severityOrder = { critical: 0, major: 1, minor: 2, info: 3 };
|
|
1454
1517
|
const maxSeverityA = Math.min(
|
|
1455
|
-
...fileResultA.issues.map(
|
|
1518
|
+
...fileResultA.issues.map(
|
|
1519
|
+
(i) => severityOrder[i.severity]
|
|
1520
|
+
)
|
|
1456
1521
|
);
|
|
1457
1522
|
const maxSeverityB = Math.min(
|
|
1458
|
-
...fileResultB.issues.map(
|
|
1523
|
+
...fileResultB.issues.map(
|
|
1524
|
+
(i) => severityOrder[i.severity]
|
|
1525
|
+
)
|
|
1459
1526
|
);
|
|
1460
1527
|
if (maxSeverityA !== maxSeverityB) {
|
|
1461
1528
|
return maxSeverityA - maxSeverityB;
|
|
@@ -1463,8 +1530,12 @@ async function analyzeConsistency(options) {
|
|
|
1463
1530
|
return fileResultB.issues.length - fileResultA.issues.length;
|
|
1464
1531
|
});
|
|
1465
1532
|
const recommendations = generateRecommendations(namingIssues, patternIssues);
|
|
1466
|
-
const namingCountFiltered = namingIssues.filter(
|
|
1467
|
-
|
|
1533
|
+
const namingCountFiltered = namingIssues.filter(
|
|
1534
|
+
(i) => shouldIncludeSeverity(i.severity, minSeverity)
|
|
1535
|
+
).length;
|
|
1536
|
+
const patternCountFiltered = patternIssues.filter(
|
|
1537
|
+
(i) => shouldIncludeSeverity(i.severity, minSeverity)
|
|
1538
|
+
).length;
|
|
1468
1539
|
return {
|
|
1469
1540
|
summary: {
|
|
1470
1541
|
totalIssues: namingCountFiltered + patternCountFiltered,
|
|
@@ -1483,19 +1554,26 @@ function shouldIncludeSeverity(severity, minSeverity) {
|
|
|
1483
1554
|
}
|
|
1484
1555
|
function calculateConsistencyScore(issues) {
|
|
1485
1556
|
const weights = { critical: 10, major: 5, minor: 2, info: 1 };
|
|
1486
|
-
const totalWeight = issues.reduce(
|
|
1557
|
+
const totalWeight = issues.reduce(
|
|
1558
|
+
(sum, issue) => sum + weights[issue.severity],
|
|
1559
|
+
0
|
|
1560
|
+
);
|
|
1487
1561
|
return Math.max(0, 1 - totalWeight / 100);
|
|
1488
1562
|
}
|
|
1489
1563
|
function generateRecommendations(namingIssues, patternIssues) {
|
|
1490
1564
|
const recommendations = [];
|
|
1491
1565
|
if (namingIssues.length > 0) {
|
|
1492
|
-
const conventionMixCount = namingIssues.filter(
|
|
1566
|
+
const conventionMixCount = namingIssues.filter(
|
|
1567
|
+
(i) => i.type === "convention-mix"
|
|
1568
|
+
).length;
|
|
1493
1569
|
if (conventionMixCount > 0) {
|
|
1494
1570
|
recommendations.push(
|
|
1495
1571
|
`Standardize naming conventions: Found ${conventionMixCount} snake_case variables in TypeScript/JavaScript (use camelCase)`
|
|
1496
1572
|
);
|
|
1497
1573
|
}
|
|
1498
|
-
const poorNamingCount = namingIssues.filter(
|
|
1574
|
+
const poorNamingCount = namingIssues.filter(
|
|
1575
|
+
(i) => i.type === "poor-naming"
|
|
1576
|
+
).length;
|
|
1499
1577
|
if (poorNamingCount > 0) {
|
|
1500
1578
|
recommendations.push(
|
|
1501
1579
|
`Improve variable naming: Found ${poorNamingCount} single-letter or unclear variable names`
|
|
@@ -1503,7 +1581,9 @@ function generateRecommendations(namingIssues, patternIssues) {
|
|
|
1503
1581
|
}
|
|
1504
1582
|
}
|
|
1505
1583
|
if (patternIssues.length > 0) {
|
|
1506
|
-
const errorHandlingIssues = patternIssues.filter(
|
|
1584
|
+
const errorHandlingIssues = patternIssues.filter(
|
|
1585
|
+
(i) => i.type === "error-handling"
|
|
1586
|
+
);
|
|
1507
1587
|
if (errorHandlingIssues.length > 0) {
|
|
1508
1588
|
recommendations.push(
|
|
1509
1589
|
"Standardize error handling strategy across the codebase (prefer try-catch with typed errors)"
|
|
@@ -1523,7 +1603,9 @@ function generateRecommendations(namingIssues, patternIssues) {
|
|
|
1523
1603
|
}
|
|
1524
1604
|
}
|
|
1525
1605
|
if (recommendations.length === 0) {
|
|
1526
|
-
recommendations.push(
|
|
1606
|
+
recommendations.push(
|
|
1607
|
+
"No major consistency issues found! Your codebase follows good practices."
|
|
1608
|
+
);
|
|
1527
1609
|
}
|
|
1528
1610
|
return recommendations;
|
|
1529
1611
|
}
|
|
@@ -1535,7 +1617,13 @@ async function analyzeNaming(files) {
|
|
|
1535
1617
|
const { customAbbreviations, customShortWords, disabledChecks } = await loadNamingConfig(files);
|
|
1536
1618
|
for (const file of files) {
|
|
1537
1619
|
const content = await (0, import_core5.readFileContent)(file);
|
|
1538
|
-
const fileIssues = analyzeFileNaming(
|
|
1620
|
+
const fileIssues = analyzeFileNaming(
|
|
1621
|
+
file,
|
|
1622
|
+
content,
|
|
1623
|
+
customAbbreviations,
|
|
1624
|
+
customShortWords,
|
|
1625
|
+
disabledChecks
|
|
1626
|
+
);
|
|
1539
1627
|
issues.push(...fileIssues);
|
|
1540
1628
|
}
|
|
1541
1629
|
return issues;
|
|
@@ -1544,7 +1632,10 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1544
1632
|
const issues = [];
|
|
1545
1633
|
const isTestFile = file.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/);
|
|
1546
1634
|
const lines = content.split("\n");
|
|
1547
|
-
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
1635
|
+
const allAbbreviations = /* @__PURE__ */ new Set([
|
|
1636
|
+
...ACCEPTABLE_ABBREVIATIONS,
|
|
1637
|
+
...customAbbreviations
|
|
1638
|
+
]);
|
|
1548
1639
|
const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
|
|
1549
1640
|
const getContextWindow = (index, windowSize = 3) => {
|
|
1550
1641
|
const start = Math.max(0, index - windowSize);
|
|
@@ -1568,7 +1659,9 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1568
1659
|
const lineNumber = index + 1;
|
|
1569
1660
|
const contextWindow = getContextWindow(index);
|
|
1570
1661
|
if (!disabledChecks.has("single-letter")) {
|
|
1571
|
-
const singleLetterMatches = line.matchAll(
|
|
1662
|
+
const singleLetterMatches = line.matchAll(
|
|
1663
|
+
/\b(?:const|let|var)\s+([a-hm-z])\s*=/gi
|
|
1664
|
+
);
|
|
1572
1665
|
for (const match of singleLetterMatches) {
|
|
1573
1666
|
const letter = match[1].toLowerCase();
|
|
1574
1667
|
const isCoverageContext2 = /coverage|summary|metrics|pct|percent/i.test(line) || /\.(?:statements|branches|functions|lines)\.pct/i.test(line);
|
|
@@ -1582,7 +1675,9 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1582
1675
|
/[a-z]\s*=>/.test(line) || // s => on same line
|
|
1583
1676
|
// Multi-line arrow function detection: look for pattern in context window
|
|
1584
1677
|
new RegExp(`\\b${letter}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
|
|
1585
|
-
new RegExp(
|
|
1678
|
+
new RegExp(
|
|
1679
|
+
`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`
|
|
1680
|
+
).test(lines[index - 1] || "") && /=>/.test(contextWindow);
|
|
1586
1681
|
const isShortLived = isShortLivedVariable(letter, index);
|
|
1587
1682
|
if (!isInLoopContext && !isI18nContext && !isArrowFunctionParam && !isShortLived && !["x", "y", "z", "i", "j", "k", "l", "n", "m"].includes(letter)) {
|
|
1588
1683
|
if (isTestFile && ["a", "b", "c", "d", "e", "f", "s"].includes(letter)) {
|
|
@@ -1600,7 +1695,9 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1600
1695
|
}
|
|
1601
1696
|
}
|
|
1602
1697
|
if (!disabledChecks.has("abbreviation")) {
|
|
1603
|
-
const abbreviationMatches = line.matchAll(
|
|
1698
|
+
const abbreviationMatches = line.matchAll(
|
|
1699
|
+
/\b(?:const|let|var)\s+([a-z]{1,3})(?=[A-Z]|_|\s*=)/g
|
|
1700
|
+
);
|
|
1604
1701
|
for (const match of abbreviationMatches) {
|
|
1605
1702
|
const abbrev = match[1].toLowerCase();
|
|
1606
1703
|
if (allShortWords.has(abbrev)) {
|
|
@@ -1613,7 +1710,9 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1613
1710
|
new RegExp(`\\b${abbrev}\\s*=>`).test(line) || // s => on same line
|
|
1614
1711
|
// Multi-line arrow function: check context window
|
|
1615
1712
|
new RegExp(`\\b${abbrev}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
|
|
1616
|
-
new RegExp(
|
|
1713
|
+
new RegExp(
|
|
1714
|
+
`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`
|
|
1715
|
+
).test(lines[index - 1] || "") && new RegExp(`^\\s*${abbrev}\\s*=>`).test(line);
|
|
1617
1716
|
if (isArrowFunctionParam) {
|
|
1618
1717
|
continue;
|
|
1619
1718
|
}
|
|
@@ -1638,8 +1737,13 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1638
1737
|
}
|
|
1639
1738
|
}
|
|
1640
1739
|
if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
1641
|
-
const camelCaseVars = line.match(
|
|
1642
|
-
|
|
1740
|
+
const camelCaseVars = line.match(
|
|
1741
|
+
/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*=/
|
|
1742
|
+
);
|
|
1743
|
+
void camelCaseVars;
|
|
1744
|
+
const snakeCaseVars = line.match(
|
|
1745
|
+
/\b(?:const|let|var)\s+([a-z][a-z0-9]*_[a-z0-9_]*)\s*=/
|
|
1746
|
+
);
|
|
1643
1747
|
if (snakeCaseVars) {
|
|
1644
1748
|
issues.push({
|
|
1645
1749
|
file,
|
|
@@ -1652,7 +1756,9 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1652
1756
|
}
|
|
1653
1757
|
}
|
|
1654
1758
|
if (!disabledChecks.has("unclear")) {
|
|
1655
|
-
const booleanMatches = line.matchAll(
|
|
1759
|
+
const booleanMatches = line.matchAll(
|
|
1760
|
+
/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*:\s*boolean/gi
|
|
1761
|
+
);
|
|
1656
1762
|
for (const match of booleanMatches) {
|
|
1657
1763
|
const name = match[1];
|
|
1658
1764
|
if (!name.match(/^(is|has|should|can|will|did)/i)) {
|
|
@@ -1671,25 +1777,59 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1671
1777
|
const functionMatches = line.matchAll(/function\s+([a-z][a-zA-Z0-9]*)/g);
|
|
1672
1778
|
for (const match of functionMatches) {
|
|
1673
1779
|
const name = match[1];
|
|
1674
|
-
const isKeyword = [
|
|
1780
|
+
const isKeyword = [
|
|
1781
|
+
"for",
|
|
1782
|
+
"if",
|
|
1783
|
+
"else",
|
|
1784
|
+
"while",
|
|
1785
|
+
"do",
|
|
1786
|
+
"switch",
|
|
1787
|
+
"case",
|
|
1788
|
+
"break",
|
|
1789
|
+
"continue",
|
|
1790
|
+
"return",
|
|
1791
|
+
"throw",
|
|
1792
|
+
"try",
|
|
1793
|
+
"catch",
|
|
1794
|
+
"finally",
|
|
1795
|
+
"with",
|
|
1796
|
+
"yield",
|
|
1797
|
+
"await"
|
|
1798
|
+
].includes(name);
|
|
1675
1799
|
if (isKeyword) {
|
|
1676
1800
|
continue;
|
|
1677
1801
|
}
|
|
1678
|
-
const isEntryPoint = ["main", "init", "setup", "bootstrap"].includes(
|
|
1802
|
+
const isEntryPoint = ["main", "init", "setup", "bootstrap"].includes(
|
|
1803
|
+
name
|
|
1804
|
+
);
|
|
1679
1805
|
if (isEntryPoint) {
|
|
1680
1806
|
continue;
|
|
1681
1807
|
}
|
|
1682
|
-
const isFactoryPattern = name.match(
|
|
1808
|
+
const isFactoryPattern = name.match(
|
|
1809
|
+
/(Factory|Builder|Creator|Generator|Provider|Adapter|Mock)$/
|
|
1810
|
+
);
|
|
1683
1811
|
const isEventHandler = name.match(/^on[A-Z]/);
|
|
1684
1812
|
const isDescriptiveLong = name.length > 15;
|
|
1685
1813
|
const isReactHook = name.match(/^use[A-Z]/);
|
|
1686
|
-
const isDescriptivePattern = name.match(
|
|
1814
|
+
const isDescriptivePattern = name.match(
|
|
1815
|
+
/^(default|total|count|sum|avg|max|min|initial|current|previous|next)\w+/
|
|
1816
|
+
) || name.match(
|
|
1817
|
+
/\w+(Count|Total|Sum|Average|List|Map|Set|Config|Settings|Options|Props|Data|Info|Details|State|Status|Response|Result)$/
|
|
1818
|
+
);
|
|
1687
1819
|
const isHelperPattern = name.match(/^(to|from|with|without|for|as|into)\w+/) || // toMetadata, withLogger, forPath
|
|
1688
1820
|
name.match(/^\w+(To|From|With|Without|For|As|Into)\w*$/);
|
|
1689
|
-
const isUtilityName = [
|
|
1821
|
+
const isUtilityName = [
|
|
1822
|
+
"cn",
|
|
1823
|
+
"proxy",
|
|
1824
|
+
"sitemap",
|
|
1825
|
+
"robots",
|
|
1826
|
+
"gtag"
|
|
1827
|
+
].includes(name);
|
|
1690
1828
|
const capitalCount = (name.match(/[A-Z]/g) || []).length;
|
|
1691
1829
|
const isCompoundWord = capitalCount >= 3;
|
|
1692
|
-
const hasActionVerb = name.match(
|
|
1830
|
+
const hasActionVerb = name.match(
|
|
1831
|
+
/^(get|set|is|has|can|should|create|update|delete|fetch|load|save|process|handle|validate|check|find|search|filter|map|reduce|make|do|run|start|stop|build|parse|format|render|calculate|compute|generate|transform|convert|normalize|sanitize|encode|decode|compress|extract|merge|split|join|sort|compare|test|verify|ensure|apply|execute|invoke|call|emit|dispatch|trigger|listen|subscribe|unsubscribe|add|remove|clear|reset|toggle|enable|disable|open|close|connect|disconnect|send|receive|read|write|import|export|register|unregister|mount|unmount|track|store|persist|upsert|derive|classify|combine|discover|activate|require|assert|expect|mask|escape|sign|put|list|complete|page|safe|mock|pick|pluralize|text)/
|
|
1832
|
+
);
|
|
1693
1833
|
if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isDescriptivePattern && !isCompoundWord && !isHelperPattern && !isUtilityName && !isReactHook) {
|
|
1694
1834
|
issues.push({
|
|
1695
1835
|
file,
|
|
@@ -1709,6 +1849,7 @@ function analyzeFileNaming(file, content, customAbbreviations, customShortWords,
|
|
|
1709
1849
|
// src/scoring.ts
|
|
1710
1850
|
var import_core6 = require("@aiready/core");
|
|
1711
1851
|
function calculateConsistencyScore2(issues, totalFilesAnalyzed, costConfig) {
|
|
1852
|
+
void costConfig;
|
|
1712
1853
|
const criticalIssues = issues.filter((i) => i.severity === "critical").length;
|
|
1713
1854
|
const majorIssues = issues.filter((i) => i.severity === "major").length;
|
|
1714
1855
|
const minorIssues = issues.filter((i) => i.severity === "minor").length;
|