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 +12 -1
- package/README-vi.md +6 -0
- package/README.md +7 -1
- package/cli.js +53 -2
- package/lib/fixer.js +221 -41
- package/package.json +3 -1
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(
|
|
6315
|
-
this.addNormalizedUrl(references,
|
|
6316
|
-
} else if (this.isAbsoluteUrlToLocalFile(
|
|
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(
|
|
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(
|
|
6328
|
-
this.addNormalizedUrl(references,
|
|
6329
|
-
} else if (this.isAbsoluteUrlToLocalFile(
|
|
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(
|
|
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
|
-
|
|
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
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
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
|
|
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(
|
|
6377
|
-
if (
|
|
6378
|
-
references.push(
|
|
6546
|
+
references.push(normalizedUrl);
|
|
6547
|
+
if (normalizedUrl.startsWith('/')) {
|
|
6548
|
+
references.push(normalizedUrl.substring(1)); // Remove leading slash
|
|
6379
6549
|
}
|
|
6380
|
-
if (!
|
|
6381
|
-
references.push('./' +
|
|
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
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
!
|
|
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 (
|
|
7383
|
-
const resolved = this.resolveFilePath(
|
|
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.
|
|
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",
|