@chahuadev/fix-comments
Version:
Professional Comment Standardization Tool v2.0.0 Beta - Advanced Pattern Recognition and Enhanced Context Detection for JavaScript/TypeScript projects with Smart Learning capabilities and enterprise security
1,203 lines (1,010 loc) • 430 kB
JavaScript
#!/usr/bin/env node
// ======================================================================
// Universal Comment Fixer v2.0.0/เครื่องมือแก้ไขคอมเมนต์สากล v2.0.0
// ======================================================================
// @author บริษัท ชาหัว ดีเวลลอปเมนต์ จำกัด (Chahua Development Co., Ltd.)
// @version 2.0.0
// @description Professional comment standardization tool with AI-friendly format
// @security_features Path Traversal Protection, File Size Limits, Symlink Protection
// SECURITY FEATURES:
// ┌─────────────────────────────────────────────────────────────────┐
// │ • Path Traversal Protection: Prevents ../../../etc/passwd attacks │
// │ • File Size Limits: 10MB maximum to prevent DoS attacks │
// │ • Symbolic Link Protection: Prevents infinite loop attacks │
// │ • System Directory Blacklist: Blocks access to sensitive paths │
// │ • Backup Path Validation: Secure backup creation with sanitization │
// │ • Working Directory Enforcement: All operations within project │
// └─────────────────────────────────────────────────────────────────┘
const fs = require('fs');
const path = require('path');
// ======================================================================
// Professional Logging System/ระบบบันทึกมืออาชีพ
// ======================================================================
class ProfessionalLogger {
constructor() {
this.projectName = path.basename(process.cwd());
this.logsDir = path.join(process.cwd(), 'logs');
// สร้างโฟลเดอร์ logs หลักถ้ายังไม่มี
if (!fs.existsSync(this.logsDir)) {
fs.mkdirSync(this.logsDir, { recursive: true });
}
// สร้างโฟลเดอร์ย่อยสำหรับโปรเจกต์นี้
this.projectLogsDir = path.join(this.logsDir, this.projectName);
if (!fs.existsSync(this.projectLogsDir)) {
fs.mkdirSync(this.projectLogsDir, { recursive: true });
}
// สร้างโฟลเดอร์ session ตามวันเวลา
const now = new Date();
const dateFolder = now.toISOString().slice(0, 10); // 2025-09-24
const timeFolder = now.toTimeString().slice(0, 8).replace(/:/g, '-'); // 03-53-12
this.sessionFolder = `${dateFolder}_${timeFolder}`;
this.sessionLogsDir = path.join(this.projectLogsDir, this.sessionFolder);
if (!fs.existsSync(this.sessionLogsDir)) {
fs.mkdirSync(this.sessionLogsDir, { recursive: true });
}
this.logFiles = {
error: path.join(this.sessionLogsDir, 'error.log'),
debug: path.join(this.sessionLogsDir, 'debug.log'),
audit: path.join(this.sessionLogsDir, 'audit.log'),
performance: path.join(this.sessionLogsDir, 'performance.log'),
diagnostic: path.join(this.sessionLogsDir, 'diagnostic.log')
};
// เขียน session header
this.writeSessionHeader();
}
writeSessionHeader() {
const timestamp = new Date().toISOString();
const header = `\n${'='.repeat(80)}\nSESSION START: ${timestamp} | Project: ${this.projectName}\n${'='.repeat(80)}\n`;
Object.values(this.logFiles).forEach(logFile => {
fs.appendFileSync(logFile, header, 'utf8');
});
}
formatLogEntry(level, category, message, data = null) {
const timestamp = new Date().toISOString();
let entry = `[${timestamp}] [${level.toUpperCase()}] [${category}] ${message}`;
if (data) {
entry += `\n Data: ${JSON.stringify(data, null, 2)}`;
}
entry += '\n';
return entry;
}
error(category, message, error = null, data = null) {
const logData = { ...data };
if (error) {
logData.error = {
message: error.message,
stack: error.stack,
name: error.name
};
}
const entry = this.formatLogEntry('ERROR', category, message, logData);
fs.appendFileSync(this.logFiles.error, entry, 'utf8');
// แสดงใน console ด้วย
console.error(`[ERROR] ${category}: ${message}`);
}
debug(category, message, data = null) {
const entry = this.formatLogEntry('DEBUG', category, message, data);
fs.appendFileSync(this.logFiles.debug, entry, 'utf8');
}
audit(action, filePath, details = null) {
const entry = this.formatLogEntry('AUDIT', 'FILE_OPERATION',
`${action}: ${filePath}`, details);
fs.appendFileSync(this.logFiles.audit, entry, 'utf8');
}
performance(operation, duration, details = null) {
const entry = this.formatLogEntry('PERFORMANCE', operation,
`Duration: ${duration}ms`, details);
fs.appendFileSync(this.logFiles.performance, entry, 'utf8');
}
info(category, message, data = null) {
const entry = this.formatLogEntry('INFO', category, message, data);
fs.appendFileSync(this.logFiles.debug, entry, 'utf8');
}
// สร้าง diagnostic report แยกต่างหาก
diagnostic(category, message, data = null) {
const entry = this.formatLogEntry('DIAGNOSTIC', category, message, data);
// เขียนลงทั้ง audit และ debug ในโฟลเดอร์ session
fs.appendFileSync(this.logFiles.audit, entry, 'utf8');
fs.appendFileSync(this.logFiles.debug, `\n=== DIAGNOSTIC REPORT ===\n${entry}=== END DIAGNOSTIC ===\n`, 'utf8');
// เขียนลงไฟล์ diagnostic ใน session folder
if (!fs.existsSync(this.logFiles.diagnostic)) {
const header = `DIAGNOSTIC REPORTS LOG - ${new Date().toISOString()}\nProject: ${this.projectName} | Session: ${this.sessionFolder}\n${'='.repeat(80)}\n\n`;
fs.writeFileSync(this.logFiles.diagnostic, header, 'utf8');
}
fs.appendFileSync(this.logFiles.diagnostic, entry, 'utf8');
}
}
// สร้าง logger instance
const logger = new ProfessionalLogger();
// ======================================================================
// Organized Backup System/ระบบสำรองข้อมูลที่เป็นระบบ
// ======================================================================
class OrganizedBackupManager {
constructor() {
this.projectName = path.basename(process.cwd());
this.backupsDir = path.join(process.cwd(), '.backups');
this.projectBackupDir = path.join(this.backupsDir, this.projectName);
// สร้างโฟลเดอร์ backup ถ้ายังไม่มี
this.ensureBackupDirectories();
}
ensureBackupDirectories() {
if (!fs.existsSync(this.backupsDir)) {
fs.mkdirSync(this.backupsDir, { recursive: true });
logger.audit('CREATE_BACKUP_DIR', this.backupsDir);
}
if (!fs.existsSync(this.projectBackupDir)) {
fs.mkdirSync(this.projectBackupDir, { recursive: true });
logger.audit('CREATE_PROJECT_BACKUP_DIR', this.projectBackupDir);
}
}
createBackup(originalFilePath) {
try {
// ตรวจสอบว่าไฟล์ต้นฉบับมีอยู่จริง
if (!fs.existsSync(originalFilePath)) {
throw new Error(`Original file does not exist: ${originalFilePath}`);
}
// สร้างโฟลเดอร์ตามวันที่-เวลา
const now = new Date();
const dateFolder = now.toISOString().slice(0, 10); // 2025-09-24
const timeFolder = now.toTimeString().slice(0, 8).replace(/:/g, '-'); // 03-40-40
const sessionFolder = `${dateFolder}_${timeFolder}`;
// สร้างโฟลเดอร์ backup session
const sessionBackupDir = path.join(this.projectBackupDir, sessionFolder);
if (!fs.existsSync(sessionBackupDir)) {
fs.mkdirSync(sessionBackupDir, { recursive: true });
logger.audit('CREATE_SESSION_BACKUP_DIR', sessionBackupDir);
}
// ใช้ชื่อไฟล์ต้นฉบับธรรมดา
const originalFileName = path.basename(originalFilePath);
const backupFilePath = path.join(sessionBackupDir, originalFileName);
// คัดลอกไฟล์ต้นฉบับไป backup (เก็บไฟล์ต้นฉบับ)
const originalContent = fs.readFileSync(originalFilePath, 'utf8');
fs.writeFileSync(backupFilePath, originalContent, 'utf8');
// บันทึก audit log
logger.audit('BACKUP_CREATED', originalFilePath, {
backupPath: backupFilePath,
sessionFolder: sessionFolder,
originalSize: originalContent.length,
originalFileName: originalFileName
});
logger.info('BACKUP', `Original file backed up to: ${sessionFolder}/${originalFileName}`);
return backupFilePath;
} catch (error) {
logger.error('BACKUP', `Failed to create backup for ${originalFilePath}`, error);
throw error;
}
}
cleanupOldBackups(maxAge = 7) {
try {
const sessionFolders = fs.readdirSync(this.projectBackupDir);
const cutoffTime = Date.now() - (maxAge * 24 * 60 * 60 * 1000);
let cleanedCount = 0;
sessionFolders.forEach(sessionFolder => {
const sessionPath = path.join(this.projectBackupDir, sessionFolder);
const stats = fs.statSync(sessionPath);
// ถ้าเป็นโฟลเดอร์และเก่าเกินกำหนด
if (stats.isDirectory() && stats.mtime.getTime() < cutoffTime) {
// ลบทั้งโฟลเดอร์
fs.rmSync(sessionPath, { recursive: true, force: true });
cleanedCount++;
logger.audit('BACKUP_SESSION_CLEANED', sessionPath, {
sessionFolder: sessionFolder,
age: maxAge
});
}
});
if (cleanedCount > 0) {
logger.info('BACKUP', `Cleaned up ${cleanedCount} old backup sessions`);
}
} catch (error) {
logger.error('BACKUP', 'Failed to cleanup old backups', error);
}
}
}
// ======================================================================
// File Comparison & Analysis System - ระบบเปรียบเทียบและวิเคราะห์ไฟล์
// ======================================================================
class FileComparisonAnalyzer {
constructor() {
this.comparisonResults = [];
}
// เปรียบเทียบไฟล์ก่อนและหลัง แล้วสร้าง detailed report
compareAndAnalyze(originalContent, modifiedContent, filePath) {
try {
const comparisonId = Date.now();
logger.debug('FILE_COMPARISON', `Starting comparison analysis for: ${filePath}`, { comparisonId });
// วิเคราะห์ไฟล์ก่อน (Original)
const originalAnalysis = this.analyzeFileStructures(originalContent, 'ORIGINAL');
logger.debug('FILE_ANALYSIS', `Original file analysis completed`, {
filePath,
structures: originalAnalysis.summary
});
// วิเคราะห์ไฟล์หลัง (Modified)
const modifiedAnalysis = this.analyzeFileStructures(modifiedContent, 'MODIFIED');
logger.debug('FILE_ANALYSIS', `Modified file analysis completed`, {
filePath,
structures: modifiedAnalysis.summary
});
// เปรียบเทียบและสร้าง report
const comparisonReport = this.generateComparisonReport(
originalAnalysis,
modifiedAnalysis,
filePath
);
// บันทึก detailed report ลง audit log
this.logDetailedReport(comparisonReport, filePath);
// บันทึก error และ skip report
this.logErrorAndSkipReport(comparisonReport, filePath);
this.comparisonResults.push(comparisonReport);
return comparisonReport;
} catch (error) {
logger.error('FILE_COMPARISON', `Failed to compare files: ${filePath}`, error);
return null;
}
}
// วิเคราะห์โครงสร้างไฟล์โดยใช้ระบบที่มีอยู่
analyzeFileStructures(content, type) {
try {
// ใช้ SmartFileAnalyzer ที่มีอยู่แล้ว
const analyzer = new SmartFileAnalyzer(content, {
maxDepth: 50,
maxTokens: 100000,
maxParsingTime: 15000
});
const analysis = analyzer.analyzeFile();
// นับจำนวน structures แต่ละประเภท
const summary = {
totalFunctions: analysis.functions ? analysis.functions.length : 0,
totalClasses: analysis.classes ? analysis.classes.length : 0,
totalMethods: analysis.methods ? analysis.methods.length : 0,
totalVariables: analysis.variables ? analysis.variables.length : 0,
totalInterfaces: 0,
totalTypeAliases: 0,
totalAbstractClasses: 0,
totalStaticMethods: 0,
totalArrowFunctions: 0
};
// นับ TypeScript structures
if (analysis.interfaces) summary.totalInterfaces = analysis.interfaces.length;
if (analysis.typeAliases) summary.totalTypeAliases = analysis.typeAliases.length;
if (analysis.abstractClasses) summary.totalAbstractClasses = analysis.abstractClasses.length;
if (analysis.staticMethods) summary.totalStaticMethods = analysis.staticMethods.length;
if (analysis.arrowFunctions) summary.totalArrowFunctions = analysis.arrowFunctions.length;
// ตรวจสอบ functions ที่ไม่มี comment
const functionsWithoutComments = this.findFunctionsWithoutComments(content, analysis.functions || []);
const classesWithoutComments = this.findClassesWithoutComments(content, analysis.classes || []);
return {
type,
analysis,
summary,
functionsWithoutComments,
classesWithoutComments,
totalStructures: Object.values(summary).reduce((a, b) => a + b, 0)
};
} catch (error) {
logger.error('STRUCTURE_ANALYSIS', `Failed to analyze ${type} content`, error);
return { type, error: error.message, summary: {}, totalStructures: 0 };
}
}
// หา functions ที่ไม่มี comment
findFunctionsWithoutComments(content, functions) {
const withoutComments = [];
const lines = content.split('\n');
functions.forEach(func => {
const funcLine = func.line - 1; // 0-based index
// Smart Function Detection - กรองตัวแปรและ patterns พิเศษออก
if (this.isLikelyVariable(func.name, content, funcLine)) {
return; // ข้ามตัวแปรที่ถูก misidentify เป็น function
}
// กรอง React Components ที่เป็น const assignments ออก
if (this.isReactComponent(func.name, content, funcLine)) {
return; // ข้าม React Components - อันนี้ควรเป็น component จริงๆ
}
let hasComment = false;
// ตรวจสอบ 3 บรรทัดก่อนหน้า
for (let i = 1; i <= 3; i++) {
const checkLine = funcLine - i;
if (checkLine >= 0 && lines[checkLine]) {
const line = lines[checkLine].trim();
if (line.startsWith('//') || line.startsWith('/*') || line.startsWith('*')) {
hasComment = true;
break;
}
}
}
if (!hasComment) {
withoutComments.push({
name: func.name,
line: func.line,
type: func.type || 'function'
});
}
});
return withoutComments;
}
// หา classes ที่ไม่มี comment (พร้อม Smart Detection)
findClassesWithoutComments(content, classes) {
const withoutComments = [];
const lines = content.split('\n');
classes.forEach(cls => {
const classLine = cls.line - 1; // 0-based index
// Smart Class Detection - กรองตัวแปรออก
if (this.isLikelyVariable(cls.name, content, classLine)) {
return; // ข้ามตัวแปรที่ถูก misidentify
}
// กรอง React Components ที่เป็น const assignments ออก
if (this.isReactComponent(cls.name, content, classLine)) {
return; // ข้าม React Components ที่ควรเป็น functions
}
let hasComment = false; // ตรวจสอบ 3 บรรทัดก่อนหน้า
for (let i = 1; i <= 3; i++) {
const checkLine = classLine - i;
if (checkLine >= 0 && lines[checkLine]) {
const line = lines[checkLine].trim();
if (line.startsWith('//') || line.startsWith('/*') || line.startsWith('*')) {
hasComment = true;
break;
}
}
}
if (!hasComment) {
withoutComments.push({
name: cls.name,
line: cls.line,
type: cls.type || 'class'
});
}
});
return withoutComments;
}
// Smart Detection - ตรวจสอบว่าชื่อนั้นเป็นตัวแปรหรือไม่
isLikelyVariable(name, content, lineIndex) {
const lines = content.split('\n');
if (lineIndex < 0 || lineIndex >= lines.length) return false;
const currentLine = lines[lineIndex].trim();
const lowerName = name.toLowerCase();
// Escape ตัวอักษรพิเศษใน name ก่อนใช้ใน RegExp
const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
// ตรวจสอบ patterns ที่เป็นตัวแปร
const variablePatterns = [
// Variable declarations
new RegExp(`\\b(const|let|var)\\s+${escapedName}\\s*=`),
new RegExp(`\\b${escapedName}\\s*=\\s*`),
// Function parameters
new RegExp(`function\\s*\\([^)]*\\b${escapedName}\\b[^)]*\\)`),
new RegExp(`\\([^)]*\\b${escapedName}\\b[^)]*\\)\\s*=>`),
// Destructuring
new RegExp(`{[^}]*\\b${escapedName}\\b[^}]*}`),
new RegExp(`\\[[^\\]]*\\b${escapedName}\\b[^\\]]*\\]`),
// Loop variables
new RegExp(`for\\s*\\([^;]*\\b${escapedName}\\b`),
// React Context patterns
new RegExp(`\\b${escapedName}\\s*=\\s*createContext`),
new RegExp(`\\bconst\\s+${escapedName}\\s*=\\s*createContext`),
// React Hook patterns
new RegExp(`\\b${escapedName}\\s*=\\s*use[A-Z]`),
new RegExp(`\\bconst\\s+\\[.*${escapedName}.*\\]\\s*=\\s*useState`),
// Arrow function assignments
new RegExp(`\\bconst\\s+${escapedName}\\s*=\\s*\\(`),
new RegExp(`\\bconst\\s+${escapedName}\\s*=\\s*\\w+\\s*=>`),
];
// ตรวจสอบบรรทัดปัจจุบันและใกล้เคียง (เพิ่มช่วงการตรวจสอบ)
for (let i = Math.max(0, lineIndex - 3); i <= Math.min(lines.length - 1, lineIndex + 3); i++) {
const line = lines[i];
if (variablePatterns.some(pattern => pattern.test(line))) {
return true;
}
}
// ตรวจสอบ common variable names
const commonVariableNames = [
'result', 'data', 'item', 'value', 'temp', 'node', 'element',
'current', 'next', 'prev', 'parent', 'child', 'left', 'right',
'count', 'index', 'length', 'size', 'max', 'min', 'sum',
'start', 'end', 'pos', 'queue', 'stack', 'list', 'array',
'map', 'set', 'key', 'val', 'obj', 'target', 'source',
'input', 'output', 'buffer', 'cache', 'config', 'options',
// Algorithm-specific variables
'dp', 'memo', 'visited', 'distances', 'graph', 'matrix',
'heap', 'buckets', 'intervals', 'points', 'edges', 'vertices',
// React-specific variable names
'user', 'users', 'state', 'props', 'context', 'ref', 'refs',
'handler', 'handlers', 'callback', 'callbacks', 'event', 'events',
'timeout', 'interval', 'timer', 'loading', 'error', 'success'
];
if (commonVariableNames.includes(lowerName)) {
return true;
}
// ตรวจสอบ React Context patterns (ขึ้นต้นด้วยตัวพิมพ์ใหญ่ แต่ลงท้ายด้วย Context)
if (name.endsWith('Context') && /^[A-Z][a-zA-Z]*Context$/.test(name)) {
return true;
}
// ตรวจสอบ React Component patterns ที่เป็น const assignment
if (/^[A-Z][a-zA-Z0-9]*$/.test(name)) {
// ตรวจสอบว่าเป็น const component assignment หรือไม่
const componentPattern = new RegExp(`const\\s+${name}\\s*=`);
for (let i = Math.max(0, lineIndex - 2); i <= Math.min(lines.length - 1, lineIndex + 2); i++) {
if (componentPattern.test(lines[i])) {
return true;
}
}
}
// ตรวจสอบ short variable names (มักเป็น loop counters)
if (name.length <= 2 && /^[a-zA-Z][0-9]?$/.test(name)) {
return true;
}
// ตรวจสอบ camelCase variables (ขึ้นต้นด้วยตัวพิมพ์เล็ก)
if (/^[a-z][a-zA-Z0-9]*$/.test(name)) {
return true;
}
return false;
}
// ตรวจสอบว่าเป็น React Component หรือไม่
isReactComponent(name, content, lineIndex) {
const lines = content.split('\n');
if (lineIndex < 0 || lineIndex >= lines.length) return false;
// React Component ต้องขึ้นต้นด้วยตัวพิมพ์ใหญ่
if (!/^[A-Z][a-zA-Z0-9]*$/.test(name)) return false;
// ตรวจสอบ patterns ของ React Components
const reactComponentPatterns = [
// const Component = () => {}
new RegExp(`const\\s+${name}\\s*=\\s*\\([^)]*\\)\\s*=>`),
// const Component = function() {}
new RegExp(`const\\s+${name}\\s*=\\s*function`),
// const Component = React.memo()
new RegExp(`const\\s+${name}\\s*=\\s*React\\.memo`),
// const Component = forwardRef()
new RegExp(`const\\s+${name}\\s*=\\s*forwardRef`),
// export const Component =
new RegExp(`export\\s+const\\s+${name}\\s*=`),
];
// ตรวจสอบบรรทัดใกล้เคียง
for (let i = Math.max(0, lineIndex - 2); i <= Math.min(lines.length - 1, lineIndex + 2); i++) {
const line = lines[i];
if (reactComponentPatterns.some(pattern => pattern.test(line))) {
return true;
}
}
// ตรวจสอบ JSX return patterns
const jsxPatterns = [
/return\s*\(/,
/return\s*</,
/>\s*$/,
/<\/[a-zA-Z]/
];
// ตรวจสอบใน function body ว่ามี JSX หรือไม่
for (let i = lineIndex; i < Math.min(lines.length, lineIndex + 20); i++) {
const line = lines[i].trim();
if (jsxPatterns.some(pattern => pattern.test(line))) {
return true;
}
// หยุดตรวจสอบถ้าเจอปิด function
if (line.includes('}') && !line.includes('{')) {
break;
}
}
return false;
}
// สร้าง comparison report
generateComparisonReport(originalAnalysis, modifiedAnalysis, filePath) {
const report = {
filePath,
timestamp: new Date().toISOString(),
original: originalAnalysis,
modified: modifiedAnalysis,
changes: {
functionsAdded: modifiedAnalysis.summary.totalFunctions - originalAnalysis.summary.totalFunctions,
classesAdded: modifiedAnalysis.summary.totalClasses - originalAnalysis.summary.totalClasses,
commentsAdded: 0, // จะคำนวณจากการเปรียบเทียบ
structuresSkipped: (modifiedAnalysis.functionsWithoutComments?.length || 0) + (modifiedAnalysis.classesWithoutComments?.length || 0),
errors: []
},
skippedStructures: {
functions: modifiedAnalysis.functionsWithoutComments || [],
classes: modifiedAnalysis.classesWithoutComments || []
}
};
// คำนวณจำนวน comments ที่เพิ่ม
report.changes.commentsAdded = this.calculateCommentsAdded(originalAnalysis, modifiedAnalysis);
return report;
}
// คำนวณจำนวน comments ที่เพิ่มขึ้น
calculateCommentsAdded(original, modified) {
const originalCommentLines = this.countCommentLines(original.analysis?.content || '');
const modifiedCommentLines = this.countCommentLines(modified.analysis?.content || '');
return Math.max(0, modifiedCommentLines - originalCommentLines);
}
// นับจำนวนบรรทัดที่มี comment
countCommentLines(content) {
const lines = content.split('\n');
return lines.filter(line => {
const trimmed = line.trim();
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
}).length;
}
// บันทึก detailed report ลง audit log
logDetailedReport(report, filePath) {
logger.audit('FILE_COMPARISON_REPORT', filePath, {
summary: {
totalStructuresOriginal: report.original.totalStructures,
totalStructuresModified: report.modified.totalStructures,
functionsAdded: report.changes.functionsAdded,
classesAdded: report.changes.classesAdded,
commentsAdded: report.changes.commentsAdded,
structuresSkipped: report.changes.structuresSkipped
},
originalStructures: report.original.summary,
modifiedStructures: report.modified.summary
});
}
// บันทึก error และ skip report แยกต่างหาก พร้อมวิเคราะห์ละเอียดและแนะนำวิธีแก้ไข
logErrorAndSkipReport(report, filePath) {
if (report.changes.structuresSkipped > 0) {
// วิเคราะห์รูปแบบที่ skip
const analysisResult = this.analyzeSkippedPatterns(report, filePath);
logger.error('STRUCTURES_SKIPPED',
`${report.changes.structuresSkipped} structures were skipped in ${filePath}`,
null,
{
skippedFunctions: report.skippedStructures.functions.map(f => `${f.name} (line ${f.line})`),
skippedClasses: report.skippedStructures.classes.map(c => `${c.name} (line ${c.line})`),
totalSkipped: report.changes.structuresSkipped,
reasons: 'Functions/Classes without comments detected',
patternAnalysis: analysisResult.patterns,
suggestedSolutions: analysisResult.solutions,
codeExamples: analysisResult.examples
}
);
// สร้าง detailed diagnostic report
this.generateDetailedDiagnosticReport(report, filePath, analysisResult);
}
// สร้าง summary report
logger.performance('FILE_PROCESSING_SUMMARY', 0, {
filePath,
success: true,
totalStructures: report.modified.totalStructures,
commentsAdded: report.changes.commentsAdded,
functionsSkipped: report.skippedStructures.functions.length,
classesSkipped: report.skippedStructures.classes.length,
errorCount: report.changes.errors.length
});
}
// วิเคราะห์รูปแบบของ structures ที่ถูก skip
analyzeSkippedPatterns(report, filePath) {
const analysis = {
patterns: {
variableDeclarations: 0,
shortVariableNames: 0,
nestedStructures: 0,
generatedCode: 0,
utilityVariables: 0
},
solutions: [],
examples: []
};
// วิเคราะห์ functions ที่ skip
report.skippedStructures.functions.forEach(func => {
if (func.name.length <= 2) {
analysis.patterns.shortVariableNames++;
analysis.solutions.push(`Short variable name detected: "${func.name}" - Consider using descriptive names`);
}
if (['i', 'j', 'k', 'x', 'y', 'z', 'n', 'm'].includes(func.name)) {
analysis.patterns.utilityVariables++;
analysis.solutions.push(`Loop/utility variable detected: "${func.name}" - This is likely a loop counter or temporary variable`);
}
});
// วิเคราะห์ classes ที่ skip
report.skippedStructures.classes.forEach(cls => {
if (cls.name.length <= 2) {
analysis.patterns.shortVariableNames++;
analysis.solutions.push(`Short variable name detected: "${cls.name}" - This appears to be a variable, not a class`);
}
if (['temp', 'result', 'data', 'item', 'node', 'value'].includes(cls.name.toLowerCase())) {
analysis.patterns.variableDeclarations++;
analysis.solutions.push(`Variable declaration detected: "${cls.name}" - This is likely a variable assignment`);
}
if (cls.name.match(/^[a-z][a-zA-Z]*$/)) {
analysis.patterns.variableDeclarations++;
analysis.solutions.push(`Camel case variable detected: "${cls.name}" - This follows variable naming convention`);
}
});
// สร้าง code examples สำหรับการแก้ไข
if (analysis.patterns.variableDeclarations > 0) {
analysis.examples.push({
type: 'Variable Declaration Fix',
problem: 'const result = someFunction();',
solution: '// Calculate processing result\nconst result = someFunction();'
});
}
if (analysis.patterns.shortVariableNames > 0) {
analysis.examples.push({
type: 'Short Variable Name Fix',
problem: 'let i = 0;',
solution: '// Loop counter for iteration\nlet i = 0;'
});
}
return analysis;
}
// สร้าง detailed diagnostic report
generateDetailedDiagnosticReport(report, filePath, analysisResult) {
const diagnosticData = {
filePath: filePath,
timestamp: new Date().toISOString(),
issuesSummary: {
totalIssues: report.changes.structuresSkipped,
functionIssues: report.skippedStructures.functions.length,
classIssues: report.skippedStructures.classes.length,
mainCauses: this.identifyMainCauses(analysisResult.patterns, report)
},
detailedAnalysis: {
likelyVariableDeclarations: analysisResult.patterns.variableDeclarations,
shortVariableNames: analysisResult.patterns.shortVariableNames,
utilityVariables: analysisResult.patterns.utilityVariables,
suggestedFixes: analysisResult.solutions.length
},
actionableRecommendations: this.generateActionableRecommendations(report, analysisResult),
codeImprovementSuggestions: analysisResult.examples
};
logger.diagnostic('STRUCTURE_ANALYSIS',
`Detailed diagnostic for ${path.basename(filePath)}`,
diagnosticData);
}
// ระบุสาเหตุหลักของปัญหา
identifyMainCauses(patterns, report) {
const causes = [];
if (patterns.variableDeclarations > 0) {
causes.push('Variable declarations misidentified as classes');
}
if (patterns.shortVariableNames > 0) {
causes.push('Short variable names detected');
}
if (patterns.utilityVariables > 0) {
causes.push('Loop counters and utility variables present');
}
// เพิ่ม Advanced Pattern Analysis
const advancedPatterns = this.analyzeAdvancedPatterns(report.skippedStructures);
if (advancedPatterns.typeScriptPatterns > 0) {
causes.push('TypeScript advanced constructs (interfaces, types, generics)');
}
if (advancedPatterns.reactPatterns > 0) {
causes.push('React patterns (hooks, components, context)');
}
if (advancedPatterns.modernJsPatterns > 0) {
causes.push('Modern JavaScript patterns (destructuring, async/await)');
}
if (advancedPatterns.classMethodPatterns > 0) {
causes.push('Advanced class methods and decorators');
}
return causes.length > 0 ? causes : ['Unknown pattern - requires manual review'];
}
// วิเคราะห์ Advanced Patterns ที่ Parser ยังไม่รู้จัก
analyzeAdvancedPatterns(skippedStructures) {
const patterns = {
typeScriptPatterns: 0,
reactPatterns: 0,
modernJsPatterns: 0,
classMethodPatterns: 0,
algorithmPatterns: 0
};
const allStructures = [
...(skippedStructures.functions || []),
...(skippedStructures.classes || [])
];
allStructures.forEach(structure => {
const name = structure.name;
const lowerName = name.toLowerCase();
// TypeScript Patterns
if (this.isTypeScriptPattern(name)) {
patterns.typeScriptPatterns++;
}
// React Patterns
if (this.isReactPattern(name)) {
patterns.reactPatterns++;
}
// Modern JavaScript Patterns
if (this.isModernJsPattern(name)) {
patterns.modernJsPatterns++;
}
// Class Method Patterns
if (this.isClassMethodPattern(name)) {
patterns.classMethodPatterns++;
}
// Algorithm Patterns
if (this.isAlgorithmPattern(name)) {
patterns.algorithmPatterns++;
}
});
return patterns;
}
// ตรวจสอบ TypeScript Patterns
isTypeScriptPattern(name) {
const tsPatterns = [
/^[A-Z][a-zA-Z]*Options$/, // ConnectionOptions, QueryOptions
/^[A-Z][a-zA-Z]*Config$/, // DatabaseConfig, ApiConfig
/^[A-Z][a-zA-Z]*Handler$/, // EventHandler, ErrorHandler
/^[A-Z][a-zA-Z]*Listener$/, // EventListener, ChangeListener
/^[A-Z][a-zA-Z]*Result$/, // QueryResult, ValidationResult
/^[A-Z][a-zA-Z]*Error$/, // ValidationError, ConnectionError
/^[A-Z][a-zA-Z]*Stats$/, // ConnectionStats, PerformanceStats
/^[A-Z][a-zA-Z]*Schema$/, // ValidationSchema, DatabaseSchema
/^I[A-Z][a-zA-Z]*$/, // Interface naming (IUser, IDatabase)
/^T[A-Z][a-zA-Z]*$/, // Type parameter naming
];
return tsPatterns.some(pattern => pattern.test(name));
}
// ตรวจสอบ React Patterns
isReactPattern(name) {
const reactPatterns = [
/^use[A-Z][a-zA-Z]*$/, // Custom hooks (useApi, useLocalStorage)
/^[A-Z][a-zA-Z]*Context$/, // React Context (ThemeContext, UserContext)
/^[A-Z][a-zA-Z]*Provider$/, // Context Provider (NotificationProvider)
/^handle[A-Z][a-zA-Z]*$/, // Event handlers (handleClick, handleSubmit)
/^on[A-Z][a-zA-Z]*$/, // Event callbacks (onClick, onSubmit)
/^render[A-Z][a-zA-Z]*$/, // Render methods (renderItem, renderField)
/^[a-z]+Ref$/, // React refs (inputRef, containerRef)
/^[A-Z][a-zA-Z]*Table$/, // React Table components (DataTable, UserTable)
/^[A-Z][a-zA-Z]*Form$/, // React Form components (LoginForm, ContactForm)
/^[A-Z][a-zA-Z]*Modal$/, // React Modal components (ConfirmModal, EditModal)
/^[A-Z][a-zA-Z]*Button$/, // React Button components (SubmitButton, CancelButton)
/^[A-Z][a-zA-Z]*Input$/, // React Input components (TextInput, DateInput)
/^[A-Z][a-zA-Z]*List$/, // React List components (ItemList, UserList)
/^[A-Z][a-zA-Z]*Card$/, // React Card components (ProfileCard, NewsCard)
/^[A-Z][a-zA-Z]*Builder$/, // React Builder components (FormBuilder, LayoutBuilder)
/^[A-Z][a-zA-Z]*Dashboard$/, // React Dashboard components
/^[A-Z][a-zA-Z]*Panel$/, // React Panel components
/^[A-Z][a-zA-Z]*Widget$/, // React Widget components
];
return reactPatterns.some(pattern => pattern.test(name));
}
// ตรวจสอบ Modern JavaScript Patterns
isModernJsPattern(name) {
const modernPatterns = [
/^[a-z]+Async$/, // Async functions (fetchAsync, loadAsync)
/^[a-z]+Promise$/, // Promise-based functions
/^[a-z]+Generator$/, // Generator functions
/^[a-z]+Iterator$/, // Iterator functions
/^create[A-Z][a-zA-Z]*$/, // Factory functions (createConnection, createUser)
/^build[A-Z][a-zA-Z]*$/, // Builder functions
/^process[A-Z][a-zA-Z]*$/, // Processing functions
];
return modernPatterns.some(pattern => pattern.test(name));
}
// ตรวจสอบ Class Method Patterns
isClassMethodPattern(name) {
const classPatterns = [
/^[a-z]+Connection$/, // Database connections
/^[a-z]+Manager$/, // Manager classes
/^[a-z]+Builder$/, // Builder classes
/^[a-z]+Factory$/, // Factory classes
/^[a-z]+Repository$/, // Repository pattern
/^[a-z]+Service$/, // Service classes
/^[a-z]+Controller$/, // Controller classes
];
return classPatterns.some(pattern => pattern.test(name));
}
// ตรวจสอบ Algorithm Patterns
isAlgorithmPattern(name) {
const algoPatterns = [
/^[a-z]{1,3}$/, // Short algorithm variables (dp, bfs, dfs)
/^[a-z]+Tree$/, // Tree structures
/^[a-z]+Node$/, // Node structures
/^[a-z]+Graph$/, // Graph structures
/^[a-z]+Queue$/, // Queue structures
/^[a-z]+Stack$/, // Stack structures
];
return algoPatterns.some(pattern => pattern.test(name));
}
// สร้างคำแนะนำที่สามารถทำได้จริง
generateActionableRecommendations(report, analysisResult) {
const recommendations = [];
if (analysisResult.patterns.variableDeclarations > 5) {
recommendations.push({
priority: 'HIGH',
action: 'Review variable declarations',
description: 'Many variables were misidentified as classes. Consider adding comments above variable declarations.',
example: '// Store calculation result\nconst result = calculate();'
});
}
if (analysisResult.patterns.shortVariableNames > 3) {
recommendations.push({
priority: 'MEDIUM',
action: 'Use descriptive variable names',
description: 'Short variable names (i, j, x, y) are common and may not need comments.',
example: 'for (let index = 0; index < items.length; index++)'
});
}
if (report.changes.structuresSkipped > 20) {
recommendations.push({
priority: 'LOW',
action: 'Consider code refactoring',
description: 'High number of skipped structures suggests complex code that may benefit from refactoring.',
example: 'Break down large functions into smaller, well-documented functions'
});
}
// Advanced Pattern Recommendations
const advancedPatterns = this.analyzeAdvancedPatterns(report.skippedStructures);
if (advancedPatterns.typeScriptPatterns > 0) {
recommendations.push({
priority: 'HIGH',
action: 'Add TypeScript interface/type documentation',
description: 'TypeScript constructs detected. These need proper documentation for better code understanding.',
example: '// Interface defining database connection options\ninterface ConnectionOptions { ... }'
});
}
if (advancedPatterns.reactPatterns > 0) {
recommendations.push({
priority: 'HIGH',
action: 'Document React components and hooks',
description: 'React patterns detected. Components and custom hooks should have clear documentation.',
example: '// Custom hook for managing API calls with loading state\nconst useApi = () => { ... }'
});
}
if (advancedPatterns.modernJsPatterns > 0) {
recommendations.push({
priority: 'MEDIUM',
action: 'Document async/await and modern patterns',
description: 'Modern JavaScript patterns detected. Async operations need clear documentation.',
example: '// Asynchronously processes user data with error handling\nasync function processUserData() { ... }'
});
}
if (advancedPatterns.classMethodPatterns > 0) {
recommendations.push({
priority: 'MEDIUM',
action: 'Add class method documentation',
description: 'Advanced class methods detected. Repository/Service patterns need clear documentation.',
example: '// Repository class for managing user database operations\nclass UserRepository { ... }'
});
}
return recommendations;
}
}
// สร้าง file comparison analyzer instance
const fileComparisonAnalyzer = new FileComparisonAnalyzer();
// สร้าง backup manager instance
const backupManager = new OrganizedBackupManager();
// ======================================================================
// Helper Functions - ฟังก์ชันช่วยเหลือสำหรับการประมวลผล
// ======================================================================
// ฟังก์ชันคำนวณหมายเลขบรรทัดที่จัดการ line endings ได้ถูกต้อง
// @param {string} content - เนื้อหาของไฟล์
// @param {number} index - ตำแหน่งที่ต้องการหาหมายเลขบรรทัด
// @returns {number} หมายเลขบรรทัด (เริ่มต้นที่ 1)
function calculateLineNumber(content, index) {
const normalizedContent = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
return normalizedContent.substring(0, index).split('\n').length;
}
// ฟังก์ชันสำหรับ normalize line endings
// @param {string} content - เนื้อหาของไฟล์
// @returns {string} เนื้อหาที่ normalize แล้ว
function normalizeLineEndings(content) {
return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
}
// ฟังก์ชันตรวจสอบว่าแต่ละ structure มี comment เฉพาะตัวหรือไม่
// @param {string} content - เนื้อหาของไฟล์
// @param {Array} structures - รายการ structures ที่ตรวจพบ
// @returns {Map} Map ที่เก็บข้อมูล comment status ของแต่ละ structure
function analyzeCommentStatus(content, structures) {
const commentStatusMap = new Map();
const normalizedContent = normalizeLineEndings(content);
const lines = normalizedContent.split('\n');
structures.forEach(structure => {
const structureLine = structure.line - 1; // แปลงเป็น 0-based index
let hasComment = false;
let commentLines = [];
// ตรวจสอบ 3-5 บรรทัดก่อนหน้าเฉพาะ structure นี้
for (let i = 1; i <= 5; i++) {
const checkIndex = structureLine - i;
if (checkIndex >= 0 && checkIndex < lines.length) {
const line = lines[checkIndex];
const trimmed = line.trim();
// เจอ comment line
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.includes('*/')) {
const commentContent = trimmed.toLowerCase();
const structureName = structure.name.toLowerCase();
// ตรวจสอบว่า comment นี้เกี่ยวข้องกับ structure นี้หรือไม่
// หลัก: ชื่อ structure ต้องตรงกัน หรือ comment ต้องเฉพาะเจาะจง
if (commentContent.includes(structureName) ||
(commentContent.includes('en:') &&
(commentContent.includes(structureName) ||
(commentContent.includes('interface') && structure.type === 'interface_declaration') ||
(commentContent.includes('type') && structure.type === 'type_alias' && commentContent.includes('alias')) ||
(commentContent.includes('static') && structure.type === 'static_method') ||
(commentContent.includes('abstract') && structure.type === 'abstract_class'))) ||
(commentContent.includes('th:') &&
(commentContent.includes(structureName) ||
(commentContent.includes('interface') && structure.type === 'interface_declaration') ||
(commentContent.includes('type') && structure.type === 'type_alias' && commentContent.includes('alias')) ||
(commentContent.includes('static') && structure.type === 'static_method') ||
(commentContent.includes('abstract') && structure.type === 'abstract_class'))) ||
commentContent.includes('======')) {
hasComment = true;
commentLines.push(checkIndex + 1); // แปลงกลับเป็น 1-based
break;
}
}
// เจอ code line อื่นที่ไม่เกี่ยวข้อง ให้หยุดค้นหา
else if (trimmed &&
!trimmed.startsWith('export') &&
!trimmed.startsWith('@') &&
!trimmed.startsWith('{') &&
!trimmed.startsWith('}')) {
break;
}
}
}
commentStatusMap.set(structure.name, {
hasComment,
commentLines,
structure: structure
});
});
return commentStatusMap;
}
// ======================================================================
// JavaScript Tokenizer Engine/เครื่องมือ Tokenizer ของ JavaScript
// ======================================================================
// Token types:ประเภทของโทเค็น
const TOKEN_TYPES = {
// คำหลัก - Keywords
KEYWORD: 'KEYWORD',
// ชื่อตัวแปร/ฟังก์ชัน - Identifiers
IDENTIFIER: 'IDENTIFIER',
// เครื่องหมาย - Operators and punctuation
EQUALS: 'EQUALS', // =
ARROW: 'ARROW', // =>
PAREN_OPEN: 'PAREN_OPEN', // (
PAREN_CLOSE: 'PAREN_CLOSE', // )
BRACE_OPEN: 'BRACE_OPEN', // {
BRACE_CLOSE: 'BRACE_CLOSE', // }
BRACKET_OPEN: 'BRACKET_OPEN', // [
BRACKET_CLOSE: 'BRACKET_CLOSE', // ]
SEMICOLON: 'SEMICOLON', // ;
COMMA: 'COMMA', // ,
// คอมเมนต์ - Comments
LINE_COMMENT: 'LINE_COMMENT', // //
BLOCK_COMMENT: 'BLOCK_COMMENT', // /* */
// สตริง - Strings
STRING: 'STRING',
// ตัวเลข - Numbers
NUMBER: 'NUMBER',
// ช่องว่าง - Whitespace
WHITESPACE: 'WHITESPACE',
NEWLINE: 'NEWLINE',
// จุดสิ้นสุด - End of file
EOF: 'EOF'
};
// คำหลักของ JavaScript, TypeScript, JSX, TSX ครบถ้วน - Complete keywords for JS/TS/JSX/TSX
const KEYWORDS = new Set([
// ═══════════════════════════════════════════════════════════════
// JavaScript Core Keywords
// ═══════════════════════════════════════════════════════════════
'function', 'const', 'let', 'var', 'async', 'await',
'class', 'constructor', 'static', 'get', 'set', 'abstract',
'if', 'else', 'for', 'while', 'do', 'switch', 'case',
'return', 'break', 'continue', 'throw', 'try', 'catch',
'import', 'export', 'default', 'from', 'as', 'finally',
'with', 'delete', 'new', 'this', 'super', 'instanceof',
'of', 'in', 'null', 'undefined', 'true', 'false',
'yield', 'debugger', 'arguments', 'eval',
// ═══════════════════════════════════════════════════════════════
// TypeScript Specific Keywords
// ═══════════════════════════════════════════════════════════════
'interface', 'type', 'enum', 'namespace', 'module',
'declare', 'readonly', 'public', 'private', 'protected',
'implements', 'extends', 'keyof', 'typeof', 'infer',
'never', 'unknown', 'any', 'void', 'string', 'number', 'boolean',
'object', 'symbol', 'bigint', 'unique', 'is', 'asserts',
'override', 'satisfies', 'out', 'in', 'const',
// ═══════════════════════════════════════════════════════════════
// Advanced TypeScript Utility Types & Keywords
// ═══════════════════════════════════════════════════════════════
'Partial', 'Required', 'Pick', 'Omit', 'Exclude', 'Extract',
'NonNullable', 'Parameters', 'ReturnType', 'InstanceType',
'ThisType', 'Record', 'Readonly', 'Array', 'Promise',
'Awaited', 'ConstructorParameters', 'ThisParameterType',
'OmitThisParameter', 'Uppercase', 'Lowercase', 'Capitalize', 'Uncapitalize',
// ═══════════════════════════════════════════════════════════════
// React Core Components & Hooks
// ═══════════════════════════════════════════════════════════════
'React', 'Component', 'PureComponent', 'memo', 'forwardRef',
'createContext', 'useContext', 'createRef', 'useRef',
'useState', 'useEffect', 'us