gbu-accessibility-package 3.8.5 → 3.8.7

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/README-vi.md CHANGED
@@ -362,7 +362,7 @@ await fixer.checkFileSizes('./src');
362
362
  ### Tối ưu hóa dự án
363
363
 
364
364
  - **File không sử dụng** → Phát hiện file không được tham chiếu ở đâu trong toàn bộ dự án
365
- - **File types được kiểm tra**: Hình ảnh, CSS, JavaScript, JSX, TypeScript, Vue, PHP, JSON, Markdown, XML, PDF, Video, Audio files (không bao gồm HTML)
365
+ - **File types được kiểm tra**: Hình ảnh, CSS, SCSS/Sass, JavaScript, JSX, TypeScript, Vue, PHP, JSON, Markdown, XML, PDF, Video, Audio files (không bao gồm HTML)
366
366
  - **Quét toàn diện**: Phân tích từ project root, không giới hạn thư mục hiện tại
367
367
  - **Cross-reference detection**: Tìm tham chiếu từ HTML, CSS, JavaScript, JSON, và các file khác
368
368
  - **Multiple path formats**: Hỗ trợ relative paths, absolute paths, imports, requires
package/README.md CHANGED
@@ -349,7 +349,7 @@ await fixer.checkFileSizes('./src');
349
349
 
350
350
  ### Project Optimization
351
351
  - **Unused Files** → Detect files not referenced anywhere in the project
352
- - Images, CSS, JavaScript, JSX, TypeScript, Vue, PHP, JSON, Markdown, XML, PDF, Video, Audio files
352
+ - Images, CSS, SCSS/Sass, JavaScript, JSX, TypeScript, Vue, PHP, JSON, Markdown, XML, PDF, Video, Audio files
353
353
  - Local file references analysis
354
354
  - Heuristic detection with manual review recommendations
355
355
  - **Dead Code Analysis** → Find unused CSS rules and JavaScript functions
