@aiready/consistency 0.21.9 → 0.21.12

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.
Files changed (132) hide show
  1. package/.turbo/turbo-build.log +23 -24
  2. package/dist/index.d.mts +3 -3
  3. package/dist/index.d.ts +3 -3
  4. package/dist/index.js +4 -4
  5. package/dist/index.mjs +3 -3
  6. package/package.json +2 -2
  7. package/src/__tests__/provider.test.ts +6 -34
  8. package/src/index.ts +3 -3
  9. package/src/provider.ts +2 -2
  10. package/dist/__tests__/analyzer.test.d.ts +0 -2
  11. package/dist/__tests__/analyzer.test.d.ts.map +0 -1
  12. package/dist/__tests__/analyzer.test.js +0 -157
  13. package/dist/__tests__/analyzer.test.js.map +0 -1
  14. package/dist/__tests__/language-filter.test.d.ts +0 -2
  15. package/dist/__tests__/language-filter.test.d.ts.map +0 -1
  16. package/dist/__tests__/language-filter.test.js +0 -46
  17. package/dist/__tests__/language-filter.test.js.map +0 -1
  18. package/dist/__tests__/scoring.test.d.ts +0 -2
  19. package/dist/__tests__/scoring.test.d.ts.map +0 -1
  20. package/dist/__tests__/scoring.test.js +0 -118
  21. package/dist/__tests__/scoring.test.js.map +0 -1
  22. package/dist/analyzer.d.ts +0 -7
  23. package/dist/analyzer.d.ts.map +0 -1
  24. package/dist/analyzer.js +0 -160
  25. package/dist/analyzer.js.map +0 -1
  26. package/dist/analyzers/naming-ast.d.ts +0 -7
  27. package/dist/analyzers/naming-ast.d.ts.map +0 -1
  28. package/dist/analyzers/naming-ast.js +0 -253
  29. package/dist/analyzers/naming-ast.js.map +0 -1
  30. package/dist/analyzers/naming-constants.d.ts +0 -21
  31. package/dist/analyzers/naming-constants.d.ts.map +0 -1
  32. package/dist/analyzers/naming-constants.js +0 -96
  33. package/dist/analyzers/naming-constants.js.map +0 -1
  34. package/dist/analyzers/naming-python.d.ts +0 -16
  35. package/dist/analyzers/naming-python.d.ts.map +0 -1
  36. package/dist/analyzers/naming-python.js +0 -165
  37. package/dist/analyzers/naming-python.js.map +0 -1
  38. package/dist/analyzers/naming.d.ts +0 -6
  39. package/dist/analyzers/naming.d.ts.map +0 -1
  40. package/dist/analyzers/naming.js +0 -234
  41. package/dist/analyzers/naming.js.map +0 -1
  42. package/dist/analyzers/patterns.d.ts +0 -10
  43. package/dist/analyzers/patterns.d.ts.map +0 -1
  44. package/dist/analyzers/patterns.js +0 -197
  45. package/dist/analyzers/patterns.js.map +0 -1
  46. package/dist/chunk-2BTBNG6X.mjs +0 -814
  47. package/dist/chunk-3ZB6FFRL.mjs +0 -661
  48. package/dist/chunk-5UFRGXSB.mjs +0 -783
  49. package/dist/chunk-66M3TIO7.mjs +0 -837
  50. package/dist/chunk-6BM5MV3S.mjs +0 -719
  51. package/dist/chunk-6H3JHDP7.mjs +0 -832
  52. package/dist/chunk-7PHHJOGC.mjs +0 -1374
  53. package/dist/chunk-AASFXGUR.mjs +0 -1622
  54. package/dist/chunk-AR7DIZLP.mjs +0 -827
  55. package/dist/chunk-BDDMOIU2.mjs +0 -385
  56. package/dist/chunk-BMILMNKJ.mjs +0 -1633
  57. package/dist/chunk-BYY6MD5T.mjs +0 -729
  58. package/dist/chunk-CA4Q5JBK.mjs +0 -1143
  59. package/dist/chunk-CF4LU7KE.mjs +0 -384
  60. package/dist/chunk-CJINEUIH.mjs +0 -1369
  61. package/dist/chunk-CLWNLHDB.mjs +0 -909
  62. package/dist/chunk-CZUJTDNH.mjs +0 -848
  63. package/dist/chunk-DNGW3WQK.mjs +0 -810
  64. package/dist/chunk-DSI3TEO2.mjs +0 -662
  65. package/dist/chunk-EIQ5K6OO.mjs +0 -1579
  66. package/dist/chunk-FEJODRK5.mjs +0 -783
  67. package/dist/chunk-FK56AZ43.mjs +0 -817
  68. package/dist/chunk-H6S7WKSQ.mjs +0 -729
  69. package/dist/chunk-HAOJLJNB.mjs +0 -1290
  70. package/dist/chunk-HJCP36VW.mjs +0 -821
  71. package/dist/chunk-HPG7P6PD.mjs +0 -1372
  72. package/dist/chunk-IVRBV7SE.mjs +0 -1295
  73. package/dist/chunk-IXBC6GVT.mjs +0 -832
  74. package/dist/chunk-J5IFYDVU.mjs +0 -1579
  75. package/dist/chunk-KWQVBF7K.mjs +0 -831
  76. package/dist/chunk-LD3CHHU2.mjs +0 -1297
  77. package/dist/chunk-LMOXGPCM.mjs +0 -722
  78. package/dist/chunk-LSXZH6X6.mjs +0 -810
  79. package/dist/chunk-LUAREV6A.mjs +0 -508
  80. package/dist/chunk-MAPVFXBP.mjs +0 -708
  81. package/dist/chunk-MM2PLUCH.mjs +0 -1376
  82. package/dist/chunk-NPWCJZUG.mjs +0 -708
  83. package/dist/chunk-ON73WHHU.mjs +0 -1310
  84. package/dist/chunk-P6NVKUBB.mjs +0 -831
  85. package/dist/chunk-Q3KTWDSL.mjs +0 -808
  86. package/dist/chunk-Q5XMWG33.mjs +0 -661
  87. package/dist/chunk-QOIPVP6P.mjs +0 -1607
  88. package/dist/chunk-RMEQWG52.mjs +0 -1633
  89. package/dist/chunk-S6BZVTWN.mjs +0 -731
  90. package/dist/chunk-TE6JYZD3.mjs +0 -810
  91. package/dist/chunk-TLVLM3M5.mjs +0 -771
  92. package/dist/chunk-TXHPUU7A.mjs +0 -863
  93. package/dist/chunk-UMBBTNQN.mjs +0 -787
  94. package/dist/chunk-V2UPXL7L.mjs +0 -842
  95. package/dist/chunk-VODCPPET.mjs +0 -1292
  96. package/dist/chunk-W6UGMKRV.mjs +0 -1310
  97. package/dist/chunk-WGH4TGZ3.mjs +0 -1288
  98. package/dist/chunk-WTBDNCEN.mjs +0 -1352
  99. package/dist/chunk-XVW5DKJQ.mjs +0 -1619
  100. package/dist/chunk-YCDCIOJN.mjs +0 -842
  101. package/dist/chunk-YEHXYHGY.mjs +0 -1497
  102. package/dist/chunk-YHHXE2JX.mjs +0 -912
  103. package/dist/chunk-ZB6UK276.mjs +0 -662
  104. package/dist/chunk-ZG3KFSD3.mjs +0 -1142
  105. package/dist/cli.d.ts.map +0 -1
  106. package/dist/cli.js.map +0 -1
  107. package/dist/index.d.ts.map +0 -1
  108. package/dist/index.js.map +0 -1
  109. package/dist/scoring.d.ts +0 -12
  110. package/dist/scoring.d.ts.map +0 -1
  111. package/dist/scoring.js +0 -110
  112. package/dist/scoring.js.map +0 -1
  113. package/dist/types.d.ts +0 -53
  114. package/dist/types.d.ts.map +0 -1
  115. package/dist/types.js +0 -2
  116. package/dist/types.js.map +0 -1
  117. package/dist/utils/ast-parser.d.ts +0 -46
  118. package/dist/utils/ast-parser.d.ts.map +0 -1
  119. package/dist/utils/ast-parser.js +0 -157
  120. package/dist/utils/ast-parser.js.map +0 -1
  121. package/dist/utils/config-loader.d.ts +0 -19
  122. package/dist/utils/config-loader.d.ts.map +0 -1
  123. package/dist/utils/config-loader.js +0 -31
  124. package/dist/utils/config-loader.js.map +0 -1
  125. package/dist/utils/context-detector.d.ts +0 -40
  126. package/dist/utils/context-detector.d.ts.map +0 -1
  127. package/dist/utils/context-detector.js +0 -225
  128. package/dist/utils/context-detector.js.map +0 -1
  129. package/dist/utils/scope-tracker.d.ts +0 -87
  130. package/dist/utils/scope-tracker.d.ts.map +0 -1
  131. package/dist/utils/scope-tracker.js +0 -161
  132. package/dist/utils/scope-tracker.js.map +0 -1
