smart-renamer
Version:
🚀 Intelligent file naming suggestions based on project-specific naming conventions. Interactive CLI tool that asks yes/no for each file renaming.
153 lines • 5.89 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SuggestionEngine = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const naming_validator_1 = require("./naming-validator");
class SuggestionEngine {
projectRoot;
constructor(projectRoot = process.cwd()) {
this.projectRoot = projectRoot;
}
suggestName(filename, convention, exceptions = []) {
const baseName = this.getBaseName(filename);
const extension = (0, path_1.extname)(filename);
if (exceptions.includes(baseName.toLowerCase())) {
return {
original: filename,
suggested: filename,
convention,
confidence: 1.0
};
}
const suggestedBaseName = naming_validator_1.NamingValidator.convertToConvention(baseName, convention);
const suggestedFilename = suggestedBaseName + extension;
const confidence = this.calculateConfidence(filename, suggestedFilename, convention);
return {
original: filename,
suggested: suggestedFilename,
convention,
confidence
};
}
suggestMultipleNames(filename, primaryConvention, exceptions = []) {
const conventions = ['camelCase', 'snake_case', 'kebab-case', 'PascalCase', 'UPPER_SNAKE_CASE'];
return conventions.map(convention => {
const suggestion = this.suggestName(filename, convention, exceptions);
if (convention === primaryConvention) {
suggestion.confidence = Math.min(suggestion.confidence + 0.2, 1.0);
}
return suggestion;
}).sort((a, b) => b.confidence - a.confidence);
}
detectProjectConvention() {
const files = this.getAllProjectFiles();
const filenames = files
.filter(file => !file.isDirectory)
.map(file => file.name);
return naming_validator_1.NamingValidator.detectConvention(filenames);
}
analyzeProjectStructure() {
const files = this.getAllProjectFiles();
const filenames = files
.filter(file => !file.isDirectory)
.map(file => file.name);
const conventions = {
'camelCase': 0,
'snake_case': 0,
'kebab-case': 0,
'PascalCase': 0,
'UPPER_SNAKE_CASE': 0
};
for (const filename of filenames) {
const baseName = this.getBaseName(filename);
for (const [convention, pattern] of Object.entries(naming_validator_1.NamingValidator['CONVENTION_PATTERNS'])) {
if (pattern.test(baseName)) {
conventions[convention]++;
}
}
}
const totalFiles = filenames.length;
const maxCount = Math.max(...Object.values(conventions));
const mostCommon = maxCount > 0
? Object.entries(conventions).find(([, count]) => count === maxCount)?.[0] || null
: null;
const consistency = totalFiles > 0 ? (maxCount / totalFiles) : 0;
return {
totalFiles,
conventions,
mostCommon,
consistency
};
}
getAllProjectFiles(dir = this.projectRoot, maxDepth = 3, currentDepth = 0) {
if (currentDepth >= maxDepth)
return [];
const files = [];
try {
const entries = (0, fs_1.readdirSync)(dir);
for (const entry of entries) {
if (this.shouldIgnore(entry))
continue;
const fullPath = (0, path_1.join)(dir, entry);
const stats = (0, fs_1.statSync)(fullPath);
const fileInfo = {
name: entry,
path: fullPath,
extension: (0, path_1.extname)(entry),
isDirectory: stats.isDirectory()
};
files.push(fileInfo);
if (stats.isDirectory()) {
files.push(...this.getAllProjectFiles(fullPath, maxDepth, currentDepth + 1));
}
}
}
catch (error) {
console.warn(`Error reading directory ${dir}:`, error);
}
return files;
}
shouldIgnore(filename) {
const ignorePatterns = [
'node_modules',
'.git',
'dist',
'build',
'.DS_Store',
'Thumbs.db',
'CLAUDE.md'
];
return ignorePatterns.some(pattern => filename === pattern || filename.startsWith('.') && filename !== '.gitignore');
}
getBaseName(filename) {
const lastDotIndex = filename.lastIndexOf('.');
return lastDotIndex > 0 ? filename.substring(0, lastDotIndex) : filename;
}
calculateConfidence(original, suggested, convention) {
if (original === suggested)
return 1.0;
const originalBase = this.getBaseName(original);
const suggestedBase = this.getBaseName(suggested);
let confidence = 0.5;
const words1 = this.extractWords(originalBase);
const words2 = this.extractWords(suggestedBase);
if (words1.join('').toLowerCase() === words2.join('').toLowerCase()) {
confidence += 0.3;
}
const pattern = naming_validator_1.NamingValidator['CONVENTION_PATTERNS'][convention];
if (pattern && pattern.test(suggestedBase)) {
confidence += 0.2;
}
return Math.min(confidence, 1.0);
}
extractWords(name) {
return name
.replace(/([a-z])([A-Z])/g, '$1 $2')
.replace(/[_-]/g, ' ')
.split(/\s+/)
.filter(word => word.length > 0);
}
}
exports.SuggestionEngine = SuggestionEngine;
//# sourceMappingURL=suggestion-engine.js.map