@aiready/consistency 0.6.17 → 0.7.1
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 +9 -9
- package/.turbo/turbo-test.log +21 -12
- package/README.md +31 -2
- package/dist/chunk-YEHXYHGY.mjs +1497 -0
- package/dist/cli.js +131 -12
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +1 -2
- package/dist/index.d.ts +1 -2
- package/dist/index.js +138 -9
- package/dist/index.mjs +1 -1
- package/package.json +2 -2
- package/src/analyzer.ts +14 -4
- package/src/analyzers/naming-python.ts +183 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Python Naming Analyzer - PEP 8 Compliant
|
|
3
|
+
*
|
|
4
|
+
* Analyzes Python code for PEP 8 naming convention violations
|
|
5
|
+
* https://peps.python.org/pep-0008/#naming-conventions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getParser, Language } from '@aiready/core';
|
|
9
|
+
import type { NamingIssue } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Analyze Python files for PEP 8 naming violations
|
|
13
|
+
*/
|
|
14
|
+
export async function analyzePythonNaming(files: string[]): Promise<NamingIssue[]> {
|
|
15
|
+
const issues: NamingIssue[] = [];
|
|
16
|
+
const parser = getParser('dummy.py'); // Get Python parser instance
|
|
17
|
+
|
|
18
|
+
if (!parser) {
|
|
19
|
+
console.warn('Python parser not available');
|
|
20
|
+
return issues;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Filter to only Python files
|
|
24
|
+
const pythonFiles = files.filter(f => f.toLowerCase().endsWith('.py'));
|
|
25
|
+
|
|
26
|
+
for (const file of pythonFiles) {
|
|
27
|
+
try {
|
|
28
|
+
const fs = await import('fs');
|
|
29
|
+
const code = await fs.promises.readFile(file, 'utf-8');
|
|
30
|
+
const result = parser.parse(code, file);
|
|
31
|
+
|
|
32
|
+
// Analyze each export for naming violations
|
|
33
|
+
for (const exp of result.exports) {
|
|
34
|
+
const nameIssue = checkPythonNaming(exp.name, exp.type, file, exp.loc?.start.line || 0);
|
|
35
|
+
if (nameIssue) {
|
|
36
|
+
issues.push(nameIssue);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Analyze imports for naming issues (optional, less critical)
|
|
41
|
+
for (const imp of result.imports) {
|
|
42
|
+
for (const spec of imp.specifiers) {
|
|
43
|
+
if (spec !== '*' && spec !== 'default') {
|
|
44
|
+
const nameIssue = checkPythonNaming(spec, 'variable', file, imp.loc?.start.line || 0);
|
|
45
|
+
if (nameIssue) {
|
|
46
|
+
issues.push(nameIssue);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn(`Skipping ${file} due to error:`, error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return issues;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check a Python identifier against PEP 8 conventions
|
|
61
|
+
*/
|
|
62
|
+
function checkPythonNaming(
|
|
63
|
+
identifier: string,
|
|
64
|
+
type: string,
|
|
65
|
+
file: string,
|
|
66
|
+
line: number
|
|
67
|
+
): NamingIssue | null {
|
|
68
|
+
// Get naming conventions from parser
|
|
69
|
+
const parser = getParser('dummy.py');
|
|
70
|
+
const conventions = parser?.getNamingConventions();
|
|
71
|
+
if (!conventions) return null;
|
|
72
|
+
|
|
73
|
+
// Skip special methods and exceptions
|
|
74
|
+
if (conventions.exceptions?.includes(identifier)) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Check based on type
|
|
79
|
+
if (type === 'class') {
|
|
80
|
+
// Classes should be PascalCase
|
|
81
|
+
if (!conventions.classPattern.test(identifier)) {
|
|
82
|
+
return {
|
|
83
|
+
type: 'poor-naming',
|
|
84
|
+
identifier,
|
|
85
|
+
file,
|
|
86
|
+
line,
|
|
87
|
+
column: 0,
|
|
88
|
+
severity: 'major',
|
|
89
|
+
category: 'naming',
|
|
90
|
+
suggestion: `Class names should use PascalCase (e.g., ${toPascalCase(identifier)})`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
} else if (type === 'function') {
|
|
94
|
+
// Functions should be snake_case
|
|
95
|
+
if (!conventions.functionPattern.test(identifier)) {
|
|
96
|
+
// Check if it's incorrectly using camelCase
|
|
97
|
+
if (/^[a-z][a-zA-Z0-9]*$/.test(identifier) && /[A-Z]/.test(identifier)) {
|
|
98
|
+
return {
|
|
99
|
+
type: 'convention-mix',
|
|
100
|
+
identifier,
|
|
101
|
+
file,
|
|
102
|
+
line,
|
|
103
|
+
column: 0,
|
|
104
|
+
severity: 'major',
|
|
105
|
+
category: 'naming',
|
|
106
|
+
suggestion: `Function names should use snake_case, not camelCase (e.g., ${toSnakeCase(identifier)})`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} else if (type === 'const' || type === 'variable') {
|
|
111
|
+
// Check if it looks like a constant (all uppercase)
|
|
112
|
+
if (identifier === identifier.toUpperCase() && identifier.length > 1) {
|
|
113
|
+
// Constants should be UPPER_CASE_WITH_UNDERSCORES
|
|
114
|
+
if (!conventions.constantPattern.test(identifier)) {
|
|
115
|
+
return {
|
|
116
|
+
type: 'poor-naming',
|
|
117
|
+
identifier,
|
|
118
|
+
file,
|
|
119
|
+
line,
|
|
120
|
+
column: 0,
|
|
121
|
+
severity: 'minor',
|
|
122
|
+
category: 'naming',
|
|
123
|
+
suggestion: 'Constants should use UPPER_CASE_WITH_UNDERSCORES',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// Regular variables should be snake_case
|
|
128
|
+
if (!conventions.variablePattern.test(identifier)) {
|
|
129
|
+
// Check if it's using camelCase (common mistake from JS/TS developers)
|
|
130
|
+
if (/^[a-z][a-zA-Z0-9]*$/.test(identifier) && /[A-Z]/.test(identifier)) {
|
|
131
|
+
return {
|
|
132
|
+
type: 'convention-mix',
|
|
133
|
+
identifier,
|
|
134
|
+
file,
|
|
135
|
+
line,
|
|
136
|
+
column: 0,
|
|
137
|
+
severity: 'major',
|
|
138
|
+
category: 'naming',
|
|
139
|
+
suggestion: `Variable names should use snake_case, not camelCase (e.g., ${toSnakeCase(identifier)})`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Convert camelCase to snake_case
|
|
151
|
+
*/
|
|
152
|
+
function toSnakeCase(str: string): string {
|
|
153
|
+
return str
|
|
154
|
+
.replace(/([A-Z])/g, '_$1')
|
|
155
|
+
.toLowerCase()
|
|
156
|
+
.replace(/^_/, '');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Convert snake_case to PascalCase
|
|
161
|
+
*/
|
|
162
|
+
function toPascalCase(str: string): string {
|
|
163
|
+
return str
|
|
164
|
+
.split('_')
|
|
165
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
166
|
+
.join('');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Detect common Python anti-patterns in naming
|
|
171
|
+
*/
|
|
172
|
+
export function detectPythonNamingAntiPatterns(files: string[]): NamingIssue[] {
|
|
173
|
+
const issues: NamingIssue[] = [];
|
|
174
|
+
|
|
175
|
+
// Anti-pattern 1: Using camelCase in Python (common for JS/TS developers)
|
|
176
|
+
// Anti-pattern 2: Using PascalCase for functions
|
|
177
|
+
// Anti-pattern 3: Not using leading underscore for private methods
|
|
178
|
+
// Anti-pattern 4: Using single letter names outside of comprehensions
|
|
179
|
+
|
|
180
|
+
// These will be implemented as we refine the analyzer
|
|
181
|
+
|
|
182
|
+
return issues;
|
|
183
|
+
}
|