@@ -1,814 +0,0 @@
1
- // src/analyzers/naming.ts
2
- import { readFileContent, loadConfig } from "@aiready/core";
3
- import { dirname } from "path";
4
- var COMMON_SHORT_WORDS = /* @__PURE__ */ new Set([
5
- // Full English words (1-3 letters)
6
- "day",
7
- "key",
8
- "net",
9
- "to",
10
- "go",
11
- "for",
12
- "not",
13
- "new",
14
- "old",
15
- "top",
16
- "end",
17
- "run",
18
- "try",
19
- "use",
20
- "get",
21
- "set",
22
- "add",
23
- "put",
24
- "map",
25
- "log",
26
- "row",
27
- "col",
28
- "tab",
29
- "box",
30
- "div",
31
- "nav",
32
- "tag",
33
- "any",
34
- "all",
35
- "one",
36
- "two",
37
- "out",
38
- "off",
39
- "on",
40
- "yes",
41
- "no",
42
- "now",
43
- "max",
44
- "min",
45
- "sum",
46
- "avg",
47
- "ref",
48
- "src",
49
- "dst",
50
- "raw",
51
- "def",
52
- "sub",
53
- "pub",
54
- "pre",
55
- "mid",
56
- "alt",
57
- "opt",
58
- "tmp",
59
- "ext",
60
- "sep",
61
- // Additional full words commonly flagged
62
- "tax",
63
- "cat",
64
- "dog",
65
- "car",
66
- "bus",
67
- "web",
68
- "app",
69
- "war",
70
- "law",
71
- "pay",
72
- "buy",
73
- "win",
74
- "cut",
75
- "hit",
76
- "hot",
77
- "pop",
78
- "job",
79
- "age",
80
- "act",
81
- "let",
82
- "lot",
83
- "bad",
84
- "big",
85
- "far",
86
- "few",
87
- "own",
88
- "per",
89
- "red",
90
- "low",
91
- "see",
92
- "six",
93
- "ten",
94
- "way",
95
- "who",
96
- "why",
97
- "yet",
98
- "via",
99
- "due",
100
- "fee",
101
- "fun",
102
- "gas",
103
- "gay",
104
- "god",
105
- "gun",
106
- "guy",
107
- "ice",
108
- "ill",
109
- "kid",
110
- "mad",
111
- "man",
112
- "mix",
113
- "mom",
114
- "mrs",
115
- "nor",
116
- "odd",
117
- "oil",
118
- "pan",
119
- "pet",
120
- "pit",
121
- "pot",
122
- "pow",
123
- "pro",
124
- "raw",
125
- "rep",
126
- "rid",
127
- "sad",
128
- "sea",
129
- "sit",
130
- "sky",
131
- "son",
132
- "tea",
133
- "tie",
134
- "tip",
135
- "van",
136
- "war",
137
- "win",
138
- "won"
139
- ]);
140
- var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
141
- // Standard identifiers
142
- "id",
143
- "uid",
144
- "gid",
145
- "pid",
146
- // Loop counters and iterators
147
- "i",
148
- "j",
149
- "k",
150
- "n",
151
- "m",
152
- // Web/Network
153
- "url",
154
- "uri",
155
- "api",
156
- "cdn",
157
- "dns",
158
- "ip",
159
- "tcp",
160
- "udp",
161
- "http",
162
- "ssl",
163
- "tls",
164
- "utm",
165
- "seo",
166
- "rss",
167
- "xhr",
168
- "ajax",
169
- "cors",
170
- "ws",
171
- "wss",
172
- // Data formats
173
- "json",
174
- "xml",
175
- "yaml",
176
- "csv",
177
- "html",
178
- "css",
179
- "svg",
180
- "pdf",
181
- // File types & extensions
182
- "img",
183
- "txt",
184
- "doc",
185
- "docx",
186
- "xlsx",
187
- "ppt",
188
- "md",
189
- "rst",
190
- "jpg",
191
- "png",
192
- "gif",
193
- // Databases
194
- "db",
195
- "sql",
196
- "orm",
197
- "dao",
198
- "dto",
199
- "ddb",
200
- "rds",
201
- "nosql",
202
- // File system
203
- "fs",
204
- "dir",
205
- "tmp",
206
- "src",
207
- "dst",
208
- "bin",
209
- "lib",
210
- "pkg",
211
- // Operating system
212
- "os",
213
- "env",
214
- "arg",
215
- "cli",
216
- "cmd",
217
- "exe",
218
- "cwd",
219
- "pwd",
220
- // UI/UX
221
- "ui",
222
- "ux",
223
- "gui",
224
- "dom",
225
- "ref",
226
- // Request/Response
227
- "req",
228
- "res",
229
- "ctx",
230
- "err",
231
- "msg",
232
- "auth",
233
- // Mathematics/Computing
234
- "max",
235
- "min",
236
- "avg",
237
- "sum",
238
- "abs",
239
- "cos",
240
- "sin",
241
- "tan",
242
- "log",
243
- "exp",
244
- "pow",
245
- "sqrt",
246
- "std",
247
- "var",
248
- "int",
249
- "num",
250
- "idx",
251
- // Time
252
- "now",
253
- "utc",
254
- "tz",
255
- "ms",
256
- "sec",
257
- "hr",
258
- "min",
259
- "yr",
260
- "mo",
261
- // Common patterns
262
- "app",
263
- "cfg",
264
- "config",
265
- "init",
266
- "len",
267
- "val",
268
- "str",
269
- "obj",
270
- "arr",
271
- "gen",
272
- "def",
273
- "raw",
274
- "new",
275
- "old",
276
- "pre",
277
- "post",
278
- "sub",
279
- "pub",
280
- // Programming/Framework specific
281
- "ts",
282
- "js",
283
- "jsx",
284
- "tsx",
285
- "py",
286
- "rb",
287
- "vue",
288
- "re",
289
- "fn",
290
- "fns",
291
- "mod",
292
- "opts",
293
- "dev",
294
- // Cloud/Infrastructure
295
- "s3",
296
- "ec2",
297
- "sqs",
298
- "sns",
299
- "vpc",
300
- "ami",
301
- "iam",
302
- "acl",
303
- "elb",
304
- "alb",
305
- "nlb",
306
- "aws",
307
- // Metrics/Performance
308
- "fcp",
309
- "lcp",
310
- "cls",
311
- "ttfb",
312
- "tti",
313
- "fid",
314
- "fps",
315
- "qps",
316
- "rps",
317
- "tps",
318
- // Testing & i18n
319
- "po",
320
- "e2e",
321
- "a11y",
322
- "i18n",
323
- "l10n",
324
- // Domain-specific abbreviations (context-aware)
325
- "sk",
326
- "fy",
327
- "faq",
328
- "og",
329
- "seo",
330
- "cta",
331
- "roi",
332
- "kpi",
333
- // Boolean helpers (these are intentional short names)
334
- "is",
335
- "has",
336
- "can",
337
- "did",
338
- "was",
339
- "are",
340
- // Date/Time context (when in date contexts)
341
- "d",
342
- "t",
343
- "dt"
344
- ]);
345
- async function analyzeNaming(files) {
346
- const issues = [];
347
- const rootDir = files.length > 0 ? dirname(files[0]) : process.cwd();
348
- const config = loadConfig(rootDir);
349
- const consistencyConfig = config?.tools?.["consistency"];
350
- const customAbbreviations = new Set(consistencyConfig?.acceptedAbbreviations || []);
351
- const customShortWords = new Set(consistencyConfig?.shortWords || []);
352
- const disabledChecks = new Set(consistencyConfig?.disableChecks || []);
353
- for (const file of files) {
354
- const content = await readFileContent(file);
355
- const fileIssues = analyzeFileNaming(file, content, customAbbreviations, customShortWords, disabledChecks);
356
- issues.push(...fileIssues);
357
- }
358
- return issues;
359
- }
360
- function analyzeFileNaming(file, content, customAbbreviations, customShortWords, disabledChecks) {
361
- const issues = [];
362
- const isTestFile = file.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/);
363
- const lines = content.split("\n");
364
- const allAbbreviations = /* @__PURE__ */ new Set([...ACCEPTABLE_ABBREVIATIONS, ...customAbbreviations]);
365
- const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
366
- const getContextWindow = (index, windowSize = 3) => {
367
- const start = Math.max(0, index - windowSize);
368
- const end = Math.min(lines.length, index + windowSize + 1);
369
- return lines.slice(start, end).join("\n");
370
- };
371
- const isShortLivedVariable = (varName, declarationIndex) => {
372
- const searchRange = 5;
373
- const endIndex = Math.min(lines.length, declarationIndex + searchRange + 1);
374
- let usageCount = 0;
375
- for (let i = declarationIndex; i < endIndex; i++) {
376
- const regex = new RegExp(`\\b${varName}\\b`, "g");
377
- const matches = lines[i].match(regex);
378
- if (matches) {
379
- usageCount += matches.length;
380
- }
381
- }
382
- return usageCount >= 2 && usageCount <= 3;
383
- };
384
- lines.forEach((line, index) => {
385
- const lineNumber = index + 1;
386
- const contextWindow = getContextWindow(index);
387
- if (!disabledChecks.has("single-letter")) {
388
- const singleLetterMatches = line.matchAll(/\b(?:const|let|var)\s+([a-hm-z])\s*=/gi);
389
- for (const match of singleLetterMatches) {
390
- const letter = match[1].toLowerCase();
391
- const isInLoopContext = line.includes("for") || /\.(map|filter|forEach|reduce|find|some|every)\s*\(/.test(line) || line.includes("=>") || // Arrow function
392
- /\w+\s*=>\s*/.test(line);
393
- const isI18nContext = line.includes("useTranslation") || line.includes("i18n.t") || /\bt\s*\(['"]/.test(line);
394
- const isArrowFunctionParam = /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
395
- /[a-z]\s*=>/.test(line) || // s => on same line
396
- // Multi-line arrow function detection: look for pattern in context window
397
- new RegExp(`\\b${letter}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
398
- new RegExp(`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`).test(lines[index - 1] || "") && /=>/.test(contextWindow);
399
- const isShortLived = isShortLivedVariable(letter, index);
400
- if (!isInLoopContext && !isI18nContext && !isArrowFunctionParam && !isShortLived && !["x", "y", "z", "i", "j", "k", "l", "n", "m"].includes(letter)) {
401
- if (isTestFile && ["a", "b", "c", "d", "e", "f", "s"].includes(letter)) {
402
- continue;
403
- }
404
- issues.push({
405
- file,
406
- line: lineNumber,
407
- type: "poor-naming",
408
- identifier: match[1],
409
- severity: "minor",
410
- suggestion: `Use descriptive variable name instead of single letter '${match[1]}'`
411
- });
412
- }
413
- }
414
- }
415
- if (!disabledChecks.has("abbreviation")) {
416
- const abbreviationMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z]{1,3})(?=[A-Z]|_|\s*=)/g);
417
- for (const match of abbreviationMatches) {
418
- const abbrev = match[1].toLowerCase();
419
- if (allShortWords.has(abbrev)) {
420
- continue;
421
- }
422
- if (allAbbreviations.has(abbrev)) {
423
- continue;
424
- }
425
- const isArrowFunctionParam = /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
426
- new RegExp(`\\b${abbrev}\\s*=>`).test(line) || // s => on same line
427
- // Multi-line arrow function: check context window
428
- new RegExp(`\\b${abbrev}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
429
- new RegExp(`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`).test(lines[index - 1] || "") && new RegExp(`^\\s*${abbrev}\\s*=>`).test(line);
430
- if (isArrowFunctionParam) {
431
- continue;
432
- }
433
- if (abbrev.length <= 2) {
434
- const isDateTimeContext = /date|time|day|hour|minute|second|timestamp/i.test(line);
435
- if (isDateTimeContext && ["d", "t", "dt"].includes(abbrev)) {
436
- continue;
437
- }
438
- const isUserContext = /user|auth|account/i.test(line);
439
- if (isUserContext && abbrev === "u") {
440
- continue;
441
- }
442
- }
443
- issues.push({
444
- file,
445
- line: lineNumber,
446
- type: "abbreviation",
447
- identifier: match[1],
448
- severity: "info",
449
- suggestion: `Consider using full word instead of abbreviation '${match[1]}'`
450
- });
451
- }
452
- }
453
- if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
454
- const camelCaseVars = line.match(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*=/);
455
- const snakeCaseVars = line.match(/\b(?:const|let|var)\s+([a-z][a-z0-9]*_[a-z0-9_]*)\s*=/);
456
- if (snakeCaseVars) {
457
- issues.push({
458
- file,
459
- line: lineNumber,
460
- type: "convention-mix",
461
- identifier: snakeCaseVars[1],
462
- severity: "minor",
463
- suggestion: `Use camelCase '${snakeCaseToCamelCase(snakeCaseVars[1])}' instead of snake_case in TypeScript/JavaScript`
464
- });
465
- }
466
- }
467
- if (!disabledChecks.has("unclear")) {
468
- const booleanMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*:\s*boolean/gi);
469
- for (const match of booleanMatches) {
470
- const name = match[1];
471
- if (!name.match(/^(is|has|should|can|will|did)/i)) {
472
- issues.push({
473
- file,
474
- line: lineNumber,
475
- type: "unclear",
476
- identifier: name,
477
- severity: "info",
478
- suggestion: `Boolean variable '${name}' should start with is/has/should/can for clarity`
479
- });
480
- }
481
- }
482
- }
483
- if (!disabledChecks.has("unclear")) {
484
- const functionMatches = line.matchAll(/function\s+([a-z][a-zA-Z0-9]*)/g);
485
- for (const match of functionMatches) {
486
- const name = match[1];
487
- const isKeyword = ["for", "if", "else", "while", "do", "switch", "case", "break", "continue", "return", "throw", "try", "catch", "finally", "with", "yield", "await"].includes(name);
488
- if (isKeyword) {
489
- continue;
490
- }
491
- const isEntryPoint = ["main", "init", "setup", "bootstrap"].includes(name);
492
- if (isEntryPoint) {
493
- continue;
494
- }
495
- const isFactoryPattern = name.match(/(Factory|Builder|Creator|Generator)$/);
496
- const isEventHandler = name.match(/^on[A-Z]/);
497
- const isDescriptiveLong = name.length > 15;
498
- const isDescriptivePattern = name.match(/^(default|total|count|sum|avg|max|min|initial|current|previous|next)\w+/) || name.match(/\w+(Count|Total|Sum|Average|List|Map|Set|Config|Settings|Options|Props)$/);
499
- const capitalCount = (name.match(/[A-Z]/g) || []).length;
500
- const isCompoundWord = capitalCount >= 3;
501
- const hasActionVerb = name.match(/^(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)/);
502
- if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isDescriptivePattern && !isCompoundWord) {
503
- issues.push({
504
- file,
505
- line: lineNumber,
506
- type: "unclear",
507
- identifier: name,
508
- severity: "info",
509
- suggestion: `Function '${name}' should start with an action verb (get, set, create, etc.)`
510
- });
511
- }
512
- }
513
- }
514
- });
515
- return issues;
516
- }
517
- function snakeCaseToCamelCase(str) {
518
- return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
519
- }
520
- function detectNamingConventions(files, allIssues) {
521
- const camelCaseCount = allIssues.filter((i) => i.type === "convention-mix").length;
522
- const totalChecks = files.length * 10;
523
- if (camelCaseCount / totalChecks > 0.3) {
524
- return { dominantConvention: "mixed", conventionScore: 0.5 };
525
- }
526
- return { dominantConvention: "camelCase", conventionScore: 0.9 };
527
- }
528
-
529
- // src/analyzers/patterns.ts
530
- import { readFileContent as readFileContent2 } from "@aiready/core";
531
- async function analyzePatterns(files) {
532
- const issues = [];
533
- const errorHandlingIssues = await analyzeErrorHandling(files);
534
- issues.push(...errorHandlingIssues);
535
- const asyncIssues = await analyzeAsyncPatterns(files);
536
- issues.push(...asyncIssues);
537
- const importIssues = await analyzeImportStyles(files);
538
- issues.push(...importIssues);
539
- return issues;
540
- }
541
- async function analyzeErrorHandling(files) {
542
- const patterns = {
543
- tryCatch: [],
544
- throwsError: [],
545
- returnsNull: [],
546
- returnsError: []
547
- };
548
- for (const file of files) {
549
- const content = await readFileContent2(file);
550
- if (content.includes("try {") || content.includes("} catch")) {
551
- patterns.tryCatch.push(file);
552
- }
553
- if (content.match(/throw new \w*Error/)) {
554
- patterns.throwsError.push(file);
555
- }
556
- if (content.match(/return null/)) {
557
- patterns.returnsNull.push(file);
558
- }
559
- if (content.match(/return \{ error:/)) {
560
- patterns.returnsError.push(file);
561
- }
562
- }
563
- const issues = [];
564
- const strategiesUsed = Object.values(patterns).filter((p) => p.length > 0).length;
565
- if (strategiesUsed > 2) {
566
- issues.push({
567
- files: [.../* @__PURE__ */ new Set([
568
- ...patterns.tryCatch,
569
- ...patterns.throwsError,
570
- ...patterns.returnsNull,
571
- ...patterns.returnsError
572
- ])],
573
- type: "error-handling",
574
- description: "Inconsistent error handling strategies across codebase",
575
- examples: [
576
- patterns.tryCatch.length > 0 ? `Try-catch used in ${patterns.tryCatch.length} files` : "",
577
- patterns.throwsError.length > 0 ? `Throws errors in ${patterns.throwsError.length} files` : "",
578
- patterns.returnsNull.length > 0 ? `Returns null in ${patterns.returnsNull.length} files` : "",
579
- patterns.returnsError.length > 0 ? `Returns error objects in ${patterns.returnsError.length} files` : ""
580
- ].filter((e) => e),
581
- severity: "major"
582
- });
583
- }
584
- return issues;
585
- }
586
- async function analyzeAsyncPatterns(files) {
587
- const patterns = {
588
- asyncAwait: [],
589
- promises: [],
590
- callbacks: []
591
- };
592
- for (const file of files) {
593
- const content = await readFileContent2(file);
594
- if (content.match(/async\s+(function|\(|[a-zA-Z])/)) {
595
- patterns.asyncAwait.push(file);
596
- }
597
- if (content.match(/\.then\(/) || content.match(/\.catch\(/)) {
598
- patterns.promises.push(file);
599
- }
600
- if (content.match(/callback\s*\(/) || content.match(/\(\s*err\s*,/)) {
601
- patterns.callbacks.push(file);
602
- }
603
- }
604
- const issues = [];
605
- if (patterns.callbacks.length > 0 && patterns.asyncAwait.length > 0) {
606
- issues.push({
607
- files: [...patterns.callbacks, ...patterns.asyncAwait],
608
- type: "async-style",
609
- description: "Mixed async patterns: callbacks and async/await",
610
- examples: [
611
- `Callbacks found in: ${patterns.callbacks.slice(0, 3).join(", ")}`,
612
- `Async/await used in: ${patterns.asyncAwait.slice(0, 3).join(", ")}`
613
- ],
614
- severity: "minor"
615
- });
616
- }
617
- if (patterns.promises.length > patterns.asyncAwait.length * 0.3 && patterns.asyncAwait.length > 0) {
618
- issues.push({
619
- files: patterns.promises,
620
- type: "async-style",
621
- description: "Consider using async/await instead of promise chains for consistency",
622
- examples: patterns.promises.slice(0, 5),
623
- severity: "info"
624
- });
625
- }
626
- return issues;
627
- }
628
- async function analyzeImportStyles(files) {
629
- const patterns = {
630
- esModules: [],
631
- commonJS: [],
632
- mixed: []
633
- };
634
- for (const file of files) {
635
- const content = await readFileContent2(file);
636
- const hasESM = content.match(/^import\s+/m);
637
- const hasCJS = content.match(/require\s*\(/);
638
- if (hasESM && hasCJS) {
639
- patterns.mixed.push(file);
640
- } else if (hasESM) {
641
- patterns.esModules.push(file);
642
- } else if (hasCJS) {
643
- patterns.commonJS.push(file);
644
- }
645
- }
646
- const issues = [];
647
- if (patterns.mixed.length > 0) {
648
- issues.push({
649
- files: patterns.mixed,
650
- type: "import-style",
651
- description: "Mixed ES modules and CommonJS imports in same files",
652
- examples: patterns.mixed.slice(0, 5),
653
- severity: "major"
654
- });
655
- }
656
- if (patterns.esModules.length > 0 && patterns.commonJS.length > 0) {
657
- const ratio = patterns.commonJS.length / (patterns.esModules.length + patterns.commonJS.length);
658
- if (ratio > 0.2 && ratio < 0.8) {
659
- issues.push({
660
- files: [...patterns.esModules, ...patterns.commonJS],
661
- type: "import-style",
662
- description: "Inconsistent import styles across project",
663
- examples: [
664
- `ES modules: ${patterns.esModules.length} files`,
665
- `CommonJS: ${patterns.commonJS.length} files`
666
- ],
667
- severity: "minor"
668
- });
669
- }
670
- }
671
- return issues;
672
- }
673
-
674
- // src/analyzer.ts
675
- import { scanFiles } from "@aiready/core";
676
- async function analyzeConsistency(options) {
677
- const {
678
- checkNaming = true,
679
- checkPatterns = true,
680
- checkArchitecture = false,
681
- // Not implemented yet
682
- minSeverity = "info",
683
- ...scanOptions
684
- } = options;
685
- const filePaths = await scanFiles(scanOptions);
686
- const namingIssues = checkNaming ? await analyzeNaming(filePaths) : [];
687
- const patternIssues = checkPatterns ? await analyzePatterns(filePaths) : [];
688
- const results = [];
689
- const fileIssuesMap = /* @__PURE__ */ new Map();
690
- for (const issue of namingIssues) {
691
- if (!shouldIncludeSeverity(issue.severity, minSeverity)) {
692
- continue;
693
- }
694
- const consistencyIssue = {
695
- type: issue.type === "convention-mix" ? "naming-inconsistency" : "naming-quality",
696
- category: "naming",
697
- severity: issue.severity,
698
- message: `${issue.type}: ${issue.identifier}`,
699
- location: {
700
- file: issue.file,
701
- line: issue.line,
702
- column: issue.column
703
- },
704
- suggestion: issue.suggestion
705
- };
706
- if (!fileIssuesMap.has(issue.file)) {
707
- fileIssuesMap.set(issue.file, []);
708
- }
709
- fileIssuesMap.get(issue.file).push(consistencyIssue);
710
- }
711
- for (const issue of patternIssues) {
712
- if (!shouldIncludeSeverity(issue.severity, minSeverity)) {
713
- continue;
714
- }
715
- const consistencyIssue = {
716
- type: "pattern-inconsistency",
717
- category: "patterns",
718
- severity: issue.severity,
719
- message: issue.description,
720
- location: {
721
- file: issue.files[0] || "multiple files",
722
- line: 1
723
- },
724
- examples: issue.examples,
725
- suggestion: `Standardize ${issue.type} patterns across ${issue.files.length} files`
726
- };
727
- const firstFile = issue.files[0];
728
- if (firstFile && !fileIssuesMap.has(firstFile)) {
729
- fileIssuesMap.set(firstFile, []);
730
- }
731
- if (firstFile) {
732
- fileIssuesMap.get(firstFile).push(consistencyIssue);
733
- }
734
- }
735
- for (const [fileName, issues] of fileIssuesMap) {
736
- results.push({
737
- fileName,
738
- issues,
739
- metrics: {
740
- consistencyScore: calculateConsistencyScore(issues)
741
- }
742
- });
743
- }
744
- const recommendations = generateRecommendations(namingIssues, patternIssues);
745
- const conventionAnalysis = detectNamingConventions(filePaths, namingIssues);
746
- return {
747
- summary: {
748
- totalIssues: namingIssues.length + patternIssues.length,
749
- namingIssues: namingIssues.length,
750
- patternIssues: patternIssues.length,
751
- architectureIssues: 0,
752
- filesAnalyzed: filePaths.length
753
- },
754
- results,
755
- recommendations
756
- };
757
- }
758
- function shouldIncludeSeverity(severity, minSeverity) {
759
- const severityLevels = { info: 0, minor: 1, major: 2, critical: 3 };
760
- return severityLevels[severity] >= severityLevels[minSeverity];
761
- }
762
- function calculateConsistencyScore(issues) {
763
- const weights = { critical: 10, major: 5, minor: 2, info: 1 };
764
- const totalWeight = issues.reduce((sum, issue) => sum + weights[issue.severity], 0);
765
- return Math.max(0, 1 - totalWeight / 100);
766
- }
767
- function generateRecommendations(namingIssues, patternIssues) {
768
- const recommendations = [];
769
- if (namingIssues.length > 0) {
770
- const conventionMixCount = namingIssues.filter((i) => i.type === "convention-mix").length;
771
- if (conventionMixCount > 0) {
772
- recommendations.push(
773
- `Standardize naming conventions: Found ${conventionMixCount} snake_case variables in TypeScript/JavaScript (use camelCase)`
774
- );
775
- }
776
- const poorNamingCount = namingIssues.filter((i) => i.type === "poor-naming").length;
777
- if (poorNamingCount > 0) {
778
- recommendations.push(
779
- `Improve variable naming: Found ${poorNamingCount} single-letter or unclear variable names`
780
- );
781
- }
782
- }
783
- if (patternIssues.length > 0) {
784
- const errorHandlingIssues = patternIssues.filter((i) => i.type === "error-handling");
785
- if (errorHandlingIssues.length > 0) {
786
- recommendations.push(
787
- "Standardize error handling strategy across the codebase (prefer try-catch with typed errors)"
788
- );
789
- }
790
- const asyncIssues = patternIssues.filter((i) => i.type === "async-style");
791
- if (asyncIssues.length > 0) {
792
- recommendations.push(
793
- "Use async/await consistently instead of mixing with promise chains or callbacks"
794
- );
795
- }
796
- const importIssues = patternIssues.filter((i) => i.type === "import-style");
797
- if (importIssues.length > 0) {
798
- recommendations.push(
799
- "Use ES modules consistently across the project (avoid mixing with CommonJS)"
800
- );
801
- }
802
- }
803
- if (recommendations.length === 0) {
804
- recommendations.push("No major consistency issues found! Your codebase follows good practices.");
805
- }
806
- return recommendations;
807
- }
808
-
809
- export {
810
- analyzeNaming,
811
- detectNamingConventions,
812
- analyzePatterns,
813
- analyzeConsistency
814
- };