package/cli.js CHANGED
@@ -572,9 +572,14 @@ async function main() {
572
572
  // Check unused files only (no fixes, no cleanup)
573
573
  console.log(chalk.blue('🗂️ Running unused files check only...'));
574
574
  const unusedResults = await fixer.checkUnusedFiles(options.directory);
575
- const totalUnusedFiles = unusedResults.length;
575
+ const totalUnusedFiles = unusedResults.unusedCount;
576
576
 
577
- console.log(chalk.green(`\n✅ Checked project files (${totalUnusedFiles} unused files found)`));
577
+ if (totalUnusedFiles === 0) {
578
+ console.log(chalk.green(`\n✅ No unused files found! All ${unusedResults.totalFiles} files are properly referenced.`));
579
+ } else {
580
+ console.log(chalk.green(`\n✅ Analysis complete: Found ${totalUnusedFiles} unused files out of ${unusedResults.totalFiles} total files`));
581
+ console.log(chalk.gray(`📊 ${unusedResults.referencedFiles} files are referenced, ${totalUnusedFiles} potentially unused`));
582
+ }
578
583
  console.log(chalk.gray('💡 Unused file detection is heuristic - manual review recommended'));
579
584
 
580
585
  showCompletionMessage(options, 'Unused files check');
package/lib/fixer.js CHANGED
@@ -5671,6 +5671,8 @@ class AccessibilityFixer {
5671
5671
  const referencedFiles = await this.findReferencedFiles(scanDirectory);
5672
5672
  console.log(chalk.gray(`🔍 Found ${referencedFiles.size} referenced files`));
5673
5673
 
5674
+
5675
+
5674
5676
  // Find unused files
5675
5677
  const unusedFiles = [];
5676
5678
 
@@ -5684,6 +5686,8 @@ class AccessibilityFixer {
5684
5686
  const relativePath = path.relative(scanDirectory, file);
5685
5687
  const isReferenced = this.isFileReferenced(file, relativePath, referencedFiles, scanDirectory);
5686
5688
 
5689
+
5690
+
5687
5691
  if (!isReferenced) {
5688
5692
  const stats = await require('fs').promises.stat(file);
5689
5693
  const fileSize = this.formatFileSize(stats.size);
@@ -5732,7 +5736,7 @@ class AccessibilityFixer {
5732
5736
  };
5733
5737
  }
5734
5738
 
5735
- // Enhanced file reference checking
5739
+ // Enhanced file reference checking - now works with relative paths
5736
5740
  isFileReferenced(filePath, relativePath, referencedFiles, projectRoot) {
5737
5741
  // Check various possible reference formats
5738
5742
  const possibleRefs = [
@@ -5748,19 +5752,31 @@ class AccessibilityFixer {
5748
5752
  relativePath.replace(/^\//, ''), // remove leading /
5749
5753
  ];
5750
5754
 
5751
- // Check if any reference format exists
5755
+ // Check if any reference format exists in the set
5752
5756
  for (const ref of possibleRefs) {
5753
5757
  if (referencedFiles.has(ref)) {
5754
5758
  return true;
5755
5759
  }
5756
5760
  }
5757
5761
 
5758
- // Check for partial matches (for dynamic imports)
5759
- const fileName = path.basename(filePath, path.extname(filePath));
5762
+ // Additional checks for path variations
5763
+ const fileName = path.basename(filePath);
5764
+ const relativeDir = path.dirname(relativePath);
5765
+
5766
+ // Check for references that might include parent directory names
5760
5767
  for (const ref of referencedFiles) {
5761
- if (ref.includes(fileName) || ref.includes(relativePath)) {
5768
+ // If reference ends with the filename, check if path context matches
5769
+ if (ref.endsWith('/' + fileName) || ref === fileName) {
5762
5770
  return true;
5763
5771
  }
5772
+
5773
+ // Check for absolute paths that correspond to our relative path
5774
+ if (ref.startsWith('/') && ref.includes(fileName)) {
5775
+ const refWithoutLeading = ref.substring(1);
5776
+ if (refWithoutLeading === relativePath || refWithoutLeading.endsWith('/' + fileName)) {
5777
+ return true;
5778
+ }
5779
+ }
5764
5780
  }
5765
5781
 
5766
5782
  return false;
@@ -5768,8 +5784,8 @@ class AccessibilityFixer {
5768
5784
 
5769
5785
  async findAllProjectFiles(directory) {
5770
5786
  const files = [];
5771
- // Check all relevant file types except HTML files
5772
- const extensions = ['.css', '.js', '.jsx', '.ts', '.tsx', '.vue', '.php', '.json', '.md', '.xml', '.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp', '.ico', '.pdf', '.mp4', '.webm', '.mp3', '.wav'];
5787
+ // Only check web assets: CSS, HTML, JS, and images
5788
+ const extensions = ['.css', '.html', '.htm', '.js', '.jsx', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico'];
5773
5789
 
5774
5790
  const scan = async (dir) => {
5775
5791
  try {
@@ -5825,7 +5841,7 @@ class AccessibilityFixer {
5825
5841
  return referencedFiles;
5826
5842
  }
5827
5843
 
5828
- // Find all source files that could contain references
5844
+ // Find HTML and CSS/SCSS files to scan for references
5829
5845
  async findSourceFiles(directory) {
5830
5846
  const sourceFiles = [];
5831
5847
 
@@ -5842,10 +5858,9 @@ class AccessibilityFixer {
5842
5858
  await walk(filePath);
5843
5859
  }
5844
5860
  } else {
5845
- // Include files that could contain references
5861
+ // Include HTML (main entry) and CSS/SCSS (for background images)
5846
5862
  const ext = path.extname(file).toLowerCase();
5847
- if (['.html', '.htm', '.css', '.js', '.jsx', '.ts', '.tsx',
5848
- '.vue', '.php', '.json', '.md', '.xml', '.svg'].includes(ext)) {
5863
+ if (['.html', '.htm', '.css', '.scss', '.sass'].includes(ext)) {
5849
5864
  sourceFiles.push(filePath);
5850
5865
  }
5851
5866
  }
@@ -5859,59 +5874,47 @@ class AccessibilityFixer {
5859
5874
  return sourceFiles;
5860
5875
  }
5861
5876
 
5862
- // Enhanced reference extraction
5877
+ // Enhanced reference extraction from HTML and CSS files
5863
5878
  extractAllReferences(content, baseDir, sourceFile) {
5864
- const references = new Set();
5865
-
5866
- // Get file extension to determine extraction method
5879
+ const references = [];
5867
5880
  const ext = path.extname(sourceFile).toLowerCase();
5868
5881
 
5869
5882
  if (['.html', '.htm'].includes(ext)) {
5870
- // HTML references
5871
- const htmlRefs = this.extractFileReferences(content, baseDir);
5872
- htmlRefs.forEach(ref => references.add(ref));
5883
+ // Extract from HTML files - main entry points
5884
+ const htmlRefs = this.extractHtmlReferences(content, baseDir);
5885
+ references.push(...htmlRefs);
5873
5886
 
5874
- } else if (ext === '.css') {
5875
- // CSS references
5887
+ } else if (['.css', '.scss', '.sass'].includes(ext)) {
5888
+ // Extract from CSS/SCSS files - background images
5876
5889
  const cssRefs = this.extractCssReferences(content, baseDir);
5877
- cssRefs.forEach(ref => references.add(ref));
5878
-
5879
- } else if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
5880
- // JavaScript/TypeScript references
5881
- const jsRefs = this.extractJsReferences(content, baseDir);
5882
- jsRefs.forEach(ref => references.add(ref));
5883
-
5884
- // Also extract import statements
5885
- const importRefs = this.extractImportReferences(content, baseDir);
5886
- importRefs.forEach(ref => references.add(ref));
5887
-
5888
- } else if (ext === '.json') {
5889
- // JSON references (like package.json, config files)
5890
- const jsonRefs = this.extractJsonReferences(content, baseDir);
5891
- jsonRefs.forEach(ref => references.add(ref));
5892
-
5893
- } else {
5894
- // Generic file references (for .md, .xml, etc.)
5895
- const genericRefs = this.extractGenericReferences(content, baseDir);
5896
- genericRefs.forEach(ref => references.add(ref));
5890
+ references.push(...cssRefs);
5897
5891
  }
5898
5892
 
5899
- return Array.from(references);
5893
+ return references;
5900
5894
  }
5901
5895
 
5902
- extractFileReferences(content, baseDir) {
5896
+ extractHtmlReferences(content, baseDir) {
5903
5897
  const references = [];
5904
5898
 
5905
5899
  // HTML patterns for file references
5906
5900
  const patterns = [
5907
5901
  // Images
5908
5902
  /<img[^>]*src\s*=\s*["']([^"']+)["']/gi,
5903
+ // Srcset (responsive images)
5904
+ /<img[^>]*srcset\s*=\s*["']([^"']+)["']/gi,
5905
+ /<source[^>]*srcset\s*=\s*["']([^"']+)["']/gi,
5906
+ // Data attributes (lazy loading, etc.)
5907
+ /data-src\s*=\s*["']([^"']+)["']/gi,
5908
+ /data-background\s*=\s*["']([^"']+)["']/gi,
5909
+ /data-image\s*=\s*["']([^"']+)["']/gi,
5910
+ /data-bg\s*=\s*["']([^"']+)["']/gi,
5911
+ /data-lazy\s*=\s*["']([^"']+)["']/gi,
5909
5912
  // Links (CSS, other files)
5910
5913
  /<link[^>]*href\s*=\s*["']([^"']+)["']/gi,
5911
5914
  // Scripts
5912
5915
  /<script[^>]*src\s*=\s*["']([^"']+)["']/gi,
5913
- // Anchors
5914
- /<a[^>]*href\s*=\s*["']([^"']+)["']/gi,
5916
+ // Anchors to other HTML files
5917
+ /<a[^>]*href\s*=\s*["']([^"'#]+\.html?)["']/gi,
5915
5918
  // Video/Audio
5916
5919
  /<(?:video|audio)[^>]*src\s*=\s*["']([^"']+)["']/gi,
5917
5920
  // Object/Embed
@@ -5926,10 +5929,18 @@ class AccessibilityFixer {
5926
5929
  let match;
5927
5930
  while ((match = pattern.exec(content)) !== null) {
5928
5931
  const url = match[1];
5929
- if (this.isLocalFile(url)) {
5930
- const resolvedPath = this.resolveFilePath(url, baseDir);
5931
- if (resolvedPath) {
5932
- references.push(resolvedPath);
5932
+
5933
+ // Handle srcset format: "image1.jpg 1x, image2.jpg 2x"
5934
+ if (pattern.source.includes('srcset')) {
5935
+ const srcsetUrls = url.split(',').map(item => item.trim().split(' ')[0]);
5936
+ srcsetUrls.forEach(srcUrl => {
5937
+ if (this.isLocalFile(srcUrl)) {
5938
+ this.addNormalizedUrl(references, srcUrl);
5939
+ }
5940
+ });
5941
+ } else {
5942
+ if (this.isLocalFile(url)) {
5943
+ this.addNormalizedUrl(references, url);
5933
5944
  }
5934
5945
  }
5935
5946
  }
@@ -5938,15 +5949,29 @@ class AccessibilityFixer {
5938
5949
  return references;
5939
5950
  }
5940
5951
 
5952
+ // Helper method to add normalized URL variations
5953
+ addNormalizedUrl(references, url) {
5954
+ // Store original URL and normalized versions for matching
5955
+ references.push(url);
5956
+ if (url.startsWith('/')) {
5957
+ references.push(url.substring(1)); // Remove leading slash
5958
+ }
5959
+ if (!url.startsWith('./') && !url.startsWith('/')) {
5960
+ references.push('./' + url); // Add leading ./
5961
+ }
5962
+ }
5963
+
5941
5964
  extractCssReferences(content, baseDir) {
5942
5965
  const references = [];
5943
5966
 
5944
- // CSS patterns for file references
5967
+ // CSS patterns for file references
5945
5968
  const patterns = [
5946
- // url() function
5969
+ // url() function - background images, fonts, etc.
5947
5970
  /url\s*\(\s*["']?([^"')]+)["']?\s*\)/gi,
5948
- // @import
5949
- /@import\s+["']([^"']+)["']/gi
5971
+ // @import CSS files
5972
+ /@import\s+["']([^"']+)["']/gi,
5973
+ // CSS custom properties with URLs
5974
+ /--[^:]+:\s*url\s*\(\s*["']?([^"')]+)["']?\s*\)/gi
5950
5975
  ];
5951
5976
 
5952
5977
  for (const pattern of patterns) {
@@ -5954,10 +5979,7 @@ class AccessibilityFixer {
5954
5979
  while ((match = pattern.exec(content)) !== null) {
5955
5980
  const url = match[1];
5956
5981
  if (this.isLocalFile(url)) {
5957
- const resolvedPath = this.resolveFilePath(url, baseDir);
5958
- if (resolvedPath) {
5959
- references.push(resolvedPath);
5960
- }
5982
+ this.addNormalizedUrl(references, url);
5961
5983
  }
5962
5984
  }
5963
5985
  }
@@ -5985,9 +6007,13 @@ class AccessibilityFixer {
5985
6007
  while ((match = pattern.exec(content)) !== null) {
5986
6008
  const url = match[1];
5987
6009
  if (this.isLocalFile(url)) {
5988
- const resolvedPath = this.resolveFilePath(url, baseDir);
5989
- if (resolvedPath) {
5990
- references.push(resolvedPath);
6010
+ // Store both original URL and normalized versions for matching
6011
+ references.push(url);
6012
+ if (url.startsWith('/')) {
6013
+ references.push(url.substring(1)); // Remove leading slash
6014
+ }
6015
+ if (!url.startsWith('./') && !url.startsWith('/')) {
6016
+ references.push('./' + url); // Add leading ./
5991
6017
  }
5992
6018
  }
5993
6019
  }
@@ -6058,15 +6084,25 @@ class AccessibilityFixer {
6058
6084
  shouldSkipUnusedCheck(filePath) {
6059
6085
  const fileName = path.basename(filePath);
6060
6086
  const dirName = path.basename(path.dirname(filePath));
6087
+ const relativePath = path.relative(process.cwd(), filePath);
6061
6088
 
6062
- // Skip certain file types and patterns
6089
+ // Skip index files - they are main entry points
6090
+ if (/^index\d*\.(html?|htm)$/i.test(fileName)) {
6091
+ return true;
6092
+ }
6093
+
6094
+ // Skip OGP images - usually referenced with absolute paths in meta tags
6095
+ if (/^ogp\.(png|jpg|jpeg)$/i.test(fileName)) {
6096
+ return true;
6097
+ }
6098
+
6099
+ // Skip certain file types and patterns - but only in root directory
6063
6100
  const skipPatterns = [
6064
- // Common files that might not be directly referenced
6065
- /^(index|main|app)\.(html|js|css)$/i,
6101
+ // Common files that might not be directly referenced - only in root
6102
+ /^(main|app)\.(html|js|css)$/i,
6066
6103
  /^(readme|license|changelog)/i,
6067
6104
  /^package\.json$/i,
6068
6105
  /^\.gitignore$/i,
6069
- /^favicon\.(ico|png)$/i,
6070
6106
  /^robots\.txt$/i,
6071
6107
  /^sitemap\.xml$/i,
6072
6108
  // Backup files
@@ -6075,6 +6111,11 @@ class AccessibilityFixer {
6075
6111
  /test|spec|__tests__/i
6076
6112
  ];
6077
6113
 
6114
+ // Only skip favicon in root directory, not in assets folders
6115
+ if (/^favicon\.(ico|png)$/i.test(fileName) && !relativePath.includes('/')) {
6116
+ return true;
6117
+ }
6118
+
6078
6119
  return skipPatterns.some(pattern =>
6079
6120
  pattern.test(fileName) || pattern.test(dirName)
6080
6121
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbu-accessibility-package",
3
- "version": "3.8.5",
3
+ "version": "3.8.7",
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": {
@@ -35,6 +35,9 @@
35
35
  "demo": "node cli.js --dry-run demo",
36
36
  "demo-enhanced": "node cli.js --enhanced-alt --dry-run demo",
37
37
  "demo-creative": "node cli.js --enhanced-alt --alt-creativity creative --include-emotions --dry-run demo",
38
+ "test-local": "./test-local-projects.sh",
39
+ "test-unused-files": "./test-unused-files.sh",
40
+ "test-pack": "npm pack && echo 'Package created. Install in test project with: npm install ./gbu-accessibility-package-$(node -p \"require('./package.json').version\").tgz'",
38
41
  "prepublishOnly": "npm run test"
39
42
  },
40
43
  "keywords": [