@aiready/consistency 0.3.3 → 0.3.5

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/cli.js CHANGED
@@ -31,12 +31,155 @@ var import_core3 = require("@aiready/core");
31
31
 
32
32
  // src/analyzers/naming.ts
33
33
  var import_core = require("@aiready/core");
34
+ var import_path = require("path");
35
+ var COMMON_SHORT_WORDS = /* @__PURE__ */ new Set([
36
+ // Full English words (1-3 letters)
37
+ "day",
38
+ "key",
39
+ "net",
40
+ "to",
41
+ "go",
42
+ "for",
43
+ "not",
44
+ "new",
45
+ "old",
46
+ "top",
47
+ "end",
48
+ "run",
49
+ "try",
50
+ "use",
51
+ "get",
52
+ "set",
53
+ "add",
54
+ "put",
55
+ "map",
56
+ "log",
57
+ "row",
58
+ "col",
59
+ "tab",
60
+ "box",
61
+ "div",
62
+ "nav",
63
+ "tag",
64
+ "any",
65
+ "all",
66
+ "one",
67
+ "two",
68
+ "out",
69
+ "off",
70
+ "on",
71
+ "yes",
72
+ "no",
73
+ "now",
74
+ "max",
75
+ "min",
76
+ "sum",
77
+ "avg",
78
+ "ref",
79
+ "src",
80
+ "dst",
81
+ "raw",
82
+ "def",
83
+ "sub",
84
+ "pub",
85
+ "pre",
86
+ "mid",
87
+ "alt",
88
+ "opt",
89
+ "tmp",
90
+ "ext",
91
+ "sep",
92
+ // Additional full words commonly flagged
93
+ "tax",
94
+ "cat",
95
+ "dog",
96
+ "car",
97
+ "bus",
98
+ "web",
99
+ "app",
100
+ "war",
101
+ "law",
102
+ "pay",
103
+ "buy",
104
+ "win",
105
+ "cut",
106
+ "hit",
107
+ "hot",
108
+ "pop",
109
+ "job",
110
+ "age",
111
+ "act",
112
+ "let",
113
+ "lot",
114
+ "bad",
115
+ "big",
116
+ "far",
117
+ "few",
118
+ "own",
119
+ "per",
120
+ "red",
121
+ "low",
122
+ "see",
123
+ "six",
124
+ "ten",
125
+ "way",
126
+ "who",
127
+ "why",
128
+ "yet",
129
+ "via",
130
+ "due",
131
+ "fee",
132
+ "fun",
133
+ "gas",
134
+ "gay",
135
+ "god",
136
+ "gun",
137
+ "guy",
138
+ "ice",
139
+ "ill",
140
+ "kid",
141
+ "mad",
142
+ "man",
143
+ "mix",
144
+ "mom",
145
+ "mrs",
146
+ "nor",
147
+ "odd",
148
+ "oil",
149
+ "pan",
150
+ "pet",
151
+ "pit",
152
+ "pot",
153
+ "pow",
154
+ "pro",
155
+ "raw",
156
+ "rep",
157
+ "rid",
158
+ "sad",
159
+ "sea",
160
+ "sit",
161
+ "sky",
162
+ "son",
163
+ "tea",
164
+ "tie",
165
+ "tip",
166
+ "van",
167
+ "war",
168
+ "win",
169
+ "won"
170
+ ]);
34
171
  var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
35
172
  // Standard identifiers
36
173
  "id",
37
174
  "uid",
38
175
  "gid",
39
176
  "pid",
177
+ // Loop counters and iterators
178
+ "i",
179
+ "j",
180
+ "k",
181
+ "n",
182
+ "m",
40
183
  // Web/Network
41
184
  "url",
42
185
  "uri",
@@ -54,6 +197,9 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
54
197
  "rss",
55
198
  "xhr",
56
199
  "ajax",
200
+ "cors",
201
+ "ws",
202
+ "wss",
57
203
  // Data formats
58
204
  "json",
59
205
  "xml",
@@ -63,12 +209,27 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
63
209
  "css",
