gbu-accessibility-package 3.12.1 → 3.13.0

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/cli.js CHANGED
@@ -1,28 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * Accessibility Fixer CLI
5
- * Command line interface for the accessibility fixer tool
6
- */
7
-
8
- const AccessibilityFixer = require('./lib/fixer.js');
3
+ const { AccessibilityChecker } = require('./index.js');
9
4
  const chalk = require('chalk');
10
- const path = require('path');
11
5
 
12
- // Parse command line arguments
13
6
  const args = process.argv.slice(2);
14
7
  const options = {
15
8
  directory: '.',
16
9
  language: 'ja',
17
- backupFiles: false, // Default to false for faster processing
18
- dryRun: false,
19
10
  help: false,
20
11
  version: false,
21
- cleanupOnly: false,
22
- comprehensive: false, // Keep for backward compatibility
12
+ dryRun: false,
13
+ comprehensive: false,
23
14
  altOnly: false,
24
15
  langOnly: false,
25
16
  roleOnly: false,
17
+ ariaLabelOnly: false,
26
18
  formsOnly: false,
27
19
  nestedOnly: false,
28
20
  buttonsOnly: false,
@@ -30,27 +22,42 @@ const options = {
30
22
  landmarksOnly: false,
31
23
  headingsOnly: false,
32
24
  dlOnly: false,
25
+ linksCheckOnly: false,
33
26
  brokenLinksOnly: false,
27
+ missingResourcesOnly: false,
28
+ gtmCheckOnly: false,
29
+ checkMetaOnly: false,
30
+ fullReport: false,
34
31
  unusedFilesOnly: false,
35
32
  unusedFilesListOnly: false,
36
- deleteUnusedFilesFromList: false,
37
33
  deadCodeOnly: false,
38
34
  fileSizeOnly: false,
39
35
  listFile: 'unused-files-list.txt',
40
- // Enhanced alt options
36
+ reportOutput: null,
41
37
  enhancedAlt: false,
42
- altCreativity: 'balanced', // conservative, balanced, creative
38
+ altCreativity: 'balanced',
43
39
  includeEmotions: false,
44
- strictAltChecking: false,
45
- // Advanced features options
46
- autoFixHeadings: false,
47
- fixDescriptionLists: true
40
+ strictAltChecking: false
41
+ };
42
+
43
+ const removedFlags = {
44
+ '--backup': 'Source file backups were removed because the package no longer edits files.',
45
+ '--no-backup': 'Backups are no longer needed because the package is check/report-only.',
46
+ '--cleanup-only': 'Cleanup mode was removed because it modified source files.',
47
+ '--fix-meta': 'Meta auto-fix was removed. Use --check-meta to review issues.',
48
+ '--meta-fix': 'Meta auto-fix was removed. Use --check-meta to review issues.',
49
+ '--delete-unused-files': 'File deletion was removed. Use --unused-files-list to export a review list instead.',
50
+ '--auto-fix-headings': 'Heading auto-fix was removed. Use --headings-only to review issues instead.'
48
51
  };
49
52
 
50
- // Parse arguments
51
- for (let i = 0; i < args.length; i++) {
53
+ for (let i = 0; i < args.length; i += 1) {
52
54
  const arg = args[i];
53
-
55
+
56
+ if (removedFlags[arg]) {
57
+ console.error(chalk.red(`❌ ${removedFlags[arg]}`));
58
+ process.exit(1);
59
+ }
60
+
54
61
  switch (arg) {
55
62
  case '--help':
56
63
  case '-h':
@@ -62,27 +69,20 @@ for (let i = 0; i < args.length; i++) {
62
69
  break;
63
70
  case '--directory':
64
71
  case '-d':
65
- options.directory = args[++i];
72
+ options.directory = args[i + 1];
73
+ i += 1;
66
74
  break;
67
75
  case '--language':
68
76
  case '-l':
69
- options.language = args[++i];
70
- break;
71
- case '--backup':
72
- options.backupFiles = true;
73
- break;
74
- case '--no-backup':
75
- options.backupFiles = false;
77
+ options.language = args[i + 1];
78
+ i += 1;
76
79
  break;
77
80
  case '--dry-run':
78
81
  options.dryRun = true;
79
82
  break;
80
- case '--cleanup-only':
81
- options.cleanupOnly = true;
82
- break;
83
83
  case '--comprehensive':
84
84
  case '--all':
85
- options.comprehensive = true; // Keep for backward compatibility
85
+ options.comprehensive = true;
86
86
  break;
87
87
  case '--alt-only':
88
88
  options.altOnly = true;
@@ -136,10 +136,6 @@ for (let i = 0; i < args.length; i++) {
136
136
  case '--meta-check':
137
137
  options.checkMetaOnly = true;
138
138
  break;
139
- case '--fix-meta':
140
- case '--meta-fix':
141
- options.fixMetaOnly = true;
142
- break;
143
139
  case '--full-report':
144
140
  case '--report':
145
141
  case '--excel-report':
@@ -147,7 +143,8 @@ for (let i = 0; i < args.length; i++) {
147
143
  break;
148
144
  case '-o':
149
145
  case '--output':
150
- options.reportOutput = args[++i];
146
+ options.reportOutput = args[i + 1];
147
+ i += 1;
151
148
  break;
152
149
  case '--unused-files':
153
150
  options.unusedFilesOnly = true;
@@ -155,11 +152,9 @@ for (let i = 0; i < args.length; i++) {
155
152
  case '--unused-files-list':
156
153
  options.unusedFilesListOnly = true;
157
154
  break;
158
- case '--delete-unused-files':
159
- options.deleteUnusedFilesFromList = true;
160
- break;
161
155
  case '--list-file':
162
- options.listFile = args[++i];
156
+ options.listFile = args[i + 1];
157
+ i += 1;
163
158
  break;
164
159
  case '--dead-code':
165
160
  options.deadCodeOnly = true;
@@ -168,17 +163,12 @@ for (let i = 0; i < args.length; i++) {
168
163
  case '--size-check':
169
164
  options.fileSizeOnly = true;
170
165
  break;
171
- case '--auto-fix-headings':
172
- options.autoFixHeadings = true;
173
- break;
174
- case '--no-fix-dl':
175
- options.fixDescriptionLists = false;
176
- break;
177
166
  case '--enhanced-alt':
178
167
  options.enhancedAlt = true;
179
168
  break;
180
169
  case '--alt-creativity':
181
- options.altCreativity = args[++i];
170
+ options.altCreativity = args[i + 1];
171
+ i += 1;
182
172
  break;
183
173
  case '--include-emotions':
184
174
  options.includeEmotions = true;
@@ -189,555 +179,276 @@ for (let i = 0; i < args.length; i++) {
189
179
  default:
190
180
  if (!arg.startsWith('-')) {
191
181
  options.directory = arg;
182
+ } else {
183
+ console.error(chalk.red(`❌ Unknown option: ${arg}`));
184
+ process.exit(1);
192
185
  }
193
186
  }
194
187
  }
195
188
 
196
- // Show help
189
+ function showHelp() {
190
+ console.log(chalk.blue(`
191
+ GBU Accessibility Checker
192
+
193
+ Usage: gbu-a11y [options] [directory]
194
+
195
+ This package now runs in check/report-only mode.
196
+ Source files are never modified.
197
+
198
+ Core options:
199
+ -d, --directory <path> Target directory (default: current directory)
200
+ -l, --language <lang> Language context for checks (default: ja)
201
+ --dry-run Optional legacy flag; check-only mode is always enabled
202
+ -h, --help Show this help message
203
+ -v, --version Show version number
204
+
205
+ Check modes:
206
+ --comprehensive, --all Run the default comprehensive check set
207
+ --alt-only Check alt text issues only
208
+ --lang-only Check HTML lang issues only
209
+ --role-only Check role attribute issues only
210
+ --aria-label-only Check aria-label issues only
211
+ --forms-only Check form label issues only
212
+ --nested-only Check nested interactive controls only
213
+ --buttons-only Check button naming issues only
214
+ --links-only Check accessible link name issues only
215
+ --landmarks-only Check landmark issues only
216
+ --headings-only Check heading structure only
217
+ --dl-only Check description list structure only
218
+ --links-check Check external links and local missing resources
219
+ --broken-links Check broken external links only
220
+ --404-resources Check missing local resources only
221
+ --gtm-check Check Google Tag Manager installation only
222
+ --check-meta Check meta tags and OGP issues only
223
+ --unused-files Check unused files only
224
+ --unused-files-list Export unused files to a text report
225
+ --dead-code Check dead CSS and JavaScript only
226
+ --file-size Check file sizes only
227
+ --full-report Generate the comprehensive Excel report
228
+ -o, --output <file> Custom output path for --full-report
229
+ --list-file <file> Custom output path/name for --unused-files-list
230
+
231
+ Enhanced alt checks:
232
+ --enhanced-alt Enable deeper alt text analysis
233
+ --alt-creativity <mode> conservative | balanced | creative
234
+ --include-emotions Include emotional/contextual alt suggestions
235
+ --strict-alt Enable stricter alt quality rules
236
+
237
+ Removed editing features:
238
+ --backup, --no-backup
239
+ --cleanup-only
240
+ --fix-meta, --meta-fix
241
+ --delete-unused-files
242
+ --auto-fix-headings
243
+
244
+ Examples:
245
+ gbu-a11y
246
+ gbu-a11y --alt-only ./src
247
+ gbu-a11y --links-check ./public
248
+ gbu-a11y --unused-files-list --list-file reports/unused-files.txt
249
+ gbu-a11y --full-report -o reports/accessibility-report.xlsx
250
+ `));
251
+ }
252
+
253
+ function printSummary(label, result) {
254
+ if (!result) {
255
+ return;
256
+ }
257
+
258
+ if (Array.isArray(result)) {
259
+ const issueCount = result.filter((item) => (
260
+ item.status === 'issue-found'
261
+ || item.status === 'fixed'
262
+ || (typeof item.issues === 'number' && item.issues > 0)
263
+ )).length;
264
+ const cleanCount = result.filter((item) => item.status === 'clean' || item.status === 'no-change').length;
265
+ const errorCount = result.filter((item) => item.status === 'error').length;
266
+
267
+ console.log(chalk.blue(`\n📌 ${label}`));
268
+ console.log(` Files with findings: ${issueCount}`);
269
+ console.log(` Clean files: ${cleanCount}`);
270
+ console.log(` Errors: ${errorCount}`);
271
+ return;
272
+ }
273
+
274
+ if (result.filePath) {
275
+ console.log(chalk.blue(`\n📌 ${label}`));
276
+ console.log(` Report: ${result.filePath}`);
277
+ return;
278
+ }
279
+
280
+ if (typeof result.unusedCount === 'number') {
281
+ console.log(chalk.blue(`\n📌 ${label}`));
282
+ console.log(` Unused files found: ${result.unusedCount}`);
283
+ return;
284
+ }
285
+
286
+ if (typeof result.deletedCount === 'number') {
287
+ console.log(chalk.blue(`\n📌 ${label}`));
288
+ console.log(` Files flagged from list: ${result.deletedCount}`);
289
+ console.log(' Note: preview only, no files were deleted.');
290
+ return;
291
+ }
292
+
293
+ if (result.brokenLinks || result.missingResources) {
294
+ printSummary(`${label} - Broken Links`, result.brokenLinks);
295
+ printSummary(`${label} - Missing Resources`, result.missingResources);
296
+ return;
297
+ }
298
+
299
+ const nestedEntries = Object.entries(result).filter(([, value]) => Array.isArray(value));
300
+ if (nestedEntries.length > 0) {
301
+ console.log(chalk.blue(`\n📌 ${label}`));
302
+
303
+ for (const [key, value] of nestedEntries) {
304
+ const issueCount = value.filter((item) => (
305
+ item.status === 'issue-found'
306
+ || item.status === 'fixed'
307
+ || (typeof item.issues === 'number' && item.issues > 0)
308
+ )).length;
309
+ const errorCount = value.filter((item) => item.status === 'error').length;
310
+ console.log(` ${key}: ${issueCount} files with findings, ${errorCount} errors`);
311
+ }
312
+ }
313
+ }
314
+
197
315
  if (options.version) {
198
316
  const packageJson = require('./package.json');
199
- console.log(chalk.blue(`🔧 GBU Accessibility Package v${packageJson.version}`));
317
+ console.log(`GBU Accessibility Checker v${packageJson.version}`);
200
318
  process.exit(0);
201
319
  }
202
320
 
203
321
  if (options.help) {
204
- console.log(chalk.blue(`
205
- 🔧 Accessibility Fixer CLI
206
-
207
- Usage: node cli.js [options] [directory]
208
-
209
- Options:
210
- -d, --directory <path> Target directory (default: current directory)
211
- -l, --language <lang> Language for lang attribute (default: ja)
212
- --backup Create backup files
213
- --no-backup Don't create backup files (default)
214
- --dry-run Preview changes without applying
215
- --comprehensive, --all Run comprehensive fixes (same as default)
216
- --cleanup-only Only cleanup duplicate role attributes
217
- --alt-only Fix alt attributes + cleanup
218
- --lang-only Fix HTML lang attributes + cleanup
219
- --role-only Fix role attributes + cleanup
220
- --aria-label-only Fix aria-label attributes + cleanup
221
- --forms-only Fix form labels + cleanup
222
- --buttons-only Fix button names + cleanup
223
- --links-only Fix link names + cleanup
224
- --landmarks-only Fix landmarks + cleanup
225
- --headings-only Analyze heading structure (no auto-fix)
226
- --links-check Check for broken links and 404 resources (no auto-fix)
227
- --broken-links Check for broken external links only (no auto-fix)
228
- --404-resources Check for missing local resources only (no auto-fix)
229
- --gtm-check Check Google Tag Manager installation (no auto-fix)
230
- --check-meta Check meta tags and Open Graph Protocol (no auto-fix)
231
- --fix-meta Auto-fix missing meta tags and OGP tags
232
- --full-report Generate comprehensive Excel report (all checks)
233
- -o, --output <file> Output path for Excel report (use with --full-report)
234
- --unused-files Check for unused files in project (no auto-fix)
235
- --unused-files-list Create unused-files-list.txt from detected unused files
236
- --delete-unused-files Delete all files listed in unused-files-list.txt
237
- --list-file <file> Custom list file name/path inside target directory
238
- --dead-code Check for dead code in CSS and JavaScript (no auto-fix)
239
- --file-size, --size-check Check file sizes and suggest optimizations (no auto-fix)
240
- --enhanced-alt Use enhanced alt attribute analysis and generation
241
- --alt-creativity <mode> Alt text creativity: conservative, balanced, creative (default: balanced)
242
- --include-emotions Include emotional descriptors in alt text
243
- --strict-alt Enable strict alt attribute quality checking
244
- -h, --help Show this help message
245
- -v, --version Show version number
246
-
247
- Enhanced Alt Features:
248
- --enhanced-alt Comprehensive alt attribute analysis with:
249
- • Image type classification (decorative, functional, complex, etc.)
250
- • Content quality checking (length, redundancy, generic text)
251
- • Context-aware alt text generation
252
- • Multi-language vocabulary support
253
- • Brand and emotional context integration
254
- • Technical image description (charts, graphs)
255
-
256
- Alt Creativity Modes:
257
- conservative Simple, factual descriptions
258
- balanced Context-aware with moderate creativity (default)
259
- creative Rich descriptions with emotions and brand context
260
-
261
- Examples:
262
- node cli.js # Comprehensive fixes (no backup by default)
263
- node cli.js --comprehensive # Comprehensive fixes (same as default)
264
- node cli.js --alt-only # Fix alt attributes + cleanup
265
- node cli.js --forms-only # Fix form labels + cleanup
266
- node cli.js --buttons-only # Fix button names + cleanup
267
- node cli.js --links-only # Fix link names + cleanup
268
- node cli.js --landmarks-only # Fix landmarks + cleanup
269
- node cli.js --headings-only # Analyze heading structure only
270
- node cli.js --links-check # Check for broken links and 404s
271
- node cli.js --broken-links # Check for broken external links only
272
- node cli.js --404-resources # Check for missing local resources only
273
- node cli.js --gtm-check # Check Google Tag Manager installation
274
- node cli.js --check-meta # Check meta tags and Open Graph Protocol
275
- node cli.js --fix-meta # Auto-fix missing meta tags and OGP
276
- node cli.js --fix-meta --dry-run # Preview meta tag fixes
277
- node cli.js --full-report # Generate comprehensive Excel report
278
- node cli.js --full-report ./project -o report.xlsx # Custom output path
279
- node cli.js --unused-files # Check for unused files in project
280
- node cli.js --unused-files-list # Create ./unused-files-list.txt
281
- node cli.js --delete-unused-files # Delete files listed in ./unused-files-list.txt
282
- node cli.js --delete-unused-files --dry-run # Preview files that would be deleted
283
- node cli.js --dead-code # Check for dead CSS and JavaScript code
284
- node cli.js --file-size # Check file sizes and suggest optimizations
285
- node cli.js --cleanup-only # Only cleanup duplicate roles
286
- node cli.js ./src # Fix src directory (comprehensive)
287
- node cli.js -l en --dry-run ./dist # Preview comprehensive fixes in English
288
- node cli.js --backup ./public # Comprehensive fixes with backups
289
- node cli.js --enhanced-alt # Use enhanced alt attribute analysis
290
- node cli.js --enhanced-alt --alt-creativity creative # Creative alt text generation
291
- node cli.js --enhanced-alt --include-emotions # Include emotional context
292
- node cli.js --strict-alt --enhanced-alt # Strict quality checking
293
-
294
- Features:
295
- ✅ Alt attributes for images
296
- ✅ Lang attributes for HTML
297
- ✅ Role attributes for accessibility
298
- ✅ Context-aware text generation
299
- ✅ Automatic backups
300
- `));
322
+ showHelp();
301
323
  process.exit(0);
302
324
  }
303
325
 
326
+ const selectedModes = [
327
+ 'comprehensive',
328
+ 'altOnly',
329
+ 'langOnly',
330
+ 'roleOnly',
331
+ 'ariaLabelOnly',
332
+ 'formsOnly',
333
+ 'nestedOnly',
334
+ 'buttonsOnly',
335
+ 'linksOnly',
336
+ 'landmarksOnly',
337
+ 'headingsOnly',
338
+ 'dlOnly',
339
+ 'linksCheckOnly',
340
+ 'brokenLinksOnly',
341
+ 'missingResourcesOnly',
342
+ 'gtmCheckOnly',
343
+ 'checkMetaOnly',
344
+ 'fullReport',
345
+ 'unusedFilesOnly',
346
+ 'unusedFilesListOnly',
347
+ 'deadCodeOnly',
348
+ 'fileSizeOnly'
349
+ ].filter((key) => options[key]);
350
+
351
+ if (selectedModes.length > 1) {
352
+ console.error(chalk.red('❌ Please select only one check mode at a time.'));
353
+ process.exit(1);
354
+ }
355
+
356
+ const checker = new AccessibilityChecker({
357
+ language: options.language,
358
+ enhancedAltMode: options.enhancedAlt,
359
+ altCreativity: options.altCreativity,
360
+ includeEmotions: options.includeEmotions,
361
+ strictAltChecking: options.strictAltChecking
362
+ });
363
+
364
+ async function run() {
365
+ console.log(chalk.blue('🔍 GBU Accessibility Checker'));
366
+ console.log(chalk.gray(`Target: ${options.directory}`));
367
+ console.log(chalk.gray('Mode: check/report only (source files remain unchanged)'));
304
368
 
305
- // Helper function to show completion message with backup info
306
- function showCompletionMessage(options, mode = 'sửa lỗi') {
307
369
  if (options.dryRun) {
308
- console.log(chalk.cyan('\n💡 Đây chế độ xem trước. Sử dụng không có --dry-run để áp dụng thay đổi.'));
309
- } else {
310
- console.log(chalk.green(`\n🎉 ${mode} hoàn tất thành công!`));
311
- if (options.backupFiles) {
312
- console.log(chalk.gray(' 📁 Đã tạo file backup với đuôi .backup'));
313
- console.log(chalk.gray(' 💡 Sử dụng --no-backup để tắt backup trong các lần chạy sau'));
314
- } else {
315
- console.log(chalk.blue(' ⚡ Không tạo file backup (mặc định để xử lý nhanh hơn)'));
316
- console.log(chalk.gray(' 💡 Sử dụng --backup để bật tính năng backup để an toàn'));
317
- }
370
+ console.log(chalk.gray('Note: --dry-run is now redundant because check-only mode is always enabled.'));
318
371
  }
319
- }
320
372
 
321
- // Main function
322
- async function main() {
323
- console.log(chalk.blue('🚀 Đang khởi động Accessibility Fixer...'));
324
- console.log(chalk.gray(`Thư mục: ${path.resolve(options.directory)}`));
325
- console.log(chalk.gray(`Ngôn ngữ: ${options.language}`));
326
- console.log(chalk.gray(`Backup: ${options.backupFiles ? 'Có' : 'Không'}`));
327
- console.log(chalk.gray(`Chế độ: ${options.dryRun ? 'Xem trước (Preview)' : 'Áp dụng thay đổi'}`));
328
373
  console.log('');
329
374
 
330
- const fixer = new AccessibilityFixer({
331
- language: options.language,
332
- backupFiles: options.backupFiles,
333
- dryRun: options.dryRun,
334
- enhancedAltMode: options.enhancedAlt,
335
- altCreativity: options.altCreativity,
336
- includeEmotions: options.includeEmotions,
337
- strictAltChecking: options.strictAltChecking,
338
- autoFixHeadings: options.autoFixHeadings,
339
- fixDescriptionLists: options.fixDescriptionLists
340
- });
341
-
342
- try {
343
- // Handle Full Report mode first
344
- if (options.fullReport) {
345
- console.log(chalk.blue('📊 Đang tạo báo cáo toàn diện...'));
346
- await fixer.generateFullReport(options.directory, options.reportOutput);
347
- return;
348
- }
349
-
350
- // Handle different modes - All modes now include cleanup
351
- if (options.cleanupOnly || options.altOnly || options.langOnly || options.roleOnly || options.ariaLabelOnly ||
352
- options.formsOnly || options.nestedOnly || options.buttonsOnly || options.linksOnly || options.landmarksOnly ||
353
- options.headingsOnly || options.dlOnly || options.linksCheckOnly || options.brokenLinksOnly || options.missingResourcesOnly || options.gtmCheckOnly || options.checkMetaOnly || options.fixMetaOnly || options.unusedFilesOnly || options.unusedFilesListOnly || options.deleteUnusedFilesFromList || options.deadCodeOnly || options.fileSizeOnly) {
354
- // Individual modes - handle each separately, then run cleanup
355
- } else {
356
- // Default mode: Run comprehensive fix (all fixes including cleanup)
357
- console.log(chalk.blue('🎯 Đang chạy sửa lỗi accessibility toàn diện...'));
358
- const results = await fixer.fixAllAccessibilityIssues(options.directory);
359
-
360
- // Results already logged in the method
361
- return;
362
- }
363
-
364
- // Individual modes
365
- if (options.cleanupOnly) {
366
- // Only cleanup duplicate roles
367
- console.log(chalk.blue('🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
368
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
369
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
370
-
371
- console.log(chalk.green(`\n✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
372
-
373
- showCompletionMessage(options, 'Dọn dẹp');
374
- return;
375
-
376
- } else if (options.cleanupOnly) {
377
- // Only cleanup duplicate roles
378
- console.log(chalk.blue('🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
379
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
380
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
381
-
382
- console.log(chalk.green(`\n✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
383
-
384
- showCompletionMessage(options, 'Dọn dẹp');
385
- return;
386
-
387
- } else if (options.altOnly) {
388
- // Fix alt attributes + cleanup
389
- console.log(chalk.blue('🖼️ Đang sửa thuộc tính alt + dọn dẹp...'));
390
- const altResults = await fixer.fixEmptyAltAttributes(options.directory);
391
- const altFixed = altResults.filter(r => r.status === 'fixed').length;
392
- const totalAltIssues = altResults.reduce((sum, r) => sum + (r.issues || 0), 0);
393
-
394
- console.log(chalk.green(`\n✅ Đã sửa thuộc tính alt trong ${altFixed} file (${totalAltIssues} vấn đề)`));
395
-
396
- // Run cleanup
397
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
398
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
399
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
400
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
401
-
402
- showCompletionMessage(options, 'Sửa thuộc tính alt + dọn dẹp');
403
- return;
404
-
405
- } else if (options.langOnly) {
406
- // Fix lang attributes + cleanup
407
- console.log(chalk.blue('📝 Đang sửa thuộc tính HTML lang + dọn dẹp...'));
408
- const langResults = await fixer.fixHtmlLang(options.directory);
409
- const langFixed = langResults.filter(r => r.status === 'fixed').length;
410
-
411
- console.log(chalk.green(`\n✅ Đã sửa thuộc tính lang trong ${langFixed} file`));
412
-
413
- // Run cleanup
414
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
415
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
416
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
417
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
418
-
419
- showCompletionMessage(options, 'Sửa thuộc tính lang + dọn dẹp');
420
- return;
421
-
422
- } else if (options.roleOnly) {
423
- // Fix role attributes + cleanup
424
- console.log(chalk.blue('🎭 Đang sửa thuộc tính role + dọn dẹp...'));
425
- const roleResults = await fixer.fixRoleAttributes(options.directory);
426
- const roleFixed = roleResults.filter(r => r.status === 'fixed').length;
427
- const totalRoleIssues = roleResults.reduce((sum, r) => sum + (r.issues || 0), 0);
428
-
429
- console.log(chalk.green(`\n✅ Đã sửa thuộc tính role trong ${roleFixed} file (${totalRoleIssues} vấn đề)`));
430
-
431
- // Run cleanup
432
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
433
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
434
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
435
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
436
-
437
- showCompletionMessage(options, 'Sửa thuộc tính role + dọn dẹp');
438
- return;
439
-
440
- } else if (options.ariaLabelOnly) {
441
- // Fix aria-label attributes + cleanup
442
- console.log(chalk.blue('🏷️ Đang sửa thuộc tính aria-label + dọn dẹp...'));
443
- const ariaResults = await fixer.fixAriaLabels(options.directory);
444
- const ariaFixed = ariaResults.filter(r => r.status === 'processed' && r.changes > 0).length;
445
- const totalAriaIssues = ariaResults.reduce((sum, r) => sum + (r.changes || 0), 0);
446
-
447
- console.log(chalk.green(`\n✅ Đã sửa thuộc tính aria-label trong ${ariaFixed} file (${totalAriaIssues} vấn đề)`));
448
-
449
- // Run cleanup
450
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
451
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
452
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
453
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
454
-
455
- showCompletionMessage(options, 'Sửa thuộc tính aria-label + dọn dẹp');
456
- return;
457
-
458
- } else if (options.formsOnly) {
459
- // Fix form labels + cleanup
460
- console.log(chalk.blue('📋 Đang sửa nhãn form + dọn dẹp...'));
461
- const formResults = await fixer.fixFormLabels(options.directory);
462
- const formFixed = formResults.filter(r => r.status === 'fixed').length;
463
- const totalFormIssues = formResults.reduce((sum, r) => sum + (r.issues || 0), 0);
464
-
465
- console.log(chalk.green(`\n✅ Đã sửa nhãn form trong ${formFixed} file (${totalFormIssues} vấn đề)`));
466
-
467
- // Run cleanup
468
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
469
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
470
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
471
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
472
-
473
- showCompletionMessage(options, 'Sửa nhãn form + dọn dẹp');
474
- return;
475
-
476
- } else if (options.nestedOnly) {
477
- // Fix nested interactive controls + cleanup
478
- console.log(chalk.blue('🎯 Đang sửa các control tương tác lồng nhau + dọn dẹp...'));
479
- const nestedResults = await fixer.fixNestedInteractiveControls(options.directory);
480
- const nestedFixed = nestedResults.filter(r => r.status === 'fixed').length;
481
- const totalNestedIssues = nestedResults.reduce((sum, r) => sum + (r.issues || 0), 0);
482
-
483
- console.log(chalk.green(`\n✅ Đã sửa các control tương tác lồng nhau trong ${nestedFixed} file (${totalNestedIssues} vấn đề)`));
484
-
485
- // Run cleanup
486
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
487
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
488
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
489
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
490
-
491
- showCompletionMessage(options, 'Sửa các control tương tác lồng nhau + dọn dẹp');
492
- return;
493
-
494
- } else if (options.buttonsOnly) {
495
- // Fix button names + cleanup
496
- console.log(chalk.blue('🔘 Đang sửa tên button + dọn dẹp...'));
497
- const buttonResults = await fixer.fixButtonNames(options.directory);
498
- const buttonFixed = buttonResults.filter(r => r.status === 'fixed').length;
499
- const totalButtonIssues = buttonResults.reduce((sum, r) => sum + (r.issues || 0), 0);
500
-
501
- console.log(chalk.green(`\n✅ Đã sửa tên button trong ${buttonFixed} file (${totalButtonIssues} vấn đề)`));
502
-
503
- // Run cleanup
504
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
505
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
506
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
507
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
508
-
509
- showCompletionMessage(options, 'Sửa tên button + dọn dẹp');
510
- return;
511
-
512
- } else if (options.linksOnly) {
513
- // Fix link names + cleanup
514
- console.log(chalk.blue('🔗 Đang sửa tên link + dọn dẹp...'));
515
- const linkResults = await fixer.fixLinkNames(options.directory);
516
- const linkFixed = linkResults.filter(r => r.status === 'fixed').length;
517
- const totalLinkIssues = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
518
-
519
- console.log(chalk.green(`\n✅ Đã sửa tên link trong ${linkFixed} file (${totalLinkIssues} vấn đề)`));
520
-
521
- // Run cleanup
522
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
523
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
524
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
525
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
526
-
527
- showCompletionMessage(options, 'Sửa tên link + dọn dẹp');
528
- return;
529
-
530
- } else if (options.landmarksOnly) {
531
- // Fix landmarks + cleanup
532
- console.log(chalk.blue('🏛️ Đang sửa landmark + dọn dẹp...'));
533
- const landmarkResults = await fixer.fixLandmarks(options.directory);
534
- const landmarkFixed = landmarkResults.filter(r => r.status === 'fixed').length;
535
- const totalLandmarkIssues = landmarkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
536
-
537
- console.log(chalk.green(`\n✅ Đã sửa landmark trong ${landmarkFixed} file (${totalLandmarkIssues} vấn đề)`));
538
-
539
- // Run cleanup
540
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
541
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
542
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
543
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
544
-
545
- showCompletionMessage(options, 'Sửa landmark + dọn dẹp');
546
- return;
547
-
548
- } else if (options.headingsOnly) {
549
- // Fix heading structure + cleanup
550
- console.log(chalk.blue('📑 Đang sửa cấu trúc heading + dọn dẹp...'));
551
- const headingResults = await fixer.fixHeadingStructure(options.directory);
552
- const headingFixed = headingResults.filter(r => r.status === 'fixed').length;
553
- const totalHeadingIssues = headingResults.reduce((sum, r) => sum + (r.issues || 0), 0);
554
- const totalHeadingFixes = headingResults.reduce((sum, r) => sum + (r.fixes || 0), 0);
555
-
556
- console.log(chalk.green(`\n✅ Đã xử lý heading trong ${headingResults.length} file (${totalHeadingIssues} vấn đề tìm thấy)`));
557
- if (options.autoFixHeadings) {
558
- console.log(chalk.green(`✅ Đã sửa ${totalHeadingFixes} vấn đề heading tự động`));
559
- } else {
560
- console.log(chalk.gray('💡 Sử dụng --auto-fix-headings để bật tính năng tự động sửa heading'));
561
- }
562
-
563
- // Run cleanup
564
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
565
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
566
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
567
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
568
-
569
- showCompletionMessage(options, 'Sửa cấu trúc heading + dọn dẹp');
570
- return;
571
-
572
- } else if (options.dlOnly) {
573
- // Fix description lists + cleanup
574
- console.log(chalk.blue('📋 Đang sửa danh sách mô tả + dọn dẹp...'));
575
- const dlResults = await fixer.fixDescriptionLists(options.directory);
576
- const dlFixed = dlResults.filter(r => r.status === 'fixed').length;
577
- const totalDlIssues = dlResults.reduce((sum, r) => sum + (r.issues || 0), 0);
578
-
579
- console.log(chalk.green(`\n✅ Đã sửa danh sách mô tả trong ${dlFixed} file (${totalDlIssues} vấn đề)`));
580
-
581
- // Run cleanup
582
- console.log(chalk.blue('\n🧹 Đang dọn dẹp các thuộc tính role trùng lặp...'));
583
- const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
584
- const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
585
- console.log(chalk.green(`✅ Đã dọn dẹp role trùng lặp trong ${cleanupFixed} file`));
586
-
587
- showCompletionMessage(options, 'Sửa danh sách mô tả + dọn dẹp');
588
- return;
589
-
590
- } else if (options.linksCheckOnly) {
591
- // Check broken links and 404 resources (backward compatibility)
592
- console.log(chalk.blue('🔗 Đang kiểm tra link và tài nguyên toàn diện...'));
593
- const linkResults = await fixer.checkBrokenLinks(options.directory);
594
- const resourceResults = await fixer.check404Resources(options.directory);
595
- const totalLinkIssues = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
596
- const totalResourceIssues = resourceResults.reduce((sum, r) => sum + (r.issues || 0), 0);
597
-
598
- console.log(chalk.green(`\n✅ Đã kiểm tra link trong ${linkResults.length} file (${totalLinkIssues} vấn đề link)`));
599
- console.log(chalk.green(`✅ Đã kiểm tra tài nguyên trong ${resourceResults.length} file (${totalResourceIssues} vấn đề tài nguyên)`));
600
- 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'));
601
-
602
- showCompletionMessage(options, 'Kiểm tra link và tài nguyên');
603
- return;
604
-
605
- } else if (options.brokenLinksOnly) {
606
- // Check broken external links only
607
- console.log(chalk.blue('🔗 Đang kiểm tra link bên ngoài bị lỗi...'));
608
- const linkResults = await fixer.checkBrokenLinks(options.directory);
609
- const totalBrokenLinks = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
610
-
611
- console.log(chalk.green(`\n✅ Đã kiểm tra link bên ngoài trong ${linkResults.length} file (${totalBrokenLinks} vấn đề)`));
612
- 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'));
613
-
614
- showCompletionMessage(options, 'Kiểm tra link bị lỗi');
615
- return;
616
-
617
- } else if (options.missingResourcesOnly) {
618
- // Check 404 resources only (missing local files)
619
- console.log(chalk.blue('📁 Đang kiểm tra tài nguyên thiếu...'));
620
- const resourceResults = await fixer.check404Resources(options.directory);
621
- const totalMissingResources = resourceResults.reduce((sum, r) => sum + (r.issues || 0), 0);
622
-
623
- console.log(chalk.green(`\n✅ Đã kiểm tra tài nguyên cục bộ trong ${resourceResults.length} file (${totalMissingResources} vấn đề)`));
624
- 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'));
625
-
626
- showCompletionMessage(options, 'Kiểm tra tài nguyên thiếu');
627
- return;
628
-
629
- } else if (options.gtmCheckOnly) {
630
- // Check Google Tag Manager installation only (no fixes)
631
- console.log(chalk.blue('🏷️ Đang kiểm tra Google Tag Manager...'));
632
- const gtmResults = await fixer.checkGoogleTagManager(options.directory);
633
- const filesWithGTM = gtmResults.filter(r => r.gtmAnalysis?.hasGTM).length;
634
- const filesWithIssues = gtmResults.filter(r => r.gtmAnalysis?.issues?.length > 0).length;
635
-
636
- console.log(chalk.green(`\n✅ Phân tích hoàn tất: Tìm thấy ${filesWithGTM} file có GTM`));
637
- if (filesWithIssues > 0) {
638
- console.log(chalk.yellow(`⚠️ ${filesWithIssues} file có vấn đề về cài đặt GTM`));
639
- }
640
- console.log(chalk.gray('💡 GTM cần có cả <script> trong <head> và <noscript> sau <body>'));
641
-
642
- showCompletionMessage(options, 'Kiểm tra GTM');
643
- return;
644
-
645
- } else if (options.checkMetaOnly) {
646
- // Check meta tags only (no fixes)
647
- console.log(chalk.blue('🏷️ Đang kiểm tra meta tags và Open Graph Protocol...'));
648
- await fixer.checkMetaTags(options.directory);
649
-
650
- showCompletionMessage(options, 'Kiểm tra meta tags');
651
- return;
652
-
653
- } else if (options.fixMetaOnly) {
654
- // Fix meta tags
655
- console.log(chalk.blue('🔧 Đang tự động sửa meta tags...'));
656
- await fixer.fixMetaTags(options.directory, { dryRun: options.dryRun, backup: options.backupFiles });
657
-
658
- showCompletionMessage(options, 'Sửa meta tags');
659
- return;
660
-
661
- } else if (options.unusedFilesOnly) {
662
- // Check unused files only (no fixes, no cleanup)
663
- console.log(chalk.blue('🗂️ Đang kiểm tra file không sử dụng...'));
664
- const unusedResults = await fixer.checkUnusedFiles(options.directory);
665
- const totalUnusedFiles = unusedResults.unusedCount;
666
-
667
- if (totalUnusedFiles === 0) {
668
- 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.`));
669
- } else {
670
- 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`));
671
- console.log(chalk.gray(`📊 ${unusedResults.referencedFiles} file được tham chiếu, ${totalUnusedFiles} file có thể không sử dụng`));
672
- }
673
- 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'));
674
-
675
- showCompletionMessage(options, 'Kiểm tra file không sử dụng');
676
- return;
677
-
678
- } else if (options.unusedFilesListOnly) {
679
- console.log(chalk.blue('📝 Đang tạo danh sách file không sử dụng...'));
680
- const listResults = await fixer.generateUnusedFilesList(options.directory, options.listFile);
681
-
682
- console.log(chalk.green(`\n✅ Đã tạo file list: ${listResults.outputPath}`));
683
- console.log(chalk.gray(`📊 ${listResults.unusedCount} path đã được ghi vào list`));
684
- console.log(chalk.gray('💡 Danh sách dùng path tương đối so với thư mục target và có thể dùng lại với --delete-unused-files'));
685
- return;
686
-
687
- } else if (options.deleteUnusedFilesFromList) {
688
- console.log(chalk.blue('🗑️ Đang xóa file theo danh sách unused files...'));
689
- const deleteResults = await fixer.deleteUnusedFilesFromList(options.directory, options.listFile, {
690
- dryRun: options.dryRun
691
- });
692
-
693
- console.log(chalk.green(`\n✅ ${options.dryRun ? 'Đã mô phỏng xóa' : 'Đã xử lý xóa'} ${deleteResults.deletedCount} file từ list`));
694
- console.log(chalk.gray(`📄 File list: ${deleteResults.listPath}`));
695
-
696
- if (deleteResults.missingCount > 0) {
697
- console.log(chalk.yellow(`⚠️ ${deleteResults.missingCount} file trong list không còn tồn tại`));
698
- }
375
+ let result;
376
+ let label;
699
377
 
700
- if (deleteResults.skippedCount > 0) {
701
- console.log(chalk.yellow(`⚠️ ${deleteResults.skippedCount} entry bị bỏ qua vì không an toàn hoặc không hợp lệ`));
702
- }
378
+ if (options.altOnly) {
379
+ label = 'Alt Text Check';
380
+ result = await checker.checkAltText(options.directory);
381
+ } else if (options.langOnly) {
382
+ label = 'Lang Check';
383
+ result = await checker.checkLang(options.directory);
384
+ } else if (options.roleOnly) {
385
+ label = 'Role Check';
386
+ result = await checker.checkRoles(options.directory);
387
+ } else if (options.ariaLabelOnly) {
388
+ label = 'Aria Label Check';
389
+ result = await checker.checkAriaLabels(options.directory);
390
+ } else if (options.formsOnly) {
391
+ label = 'Form Label Check';
392
+ result = await checker.checkForms(options.directory);
393
+ } else if (options.nestedOnly) {
394
+ label = 'Nested Control Check';
395
+ result = await checker.checkNestedControls(options.directory);
396
+ } else if (options.buttonsOnly) {
397
+ label = 'Button Check';
398
+ result = await checker.checkButtons(options.directory);
399
+ } else if (options.linksOnly) {
400
+ label = 'Accessible Link Name Check';
401
+ result = await checker.checkLinks(options.directory);
402
+ } else if (options.landmarksOnly) {
403
+ label = 'Landmark Check';
404
+ result = await checker.checkLandmarks(options.directory);
405
+ } else if (options.headingsOnly) {
406
+ label = 'Heading Check';
407
+ result = await checker.checkHeadings(options.directory);
408
+ } else if (options.dlOnly) {
409
+ label = 'Description List Check';
410
+ result = await checker.checkDescriptionLists(options.directory);
411
+ } else if (options.linksCheckOnly) {
412
+ label = 'Link and Resource Check';
413
+ result = await checker.checkLinksAndResources(options.directory);
414
+ } else if (options.brokenLinksOnly) {
415
+ label = 'Broken External Link Check';
416
+ result = await checker.checkBrokenLinks(options.directory);
417
+ } else if (options.missingResourcesOnly) {
418
+ label = 'Missing Resource Check';
419
+ result = await checker.check404Resources(options.directory);
420
+ } else if (options.gtmCheckOnly) {
421
+ label = 'GTM Check';
422
+ result = await checker.checkGoogleTagManager(options.directory);
423
+ } else if (options.checkMetaOnly) {
424
+ label = 'Meta Tag Check';
425
+ result = await checker.checkMetaTags(options.directory);
426
+ } else if (options.fullReport) {
427
+ label = 'Full Report';
428
+ result = await checker.generateFullReport(options.directory, options.reportOutput);
429
+ } else if (options.unusedFilesOnly) {
430
+ label = 'Unused File Check';
431
+ result = await checker.checkUnusedFiles(options.directory);
432
+ } else if (options.unusedFilesListOnly) {
433
+ label = 'Unused File List Export';
434
+ result = await checker.generateUnusedFilesList(options.directory, options.listFile);
435
+ } else if (options.deadCodeOnly) {
436
+ label = 'Dead Code Check';
437
+ result = await checker.checkDeadCode(options.directory);
438
+ } else if (options.fileSizeOnly) {
439
+ label = 'File Size Check';
440
+ result = await checker.checkFileSizes(options.directory);
441
+ } else {
442
+ label = 'Comprehensive Check';
443
+ result = await checker.runComprehensiveChecks(options.directory);
444
+ }
703
445
 
704
- if (options.dryRun) {
705
- console.log(chalk.cyan('\n💡 Đây là chế độ xem trước. Chạy lại không kèm --dry-run để xóa thật.'));
706
- } else {
707
- console.log(chalk.gray('💡 File list được giữ nguyên để bạn có thể đối chiếu sau khi xóa'));
708
- }
709
- return;
710
-
711
- } else if (options.deadCodeOnly) {
712
- // Check dead code only (no fixes, no cleanup)
713
- console.log(chalk.blue('☠️ Đang kiểm tra mã không sử dụng...'));
714
- const deadCodeResults = await fixer.checkDeadCode(options.directory);
715
- const totalDeadCode = deadCodeResults.length;
716
-
717
- console.log(chalk.green(`\n✅ Đã kiểm tra mã không sử dụng (${totalDeadCode} vấn đề tiềm ẩn)`));
718
- 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'));
719
-
720
- showCompletionMessage(options, 'Kiểm tra mã không sử dụng');
721
- return;
722
-
723
- } else if (options.fileSizeOnly) {
724
- // Check file sizes only (no fixes, no cleanup)
725
- console.log(chalk.blue('📏 Đang phân tích kích thước file...'));
726
- const sizeResults = await fixer.checkFileSizes(options.directory);
727
- const totalLargeFiles = sizeResults.largeFiles.length;
728
-
729
- console.log(chalk.green(`\n✅ Đã phân tích ${sizeResults.totalFiles} file (${totalLargeFiles} file có kích thước lớn)`));
730
- console.log(chalk.gray('💡 Phân tích kích thước file dựa trên best practices phổ biến'));
731
-
732
- showCompletionMessage(options, 'Phân tích kích thước file');
733
- return;
734
- }
446
+ printSummary(label, result);
735
447
 
736
- } catch (error) {
737
- console.error(chalk.red('❌ Đã xảy ra lỗi:'), error.message);
738
- process.exit(1);
739
- }
448
+ console.log(chalk.green('\n✅ Check completed.'));
740
449
  }
741
450
 
742
- // Run the CLI
743
- main();
451
+ run().catch((error) => {
452
+ console.error(chalk.red(`\n❌ ${error.message}`));
453
+ process.exit(1);
454
+ });