@aiready/consistency 0.3.3 → 0.3.4

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.
@@ -1,35 +1,69 @@
1
1
  import { readFileContent } from '@aiready/core';
2
2
  import type { NamingIssue } from '../types';
3
3
 
4
+ // Common short English words that are NOT abbreviations (full, valid words)
5
+ const COMMON_SHORT_WORDS = new Set([
6
+ // Full English words (1-3 letters)
7
+ 'day', 'key', 'net', 'to', 'go', 'for', 'not', 'new', 'old', 'top', 'end',
8
+ 'run', 'try', 'use', 'get', 'set', 'add', 'put', 'map', 'log', 'row', 'col',
9
+ 'tab', 'box', 'div', 'nav', 'tag', 'any', 'all', 'one', 'two', 'out', 'off',
10
+ 'on', 'yes', 'no', 'now', 'max', 'min', 'sum', 'avg', 'ref', 'src', 'dst',
11
+ 'raw', 'def', 'sub', 'pub', 'pre', 'mid', 'alt', 'opt', 'tmp', 'ext', 'sep',
12
+ // Additional full words commonly flagged
13
+ 'tax', 'cat', 'dog', 'car', 'bus', 'web', 'app', 'war', 'law', 'pay', 'buy',
14
+ 'win', 'cut', 'hit', 'hot', 'pop', 'job', 'age', 'act', 'let', 'lot', 'bad',
15
+ 'big', 'far', 'few', 'own', 'per', 'red', 'low', 'see', 'six', 'ten', 'way',
16
+ 'who', 'why', 'yet', 'via', 'due', 'fee', 'fun', 'gas', 'gay', 'god', 'gun',
17
+ 'guy', 'ice', 'ill', 'kid', 'mad', 'man', 'mix', 'mom', 'mrs', 'nor', 'odd',
18
+ 'oil', 'pan', 'pet', 'pit', 'pot', 'pow', 'pro', 'raw', 'rep', 'rid', 'sad',
19
+ 'sea', 'sit', 'sky', 'son', 'tea', 'tie', 'tip', 'van', 'war', 'win', 'won'
20
+ ]);
21
+
4
22
  // Comprehensive list of acceptable abbreviations and acronyms
5
23
  const ACCEPTABLE_ABBREVIATIONS = new Set([
6
24
  // Standard identifiers
7
25
  'id', 'uid', 'gid', 'pid',
26
+ // Loop counters and iterators
27
+ 'i', 'j', 'k', 'n', 'm',
8
28
  // Web/Network
9
29
  'url', 'uri', 'api', 'cdn', 'dns', 'ip', 'tcp', 'udp', 'http', 'ssl', 'tls',
10
- 'utm', 'seo', 'rss', 'xhr', 'ajax',
30
+ 'utm', 'seo', 'rss', 'xhr', 'ajax', 'cors', 'ws', 'wss',
11
31
  // Data formats
12
32
  'json', 'xml', 'yaml', 'csv', 'html', 'css', 'svg', 'pdf',
33
+ // File types & extensions
34
+ 'img', 'txt', 'doc', 'docx', 'xlsx', 'ppt', 'md', 'rst', 'jpg', 'png', 'gif',
13
35
  // Databases
14
- 'db', 'sql', 'orm', 'dao', 'dto',
36
+ 'db', 'sql', 'orm', 'dao', 'dto', 'ddb', 'rds', 'nosql',
15
37
  // File system
16
38
  'fs', 'dir', 'tmp', 'src', 'dst', 'bin', 'lib', 'pkg',
17
39
  // Operating system
18
- 'os', 'env', 'arg', 'cli', 'cmd', 'exe',
40
+ 'os', 'env', 'arg', 'cli', 'cmd', 'exe', 'cwd', 'pwd',
19
41
  // UI/UX
20
42
  'ui', 'ux', 'gui', 'dom', 'ref',
21
43
  // Request/Response
22
- 'req', 'res', 'ctx', 'err', 'msg',
44
+ 'req', 'res', 'ctx', 'err', 'msg', 'auth',
23
45
  // Mathematics/Computing
24
46
  'max', 'min', 'avg', 'sum', 'abs', 'cos', 'sin', 'tan', 'log', 'exp',
25
- 'pow', 'sqrt', 'std', 'var', 'int', 'num',
47
+ 'pow', 'sqrt', 'std', 'var', 'int', 'num', 'idx',
26
48
  // Time
27
- 'now', 'utc', 'tz', 'ms', 'sec',
49
+ 'now', 'utc', 'tz', 'ms', 'sec', 'hr', 'min', 'yr', 'mo',
28
50
  // Common patterns
29
51
  'app', 'cfg', 'config', 'init', 'len', 'val', 'str', 'obj', 'arr',
30
52
  'gen', 'def', 'raw', 'new', 'old', 'pre', 'post', 'sub', 'pub',
53
+ // Programming/Framework specific
54
+ 'ts', 'js', 'jsx', 'tsx', 'py', 'rb', 'vue', 're', 'fn', 'fns', 'mod', 'opts', 'dev',
55
+ // Cloud/Infrastructure
56
+ 's3', 'ec2', 'sqs', 'sns', 'vpc', 'ami', 'iam', 'acl', 'elb', 'alb', 'nlb', 'aws',
57
+ // Metrics/Performance
58
+ 'fcp', 'lcp', 'cls', 'ttfb', 'tti', 'fid', 'fps', 'qps', 'rps', 'tps',
59
+ // Testing & i18n
60
+ 'po', 'e2e', 'a11y', 'i18n', 'l10n',
61
+ // Domain-specific abbreviations (context-aware)
62
+ 'sk', 'fy', 'faq', 'og', 'seo', 'cta', 'roi', 'kpi',
31
63
  // Boolean helpers (these are intentional short names)
32
- 'is', 'has', 'can', 'did', 'was', 'are'
64
+ 'is', 'has', 'can', 'did', 'was', 'are',
65
+ // Date/Time context (when in date contexts)
66
+ 'd', 't', 'dt'
33
67
  ]);