64
210
  "svg",
65
211
  "pdf",
212
+ // File types & extensions
213
+ "img",
214
+ "txt",
215
+ "doc",
216
+ "docx",
217
+ "xlsx",
218
+ "ppt",
219
+ "md",
220
+ "rst",
221
+ "jpg",
222
+ "png",
223
+ "gif",
66
224
  // Databases
67
225
  "db",
68
226
  "sql",
69
227
  "orm",
70
228
  "dao",
71
229
  "dto",
230
+ "ddb",
231
+ "rds",
232
+ "nosql",
72
233
  // File system
73
234
  "fs",
74
235
  "dir",
@@ -85,6 +246,8 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
85
246
  "cli",
86
247
  "cmd",
87
248
  "exe",
249
+ "cwd",
250
+ "pwd",
88
251
  // UI/UX
89
252
  "ui",
90
253
  "ux",
@@ -97,6 +260,7 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
97
260
  "ctx",
98
261
  "err",
99
262
  "msg",
263
+ "auth",
100
264
  // Mathematics/Computing
101
265
  "max",
102
266
  "min",
@@ -114,12 +278,17 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
114
278
  "var",
115
279
  "int",
116
280
  "num",
281
+ "idx",
117
282
  // Time
118
283
  "now",
119
284
  "utc",
120
285
  "tz",
121
286
  "ms",
122
287
  "sec",
288
+ "hr",
289
+ "min",
290
+ "yr",
291
+ "mo",
123
292
  // Common patterns
124
293
  "app",
125
294
  "cfg",
@@ -139,47 +308,169 @@ var ACCEPTABLE_ABBREVIATIONS = /* @__PURE__ */ new Set([
139
308
  "post",
140
309
  "sub",
141
310
  "pub",
311
+ // Programming/Framework specific
312
+ "ts",
313
+ "js",
314
+ "jsx",
315
+ "tsx",
316
+ "py",
317
+ "rb",
318
+ "vue",
319
+ "re",
320
+ "fn",
321
+ "fns",
322
+ "mod",
323
+ "opts",
324
+ "dev",
325
+ // Cloud/Infrastructure
326
+ "s3",
327
+ "ec2",
328
+ "sqs",
329
+ "sns",
330
+ "vpc",
331
+ "ami",
332
+ "iam",
333
+ "acl",
334
+ "elb",
335
+ "alb",
336
+ "nlb",
337
+ "aws",
338
+ // Metrics/Performance
339
+ "fcp",
340
+ "lcp",
341
+ "cls",
342
+ "ttfb",
343
+ "tti",
344
+ "fid",
345
+ "fps",
346
+ "qps",
347
+ "rps",
348
+ "tps",
349
+ // Testing & i18n
350
+ "po",
351
+ "e2e",
352
+ "a11y",
353
+ "i18n",
354
+ "l10n",
355
+ // Domain-specific abbreviations (context-aware)
356
+ "sk",
357
+ "fy",
358
+ "faq",
359
+ "og",
360
+ "seo",
361
+ "cta",
362
+ "roi",
363
+ "kpi",
142
364
  // Boolean helpers (these are intentional short names)
143
365
  "is",
144
366
  "has",
145
367
  "can",
146
368
  "did",
147
369
  "was",
148
- "are"
370
+ "are",
371
+ // Date/Time context (when in date contexts)
372
+ "d",
373
+ "t",
374
+ "dt"
149
375
  ]);
150
376
  async function analyzeNaming(files) {
151
377
  const issues = [];
378
+ const rootDir = files.length > 0 ? (0, import_path.dirname)(files[0]) : process.cwd();
379
+ const config = (0, import_core.loadConfig)(rootDir);
380
+ const consistencyConfig = config?.tools?.["consistency"];
381
+ const customAbbreviations = new Set(consistencyConfig?.acceptedAbbreviations || []);
382
+ const customShortWords = new Set(consistencyConfig?.shortWords || []);
383
+ const disabledChecks = new Set(consistencyConfig?.disableChecks || []);
152
384
  for (const file of files) {
153
385
  const content = await (0, import_core.readFileContent)(file);
154
- const fileIssues = analyzeFileNaming(file, content);
386
+ const fileIssues = analyzeFileNaming(file, content, customAbbreviations, customShortWords, disabledChecks);
155
387
  issues.push(...fileIssues);
156
388
  }
157
389
  return issues;
158
390
  }
