gbu-accessibility-package 3.12.0 → 3.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+ - **Unused Files List Workflow**:
12
+ - Added `--unused-files-list` to export detected unused files into `unused-files-list.txt`
13
+ - Added `--delete-unused-files` to delete files listed in `unused-files-list.txt`
14
+ - Added `--list-file` to customize the list file path inside the target directory
15
+
16
+ ### Fixed
17
+ - **Unused file reference matching**: `--unused-files` now ignores query strings and hash fragments like `style.css?hash` and `bundle.js#v1`
18
+
8
19
  ## [3.8.3] - 2025-10-06
9
20
 
10
21
  ### Added
@@ -265,4 +276,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
265
276
 
266
277
  ---
267
278
 
268
- For more details about any release, see the [GitHub Releases](https://github.com/example/gbu-accessibility-tool/releases) page.
279
+ For more details about any release, see the [GitHub Releases](https://github.com/example/gbu-accessibility-tool/releases) page.
package/README-vi.md CHANGED
@@ -171,6 +171,9 @@ Chế độ sửa lỗi:
171
171
  --full-report Tạo báo cáo Excel toàn diện (tất cả kiểm tra)
172
172
  -o, --output <file> Đường dẫn output cho báo cáo Excel (dùng với --full-report)
173
173
  --unused-files Kiểm tra file không sử dụng trong dự án
174
+ --unused-files-list Tạo file unused-files-list.txt từ danh sách file dư thừa
175
+ --delete-unused-files Xóa toàn bộ file có trong unused-files-list.txt
176
+ --list-file <file> Tùy chỉnh tên/đường dẫn file list trong thư mục target
174
177
  --dead-code Kiểm tra dead code trong CSS và JavaScript
175
178
  --file-size, --size-check Kiểm tra dung lượng file và đề xuất tối ưu hóa
176
179
  --cleanup-only Chỉ dọn dẹp role attributes trùng lặp
@@ -214,6 +217,9 @@ gbu-a11y --fix-meta --dry-run # Xem trước sửa lỗi meta tags
214
217
  gbu-a11y --full-report # Tạo báo cáo Excel toàn diện
215
218
  gbu-a11y --full-report -o report.xlsx # Đường dẫn output tùy chỉnh
216
219
  gbu-a11y --unused-files # Kiểm tra file không sử dụng trong dự án
220
+ gbu-a11y --unused-files-list # Tạo ./unused-files-list.txt
221
+ gbu-a11y --delete-unused-files # Xóa file theo ./unused-files-list.txt
222
+ gbu-a11y --delete-unused-files --dry-run # Xem trước các file sẽ bị xóa
217
223
  gbu-a11y --dead-code # Kiểm tra dead CSS và JavaScript code
218
224
  gbu-a11y --file-size # Kiểm tra dung lượng file và đề xuất tối ưu hóa
219
225
 
package/README.md CHANGED
@@ -166,6 +166,9 @@ Fix Modes:
166
166
  --full-report Generate comprehensive Excel report (all checks)
167
167
  -o, --output <file> Output path for Excel report (use with --full-report)
168
168
  --unused-files Check for unused files in project
169
+ --unused-files-list Create unused-files-list.txt from detected unused files
170
+ --delete-unused-files Delete all files listed in unused-files-list.txt
171
+ --list-file <file> Custom list file name/path inside target directory
169
172
  --dead-code Check for dead code in CSS and JavaScript
170
173
  --file-size, --size-check Check file sizes and suggest optimizations
171
174
  --cleanup-only Only cleanup duplicate role attributes
@@ -210,6 +213,9 @@ gbu-a11y --fix-meta --dry-run # Preview meta tag fixes
210
213
  gbu-a11y --full-report # Generate comprehensive Excel report
211
214
  gbu-a11y --full-report -o report.xlsx # Custom output path
212
215
  gbu-a11y --unused-files # Check for unused files in project
216
+ gbu-a11y --unused-files-list # Create ./unused-files-list.txt
217
+ gbu-a11y --delete-unused-files # Delete files listed in ./unused-files-list.txt
218
+ gbu-a11y --delete-unused-files --dry-run # Preview files that would be deleted
213
219
  gbu-a11y --dead-code # Check for dead CSS and JavaScript code
214
220
  gbu-a11y --file-size # Check file sizes and suggest optimizations
215
221
 
@@ -710,4 +716,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
710
716
 
711
717
  **Made with ❤️ by the GBU Team**
712
718
 
713
- For more information, visit our [GitHub repository](https://github.com/example/gbu-accessibility-tool).
719
+ For more information, visit our [GitHub repository](https://github.com/example/gbu-accessibility-tool).
package/cli.js CHANGED
@@ -32,8 +32,11 @@ const options = {
32
32
  dlOnly: false,
33
33
  brokenLinksOnly: false,
34
34
  unusedFilesOnly: false,
35
+ unusedFilesListOnly: false,
36
+ deleteUnusedFilesFromList: false,
35
37
  deadCodeOnly: false,
36
38
  fileSizeOnly: false,
39
+ listFile: 'unused-files-list.txt',
37
40
  // Enhanced alt options
38
41
  enhancedAlt: false,
39
42
  altCreativity: 'balanced', // conservative, balanced, creative
@@ -149,6 +152,15 @@ for (let i = 0; i < args.length; i++) {
149
152
  case '--unused-files':
150
153
  options.unusedFilesOnly = true;
151
154
  break;
155
+ case '--unused-files-list':
156
+ options.unusedFilesListOnly = true;
157
+ break;
158
+ case '--delete-unused-files':
159
+ options.deleteUnusedFilesFromList = true;
160
+ break;
161
+ case '--list-file':
162
+ options.listFile = args[++i];
163
+ break;
152
164
  case '--dead-code':
153
165
  options.deadCodeOnly = true;
154
166
  break;
@@ -220,6 +232,9 @@ Options:
220
232
  --full-report Generate comprehensive Excel report (all checks)
221
233
  -o, --output <file> Output path for Excel report (use with --full-report)
222
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
223
238
  --dead-code Check for dead code in CSS and JavaScript (no auto-fix)
224
239
  --file-size, --size-check Check file sizes and suggest optimizations (no auto-fix)
225
240
  --enhanced-alt Use enhanced alt attribute analysis and generation
@@ -262,6 +277,9 @@ Examples:
262
277
  node cli.js --full-report # Generate comprehensive Excel report
263
278
  node cli.js --full-report ./project -o report.xlsx # Custom output path
264
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
265
283
  node cli.js --dead-code # Check for dead CSS and JavaScript code
266
284
  node cli.js --file-size # Check file sizes and suggest optimizations
267
285
  node cli.js --cleanup-only # Only cleanup duplicate roles
@@ -332,7 +350,7 @@ async function main() {
332
350
  // Handle different modes - All modes now include cleanup
333
351
  if (options.cleanupOnly || options.altOnly || options.langOnly || options.roleOnly || options.ariaLabelOnly ||
334
352
  options.formsOnly || options.nestedOnly || options.buttonsOnly || options.linksOnly || options.landmarksOnly ||
335
- options.headingsOnly || options.dlOnly || options.linksCheckOnly || options.brokenLinksOnly || options.missingResourcesOnly || options.gtmCheckOnly || options.checkMetaOnly || options.fixMetaOnly || options.unusedFilesOnly || options.deadCodeOnly || options.fileSizeOnly) {
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) {
336
354
  // Individual modes - handle each separately, then run cleanup
337
355
  } else {
338
356
  // Default mode: Run comprehensive fix (all fixes including cleanup)
@@ -656,6 +674,39 @@ async function main() {
656
674
 
657
675
  showCompletionMessage(options, 'Kiểm tra file không sử dụng');
658
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
+ }
699
+
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
+ }
703
+
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;
659
710
 
660
711
  } else if (options.deadCodeOnly) {
661
712
  // Check dead code only (no fixes, no cleanup)
@@ -689,4 +740,4 @@ async function main() {
689
740
  }
690
741
 
691
742
  // Run the CLI
692
- main();
743
+ main();
package/lib/fixer.js CHANGED
@@ -6073,6 +6073,134 @@ class AccessibilityFixer {
6073
6073
  };
6074
6074
  }
6075
6075
 
6076
+ resolveUnusedFilesListPath(directory = '.', listFile = 'unused-files-list.txt') {
6077
+ const scanDirectory = path.resolve(directory);
6078
+ const listFileName = listFile || 'unused-files-list.txt';
6079
+
6080
+ if (path.isAbsolute(listFileName)) {
6081
+ return listFileName;
6082
+ }
6083
+
6084
+ return path.join(scanDirectory, listFileName);
6085
+ }
6086
+
6087
+ async generateUnusedFilesList(directory = '.', listFile = 'unused-files-list.txt') {
6088
+ const scanDirectory = path.resolve(directory);
6089
+ const outputPath = this.resolveUnusedFilesListPath(scanDirectory, listFile);
6090
+ const unusedResults = await this.checkUnusedFiles(scanDirectory);
6091
+ const fileContent = unusedResults.unusedFiles
6092
+ .map(file => file.relativePath.replace(/\\/g, '/'))
6093
+ .join('\n');
6094
+
6095
+ await fs.writeFile(outputPath, fileContent ? `${fileContent}\n` : '', 'utf8');
6096
+
6097
+ console.log(chalk.green(`📝 Đã tạo danh sách file dư thừa tại: ${outputPath}`));
6098
+ console.log(chalk.gray(`📊 Ghi ${unusedResults.unusedCount} path vào file list`));
6099
+
6100
+ return {
6101
+ ...unusedResults,
6102
+ outputPath
6103
+ };
6104
+ }
6105
+
6106
+ async deleteUnusedFilesFromList(directory = '.', listFile = 'unused-files-list.txt', options = {}) {
6107
+ const scanDirectory = path.resolve(directory);
6108
+ const listPath = this.resolveUnusedFilesListPath(scanDirectory, listFile);
6109
+ const dryRun = Boolean(options.dryRun);
6110
+ const deletedFiles = [];
6111
+ const missingFiles = [];
6112
+ const skippedEntries = [];
6113
+
6114
+ const content = await fs.readFile(listPath, 'utf8');
6115
+ const entries = [...new Set(
6116
+ content
6117
+ .split(/\r?\n/)
6118
+ .map(line => line.trim())
6119
+ .filter(line => line && !line.startsWith('#'))
6120
+ )];
6121
+
6122
+ for (const entry of entries) {
6123
+ const normalizedEntry = entry.replace(/\\/g, '/');
6124
+ const resolvedPath = path.resolve(scanDirectory, normalizedEntry);
6125
+ const relativeToScan = path.relative(scanDirectory, resolvedPath);
6126
+
6127
+ if (relativeToScan.startsWith('..') || path.isAbsolute(relativeToScan)) {
6128
+ skippedEntries.push({
6129
+ path: entry,
6130
+ reason: 'Path nằm ngoài thư mục target'
6131
+ });
6132
+ continue;
6133
+ }
6134
+
6135
+ if (resolvedPath === path.resolve(listPath)) {
6136
+ skippedEntries.push({
6137
+ path: entry,
6138
+ reason: 'Bỏ qua chính file list'
6139
+ });
6140
+ continue;
6141
+ }
6142
+
6143
+ try {
6144
+ const stats = await fs.lstat(resolvedPath);
6145
+
6146
+ if (!stats.isFile() && !stats.isSymbolicLink()) {
6147
+ skippedEntries.push({
6148
+ path: entry,
6149
+ reason: 'Không phải file'
6150
+ });
6151
+ continue;
6152
+ }
6153
+
6154
+ if (!dryRun) {
6155
+ await fs.unlink(resolvedPath);
6156
+ }
6157
+
6158
+ deletedFiles.push({
6159
+ path: resolvedPath,
6160
+ relativePath: relativeToScan.replace(/\\/g, '/')
6161
+ });
6162
+ } catch (error) {
6163
+ if (error.code === 'ENOENT') {
6164
+ missingFiles.push({
6165
+ path: entry,
6166
+ reason: 'File không tồn tại'
6167
+ });
6168
+ continue;
6169
+ }
6170
+
6171
+ skippedEntries.push({
6172
+ path: entry,
6173
+ reason: error.message
6174
+ });
6175
+ }
6176
+ }
6177
+
6178
+ console.log(chalk.green(
6179
+ dryRun
6180
+ ? `🧪 Dry run: ${deletedFiles.length} file trong list sẽ được xóa`
6181
+ : `🗑️ Đã xóa ${deletedFiles.length} file từ list`
6182
+ ));
6183
+
6184
+ if (missingFiles.length > 0) {
6185
+ console.log(chalk.yellow(`⚠️ ${missingFiles.length} path không còn tồn tại`));
6186
+ }
6187
+
6188
+ if (skippedEntries.length > 0) {
6189
+ console.log(chalk.yellow(`⚠️ ${skippedEntries.length} path bị bỏ qua để đảm bảo an toàn hoặc không hợp lệ`));
6190
+ }
6191
+
6192
+ return {
6193
+ listPath,
6194
+ deletedFiles,
6195
+ deletedCount: deletedFiles.length,
6196
+ missingFiles,
6197
+ missingCount: missingFiles.length,
6198
+ skippedEntries,
6199
+ skippedCount: skippedEntries.length,
6200
+ dryRun
6201
+ };
6202
+ }
6203
+
6076
6204
  // Enhanced file reference checking - now works with relative paths
6077
6205
  isFileReferenced(filePath, relativePath, referencedFiles, projectRoot) {
6078
6206
  // Check various possible reference formats
@@ -6310,12 +6438,17 @@ class AccessibilityFixer {
6310
6438
  const srcUrl = parts[0];
6311
6439
 
6312
6440
  if (srcUrl) {
6441
+ const normalizedSrcUrl = this.normalizeReferenceUrl(srcUrl);
6442
+ if (!normalizedSrcUrl) {
6443
+ return;
6444
+ }
6445
+
6313
6446
  // Check if it's a local file
6314
- if (this.isLocalFile(srcUrl)) {
6315
- this.addNormalizedUrl(references, srcUrl);
6316
- } else if (this.isAbsoluteUrlToLocalFile(srcUrl)) {
6447
+ if (this.isLocalFile(normalizedSrcUrl)) {
6448
+ this.addNormalizedUrl(references, normalizedSrcUrl);
6449
+ } else if (this.isAbsoluteUrlToLocalFile(normalizedSrcUrl)) {
6317
6450
  // Extract local path from absolute URL (e.g., https://example.com/assets/img/file.png -> /assets/img/file.png)
6318
- const localPath = this.extractLocalPathFromAbsoluteUrl(srcUrl);
6451
+ const localPath = this.extractLocalPathFromAbsoluteUrl(normalizedSrcUrl);
6319
6452
  if (localPath) {
6320
6453
  this.addNormalizedUrl(references, localPath);
6321
6454
  }
@@ -6323,12 +6456,17 @@ class AccessibilityFixer {
6323
6456
  }
6324
6457
  });
6325
6458
  } else {
6459
+ const normalizedUrl = this.normalizeReferenceUrl(url);
6460
+ if (!normalizedUrl) {
6461
+ continue;
6462
+ }
6463
+
6326
6464
  // Regular src/href/content attributes
6327
- if (this.isLocalFile(url)) {
6328
- this.addNormalizedUrl(references, url);
6329
- } else if (this.isAbsoluteUrlToLocalFile(url)) {
6465
+ if (this.isLocalFile(normalizedUrl)) {
6466
+ this.addNormalizedUrl(references, normalizedUrl);
6467
+ } else if (this.isAbsoluteUrlToLocalFile(normalizedUrl)) {
6330
6468
  // Extract local path from absolute URL
6331
- const localPath = this.extractLocalPathFromAbsoluteUrl(url);
6469
+ const localPath = this.extractLocalPathFromAbsoluteUrl(normalizedUrl);
6332
6470
  if (localPath) {
6333
6471
  this.addNormalizedUrl(references, localPath);
6334
6472
  }
@@ -6342,43 +6480,75 @@ class AccessibilityFixer {
6342
6480
 
6343
6481
  // Check if URL is an absolute URL that points to a local file (same domain or project assets)
6344
6482
  isAbsoluteUrlToLocalFile(url) {
6345
- if (!url.startsWith('http://') && !url.startsWith('https://')) {
6483
+ const normalizedUrl = this.normalizeReferenceUrl(url);
6484
+ if (!normalizedUrl) {
6485
+ return false;
6486
+ }
6487
+
6488
+ if (!normalizedUrl.startsWith('http://') && !normalizedUrl.startsWith('https://')) {
6346
6489
  return false;
6347
6490
  }
6348
6491
 
6349
6492
  // Check if URL contains common asset paths (customize based on your project structure)
6350
6493
  // This helps identify URLs like: https://www.example.com/assets/img/file.png
6351
- return url.includes('/assets/') ||
6352
- url.includes('/static/') ||
6353
- url.includes('/img/') ||
6354
- url.includes('/images/') ||
6355
- url.includes('/css/') ||
6356
- url.includes('/js/') ||
6357
- url.includes('/media/') ||
6358
- url.includes('/fonts/') ||
6359
- url.match(/\.(jpg|jpeg|png|gif|svg|webp|css|js|ico|pdf|mp4|webm)$/i);
6494
+ return normalizedUrl.includes('/assets/') ||
6495
+ normalizedUrl.includes('/static/') ||
6496
+ normalizedUrl.includes('/img/') ||
6497
+ normalizedUrl.includes('/images/') ||
6498
+ normalizedUrl.includes('/css/') ||
6499
+ normalizedUrl.includes('/js/') ||
6500
+ normalizedUrl.includes('/media/') ||
6501
+ normalizedUrl.includes('/fonts/') ||
6502
+ normalizedUrl.match(/\.(jpg|jpeg|png|gif|svg|webp|css|js|ico|pdf|mp4|webm)$/i);
6360
6503
  }
6361
6504
 
6362
6505
  // Extract local path from absolute URL
6363
6506
  // e.g., "https://www.example.com/assets/img/file.png" -> "/assets/img/file.png"
6364
6507
  extractLocalPathFromAbsoluteUrl(url) {
6365
6508
  try {
6366
- const urlObj = new URL(url);
6509
+ const normalizedUrl = this.normalizeReferenceUrl(url);
6510
+ if (!normalizedUrl) {
6511
+ return null;
6512
+ }
6513
+
6514
+ const urlObj = new URL(normalizedUrl);
6367
6515
  return urlObj.pathname; // Returns "/assets/img/file.png"
6368
6516
  } catch (error) {
6369
6517
  return null;
6370
6518
  }
6371
6519
  }
6372
6520
 
6521
+ normalizeReferenceUrl(url) {
6522
+ if (typeof url !== 'string') {
6523
+ return null;
6524
+ }
6525
+
6526
+ const trimmedUrl = url.trim();
6527
+ if (!trimmedUrl) {
6528
+ return null;
6529
+ }
6530
+
6531
+ const withoutHash = trimmedUrl.split('#')[0];
6532
+ const withoutQuery = withoutHash.split('?')[0];
6533
+ const normalizedUrl = withoutQuery.trim().replace(/\\/g, '/');
6534
+
6535
+ return normalizedUrl || null;
6536
+ }
6537
+
6373
6538
  // Helper method to add normalized URL variations
6374
6539
  addNormalizedUrl(references, url) {
6540
+ const normalizedUrl = this.normalizeReferenceUrl(url);
6541
+ if (!normalizedUrl) {
6542
+ return;
6543
+ }
6544
+
6375
6545
  // Store original URL and normalized versions for matching
6376
- references.push(url);
6377
- if (url.startsWith('/')) {
6378
- references.push(url.substring(1)); // Remove leading slash
6546
+ references.push(normalizedUrl);
6547
+ if (normalizedUrl.startsWith('/')) {
6548
+ references.push(normalizedUrl.substring(1)); // Remove leading slash
6379
6549
  }
6380
- if (!url.startsWith('./') && !url.startsWith('/')) {
6381
- references.push('./' + url); // Add leading ./
6550
+ if (!normalizedUrl.startsWith('./') && !normalizedUrl.startsWith('/')) {
6551
+ references.push('./' + normalizedUrl); // Add leading ./
6382
6552
  }
6383
6553
  }
6384
6554
 
@@ -6398,7 +6568,7 @@ class AccessibilityFixer {
6398
6568
  for (const pattern of patterns) {
6399
6569
  let match;
6400
6570
  while ((match = pattern.exec(content)) !== null) {
6401
- const url = match[1];
6571
+ const url = this.normalizeReferenceUrl(match[1]);
6402
6572
  if (this.isLocalFile(url)) {
6403
6573
  this.addNormalizedUrl(references, url);
6404
6574
  }
@@ -6424,15 +6594,15 @@ class AccessibilityFixer {
6424
6594
  // XMLHttpRequest
6425
6595
  /\.open\s*\(\s*["'][^"']*["']\s*,\s*["']([^"']+)["']/gi,
6426
6596
  // String literals that look like paths
6427
- /["']([^"']*\.(html|css|js|json|xml|jpg|jpeg|png|gif|svg|webp|ico))["']/gi,
6597
+ /["']([^"']*\.(html|css|js|json|xml|jpg|jpeg|png|gif|svg|webp|ico)(?:[?#][^"']*)?)["']/gi,
6428
6598
  // Template literals with paths
6429
- /`([^`]*\.(html|css|js|json|xml|jpg|jpeg|png|gif|svg|webp|ico))`/gi
6599
+ /`([^`]*\.(html|css|js|json|xml|jpg|jpeg|png|gif|svg|webp|ico)(?:[?#][^`]*)?)`/gi
6430
6600
  ];
6431
6601
 
6432
6602
  for (const pattern of patterns) {
6433
6603
  let match;
6434
6604
  while ((match = pattern.exec(content)) !== null) {
6435
- const url = match[1];
6605
+ const url = this.normalizeReferenceUrl(match[1]);
6436
6606
  if (this.isLocalFile(url)) {
6437
6607
  this.addNormalizedUrl(references, url);
6438
6608
  }
@@ -6532,7 +6702,7 @@ class AccessibilityFixer {
6532
6702
  for (const pattern of patterns) {
6533
6703
  let match;
6534
6704
  while ((match = pattern.exec(content)) !== null) {
6535
- const url = match[1];
6705
+ const url = this.normalizeReferenceUrl(match[1]);
6536
6706
  if (this.isLocalFile(url)) {
6537
6707
  this.addNormalizedUrl(references, url);
6538
6708
  }
@@ -6564,7 +6734,7 @@ class AccessibilityFixer {
6564
6734
  for (const pattern of patterns) {
6565
6735
  let match;
6566
6736
  while ((match = pattern.exec(content)) !== null) {
6567
- const url = match[1];
6737
+ const url = this.normalizeReferenceUrl(match[1]);
6568
6738
  if (this.isLocalFile(url)) {
6569
6739
  this.addNormalizedUrl(references, url);
6570
6740
  }
@@ -6698,13 +6868,18 @@ class AccessibilityFixer {
6698
6868
  }
6699
6869
 
6700
6870
  isLocalFile(url) {
6701
- return !url.startsWith('http://') &&
6702
- !url.startsWith('https://') &&
6703
- !url.startsWith('//') &&
6704
- !url.startsWith('data:') &&
6705
- !url.startsWith('mailto:') &&
6706
- !url.startsWith('tel:') &&
6707
- !url.startsWith('#');
6871
+ const normalizedUrl = this.normalizeReferenceUrl(url);
6872
+ if (!normalizedUrl) {
6873
+ return false;
6874
+ }
6875
+
6876
+ return !normalizedUrl.startsWith('http://') &&
6877
+ !normalizedUrl.startsWith('https://') &&
6878
+ !normalizedUrl.startsWith('//') &&
6879
+ !normalizedUrl.startsWith('data:') &&
6880
+ !normalizedUrl.startsWith('mailto:') &&
6881
+ !normalizedUrl.startsWith('tel:') &&
6882
+ !normalizedUrl.startsWith('#');
6708
6883
  }
6709
6884
 
6710
6885
  resolveFilePath(url, baseDir) {
@@ -7378,9 +7553,14 @@ class AccessibilityFixer {
7378
7553
  // Extract file references from JSON values
7379
7554
  const extractFromObject = (obj) => {
7380
7555
  if (typeof obj === 'string') {
7556
+ const normalizedValue = this.normalizeReferenceUrl(obj);
7557
+ if (!normalizedValue) {
7558
+ return;
7559
+ }
7560
+
7381
7561
  // Check if it looks like a file path
7382
- if (obj.includes('.') && (obj.includes('/') || obj.includes('\\'))) {
7383
- const resolved = this.resolveFilePath(obj, baseDir);
7562
+ if (/\.(html?|css|js|json|xml|jpe?g|png|gif|svg|webp|ico)$/i.test(normalizedValue)) {
7563
+ const resolved = this.resolveFilePath(normalizedValue, baseDir);
7384
7564
  if (resolved) {
7385
7565
  references.push(resolved);
7386
7566
  }
@@ -7419,7 +7599,7 @@ class AccessibilityFixer {
7419
7599
  patterns.forEach(pattern => {
7420
7600
  let match;
7421
7601
  while ((match = pattern.exec(content)) !== null) {
7422
- const filePath = match[1];
7602
+ const filePath = this.normalizeReferenceUrl(match[1]);
7423
7603
 
7424
7604
  if (this.isLocalFile(filePath)) {
7425
7605
  const resolved = this.resolveFilePath(filePath, baseDir);
@@ -8635,4 +8815,4 @@ class AccessibilityFixer {
8635
8815
  }
8636
8816
  }
8637
8817
 
8638
- module.exports = AccessibilityFixer;
8818
+ module.exports = AccessibilityFixer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbu-accessibility-package",
3
- "version": "3.12.0",
3
+ "version": "3.12.1",
4
4
  "description": "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 analysis, broken external links detection, and missing local resources detection. Covers major axe DevTools issues with individual fix modes.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -26,6 +26,8 @@
26
26
  "404-resources": "node cli.js --404-resources",
27
27
  "gtm-check": "node cli.js --gtm-check",
28
28
  "unused-files": "node cli.js --unused-files",
29
+ "unused-files-list": "node cli.js --unused-files-list",
30
+ "delete-unused-files": "node cli.js --delete-unused-files",
29
31
  "dead-code": "node cli.js --dead-code",
30
32
  "file-size": "node cli.js --file-size",
31
33
  "cleanup-only": "node cli.js --cleanup-only",