34
68
 
35
69
  /**
@@ -50,6 +84,9 @@ export async function analyzeNaming(files: string[]): Promise<NamingIssue[]> {
50
84
  function analyzeFileNaming(file: string, content: string): NamingIssue[] {
51
85
  const issues: NamingIssue[] = [];
52
86
 
87
+ // Check if this is a test file (more lenient rules)
88
+ const isTestFile = file.match(/\.(test|spec)\.(ts|tsx|js|jsx)$/);
89
+
53
90
  // Split into lines for line number tracking
54
91
  const lines = content.split('\n');
55
92
 
@@ -61,11 +98,31 @@ function analyzeFileNaming(file: string, content: string): NamingIssue[] {
61
98
  const singleLetterMatches = line.matchAll(/\b(?:const|let|var)\s+([a-hm-z])\s*=/gi);
62
99
  for (const match of singleLetterMatches) {
63
100
  const letter = match[1].toLowerCase();
64
- // Skip if it's in a loop context or common iterator
65
- const isInLoopContext = line.includes('for') || line.includes('.map') ||
66
- line.includes('.filter') || line.includes('.forEach') ||
67
- line.includes('.reduce');
68
- if (!isInLoopContext && !['x', 'y', 'z', 'i', 'j', 'k', 'l', 'n', 'm'].includes(letter)) {
101
+
102
+ // Enhanced loop/iterator context detection
103
+ const isInLoopContext =
104
+ line.includes('for') ||
105
+ /\.(map|filter|forEach|reduce|find|some|every)\s*\(/.test(line) ||
106
+ line.includes('=>') || // Arrow function
107
+ /\w+\s*=>\s*/.test(line); // Callback pattern
108
+
109
+ // Check for i18n/translation context
110
+ const isI18nContext =
111
+ line.includes('useTranslation') ||
112
+ line.includes('i18n.t') ||
113
+ /\bt\s*\(['"]/.test(line); // t('key') pattern
114
+
115
+ // Check for arrow function parameter (improved detection)
116
+ const isArrowFunctionParam =
117
+ /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
118
+ /[a-z]\s*=>/.test(line); // s =>
119
+
120
+ if (!isInLoopContext && !isI18nContext && !isArrowFunctionParam && !['x', 'y', 'z', 'i', 'j', 'k', 'l', 'n', 'm'].includes(letter)) {
121
+ // Skip in test files unless it's really unclear
122
+ if (isTestFile && ['a', 'b', 'c', 'd', 'e', 'f', 's'].includes(letter)) {
123
+ continue;
124
+ }
125
+
69
126
  issues.push({
70
127
  file,
71
128
  line: lineNumber,
@@ -81,17 +138,48 @@ function analyzeFileNaming(file: string, content: string): NamingIssue[] {
81
138
  const abbreviationMatches = line.matchAll(/\b(?:const|let|var)\s+([a-z]{1,3})(?=[A-Z]|_|\s*=)/g);
82
139
  for (const match of abbreviationMatches) {
83
140
  const abbrev = match[1].toLowerCase();
141
+
142
+ // Skip if it's a common short English word (full word, not abbreviation)
143
+ if (COMMON_SHORT_WORDS.has(abbrev)) {
144
+ continue;
145
+ }
146
+
84
147
  // Skip acceptable abbreviations
85
- if (!ACCEPTABLE_ABBREVIATIONS.has(abbrev)) {
86
- issues.push({
87
- file,
88
- line: lineNumber,
89
- type: 'abbreviation',
90
- identifier: match[1],
91
- severity: 'info',
92
- suggestion: `Consider using full word instead of abbreviation '${match[1]}'`
93
- });
148
+ if (ACCEPTABLE_ABBREVIATIONS.has(abbrev)) {
149
+ continue;
94
150
  }
151
+
152
+ // Check for arrow function parameter context
153
+ const isArrowFunctionParam =
154
+ /\(\s*[a-z]\s*(?:,\s*[a-z]\s*)*\)\s*=>/.test(line) || // (s) => or (a, b) =>
155
+ new RegExp(`\\b${abbrev}\\s*=>`).test(line); // s =>
156
+
157
+ if (isArrowFunctionParam) {
158
+ continue;
159
+ }
160
+
161
+ // For very short names (1-2 letters), check for date/time context
162
+ if (abbrev.length <= 2) {
163
+ const isDateTimeContext = /date|time|day|hour|minute|second|timestamp/i.test(line);
164
+ if (isDateTimeContext && ['d', 't', 'dt'].includes(abbrev)) {
165
+ continue;
166
+ }
167
+
168
+ // Check for user/auth context
169
+ const isUserContext = /user|auth|account/i.test(line);
170
+ if (isUserContext && abbrev === 'u') {
171
+ continue;
172
+ }
173
+ }
174
+
175
+ issues.push({
176
+ file,
177
+ line: lineNumber,
178
+ type: 'abbreviation',
179
+ identifier: match[1],
180
+ severity: 'info',
181
+ suggestion: `Consider using full word instead of abbreviation '${match[1]}'`
182
+ });
95
183
  }
96
184
 
97
185
  // Check for snake_case vs camelCase mixing in TypeScript/JavaScript
@@ -131,16 +219,42 @@ function analyzeFileNaming(file: string, content: string): NamingIssue[] {
131
219
  const functionMatches = line.matchAll(/function\s+([a-z][a-zA-Z0-9]*)/g);
132
220
  for (const match of functionMatches) {
133
221
  const name = match[1];
222
+
223
+ // Skip JavaScript/TypeScript keywords that shouldn't be function names
224
+ const isKeyword = ['for', 'if', 'else', 'while', 'do', 'switch', 'case', 'break', 'continue', 'return', 'throw', 'try', 'catch', 'finally', 'with', 'yield', 'await'].includes(name);
225
+ if (isKeyword) {
226
+ continue;
227
+ }
228
+
229
+ // Skip common entry point names
230
+ const isEntryPoint = ['main', 'init', 'setup', 'bootstrap'].includes(name);
231
+ if (isEntryPoint) {
232
+ continue;
233
+ }
234
+
134
235
  // Functions should typically start with verbs, but allow:
135
236
  // 1. Factory/builder patterns (ends with Factory, Builder, etc.)
136
237
  // 2. Descriptive compound names that explain what they return
137
238
  // 3. Event handlers (onClick, onSubmit, etc.)
239
+ // 4. Descriptive aggregate/collection patterns
240
+ // 5. Very long descriptive names (>15 chars)
241
+ // 6. Compound words with 3+ capitals
242
+
138
243
  const isFactoryPattern = name.match(/(Factory|Builder|Creator|Generator)$/);
139
244
  const isEventHandler = name.match(/^on[A-Z]/);
140
- const isDescriptiveLong = name.length > 20; // Long names are usually descriptive enough
245
+ const isDescriptiveLong = name.length > 15; // Reduced from 20 to 15
246
+
247
+ // Check for descriptive patterns
248
+ const isDescriptivePattern = name.match(/^(default|total|count|sum|avg|max|min|initial|current|previous|next)\w+/) ||
249
+ name.match(/\w+(Count|Total|Sum|Average|List|Map|Set|Config|Settings|Options|Props)$/);
250
+
251
+ // Count capital letters for compound detection
252
+ const capitalCount = (name.match(/[A-Z]/g) || []).length;
253
+ const isCompoundWord = capitalCount >= 3; // daysSinceLastCommit has 4 capitals
254
+
141
255
  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)/);
142
256
 
143
- if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong) {
257
+ if (!hasActionVerb && !isFactoryPattern && !isEventHandler && !isDescriptiveLong && !isDescriptivePattern && !isCompoundWord) {
144
258
  issues.push({
145
259
  file,
146
260
  line: lineNumber,
@@ -1,24 +0,0 @@
1
-
2
- 
3
- > @aiready/consistency@0.3.3 build /Users/pengcao/projects/aiready/packages/consistency
4
- > tsup src/index.ts src/cli.ts --format cjs,esm --dts
5
-
6
- CLI Building entry: src/cli.ts, src/index.ts
7
- CLI Using tsconfig: tsconfig.json
8
- CLI tsup v8.5.1
9
- CLI Target: es2020
10
- CJS Build start
11
- ESM Build start
12
- CJS dist/cli.js 25.23 KB
13
- CJS dist/index.js 16.34 KB
14
- CJS ⚡️ Build success in 79ms
15
- ESM dist/chunk-LUAREV6A.mjs 15.11 KB
16
- ESM dist/cli.mjs 8.54 KB
17
- ESM dist/index.mjs 220.00 B
18
- ESM ⚡️ Build success in 79ms
19
- DTS Build start
20
- DTS ⚡️ Build success in 587ms
21
- DTS dist/cli.d.ts 20.00 B
22
- DTS dist/index.d.ts 2.60 KB
23
- DTS dist/cli.d.mts 20.00 B
24
- DTS dist/index.d.mts 2.60 KB
@@ -1,76 +0,0 @@
1
-
2
- 
3
- > @aiready/consistency@0.3.3 test /Users/pengcao/projects/aiready/packages/consistency
4
- > vitest run
5
-
6
-
7
-  RUN  v2.1.9 /Users/pengcao/projects/aiready/packages/consistency
8
-
9
- [?25l ❯ analyzeConsistency (2)
10
- ⠙ should analyze naming issues
11
- · should detect minimum severity filtering
12
- · analyzeNaming (5)
13
- · should detect single letter variables
14
- · should NOT flag acceptable abbreviations
15
- · should detect snake_case in TypeScript files
16
- · should detect unclear boolean names
17
- · should allow common abbreviations
18
- · analyzePatterns (3)
19
- · should detect mixed error handling
20
- · should detect mixed async patterns
21
- · should detect mixed import styles
22
- · consistency scoring (2)
23
- · should calculate consistency score correctly
24
- · should weight critical issues more than info
25
- · recommendations (3)
26
- · should generate relevant recommendations
27
- · should suggest standardizing error handling
28
- · should suggest using async/await consistently
29
- [?25l ✓ analyzeConsistency (2)
30
- ✓ should analyze naming issues
31
- ✓ should detect minimum severity filtering
32
- ✓ analyzeNaming (5)
33
- ✓ should detect single letter variables
34
- ✓ should NOT flag acceptable abbreviations
35
- ✓ should detect snake_case in TypeScript files
36
- ✓ should detect unclear boolean names
37
- ✓ should allow common abbreviations
38
- ✓ analyzePatterns (3)
39
- ✓ should detect mixed error handling
40
- ✓ should detect mixed async patterns
41
- ✓ should detect mixed import styles
42
- ✓ consistency scoring (2)
43
- ✓ should calculate consistency score correctly
44
- ✓ should weight critical issues more than info
45
- ✓ recommendations (3)
46
- ✓ should generate relevant recommendations
47
- ✓ should suggest standardizing error handling
48
- ✓ should suggest using async/await consistently
49
-  ✓ src/__tests__/analyzer.test.ts (15)
50
- ✓ analyzeConsistency (2)
51
- ✓ should analyze naming issues
52
- ✓ should detect minimum severity filtering
53
- ✓ analyzeNaming (5)
54
- ✓ should detect single letter variables
55
- ✓ should NOT flag acceptable abbreviations
56
- ✓ should detect snake_case in TypeScript files
57
- ✓ should detect unclear boolean names
58
- ✓ should allow common abbreviations
59
- ✓ analyzePatterns (3)
60
- ✓ should detect mixed error handling
61
- ✓ should detect mixed async patterns
62
- ✓ should detect mixed import styles
63
- ✓ consistency scoring (2)
64
- ✓ should calculate consistency score correctly
65
- ✓ should weight critical issues more than info
66
- ✓ recommendations (3)
67
- ✓ should generate relevant recommendations
68
- ✓ should suggest standardizing error handling
69
- ✓ should suggest using async/await consistently
70
-
71
-  Test Files  1 passed (1)
72
-  Tests  15 passed (15)
73
-  Start at  15:32:25
74
-  Duration  519ms (transform 74ms, setup 0ms, collect 251ms, tests 24ms, environment 0ms, prepare 54ms)
75
-
76
- [?25h[?25h