159
- function analyzeFileNaming(file, content) {
391
+ function analyzeFileNaming(file, content, customAbbreviations, customShortWords, disabledChecks) {
160
392
  const issues = [];
393
+ const isTestFile = file.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/);
161
394
  const lines = content.split("\n");
395
+ const allAbbreviations = /* @__PURE__ */ new Set([...ACCEPTABLE_ABBREVIATIONS, ...customAbbreviations]);
396
+ const allShortWords = /* @__PURE__ */ new Set([...COMMON_SHORT_WORDS, ...customShortWords]);
397
+ const getContextWindow = (index, windowSize = 3) => {
398
+ const start = Math.max(0, index - windowSize);
399
+ const end = Math.min(lines.length, index + windowSize + 1);
400
+ return lines.slice(start, end).join("\n");
401
+ };
402
+ const isShortLivedVariable = (varName, declarationIndex) => {
403
+ const searchRange = 5;
404
+ const endIndex = Math.min(lines.length, declarationIndex + searchRange + 1);
405
+ let usageCount = 0;
406
+ for (let i = declarationIndex; i < endIndex; i++) {
407
+ const regex = new RegExp(`\\b${varName}\\b`, "g");
408
+ const matches = lines[i].match(regex);
409
+ if (matches) {
410
+ usageCount += matches.length;
411
+ }
412
+ }
413
+ return usageCount >= 2 && usageCount <= 3;
414
+ };
162
415
  lines.forEach((line, index) => {
163
416
  const lineNumber = index + 1;
164
- const singleLetterMatches = line.matchAll(/\b(?:const|let|var)\s+([a-hm-z])\s*=/gi);
165
- for (const match of singleLetterMatches) {
166
- const letter = match[1].toLowerCase();
167
- const isInLoopContext = line.includes("for") || line.includes(".map") || line.includes(".filter") || line.includes(".forEach") || line.includes(".reduce");
168
- if (!isInLoopContext && !["x", "y", "z", "i", "j", "k", "l", "n", "m"].includes(letter)) {
169
- issues.push({
170
- file,
171
- line: lineNumber,
172
- type: "poor-naming",
173
- identifier: match[1],
174
- severity: "minor",
175
- suggestion: `Use descriptive variable name instead of single letter '${match[1]}'`
176
- });
417
+ const contextWindow = getContextWindow(index);
418
+ if (!disabledChecks.has("single-letter")) {
419
+ const singleLetterMatches = line.matchAll(/\b(?:const|let|var)\s+([a-hm-z])\s*=/gi);
420
+ for (const match of singleLetterMatches) {
421
+ const letter = match[1].toLowerCase();
422
+ const isInLoopContext = line.includes("for") || /\.(map|filter|forEach|reduce|find|some|every)\s*\(/.test(line) || line.includes("=>") || // Arrow function
423
+ /\w+\s*=>\s*/.test(line);
424
+ const isI18nContext = line.includes("useTranslation") || line.includes("i18n.t") || /\bt\s*\(['"]/.test(line);
425
+ const isArrowFunctionParam = /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
426
+ /[a-z]\s*=>/.test(line) || // s => on same line
427
+ // Multi-line arrow function detection: look for pattern in context window
428
+ new RegExp(`\\b${letter}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
429
+ new RegExp(`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`).test(lines[index - 1] || "") && /=>/.test(contextWindow);
430
+ const isShortLived = isShortLivedVariable(letter, index);
431
+ if (!isInLoopContext && !isI18nContext && !isArrowFunctionParam && !isShortLived && !["x", "y", "z", "i", "j", "k", "l", "n", "m"].includes(letter)) {
432
+ if (isTestFile && ["a", "b", "c", "d", "e", "f", "s"].includes(letter)) {
433
+ continue;
434
+ }
435
+ issues.push({
436
+ file,
437
+ line: lineNumber,
438
+ type: "poor-naming",
439
+ identifier: match[1],
440
+ severity: "minor",
441
+ suggestion: `Use descriptive variable name instead of single letter '${match[1]}'`
442
+ });
443
+ }
177
444
  }
178
445
  }
179
- const abbreviationMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z]{1,3})(?=[A-Z]|_|\s*=)/g);
180
- for (const match of abbreviationMatches) {
181
- const abbrev = match[1].toLowerCase();
182
- if (!ACCEPTABLE_ABBREVIATIONS.has(abbrev)) {
446
+ if (!disabledChecks.has("abbreviation")) {
447
+ const abbreviationMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z]{1,3})(?=[A-Z]|_|\s*=)/g);
448
+ for (const match of abbreviationMatches) {
449
+ const abbrev = match[1].toLowerCase();
450
+ if (allShortWords.has(abbrev)) {
451
+ continue;
452
+ }
453
+ if (allAbbreviations.has(abbrev)) {
454
+ continue;
455
+ }
456
+ const isArrowFunctionParam = /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
457
+ new RegExp(`\\b${abbrev}\\s*=>`).test(line) || // s => on same line
458
+ // Multi-line arrow function: check context window
459
+ new RegExp(`\\b${abbrev}\\s*\\)\\s*$`).test(line) && /=>/.test(contextWindow) || // (s)\n =>
460
+ new RegExp(`\\.(?:map|filter|forEach|reduce|find|some|every)\\s*\\(\\s*$`).test(lines[index - 1] || "") && new RegExp(`^\\s*${abbrev}\\s*=>`).test(line);
461
+ if (isArrowFunctionParam) {
462
+ continue;
463
+ }
464
+ if (abbrev.length <= 2) {
465
+ const isDateTimeContext = /date|time|day|hour|minute|second|timestamp/i.test(line);
466
+ if (isDateTimeContext && ["d", "t", "dt"].includes(abbrev)) {
467
+ continue;
468
+ }
469
+ const isUserContext = /user|auth|account/i.test(line);
470
+ if (isUserContext && abbrev === "u") {
471
+ continue;
472
+ }
473
+ }
183
474
  issues.push({
184
475
  file,
185
476
  line: lineNumber,
@@ -190,7 +481,7 @@ function analyzeFileNaming(file, content) {
190
481
  });
191
482
  }
192
483
  }
193
- if (file.match(/\.(ts|tsx|js|jsx)$/)) {
484
+ if (!disabledChecks.has("convention-mix") && file.match(/\.(ts|tsx|js|jsx)$/)) {
194
485
  const camelCaseVars = line.match(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*=/);
195
486
  const snakeCaseVars = line.match(/\b(?:const|let|var)\s+([a-z][a-z0-9]*_[a-z0-9_]*)\s*=/);
196
487
  if (snakeCaseVars) {
@@ -204,36 +495,51 @@ function analyzeFileNaming(file, content) {
204
495
  });
205
496
  }
206
497
  }
207
- const booleanMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*:\s*boolean/gi);
208
- for (const match of booleanMatches) {
209
- const name = match[1];
210
- if (!name.match(/^(is|has|should|can|will|did)/i)) {
211
- issues.push({
212
- file,
213
- line: lineNumber,
214
- type: "unclear",
215
- identifier: name,
216
- severity: "info",
217
- suggestion: `Boolean variable '${name}' should start with is/has/should/can for clarity`
218
- });
498
+ if (!disabledChecks.has("unclear")) {
499
+ const booleanMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z][a-zA-Z0-9]*)\s*:\s*boolean/gi);
500
+ for (const match of booleanMatches) {
501
+ const name = match[1];
502
+ if (!name.match(/^(is|has|should|can|will|did)/i)) {
503
+ issues.push({
504
+ file,
505
+ line: lineNumber,
506
+ type: "unclear",
507
+ identifier: name,
508
+ severity: "info",
509
+ suggestion: `Boolean variable '${name}' should start with is/has/should/can for clarity`
510
+ });
511
+ }
219
512
  }
220
513
  }
221
- const functionMatches = line.matchAll(/function\s+([a-z][a-zA-Z0-9]*)/g);
222
- for (const match of functionMatches) {
223
- const name = match[1];
224
- const isFactoryPattern = name.match(/(Factory|Builder|Creator|Generator)$/);
225
- const isEventHandler = name.match(/^on[A-Z]/);
226
- const isDescriptiveLong = name.length > 20;
227
- 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)/);
228
- if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong) {
229
- issues.push({
230
- file,
231
- line: lineNumber,
232
- type: "unclear",
233
- identifier: name,
234
- severity: "info",
235
- suggestion: `Function '${name}' should start with an action verb (get, set, create, etc.)`
236
- });
514
+ if (!disabledChecks.has("unclear")) {
515
+ const functionMatches = line.matchAll(/function\s+([a-z][a-zA-Z0-9]*)/g);
516
+ for (const match of functionMatches) {
517
+ const name = match[1];
518
+ const isKeyword = ["for", "if", "else", "while", "do", "switch", "case", "break", "continue", "return", "throw", "try", "catch", "finally", "with", "yield", "await"].includes(name);
519
+ if (isKeyword) {
520
+ continue;
521
+ }
522
+ const isEntryPoint = ["main", "init", "setup", "bootstrap"].includes(name);
523
+ if (isEntryPoint) {
524
+ continue;
525
+ }
526
+ const isFactoryPattern = name.match(/(Factory|Builder|Creator|Generator)$/);
527
+ const isEventHandler = name.match(/^on[A-Z]/);
528
+ const isDescriptiveLong = name.length > 15;
529
+ 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)$/);
530
+ const capitalCount = (name.match(/[A-Z]/g) || []).length;
531
+ const isCompoundWord = capitalCount >= 3;
532
+ 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)/);
533
+ if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isDescriptivePattern && !isCompoundWord) {
534
+ issues.push({
535
+ file,
536
+ line: lineNumber,
537
+ type: "unclear",
538
+ identifier: name,
539
+ severity: "info",
540
+ suggestion: `Function '${name}' should start with an action verb (get, set, create, etc.)`
541
+ });
542
+ }
237
543
  }
238
544
  }
239
545
  });
@@ -533,7 +839,7 @@ function generateRecommendations(namingIssues, patternIssues) {
533
839
  // src/cli.ts
534
840
  var import_chalk = __toESM(require("chalk"));
535
841
  var import_fs = require("fs");
536
- var import_path = require("path");
842
+ var import_path2 = require("path");
537
843
  var import_core4 = require("@aiready/core");
538
844
  var program = new import_commander.Command();
539
845
  program.name("aiready-consistency").description("Detect consistency issues in naming, patterns, and architecture").version("0.1.0").addHelpText("after", `
@@ -582,7 +888,7 @@ EXAMPLES:
582
888
  `consistency-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.json`,
583
889
  directory
584
890
  );
585
- const dir = (0, import_path.dirname)(outputPath);
891
+ const dir = (0, import_path2.dirname)(outputPath);
586
892
  if (!(0, import_fs.existsSync)(dir)) {
587
893
  (0, import_fs.mkdirSync)(dir, { recursive: true });
588
894
  }
@@ -595,7 +901,7 @@ EXAMPLES:
595
901
  `consistency-report-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.md`,
596
902
  directory
597
903
  );
598
- const dir = (0, import_path.dirname)(outputPath);
904
+ const dir = (0, import_path2.dirname)(outputPath);
599
905
  if (!(0, import_fs.existsSync)(dir)) {
600
906
  (0, import_fs.mkdirSync)(dir, { recursive: true });
601
907
  }
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  analyzeConsistency
4
- } from "./chunk-LUAREV6A.mjs";
4
+ } from "./chunk-2BTBNG6X.mjs";
5
5
 
6
6
  // src/cli.ts
7
7
  import { Command } from "commander";