gbu-accessibility-package
Version:
Comprehensive accessibility fixes and project optimization for HTML files. Smart context-aware alt text generation, form labels, button names, link names, landmarks, heading analysis, WCAG-compliant role attributes, unused files detection, dead code analy
692 lines (595 loc) • 32.6 kB
JavaScript
/**
* Accessibility Fixer CLI
* Command line interface for the accessibility fixer tool
*/
const AccessibilityFixer = require('./lib/fixer.js');
const chalk = require('chalk');
const path = require('path');
// Parse command line arguments
const args = process.argv.slice(2);
const options = {
directory: '.',
language: 'ja',
backupFiles: false, // Default to false for faster processing
dryRun: false,
help: false,
version: false,
cleanupOnly: false,
comprehensive: false, // Keep for backward compatibility
altOnly: false,
langOnly: false,
roleOnly: false,
formsOnly: false,
nestedOnly: false,
buttonsOnly: false,
linksOnly: false,
landmarksOnly: false,
headingsOnly: false,
dlOnly: false,
brokenLinksOnly: false,
unusedFilesOnly: false,
deadCodeOnly: false,
fileSizeOnly: false,
// Enhanced alt options
enhancedAlt: false,
altCreativity: 'balanced', // conservative, balanced, creative
includeEmotions: false,
strictAltChecking: false,
// Advanced features options
autoFixHeadings: false,
fixDescriptionLists: true
};
// Parse arguments
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '--help':
case '-h':
options.help = true;
break;
case '--version':
case '-v':
options.version = true;
break;
case '--directory':
case '-d':
options.directory = args[++i];
break;
case '--language':
case '-l':
options.language = args[++i];
break;
case '--backup':
options.backupFiles = true;
break;
case '--no-backup':
options.backupFiles = false;
break;
case '--dry-run':
options.dryRun = true;
break;
case '--cleanup-only':
options.cleanupOnly = true;
break;
case '--comprehensive':
case '--all':
options.comprehensive = true; // Keep for backward compatibility
break;
case '--alt-only':
options.altOnly = true;
break;
case '--lang-only':
options.langOnly = true;
break;
case '--role-only':
options.roleOnly = true;
break;
case '--aria-label-only':
options.ariaLabelOnly = true;
break;
case '--forms-only':
options.formsOnly = true;
break;
case '--nested-only':
options.nestedOnly = true;
break;
case '--buttons-only':
options.buttonsOnly = true;
break;
case '--links-only':
options.linksOnly = true;
break;
case '--landmarks-only':
options.landmarksOnly = true;
break;
case '--headings-only':
options.headingsOnly = true;
break;
case '--dl-only':
options.dlOnly = true;
break;
case '--links-check':
options.linksCheckOnly = true;
break;
case '--broken-links':
options.brokenLinksOnly = true;
break;
case '--404-resources':
case '--missing-resources':
options.missingResourcesOnly = true;
break;
case '--gtm-check':
case '--check-gtm':
case '--google-tag-manager':
options.gtmCheckOnly = true;
break;
case '--check-meta':
case '--meta-check':
options.checkMetaOnly = true;
break;
case '--fix-meta':
case '--meta-fix':
options.fixMetaOnly = true;
break;
case '--full-report':
case '--report':
case '--excel-report':
options.fullReport = true;
break;
case '-o':
case '--output':
options.reportOutput = args[++i];
break;
case '--unused-files':
options.unusedFilesOnly = true;
break;
case '--dead-code':
options.deadCodeOnly = true;
break;
case '--file-size':
case '--size-check':
options.fileSizeOnly = true;
break;
case '--auto-fix-headings':
options.autoFixHeadings = true;
break;
case '--no-fix-dl':
options.fixDescriptionLists = false;
break;
case '--enhanced-alt':
options.enhancedAlt = true;
break;
case '--alt-creativity':
options.altCreativity = args[++i];
break;
case '--include-emotions':
options.includeEmotions = true;
break;
case '--strict-alt':
options.strictAltChecking = true;
break;
default:
if (!arg.startsWith('-')) {
options.directory = arg;
}
}
}
// Show help
if (options.version) {
const packageJson = require('./package.json');
console.log(chalk.blue(`🔧 GBU Accessibility Package v${packageJson.version}`));
process.exit(0);
}
if (options.help) {
console.log(chalk.blue(`
🔧 Accessibility Fixer CLI
Usage: node cli.js [options] [directory]
Options:
-d, --directory <path> Target directory (default: current directory)
-l, --language <lang> Language for lang attribute (default: ja)
--backup Create backup files
--no-backup Don't create backup files (default)
--dry-run Preview changes without applying
--comprehensive, --all Run comprehensive fixes (same as default)
--cleanup-only Only cleanup duplicate role attributes
--alt-only Fix alt attributes + cleanup
--lang-only Fix HTML lang attributes + cleanup
--role-only Fix role attributes + cleanup
--aria-label-only Fix aria-label attributes + cleanup
--forms-only Fix form labels + cleanup
--buttons-only Fix button names + cleanup
--links-only Fix link names + cleanup
--landmarks-only Fix landmarks + cleanup
--headings-only Analyze heading structure (no auto-fix)
--links-check Check for broken links and 404 resources (no auto-fix)
--broken-links Check for broken external links only (no auto-fix)
--404-resources Check for missing local resources only (no auto-fix)
--gtm-check Check Google Tag Manager installation (no auto-fix)
--check-meta Check meta tags and Open Graph Protocol (no auto-fix)
--fix-meta Auto-fix missing meta tags and OGP tags
--full-report Generate comprehensive Excel report (all checks)
-o, --output <file> Output path for Excel report (use with --full-report)
--unused-files Check for unused files in project (no auto-fix)
--dead-code Check for dead code in CSS and JavaScript (no auto-fix)
--file-size, --size-check Check file sizes and suggest optimizations (no auto-fix)
--enhanced-alt Use enhanced alt attribute analysis and generation
--alt-creativity <mode> Alt text creativity: conservative, balanced, creative (default: balanced)
--include-emotions Include emotional descriptors in alt text
--strict-alt Enable strict alt attribute quality checking
-h, --help Show this help message
-v, --version Show version number
Enhanced Alt Features:
--enhanced-alt Comprehensive alt attribute analysis with:
• Image type classification (decorative, functional, complex, etc.)
• Content quality checking (length, redundancy, generic text)
• Context-aware alt text generation
• Multi-language vocabulary support
• Brand and emotional context integration
• Technical image description (charts, graphs)
Alt Creativity Modes:
conservative Simple, factual descriptions
balanced Context-aware with moderate creativity (default)
creative Rich descriptions with emotions and brand context
Examples:
node cli.js # Comprehensive fixes (no backup by default)
node cli.js --comprehensive # Comprehensive fixes (same as default)
node cli.js --alt-only # Fix alt attributes + cleanup
node cli.js --forms-only # Fix form labels + cleanup
node cli.js --buttons-only # Fix button names + cleanup
node cli.js --links-only # Fix link names + cleanup
node cli.js --landmarks-only # Fix landmarks + cleanup
node cli.js --headings-only # Analyze heading structure only
node cli.js --links-check # Check for broken links and 404s
node cli.js --broken-links # Check for broken external links only
node cli.js --404-resources # Check for missing local resources only
node cli.js --gtm-check # Check Google Tag Manager installation
node cli.js --check-meta # Check meta tags and Open Graph Protocol
node cli.js --fix-meta # Auto-fix missing meta tags and OGP
node cli.js --fix-meta --dry-run # Preview meta tag fixes
node cli.js --full-report # Generate comprehensive Excel report
node cli.js --full-report ./project -o report.xlsx # Custom output path
node cli.js --unused-files # Check for unused files in project
node cli.js --dead-code # Check for dead CSS and JavaScript code
node cli.js --file-size # Check file sizes and suggest optimizations
node cli.js --cleanup-only # Only cleanup duplicate roles
node cli.js ./src # Fix src directory (comprehensive)
node cli.js -l en --dry-run ./dist # Preview comprehensive fixes in English
node cli.js --backup ./public # Comprehensive fixes with backups
node cli.js --enhanced-alt # Use enhanced alt attribute analysis
node cli.js --enhanced-alt --alt-creativity creative # Creative alt text generation
node cli.js --enhanced-alt --include-emotions # Include emotional context
node cli.js --strict-alt --enhanced-alt # Strict quality checking
Features:
✅ Alt attributes for images
✅ Lang attributes for HTML
✅ Role attributes for accessibility
✅ Context-aware text generation
✅ Automatic backups
`));
process.exit(0);
}
// Helper function to show completion message with backup info
function showCompletionMessage(options, mode = 'sửa lỗi') {
if (options.dryRun) {
console.log(chalk.cyan('\n💡 Đây là chế độ xem trước. Sử dụng không có --dry-run để áp dụng thay đổi.'));
} else {
console.log(chalk.green(`\n🎉 ${mode} hoàn tất thành công!`));
if (options.backupFiles) {
console.log(chalk.gray(' 📁 Đã tạo file backup với đuôi .backup'));
console.log(chalk.gray(' 💡 Sử dụng --no-backup để tắt backup trong các lần chạy sau'));
} else {
console.log(chalk.blue(' ⚡ Không tạo file backup (mặc định để xử lý nhanh hơn)'));
console.log(chalk.gray(' 💡 Sử dụng --backup để bật tính năng backup để an toàn'));
}
}
}
// Main function
async function main() {
console.log(chalk.blue('🚀 Đang khởi động Accessibility Fixer...'));
console.log(chalk.gray(`Thư mục: ${path.resolve(options.directory)}`));
console.log(chalk.gray(`Ngôn ngữ: ${options.language}`));
console.log(chalk.gray(`Backup: ${options.backupFiles ? 'Có' : 'Không'}`));
console.log(chalk.gray(`Chế độ: ${options.dryRun ? 'Xem trước (Preview)' : 'Áp dụng thay đổi'}`));
console.log('');
const fixer = new AccessibilityFixer({
language: options.language,
backupFiles: options.backupFiles,
dryRun: options.dryRun,
enhancedAltMode: options.enhancedAlt,
altCreativity: options.altCreativity,
includeEmotions: options.includeEmotions,
strictAltChecking: options.strictAltChecking,
autoFixHeadings: options.autoFixHeadings,
fixDescriptionLists: options.fixDescriptionLists
});
try {
// Handle Full Report mode first
if (options.fullReport) {
console.log(chalk.blue('📊 Đang tạo báo cáo toàn diện...'));
await fixer.generateFullReport(options.directory, options.reportOutput);
return;
}
// Handle different modes - All modes now include cleanup
if (options.cleanupOnly || options.altOnly || options.langOnly || options.roleOnly || options.ariaLabelOnly ||
options.formsOnly || options.nestedOnly || options.buttonsOnly || options.linksOnly || options.landmarksOnly ||
options.headingsOnly || options.dlOnly || options.linksCheckOnly || options.brokenLinksOnly || options.missingResourcesOnly || options.gtmCheckOnly || options.checkMetaOnly || options.fixMetaOnly || options.unusedFilesOnly || options.deadCodeOnly || options.fileSizeOnly) {
// Individual modes - handle each separately, then run cleanup
} else {
// Default mode: Run comprehensive fix (all fixes including cleanup)
console.log(chalk.blue('🎯 Đang chạy sửa lỗi accessibility toàn diện...'));
const results = await fixer.fixAllAccessibilityIssues(options.directory);
// Results already logged in the method
return;
}
// Individual modes
if (options.cleanupOnly) {
// Only cleanup duplicate roles
console.log(chalk.blue('🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\n✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Dọn dẹp');
return;
} else if (options.cleanupOnly) {
// Only cleanup duplicate roles
console.log(chalk.blue('🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\n✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Dọn dẹp');
return;
} else if (options.altOnly) {
// Fix alt attributes + cleanup
console.log(chalk.blue('🖼️ Đang sửa thuộc tính alt + dọn dẹp...'));
const altResults = await fixer.fixEmptyAltAttributes(options.directory);
const altFixed = altResults.filter(r => r.status === 'fixed').length;
const totalAltIssues = altResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa thuộc tính alt trong ${altFixed} file (${totalAltIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa thuộc tính alt + dọn dẹp');
return;
} else if (options.langOnly) {
// Fix lang attributes + cleanup
console.log(chalk.blue('📝 Đang sửa thuộc tính HTML lang + dọn dẹp...'));
const langResults = await fixer.fixHtmlLang(options.directory);
const langFixed = langResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\n✅ Đã sửa thuộc tính lang trong ${langFixed} file`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa thuộc tính lang + dọn dẹp');
return;
} else if (options.roleOnly) {
// Fix role attributes + cleanup
console.log(chalk.blue('🎭 Đang sửa thuộc tính role + dọn dẹp...'));
const roleResults = await fixer.fixRoleAttributes(options.directory);
const roleFixed = roleResults.filter(r => r.status === 'fixed').length;
const totalRoleIssues = roleResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa thuộc tính role trong ${roleFixed} file (${totalRoleIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa thuộc tính role + dọn dẹp');
return;
} else if (options.ariaLabelOnly) {
// Fix aria-label attributes + cleanup
console.log(chalk.blue('🏷️ Đang sửa thuộc tính aria-label + dọn dẹp...'));
const ariaResults = await fixer.fixAriaLabels(options.directory);
const ariaFixed = ariaResults.filter(r => r.status === 'processed' && r.changes > 0).length;
const totalAriaIssues = ariaResults.reduce((sum, r) => sum + (r.changes || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa thuộc tính aria-label trong ${ariaFixed} file (${totalAriaIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa thuộc tính aria-label + dọn dẹp');
return;
} else if (options.formsOnly) {
// Fix form labels + cleanup
console.log(chalk.blue('📋 Đang sửa nhãn form + dọn dẹp...'));
const formResults = await fixer.fixFormLabels(options.directory);
const formFixed = formResults.filter(r => r.status === 'fixed').length;
const totalFormIssues = formResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa nhãn form trong ${formFixed} file (${totalFormIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa nhãn form + dọn dẹp');
return;
} else if (options.nestedOnly) {
// Fix nested interactive controls + cleanup
console.log(chalk.blue('🎯 Đang sửa các control tương tác lồng nhau + dọn dẹp...'));
const nestedResults = await fixer.fixNestedInteractiveControls(options.directory);
const nestedFixed = nestedResults.filter(r => r.status === 'fixed').length;
const totalNestedIssues = nestedResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa các control tương tác lồng nhau trong ${nestedFixed} file (${totalNestedIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa các control tương tác lồng nhau + dọn dẹp');
return;
} else if (options.buttonsOnly) {
// Fix button names + cleanup
console.log(chalk.blue('🔘 Đang sửa tên button + dọn dẹp...'));
const buttonResults = await fixer.fixButtonNames(options.directory);
const buttonFixed = buttonResults.filter(r => r.status === 'fixed').length;
const totalButtonIssues = buttonResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa tên button trong ${buttonFixed} file (${totalButtonIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa tên button + dọn dẹp');
return;
} else if (options.linksOnly) {
// Fix link names + cleanup
console.log(chalk.blue('🔗 Đang sửa tên link + dọn dẹp...'));
const linkResults = await fixer.fixLinkNames(options.directory);
const linkFixed = linkResults.filter(r => r.status === 'fixed').length;
const totalLinkIssues = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa tên link trong ${linkFixed} file (${totalLinkIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa tên link + dọn dẹp');
return;
} else if (options.landmarksOnly) {
// Fix landmarks + cleanup
console.log(chalk.blue('🏛️ Đang sửa landmark + dọn dẹp...'));
const landmarkResults = await fixer.fixLandmarks(options.directory);
const landmarkFixed = landmarkResults.filter(r => r.status === 'fixed').length;
const totalLandmarkIssues = landmarkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa landmark trong ${landmarkFixed} file (${totalLandmarkIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa landmark + dọn dẹp');
return;
} else if (options.headingsOnly) {
// Fix heading structure + cleanup
console.log(chalk.blue('📑 Đang sửa cấu trúc heading + dọn dẹp...'));
const headingResults = await fixer.fixHeadingStructure(options.directory);
const headingFixed = headingResults.filter(r => r.status === 'fixed').length;
const totalHeadingIssues = headingResults.reduce((sum, r) => sum + (r.issues || 0), 0);
const totalHeadingFixes = headingResults.reduce((sum, r) => sum + (r.fixes || 0), 0);
console.log(chalk.green(`\n✅ Đã xử lý heading trong ${headingResults.length} file (${totalHeadingIssues} vấn đề tìm thấy)`));
if (options.autoFixHeadings) {
console.log(chalk.green(`✅ Đã sửa ${totalHeadingFixes} vấn đề heading tự động`));
} else {
console.log(chalk.gray('💡 Sử dụng --auto-fix-headings để bật tính năng tự động sửa heading'));
}
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa cấu trúc heading + dọn dẹp');
return;
} else if (options.dlOnly) {
// Fix description lists + cleanup
console.log(chalk.blue('📋 Đang sửa danh sách mô tả + dọn dẹp...'));
const dlResults = await fixer.fixDescriptionLists(options.directory);
const dlFixed = dlResults.filter(r => r.status === 'fixed').length;
const totalDlIssues = dlResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã sửa danh sách mô tả trong ${dlFixed} file (${totalDlIssues} vấn đề)`));
// Run cleanup
console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
showCompletionMessage(options, 'Sửa danh sách mô tả + dọn dẹp');
return;
} else if (options.linksCheckOnly) {
// Check broken links and 404 resources (backward compatibility)
console.log(chalk.blue('🔗 Đang kiểm tra link và tài nguyên toàn diện...'));
const linkResults = await fixer.checkBrokenLinks(options.directory);
const resourceResults = await fixer.check404Resources(options.directory);
const totalLinkIssues = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
const totalResourceIssues = resourceResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã kiểm tra link trong ${linkResults.length} file (${totalLinkIssues} vấn đề link)`));
console.log(chalk.green(`✅ Đã kiểm tra tài nguyên trong ${resourceResults.length} file (${totalResourceIssues} vấn đề tài nguyên)`));
console.log(chalk.gray('💡 Vấn đề về link và tài nguyên cần xem xét thủ công và không thể tự động sửa'));
showCompletionMessage(options, 'Kiểm tra link và tài nguyên');
return;
} else if (options.brokenLinksOnly) {
// Check broken external links only
console.log(chalk.blue('🔗 Đang kiểm tra link bên ngoài bị lỗi...'));
const linkResults = await fixer.checkBrokenLinks(options.directory);
const totalBrokenLinks = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã kiểm tra link bên ngoài trong ${linkResults.length} file (${totalBrokenLinks} vấn đề)`));
console.log(chalk.gray('💡 Vấn đề link bị lỗi cần xem xét thủ công và không thể tự động sửa'));
showCompletionMessage(options, 'Kiểm tra link bị lỗi');
return;
} else if (options.missingResourcesOnly) {
// Check 404 resources only (missing local files)
console.log(chalk.blue('📁 Đang kiểm tra tài nguyên thiếu...'));
const resourceResults = await fixer.check404Resources(options.directory);
const totalMissingResources = resourceResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\n✅ Đã kiểm tra tài nguyên cục bộ trong ${resourceResults.length} file (${totalMissingResources} vấn đề)`));
console.log(chalk.gray('💡 Vấn đề tài nguyên thiếu cần xem xét thủ công và không thể tự động sửa'));
showCompletionMessage(options, 'Kiểm tra tài nguyên thiếu');
return;
} else if (options.gtmCheckOnly) {
// Check Google Tag Manager installation only (no fixes)
console.log(chalk.blue('🏷️ Đang kiểm tra Google Tag Manager...'));
const gtmResults = await fixer.checkGoogleTagManager(options.directory);
const filesWithGTM = gtmResults.filter(r => r.gtmAnalysis?.hasGTM).length;
const filesWithIssues = gtmResults.filter(r => r.gtmAnalysis?.issues?.length > 0).length;
console.log(chalk.green(`\n✅ Phân tích hoàn tất: Tìm thấy ${filesWithGTM} file có GTM`));
if (filesWithIssues > 0) {
console.log(chalk.yellow(`⚠️ ${filesWithIssues} file có vấn đề về cài đặt GTM`));
}
console.log(chalk.gray('💡 GTM cần có cả <script> trong <head> và <noscript> sau <body>'));
showCompletionMessage(options, 'Kiểm tra GTM');
return;
} else if (options.checkMetaOnly) {
// Check meta tags only (no fixes)
console.log(chalk.blue('🏷️ Đang kiểm tra meta tags và Open Graph Protocol...'));
await fixer.checkMetaTags(options.directory);
showCompletionMessage(options, 'Kiểm tra meta tags');
return;
} else if (options.fixMetaOnly) {
// Fix meta tags
console.log(chalk.blue('🔧 Đang tự động sửa meta tags...'));
await fixer.fixMetaTags(options.directory, { dryRun: options.dryRun, backup: options.backupFiles });
showCompletionMessage(options, 'Sửa meta tags');
return;
} else if (options.unusedFilesOnly) {
// Check unused files only (no fixes, no cleanup)
console.log(chalk.blue('🗂️ Đang kiểm tra file không sử dụng...'));
const unusedResults = await fixer.checkUnusedFiles(options.directory);
const totalUnusedFiles = unusedResults.unusedCount;
if (totalUnusedFiles === 0) {
console.log(chalk.green(`\n✅ Không tìm thấy file không sử dụng! Tất cả ${unusedResults.totalFiles} file đều được tham chiếu đúng cách.`));
} else {
console.log(chalk.green(`\n✅ Phân tích hoàn tất: Tìm thấy ${totalUnusedFiles} file không sử dụng trong tổng số ${unusedResults.totalFiles} file`));
console.log(chalk.gray(`📊 ${unusedResults.referencedFiles} file được tham chiếu, ${totalUnusedFiles} file có thể không sử dụng`));
}
console.log(chalk.gray('💡 Phát hiện file không sử dụng dựa trên heuristic - khuyến nghị xem xét thủ công'));
showCompletionMessage(options, 'Kiểm tra file không sử dụng');
return;
} else if (options.deadCodeOnly) {
// Check dead code only (no fixes, no cleanup)
console.log(chalk.blue('☠️ Đang kiểm tra mã không sử dụng...'));
const deadCodeResults = await fixer.checkDeadCode(options.directory);
const totalDeadCode = deadCodeResults.length;
console.log(chalk.green(`\n✅ Đã kiểm tra mã không sử dụng (${totalDeadCode} vấn đề tiềm ẩn)`));
console.log(chalk.gray('💡 Phân tích mã không sử dụng dựa trên heuristic - khuyến nghị xem xét thủ công'));
showCompletionMessage(options, 'Kiểm tra mã không sử dụng');
return;
} else if (options.fileSizeOnly) {
// Check file sizes only (no fixes, no cleanup)
console.log(chalk.blue('📏 Đang phân tích kích thước file...'));
const sizeResults = await fixer.checkFileSizes(options.directory);
const totalLargeFiles = sizeResults.largeFiles.length;
console.log(chalk.green(`\n✅ Đã phân tích ${sizeResults.totalFiles} file (${totalLargeFiles} file có kích thước lớn)`));
console.log(chalk.gray('💡 Phân tích kích thước file dựa trên best practices phổ biến'));
showCompletionMessage(options, 'Phân tích kích thước file');
return;
}
} catch (error) {
console.error(chalk.red('❌ Đã xảy ra lỗi:'), error.message);
process.exit(1);
}
}
// Run the CLI
main();