gbu-accessibility-package 1.0.0 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 GBU Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/QUICK_START.md CHANGED
@@ -1,19 +1,18 @@
1
1
  # 🚀 Quick Start Guide
2
2
 
3
- Hướng dẫn nhanh để sử dụng Accessibility Fixer trong 5 phút.
3
+ Hướng dẫn nhanh để sử dụng GBU Accessibility Package trong 5 phút.
4
4
 
5
5
  ## ⚡ Cài đặt nhanh
6
6
 
7
7
  ```bash
8
- # 1. Copy package vào dự án
9
- cp -r accessibility-package /path/to/your/project/
8
+ # 1. Cài đặt global (khuyến nghị)
9
+ npm install -g gbu-accessibility-package
10
10
 
11
- # 2. Cài đặt dependencies
12
- cd your-project/accessibility-package
13
- npm install
11
+ # 2. Hoặc cài đặt local
12
+ npm install gbu-accessibility-package
14
13
 
15
14
  # 3. Chạy ngay!
16
- node cli.js
15
+ gbu-a11y
17
16
  ```
18
17
 
19
18
  ## 🎯 Sử dụng cơ bản
@@ -22,16 +21,19 @@ node cli.js
22
21
 
23
22
  ```bash
24
23
  # Fix toàn bộ dự án (current directory)
25
- node accessibility-package/cli.js
24
+ gbu-a11y
26
25
 
27
26
  # Fix thư mục cụ thể
28
- node accessibility-package/cli.js ./src
27
+ gbu-a11y ./src
29
28
 
30
29
  # Preview trước khi fix
31
- node accessibility-package/cli.js --dry-run
30
+ gbu-a11y --dry-run
32
31
 
33
32
  # Fix với ngôn ngữ khác
34
- node accessibility-package/cli.js -l en ./dist
33
+ gbu-a11y -l en ./dist
34
+
35
+ # Fix comprehensive (khuyến nghị)
36
+ gbu-a11y --comprehensive
35
37
  ```
36
38
 
37
39
  ### Cách 2: Node.js Script
@@ -39,7 +41,7 @@ node accessibility-package/cli.js -l en ./dist
39
41
  Tạo file `fix.js`:
40
42
 
41
43
  ```javascript
42
- const AccessibilityFixer = require('./accessibility-package/lib/fixer.js');
44
+ const AccessibilityFixer = require('gbu-accessibility-package');
43
45
 
44
46
  const fixer = new AccessibilityFixer({
45
47
  language: 'ja', // Thay đổi theo dự án
@@ -48,9 +50,8 @@ const fixer = new AccessibilityFixer({
48
50
  });
49
51
 
50
52
  async function fix() {
51
- await fixer.fixHtmlLang('.');
52
- await fixer.fixEmptyAltAttributes('.');
53
- await fixer.fixRoleAttributes('.');
53
+ // Fix tất cả issues
54
+ await fixer.fixAllAccessibilityIssues('.');
54
55
  console.log('✅ Done!');
55
56
  }
56
57
 
@@ -61,11 +62,10 @@ Chạy: `node fix.js`
61
62
 
62
63
  ## 📋 Checklist nhanh
63
64
 
64
- - [ ] Copy package vào dự án
65
- - [ ] `npm install` trong thư mục package
65
+ - [ ] `npm install -g gbu-accessibility-package`
66
66
  - [ ] Backup code (git commit)
67
- - [ ] Chạy `--dry-run` để preview
68
- - [ ] Chạy tool để fix
67
+ - [ ] Chạy `gbu-a11y --dry-run` để preview
68
+ - [ ] Chạy `gbu-a11y --comprehensive` để fix
69
69
  - [ ] Kiểm tra kết quả
70
70
  - [ ] Commit changes
71
71
 
@@ -103,19 +103,19 @@ language: 'en' // 'ja', 'vi', 'zh', etc.
103
103
 
104
104
  ### Không tạo backup
105
105
  ```bash
106
- node cli.js --no-backup
106
+ gbu-a11y --no-backup
107
107
  ```
108
108
 
109
109
  ### Chỉ preview
110
110
  ```bash
111
- node cli.js --dry-run
111
+ gbu-a11y --dry-run
112
112
  ```
113
113
 
114
114
  ## ❓ Troubleshooting
115
115
 
116
116
  **Lỗi "Cannot find module"**
117
117
  ```bash
118
- cd accessibility-package && npm install
118
+ npm install -g gbu-accessibility-package
119
119
  ```
120
120
 
121
121
  **Duplicate attributes**
@@ -130,7 +130,7 @@ cd accessibility-package && npm install
130
130
 
131
131
  1. Đọc [README.md](./README.md) đầy đủ
132
132
  2. Xem [example.js](./example.js)
133
- 3. Chạy `node cli.js --help`
133
+ 3. Chạy `gbu-a11y --help`
134
134
 
135
135
  ---
136
136
 
package/README.md CHANGED
@@ -1,305 +1,292 @@
1
1
  # GBU Accessibility Package
2
2
 
3
- [![npm version](https://badge.fury.io/js/gbu-accessibility-package.svg)](https://badge.fury.io/js/gbu-accessibility-package)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
3
+ 🚀 **Automated accessibility fixes for HTML files** - Smart, context-aware accessibility improvements with zero configuration.
5
4
 
6
- Một công cụ tự động sửa các vấn đề accessibility phổ biến trong HTML, bao gồm alt attributes, lang attributes, và role attributes với khả năng phân tích context thông minh.
5
+ [![npm version](https://badge.fury.io/js/gbu-accessibility-package.svg)](https://www.npmjs.com/package/gbu-accessibility-package)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Node.js Version](https://img.shields.io/badge/node-%3E%3D12.0.0-brightgreen)](https://nodejs.org/)
7
8
 
8
- ## 🚀 Tính năng
9
+ ## Features
9
10
 
10
- - **Alt Attributes**: Tự động thêm alt text thông minh cho images
11
- - **Lang Attributes**: Thêm lang attribute cho thẻ `<html>`
12
- - **Role Attributes**: Thêm role attributes cho các elements
13
- - `role="img"` cho tất cả thẻ `<img>`
14
- - `role="link"` cho thẻ `<a>`
15
- - `role="button"` cho elements onclick
16
- - `role="menubar"` `role="menuitem"` cho navigation lists
17
- - **Context-aware**: Phân tích nội dung xung quanh để tạo alt text phù hợp
18
- - ✅ **Backup tự động**: Tạo backup files trước khi sửa
19
- - ✅ **Dry run mode**: Xem preview trước khi apply changes
11
+ - 🖼️ **Smart Alt Text Generation** - Context-aware alt attributes for images
12
+ - 🌐 **HTML Lang Attributes** - Automatic language attribute fixes
13
+ - 🎭 **Role Attributes** - WCAG-compliant role attribute management
14
+ - 🧹 **Duplicate Cleanup** - Remove duplicate role attributes
15
+ - 📁 **Batch Processing** - Process entire directories recursively
16
+ - 💾 **Automatic Backups** - Safe modifications with backup files
17
+ - 🔍 **Dry Run Mode** - Preview changes before applying
18
+ - 📊 **Detailed Reports** - Comprehensive fix summaries
20
19
 
21
- ## 📦 Cài đặt
20
+ ## 🚀 Quick Start
22
21
 
23
- ### NPM Install (Khuyến nghị)
22
+ ### Installation
24
23
 
25
24
  ```bash
26
- # Cài đặt global
25
+ # Global installation (recommended)
27
26
  npm install -g gbu-accessibility-package
28
27
 
29
- # Hoặc cài đặt local cho dự án
30
- npm install gbu-accessibility-package --save-dev
28
+ # Local installation
29
+ npm install gbu-accessibility-package
31
30
  ```
32
31
 
33
- ### Yarn Install
32
+ ### Basic Usage
34
33
 
35
34
  ```bash
36
- # Global
37
- yarn global add gbu-accessibility-package
35
+ # Fix current directory
36
+ gbu-a11y
38
37
 
39
- # Local
40
- yarn add gbu-accessibility-package --dev
41
- ```
38
+ # Preview changes (dry run)
39
+ gbu-a11y --dry-run
42
40
 
43
- ## 🛠️ Sử dụng
41
+ # Fix specific directory
42
+ gbu-a11y ./src
44
43
 
45
- ### Cách 1: Sử dụng CLI (Global Install)
46
-
47
- ```bash
48
- # Sau khi cài đặt global
49
- gbu-a11y [options] [directory]
44
+ # Comprehensive fixes (recommended)
45
+ gbu-a11y --comprehensive
50
46
 
51
- # Hoặc
52
- accessibility-fixer [options] [directory]
47
+ # Fix specific file
48
+ gbu-a11y index.html
53
49
  ```
54
50
 
55
- ### Cách 1b: CLI (Local Install)
51
+ ## 📖 Detailed Usage
56
52
 
57
- ```bash
58
- # Chạy từ node_modules
59
- npx gbu-a11y [options] [directory]
53
+ ### Command Line Options
60
54
 
61
- # Hoặc thêm vào package.json scripts
62
- {
63
- "scripts": {
64
- "fix-a11y": "gbu-a11y",
65
- "preview-a11y": "gbu-a11y --dry-run"
66
- }
67
- }
55
+ ```bash
56
+ gbu-a11y [options] [directory/file]
57
+
58
+ Options:
59
+ -d, --directory <path> Target directory (default: current directory)
60
+ -l, --language <lang> Language for lang attribute (default: ja)
61
+ --no-backup Don't create backup files
62
+ --dry-run Preview changes without applying
63
+ --cleanup-only Only cleanup duplicate role attributes
64
+ --comprehensive, --all Run all fixes including cleanup (recommended)
65
+ -h, --help Show help message
68
66
  ```
69
67
 
70
- ### Cách 2: Sử dụng trong Node.js
68
+ ### Examples
71
69
 
72
- ```javascript
73
- const AccessibilityFixer = require('gbu-accessibility-package');
70
+ ```bash
71
+ # Basic fixes for current directory
72
+ gbu-a11y
74
73
 
75
- // Tạo instance với config
76
- const fixer = new AccessibilityFixer({
77
- language: 'ja', // Ngôn ngữ cho lang attribute
78
- backupFiles: true, // Tạo backup files
79
- dryRun: false // false = apply changes, true = preview only
80
- });
74
+ # Preview all changes
75
+ gbu-a11y --dry-run --comprehensive
81
76
 
82
- // Sử dụng các methods
83
- async function fixAccessibility() {
84
- // Fix lang attributes
85
- const langResults = await fixer.fixHtmlLang('.');
86
-
87
- // Fix alt attributes
88
- const altResults = await fixer.fixEmptyAltAttributes('.');
89
-
90
- // Fix role attributes
91
- const roleResults = await fixer.fixRoleAttributes('.');
92
-
93
- console.log('Hoàn thành!');
94
- }
77
+ # Fix with English language
78
+ gbu-a11y -l en ./public
95
79
 
96
- fixAccessibility();
97
- ```
80
+ # Only cleanup duplicates
81
+ gbu-a11y --cleanup-only
98
82
 
99
- ### Cách 3: Script nhanh
83
+ # Fix without creating backups
84
+ gbu-a11y --no-backup ./dist
85
+ ```
100
86
 
101
- Tạo file `fix-accessibility.js` trong dự án:
87
+ ## 🔧 Programmatic Usage
102
88
 
103
89
  ```javascript
104
90
  const AccessibilityFixer = require('gbu-accessibility-package');
105
91
 
106
92
  const fixer = new AccessibilityFixer({
107
- language: 'ja', // Thay đổi theo ngôn ngữ dự án
93
+ language: 'en',
108
94
  backupFiles: true,
109
95
  dryRun: false
110
96
  });
111
97
 
112
- async function fixAll() {
113
- console.log('🚀 Bắt đầu fix accessibility...');
114
-
115
- // Fix tất cả issues
116
- await fixer.fixHtmlLang('.');
117
- await fixer.fixEmptyAltAttributes('.');
118
- await fixer.fixRoleAttributes('.');
119
-
120
- console.log('✅ Hoàn thành!');
98
+ // Fix all accessibility issues
99
+ async function fixAccessibility() {
100
+ try {
101
+ const results = await fixer.fixAllAccessibilityIssues('./src');
102
+ console.log('Fixed files:', results);
103
+ } catch (error) {
104
+ console.error('Error:', error);
105
+ }
121
106
  }
122
107
 
123
- fixAll();
108
+ fixAccessibility();
124
109
  ```
125
110
 
126
- Chạy script:
127
- ```bash
128
- node fix-accessibility.js
129
- ```
111
+ ## 🎯 What Gets Fixed
130
112
 
131
- ## ⚙️ Configuration Options
113
+ ### 1. Alt Attributes
114
+ - **Missing alt attributes** → Adds contextual alt text
115
+ - **Empty alt attributes** → Generates meaningful descriptions
116
+ - **Context-aware generation** → Uses surrounding text, headings, captions
132
117
 
133
- ```javascript
134
- const config = {
135
- language: 'ja', // Lang attribute value ('ja', 'en', 'vi', etc.)
136
- backupFiles: true, // Tạo .backup files
137
- dryRun: false // true = chỉ preview, false = apply changes
138
- };
118
+ ```html
119
+ <!-- Before -->
120
+ <img src="logo.png">
121
+ <img src="chart.jpg" alt="">
139
122
 
140
- const fixer = new AccessibilityFixer(config);
123
+ <!-- After -->
124
+ <img src="logo.png" alt="ロゴ">
125
+ <img src="chart.jpg" alt="グラフ">
141
126
  ```
142
127
 
143
- ## 📋 Các Methods có sẵn
128
+ ### 2. HTML Lang Attributes
129
+ - **Missing lang attributes** → Adds specified language
130
+ - **Empty lang attributes** → Sets proper language code
144
131
 
145
- ### 1. fixHtmlLang(directory)
146
- Thêm lang attribute cho thẻ `<html>`
132
+ ```html
133
+ <!-- Before -->
134
+ <html>
135
+ <html lang="">
147
136
 
148
- ```javascript
149
- const results = await fixer.fixHtmlLang('./src');
137
+ <!-- After -->
138
+ <html lang="ja">
139
+ <html lang="ja">
150
140
  ```
151
141
 
152
- ### 2. fixEmptyAltAttributes(directory)
153
- Sửa missing/empty alt attributes cho images
142
+ ### 3. Role Attributes
143
+ - **Images** `role="img"`
144
+ - **Links** → `role="link"`
145
+ - **Clickable elements** → `role="button"`
146
+ - **Navigation lists** → `role="menubar"`
147
+ - **Menu items** → `role="menuitem"`
154
148
 
155
- ```javascript
156
- const results = await fixer.fixEmptyAltAttributes('./src');
149
+ ```html
150
+ <!-- Before -->
151
+ <img src="icon.png" alt="Icon">
152
+ <a href="/home">Home</a>
153
+ <div class="btn-click">Click me</div>
154
+
155
+ <!-- After -->
156
+ <img src="icon.png" alt="Icon" role="img">
157
+ <a href="/home" role="link">Home</a>
158
+ <div class="btn-click" role="button">Click me</div>
157
159
  ```
158
160
 
159
- ### 3. fixRoleAttributes(directory)
160
- Thêm role attributes cho các elements
161
+ ### 4. Duplicate Cleanup
162
+ - **Removes duplicate role attributes**
163
+ - **Preserves first occurrence**
164
+ - **Handles mixed quote styles**
161
165
 
162
- ```javascript
163
- const results = await fixer.fixRoleAttributes('./src');
164
- ```
165
-
166
- ### 4. addMainLandmarks(directory)
167
- Phát hiện và suggest main landmarks
166
+ ```html
167
+ <!-- Before -->
168
+ <img src="test.jpg" role="img" role="img" alt="Test">
168
169
 
169
- ```javascript
170
- const suggestions = await fixer.addMainLandmarks('./src');
170
+ <!-- After -->
171
+ <img src="test.jpg" role="img" alt="Test">
171
172
  ```
172
173
 
173
- ## 🎯 dụ kết quả
174
+ ## 🌟 Smart Alt Text Generation
174
175
 
175
- ### Alt Attributes
176
- ```html
177
- <!-- Trước -->
178
- <img src="logo.png">
179
- <img src="icon.png" alt="">
176
+ The package uses intelligent context analysis to generate meaningful alt text:
180
177
 
181
- <!-- Sau -->
182
- <img src="logo.png" alt="ロゴ">
183
- <img src="icon.png" alt="アイコン">
184
- ```
178
+ ### Context Sources
179
+ 1. **Title attributes**
180
+ 2. **Aria-label attributes**
181
+ 3. **Definition terms (dt elements)**
182
+ 4. **Parent link text**
183
+ 5. **Nearby headings**
184
+ 6. **Figure captions**
185
+ 7. **Surrounding text content**
185
186
 
186
- ### Role Attributes
187
- ```html
188
- <!-- Trước -->
189
- <img src="photo.jpg" alt="Beautiful sunset">
190
- <a href="/about">About Us</a>
191
- <button onclick="submit()">Submit</button>
192
-
193
- <!-- Sau -->
194
- <img src="photo.jpg" alt="Beautiful sunset" role="img">
195
- <a href="/about" role="link">About Us</a>
196
- <button onclick="submit()" role="button">Submit</button>
197
- ```
187
+ ### Fallback Patterns
188
+ - `logo.png` → "ロゴ" (Logo)
189
+ - `icon.svg` → "アイコン" (Icon)
190
+ - `banner.jpg` "バナー" (Banner)
191
+ - `chart.png` → "グラフ" (Chart)
192
+ - Generic images → "画像" (Image)
198
193
 
199
- ### Lang Attributes
200
- ```html
201
- <!-- Trước -->
202
- <html>
194
+ ## 📊 Output Examples
203
195
 
204
- <!-- Sau -->
205
- <html lang="ja">
196
+ ### Standard Mode
206
197
  ```
198
+ 🚀 Starting Accessibility Fixer...
199
+ 📝 Step 1: Fixing HTML lang attributes...
200
+ ✅ Fixed lang attributes in 5 files
207
201
 
208
- ## 🔧 Customization
202
+ 🖼️ Step 2: Fixing alt attributes...
203
+ ✅ Fixed alt attributes in 12 files (34 issues)
209
204
 
210
- ### Thay đổi ngôn ngữ cho alt text
205
+ 🎭 Step 3: Fixing role attributes...
206
+ ✅ Fixed role attributes in 8 files (67 issues)
211
207
 
212
- Sửa method `generateAltText` trong `lib/fixer.js`:
208
+ 📊 Summary:
209
+ Total files scanned: 25
210
+ Files fixed: 15
211
+ Total issues resolved: 106
213
212
 
214
- ```javascript
215
- generateAltText(imgTag, htmlContent = '', imgIndex = 0) {
216
- // Thay đổi các text này theo ngôn ngữ dự án
217
- if (srcValue.includes('logo')) {
218
- return 'Logo'; // Thay vì 'ロゴ'
219
- } else if (srcValue.includes('icon')) {
220
- return 'Icon'; // Thay vì 'アイコン'
221
- }
222
- // ... thêm các cases khác
223
- }
213
+ 🎉 All accessibility fixes completed successfully!
224
214
  ```
225
215
 
226
- ### Thêm role rules mới
227
-
228
- Sửa method `fixRoleAttributesInContent` để thêm rules:
229
-
230
- ```javascript
231
- // dụ: thêm role cho form elements
232
- fixed = fixed.replace(
233
- /<form([^>]*)(?!.*role\s*=)([^>]*>)/gi,
234
- '<form$1 role="form"$2'
235
- );
216
+ ### Comprehensive Mode
217
+ ```
218
+ 🎯 Running comprehensive accessibility fixes...
219
+ 📝 Step 1: HTML lang attributes...
220
+ 🖼️ Step 2: Alt attributes...
221
+ 🎭 Step 3: Role attributes...
222
+ 🧹 Step 4: Cleanup duplicate roles...
223
+
224
+ 🎉 All accessibility fixes completed!
225
+ 📊 Final Summary:
226
+ Total files scanned: 25
227
+ Files fixed: 15
228
+ Total issues resolved: 106
236
229
  ```
237
230
 
238
- ## 🚨 Lưu ý quan trọng
239
-
240
- 1. **Backup**: Luôn tạo backup trước khi chạy tool
241
- 2. **Test**: Chạy với `dryRun: true` trước để xem preview
242
- 3. **Review**: Kiểm tra kết quả sau khi chạy
243
- 4. **Git**: Commit code trước khi chạy tool
231
+ ## 🔒 Safety Features
244
232
 
245
- ## 📊 Monitoring Results
233
+ - **Automatic backups** with `.backup` extension
234
+ - **Dry run mode** for safe previewing
235
+ - **Non-destructive** - only adds missing attributes
236
+ - **Duplicate prevention** - won't add existing attributes
237
+ - **Error handling** - continues processing on individual file errors
246
238
 
247
- Tool sẽ hiển thị:
248
- - Số files được xử lý
249
- - Số issues được tìm thấy và sửa
250
- - Chi tiết từng file
251
- - Thống kê tổng quan
239
+ ## 🛠️ Configuration
252
240
 
253
- ```
254
- 📁 index.html:
255
- 🖼️ Missing role: Image 1 should have role="img"
256
- 🔗 Missing role: Anchor 1 should have role="link"
257
- 🖼️ Added role="img" to image element
258
- 🔗 Added role="link" to anchor element
259
- Fixed role attributes in: index.html
260
-
261
- 📊 Summary: Found 245 role attribute issues across 50 files
241
+ ### Package.json Scripts
242
+ ```json
243
+ {
244
+ "scripts": {
245
+ "a11y:fix": "gbu-a11y",
246
+ "a11y:check": "gbu-a11y --dry-run",
247
+ "a11y:comprehensive": "gbu-a11y --comprehensive",
248
+ "a11y:cleanup": "gbu-a11y --cleanup-only"
249
+ }
250
+ }
262
251
  ```
263
252
 
264
- ## 🛠️ Troubleshooting
253
+ ### CI/CD Integration
254
+ ```yaml
255
+ # GitHub Actions example
256
+ - name: Check Accessibility
257
+ run: npx gbu-accessibility-package --dry-run
265
258
 
266
- ### Lỗi "Cannot find module"
267
- ```bash
268
- cd accessibility-package
269
- npm install
259
+ - name: Fix Accessibility Issues
260
+ run: npx gbu-accessibility-package --comprehensive
270
261
  ```
271
262
 
272
- ### Duplicate role attributes
273
- Chạy cleanup script:
274
- ```javascript
275
- // Tạo file cleanup.js
276
- const fs = require('fs');
277
- const path = require('path');
278
-
279
- async function cleanup() {
280
- // Code cleanup duplicate roles
281
- const content = await fs.promises.readFile('file.html', 'utf8');
282
- const fixed = content.replace(/role="([^"]+)"(\s+role="\1")+/g, 'role="$1"');
283
- await fs.promises.writeFile('file.html', fixed);
284
- }
285
- ```
263
+ ## 🤝 Contributing
286
264
 
287
- ### Performance với project lớn
288
- - Chạy từng thư mục con
289
- - Sử dụng `dryRun: true` để test trước
290
- - Exclude thư mục không cần thiết
265
+ 1. Fork the repository
266
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
267
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
268
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
269
+ 5. Open a Pull Request
291
270
 
292
271
  ## 📝 License
293
272
 
294
- MIT License - Tự do sử dụng cho mọi dự án.
273
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
295
274
 
296
- ## 🤝 Contributing
275
+ ## 🆘 Support
276
+
277
+ - 📧 **Issues**: [GitHub Issues](https://github.com/your-org/gbu-accessibility-package/issues)
278
+ - 📖 **Documentation**: [GitHub Wiki](https://github.com/your-org/gbu-accessibility-package/wiki)
279
+ - 💬 **Discussions**: [GitHub Discussions](https://github.com/your-org/gbu-accessibility-package/discussions)
280
+
281
+ ## 🏆 Why Choose GBU Accessibility Package?
297
282
 
298
- 1. Fork repository
299
- 2. Tạo feature branch
300
- 3. Commit changes
301
- 4. Tạo Pull Request
283
+ - **Zero Configuration** - Works out of the box
284
+ - **Smart & Context-Aware** - Not just generic fixes
285
+ - **Safe & Reliable** - Automatic backups and dry run
286
+ - **Comprehensive** - Covers all major accessibility issues
287
+ - ✅ **Fast & Efficient** - Batch processing with detailed reports
288
+ - ✅ **WCAG Compliant** - Follows accessibility standards
302
289
 
303
290
  ---
304
291
 
305
- **Happy coding! 🚀**
292
+ Made with ❤️ by the GBU Team
package/cli.js CHANGED
@@ -16,7 +16,9 @@ const options = {
16
16
  language: 'ja',
17
17
  backupFiles: true,
18
18
  dryRun: false,
19
- help: false
19
+ help: false,
20
+ cleanupOnly: false,
21
+ comprehensive: false
20
22
  };
21
23
 
22
24
  // Parse arguments
@@ -42,6 +44,13 @@ for (let i = 0; i < args.length; i++) {
42
44
  case '--dry-run':
43
45
  options.dryRun = true;
44
46
  break;
47
+ case '--cleanup-only':
48
+ options.cleanupOnly = true;
49
+ break;
50
+ case '--comprehensive':
51
+ case '--all':
52
+ options.comprehensive = true;
53
+ break;
45
54
  default:
46
55
  if (!arg.startsWith('-')) {
47
56
  options.directory = arg;
@@ -61,10 +70,14 @@ Options:
61
70
  -l, --language <lang> Language for lang attribute (default: ja)
62
71
  --no-backup Don't create backup files
63
72
  --dry-run Preview changes without applying
73
+ --cleanup-only Only cleanup duplicate role attributes
74
+ --comprehensive, --all Run all fixes including cleanup (recommended)
64
75
  -h, --help Show this help message
65
76
 
66
77
  Examples:
67
- node cli.js # Fix current directory
78
+ node cli.js # Fix current directory (standard fixes)
79
+ node cli.js --comprehensive # Run all fixes including cleanup
80
+ node cli.js --cleanup-only # Only cleanup duplicate roles
68
81
  node cli.js ./src # Fix src directory
69
82
  node cli.js -l en --dry-run ./dist # Preview fixes for dist directory in English
70
83
  node cli.js --no-backup ./public # Fix without creating backups
@@ -95,6 +108,32 @@ async function main() {
95
108
  });
96
109
 
97
110
  try {
111
+ // Handle different modes
112
+ if (options.comprehensive) {
113
+ // Run comprehensive fix (all fixes including cleanup)
114
+ console.log(chalk.blue('🎯 Running comprehensive accessibility fixes...'));
115
+ const results = await fixer.fixAllAccessibilityIssues(options.directory);
116
+
117
+ // Results already logged in the method
118
+ return;
119
+
120
+ } else if (options.cleanupOnly) {
121
+ // Only cleanup duplicate roles
122
+ console.log(chalk.blue('🧹 Running cleanup for duplicate role attributes...'));
123
+ const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
124
+ const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
125
+
126
+ console.log(chalk.green(`\n✅ Cleaned duplicate roles in ${cleanupFixed} files`));
127
+
128
+ if (options.dryRun) {
129
+ console.log(chalk.cyan('\n💡 This was a dry run. Use without --dry-run to apply changes.'));
130
+ } else {
131
+ console.log(chalk.green('\n🎉 Cleanup completed successfully!'));
132
+ }
133
+ return;
134
+ }
135
+
136
+ // Standard mode - run individual fixes
98
137
  // Fix HTML lang attributes
99
138
  console.log(chalk.yellow('📝 Step 1: Fixing HTML lang attributes...'));
100
139
  const langResults = await fixer.fixHtmlLang(options.directory);
@@ -146,6 +185,9 @@ async function main() {
146
185
  console.log(chalk.gray(' Backup files created with .backup extension'));
147
186
  }
148
187
  }
188
+
189
+ // Suggest cleanup if not comprehensive mode
190
+ console.log(chalk.blue('\n💡 Pro tip: Use --comprehensive to include duplicate role cleanup!'));
149
191
 
150
192
  } catch (error) {
151
193
  console.error(chalk.red('❌ Error occurred:'), error.message);
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <title>Test Duplicate Roles</title>
5
+ </head>
6
+ <body>
7
+ <h1>Test Duplicate Role Attributes</h1>
8
+
9
+ <!-- Images with duplicate roles -->
10
+ <img src="logo.png" alt="Logo" role="img" role="img" role="img" role="img" role="img">
11
+ <img src="banner.jpg" alt="Banner" role="img" role="img" role="img" role="img" role="img" role="img">
12
+
13
+ <!-- Links with duplicate roles -->
14
+ <a href="/home" role="link" role="link" role="link" role="link" role="link">Home</a>
15
+ <a href="/about" role="link" role="link" role="link" role="link" role="link" role="link">About</a>
16
+
17
+ <!-- Buttons with duplicate roles -->
18
+ <button onclick="submit()" role="button" role="button" role="button" role="button" role="button">Submit</button>
19
+ <button type="button" role="button" role="button" role="button">Click Me</button>
20
+
21
+ <!-- Mixed quotes -->
22
+ <div onclick="toggle()" role="button" role='button' role="button" role="button" role="button">Toggle</div>
23
+ <span onclick="show()" role='button' role="button" role="button" role="button" role="button">Show</span>
24
+
25
+ <!-- Navigation with duplicates -->
26
+ <nav>
27
+ <ul class="nav-menu" role="menubar" role="menubar" role="menubar" role="menubar" role="menubar">
28
+ <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
29
+ <a href="/products" role="link" role="link" role="link" role="link" role="link">Products</a>
30
+ </li>
31
+ <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
32
+ <a href="/services" role="link" role="link" role="link" role="link" role="link" role="link">Services</a>
33
+ </li>
34
+ </ul>
35
+ </nav>
36
+
37
+ <!-- Complex duplicates -->
38
+ <article>
39
+ <h2>Article with Issues</h2>
40
+ <img src="article1.jpg" alt="Article Image" role="img" role="img" role="img" role="img" role="img">
41
+ <p>Some content...</p>
42
+ <a href="/read-more" role="link" role="link" role="link" role="link" role="link" role="link" role="link">Read More</a>
43
+ </article>
44
+ </body>
45
+ </html>
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <title>Test Duplicate Roles</title>
5
+ </head>
6
+ <body>
7
+ <h1>Test Duplicate Role Attributes</h1>
8
+
9
+ <!-- Images with duplicate roles -->
10
+ <img src="logo.png" alt="Logo" role="img" role="img" role="img" role="img">
11
+ <img src="banner.jpg" alt="Banner" role="img" role="img" role="img" role="img" role="img">
12
+
13
+ <!-- Links with duplicate roles -->
14
+ <a href="/home" role="link" role="link" role="link" role="link">Home</a>
15
+ <a href="/about" role="link" role="link" role="link" role="link" role="link">About</a>
16
+
17
+ <!-- Buttons with duplicate roles -->
18
+ <button onclick="submit()" role="button" role="button" role="button" role="button">Submit</button>
19
+ <button type="button" role="button" role="button" role="button">Click Me</button>
20
+
21
+ <!-- Mixed quotes -->
22
+ <div onclick="toggle()" role="button" role='button' role="button" role="button">Toggle</div>
23
+ <span onclick="show()" role='button' role="button" role="button" role="button">Show</span>
24
+
25
+ <!-- Navigation with duplicates -->
26
+ <nav>
27
+ <ul class="nav-menu" role="menubar" role="menubar" role="menubar" role="menubar">
28
+ <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
29
+ <a href="/products" role="link" role="link" role="link" role="link">Products</a>
30
+ </li>
31
+ <li class="nav-item" role="menuitem" role="menuitem" role="menuitem" role="menuitem" role="menuitem">
32
+ <a href="/services" role="link" role="link" role="link" role="link" role="link">Services</a>
33
+ </li>
34
+ </ul>
35
+ </nav>
36
+
37
+ <!-- Complex duplicates -->
38
+ <article>
39
+ <h2>Article with Issues</h2>
40
+ <img src="article1.jpg" alt="Article Image" role="img" role="img" role="img" role="img">
41
+ <p>Some content...</p>
42
+ <a href="/read-more" role="link" role="link" role="link" role="link" role="link" role="link">Read More</a>
43
+ </article>
44
+ </body>
45
+ </html>
package/demo/sample.html CHANGED
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="ja">
3
3
  <head>
4
4
  <title>Demo HTML for Accessibility Testing</title>
5
5
  </head>
@@ -7,27 +7,27 @@
7
7
  <h1>Sample Website</h1>
8
8
 
9
9
  <!-- Images without alt or role -->
10
- <img src="logo.png">
11
- <img src="banner.jpg" alt="">
12
- <img src="icon.svg" alt="Home Icon">
10
+ <img src="logo.png" alt="Sample Website" role="img" role="img" role="img">
11
+ <img src="banner.jpg" alt="Sample Website" role="img" role="img" role="img">
12
+ <img src="icon.svg" alt="Home Icon" role="img" role="img" role="img">
13
13
 
14
14
  <!-- Links without role -->
15
- <a href="/home">Home</a>
16
- <a href="/about">About Us</a>
15
+ <a href="/home" role="link" role="link" role="link">Home</a>
16
+ <a href="/about" role="link" role="link" role="link">About Us</a>
17
17
 
18
18
  <!-- Buttons without role -->
19
- <button onclick="submit()">Submit Form</button>
19
+ <button onclick="submit()" role="button" role="button" role="button">Submit Form</button>
20
20
  <button type="button">Regular Button</button>
21
21
 
22
22
  <!-- Clickable elements -->
23
- <div onclick="navigate()" class="btn">Click Me</div>
24
- <span onclick="toggle()">Toggle</span>
23
+ <div onclick="navigate()" class="btn" role="button" role="button" role="button" role="button" role="button" role="button">Click Me</div>
24
+ <span onclick="toggle()" role="button" role="button" role="button">Toggle</span>
25
25
 
26
26
  <!-- Navigation -->
27
27
  <nav>
28
- <ul class="nav-menu">
29
- <li class="nav-item"><a href="/products">Products</a></li>
30
- <li class="nav-item"><a href="/services">Services</a></li>
28
+ <ul class="nav-menu" role="menubar" role="menubar" role="menubar">
29
+ <li class="nav-item" role="menuitem"><a href="/products" role="link" role="link" role="link">Products</a></li>
30
+ <li class="nav-item" role="menuitem"><a href="/services" role="link" role="link" role="link">Services</a></li>
31
31
  </ul>
32
32
  </nav>
33
33
 
@@ -36,7 +36,7 @@
36
36
  <article>
37
37
  <h2>Article Title</h2>
38
38
  <p>Some content here...</p>
39
- <img src="article-image.jpg" alt="">
39
+ <img src="article-image.jpg" alt="Article Title" role="img" role="img" role="img">
40
40
  </article>
41
41
  </main>
42
42
 
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html>
2
+ <html lang="ja">
3
3
  <head>
4
4
  <title>Demo HTML for Accessibility Testing</title>
5
5
  </head>
@@ -7,27 +7,27 @@
7
7
  <h1>Sample Website</h1>
8
8
 
9
9
  <!-- Images without alt or role -->
10
- <img src="logo.png">
11
- <img src="banner.jpg" alt="">
12
- <img src="icon.svg" alt="Home Icon">
10
+ <img src="logo.png" alt="Sample Website" role="img" role="img" role="img">
11
+ <img src="banner.jpg" alt="Sample Website" role="img" role="img" role="img">
12
+ <img src="icon.svg" alt="Home Icon" role="img" role="img" role="img">
13
13
 
14
14
  <!-- Links without role -->
15
- <a href="/home">Home</a>
16
- <a href="/about">About Us</a>
15
+ <a href="/home" role="link" role="link" role="link">Home</a>
16
+ <a href="/about" role="link" role="link" role="link">About Us</a>
17
17
 
18
18
  <!-- Buttons without role -->
19
- <button onclick="submit()">Submit Form</button>
19
+ <button onclick="submit()" role="button" role="button" role="button">Submit Form</button>
20
20
  <button type="button">Regular Button</button>
21
21
 
22
22
  <!-- Clickable elements -->
23
- <div onclick="navigate()" class="btn">Click Me</div>
24
- <span onclick="toggle()">Toggle</span>
23
+ <div onclick="navigate()" class="btn" role="button" role="button" role="button" role="button" role="button" role="button">Click Me</div>
24
+ <span onclick="toggle()" role="button" role="button" role="button">Toggle</span>
25
25
 
26
26
  <!-- Navigation -->
27
27
  <nav>
28
- <ul class="nav-menu">
29
- <li class="nav-item"><a href="/products">Products</a></li>
30
- <li class="nav-item"><a href="/services">Services</a></li>
28
+ <ul class="nav-menu" role="menubar" role="menubar" role="menubar">
29
+ <li class="nav-item"><a href="/products" role="link" role="link" role="link">Products</a></li>
30
+ <li class="nav-item"><a href="/services" role="link" role="link" role="link">Services</a></li>
31
31
  </ul>
32
32
  </nav>
33
33
 
@@ -36,7 +36,7 @@
36
36
  <article>
37
37
  <h2>Article Title</h2>
38
38
  <p>Some content here...</p>
39
- <img src="article-image.jpg" alt="">
39
+ <img src="article-image.jpg" alt="Article Title" role="img" role="img" role="img">
40
40
  </article>
41
41
  </main>
42
42
 
package/lib/fixer.js CHANGED
@@ -645,73 +645,105 @@ class AccessibilityFixer {
645
645
 
646
646
  // Fix all images - add role="img" (only if no role exists)
647
647
  fixed = fixed.replace(
648
- /<img([^>]*)(?!.*role\s*=)([^>]*>)/gi,
649
- (match, attrs, end) => {
648
+ /<img([^>]*>)/gi,
649
+ (match, fullTag) => {
650
+ // Check if role attribute already exists
651
+ if (/role\s*=/i.test(match)) {
652
+ return match; // Return unchanged if role already exists
653
+ }
650
654
  console.log(chalk.yellow(` 🖼️ Added role="img" to image element`));
651
- return `<img${attrs} role="img"${end}`;
655
+ return match.replace(/(<img[^>]*?)(\s*>)/i, '$1 role="img"$2');
652
656
  }
653
657
  );
654
658
 
655
659
  // Fix button elements with onclick - add role="button"
656
660
  fixed = fixed.replace(
657
- /<button([^>]*onclick[^>]*)(?!.*role\s*=)([^>]*>)/gi,
658
- (match, attrs, end) => {
661
+ /<button([^>]*onclick[^>]*>)/gi,
662
+ (match) => {
663
+ // Check if role attribute already exists
664
+ if (/role\s*=/i.test(match)) {
665
+ return match; // Return unchanged if role already exists
666
+ }
659
667
  console.log(chalk.yellow(` 🔘 Added role="button" to button with onclick`));
660
- return `<button${attrs} role="button"${end}`;
668
+ return match.replace(/(<button[^>]*?)(\s*>)/i, '$1 role="button"$2');
661
669
  }
662
670
  );
663
671
 
664
672
  // Fix anchor elements - add role="link"
665
673
  fixed = fixed.replace(
666
- /<a([^>]*href[^>]*)(?!.*role\s*=)([^>]*>)/gi,
667
- (match, attrs, end) => {
674
+ /<a([^>]*href[^>]*>)/gi,
675
+ (match) => {
676
+ // Check if role attribute already exists
677
+ if (/role\s*=/i.test(match)) {
678
+ return match; // Return unchanged if role already exists
679
+ }
668
680
  console.log(chalk.yellow(` 🔗 Added role="link" to anchor element`));
669
- return `<a${attrs} role="link"${end}`;
681
+ return match.replace(/(<a[^>]*?)(\s*>)/i, '$1 role="link"$2');
670
682
  }
671
683
  );
672
684
 
673
685
  // Fix any element with onclick (except a and button) - add role="button"
674
686
  fixed = fixed.replace(
675
- /<((?!a|button)[a-zA-Z][a-zA-Z0-9]*)([^>]*onclick[^>]*)(?!.*role\s*=)([^>]*>)/gi,
676
- (match, tag, attrs, end) => {
687
+ /<((?!a|button)[a-zA-Z][a-zA-Z0-9]*)([^>]*onclick[^>]*>)/gi,
688
+ (match, tag) => {
689
+ // Check if role attribute already exists
690
+ if (/role\s*=/i.test(match)) {
691
+ return match; // Return unchanged if role already exists
692
+ }
677
693
  console.log(chalk.yellow(` 🔘 Added role="button" to ${tag} with onclick`));
678
- return `<${tag}${attrs} role="button"${end}`;
694
+ return match.replace(/(<[^>]*?)(\s*>)/i, '$1 role="button"$2');
679
695
  }
680
696
  );
681
697
 
682
698
  // Fix clickable divs - add role="button"
683
699
  fixed = fixed.replace(
684
- /<div([^>]*class="[^"]*(?:btn|button|click)[^"]*"[^>]*)(?!.*role\s*=)([^>]*>)/gi,
685
- (match, attrs, end) => {
700
+ /<div([^>]*class="[^"]*(?:btn|button|click)[^"]*"[^>]*>)/gi,
701
+ (match) => {
702
+ // Check if role attribute already exists
703
+ if (/role\s*=/i.test(match)) {
704
+ return match; // Return unchanged if role already exists
705
+ }
686
706
  console.log(chalk.yellow(` 🔘 Added role="button" to clickable div`));
687
- return `<div${attrs} role="button"${end}`;
707
+ return match.replace(/(<div[^>]*?)(\s*>)/i, '$1 role="button"$2');
688
708
  }
689
709
  );
690
710
 
691
711
  // Fix focusable elements with tabindex
692
712
  fixed = fixed.replace(
693
- /<(div|span)([^>]*tabindex\s*=\s*[""']?[0-9-]+[""']?[^>]*)(?!.*role\s*=)([^>]*>)/gi,
694
- (match, tag, attrs, end) => {
713
+ /<(div|span)([^>]*tabindex\s*=\s*[""']?[0-9-]+[""']?[^>]*>)/gi,
714
+ (match, tag) => {
715
+ // Check if role attribute already exists
716
+ if (/role\s*=/i.test(match)) {
717
+ return match; // Return unchanged if role already exists
718
+ }
695
719
  console.log(chalk.yellow(` ⌨️ Added role="button" to focusable ${tag}`));
696
- return `<${tag}${attrs} role="button"${end}`;
720
+ return match.replace(/(<[^>]*?)(\s*>)/i, '$1 role="button"$2');
697
721
  }
698
722
  );
699
723
 
700
724
  // Fix navigation lists that should be menus
701
725
  fixed = fixed.replace(
702
- /<ul([^>]*class="[^"]*(?:nav|menu)[^"]*"[^>]*)(?!.*role\s*=)([^>]*>)/gi,
703
- (match, attrs, end) => {
726
+ /<ul([^>]*class="[^"]*(?:nav|menu)[^"]*"[^>]*>)/gi,
727
+ (match) => {
728
+ // Check if role attribute already exists
729
+ if (/role\s*=/i.test(match)) {
730
+ return match; // Return unchanged if role already exists
731
+ }
704
732
  console.log(chalk.yellow(` 📋 Added role="menubar" to navigation list`));
705
- return `<ul${attrs} role="menubar"${end}`;
733
+ return match.replace(/(<ul[^>]*?)(\s*>)/i, '$1 role="menubar"$2');
706
734
  }
707
735
  );
708
736
 
709
737
  // Fix list items in navigation menus
710
738
  fixed = fixed.replace(
711
- /<li([^>]*class="[^"]*(?:nav|menu)[^"]*"[^>]*)(?!.*role\s*=)([^>]*>)/gi,
712
- (match, attrs, end) => {
739
+ /<li([^>]*class="[^"]*(?:nav|menu)[^"]*"[^>]*>)/gi,
740
+ (match) => {
741
+ // Check if role attribute already exists
742
+ if (/role\s*=/i.test(match)) {
743
+ return match; // Return unchanged if role already exists
744
+ }
713
745
  console.log(chalk.yellow(` 📋 Added role="menuitem" to navigation list item`));
714
- return `<li${attrs} role="menuitem"${end}`;
746
+ return match.replace(/(<li[^>]*?)(\s*>)/i, '$1 role="menuitem"$2');
715
747
  }
716
748
  );
717
749
 
@@ -739,9 +771,153 @@ class AccessibilityFixer {
739
771
  return candidates;
740
772
  }
741
773
 
774
+ async cleanupDuplicateRoles(directory = '.') {
775
+ console.log(chalk.blue('🧹 Cleaning up duplicate role attributes...'));
776
+
777
+ const htmlFiles = await this.findHtmlFiles(directory);
778
+ const results = [];
779
+ let totalFixedFiles = 0;
780
+
781
+ for (const file of htmlFiles) {
782
+ try {
783
+ const content = await fs.readFile(file, 'utf8');
784
+ const fixed = this.cleanupDuplicateRolesInContent(content);
785
+
786
+ if (fixed !== content) {
787
+ if (this.config.backupFiles) {
788
+ await fs.writeFile(`${file}.backup`, content);
789
+ }
790
+
791
+ if (!this.config.dryRun) {
792
+ await fs.writeFile(file, fixed);
793
+ }
794
+
795
+ console.log(chalk.green(`✅ Cleaned duplicate roles in: ${file}`));
796
+ results.push({ file, status: 'fixed' });
797
+ totalFixedFiles++;
798
+ } else {
799
+ results.push({ file, status: 'no-change' });
800
+ }
801
+ } catch (error) {
802
+ console.error(chalk.red(`❌ Error processing ${file}: ${error.message}`));
803
+ results.push({ file, status: 'error', error: error.message });
804
+ }
805
+ }
806
+
807
+ console.log(chalk.blue(`\n📊 Summary: Cleaned duplicate roles in ${totalFixedFiles} files`));
808
+ return results;
809
+ }
810
+
811
+ cleanupDuplicateRolesInContent(content) {
812
+ let fixed = content;
813
+ let changesMade = false;
814
+
815
+ // Pattern to match duplicate role attributes
816
+ // Matches: role="value" role="value" or role="value" role="value" role="value" etc.
817
+ const duplicateRolePattern = /(\s+role\s*=\s*["']([^"']+)["'])(\s+role\s*=\s*["']\2["'])+/gi;
818
+
819
+ fixed = fixed.replace(duplicateRolePattern, (match, firstRole, roleValue) => {
820
+ changesMade = true;
821
+ console.log(chalk.yellow(` 🧹 Removed duplicate role="${roleValue}" attributes`));
822
+ return firstRole; // Keep only the first occurrence
823
+ });
824
+
825
+ // Also handle cases where roles have different quotes but same value
826
+ // e.g., role="button" role='button'
827
+ const mixedQuotePattern = /(\s+role\s*=\s*["']([^"']+)["'])(\s+role\s*=\s*['"]?\2['"]?)+/gi;
828
+
829
+ fixed = fixed.replace(mixedQuotePattern, (match, firstRole, roleValue) => {
830
+ if (!changesMade) { // Only log if not already logged above
831
+ changesMade = true;
832
+ console.log(chalk.yellow(` 🧹 Removed duplicate role="${roleValue}" attributes (mixed quotes)`));
833
+ }
834
+ return firstRole;
835
+ });
836
+
837
+ return fixed;
838
+ }
839
+
840
+ async fixAllAccessibilityIssues(directory = '.') {
841
+ console.log(chalk.blue('🚀 Starting comprehensive accessibility fixes...'));
842
+
843
+ const results = {
844
+ lang: [],
845
+ alt: [],
846
+ roles: [],
847
+ cleanup: []
848
+ };
849
+
850
+ try {
851
+ // Step 1: Fix lang attributes
852
+ console.log(chalk.yellow('\n📝 Step 1: HTML lang attributes...'));
853
+ results.lang = await this.fixHtmlLang(directory);
854
+
855
+ // Step 2: Fix alt attributes
856
+ console.log(chalk.yellow('\n🖼️ Step 2: Alt attributes...'));
857
+ results.alt = await this.fixEmptyAltAttributes(directory);
858
+
859
+ // Step 3: Fix role attributes
860
+ console.log(chalk.yellow('\n🎭 Step 3: Role attributes...'));
861
+ results.roles = await this.fixRoleAttributes(directory);
862
+
863
+ // Step 4: Cleanup duplicate roles
864
+ console.log(chalk.yellow('\n🧹 Step 4: Cleanup duplicate roles...'));
865
+ results.cleanup = await this.cleanupDuplicateRoles(directory);
866
+
867
+ // Summary
868
+ const totalFiles = new Set([
869
+ ...results.lang.map(r => r.file),
870
+ ...results.alt.map(r => r.file),
871
+ ...results.roles.map(r => r.file),
872
+ ...results.cleanup.map(r => r.file)
873
+ ]).size;
874
+
875
+ const totalFixed = new Set([
876
+ ...results.lang.filter(r => r.status === 'fixed').map(r => r.file),
877
+ ...results.alt.filter(r => r.status === 'fixed').map(r => r.file),
878
+ ...results.roles.filter(r => r.status === 'fixed').map(r => r.file),
879
+ ...results.cleanup.filter(r => r.status === 'fixed').map(r => r.file)
880
+ ]).size;
881
+
882
+ const totalIssues =
883
+ results.lang.filter(r => r.status === 'fixed').length +
884
+ results.alt.reduce((sum, r) => sum + (r.issues || 0), 0) +
885
+ results.roles.reduce((sum, r) => sum + (r.issues || 0), 0) +
886
+ results.cleanup.filter(r => r.status === 'fixed').length;
887
+
888
+ console.log(chalk.green('\n🎉 All accessibility fixes completed!'));
889
+ console.log(chalk.blue('📊 Final Summary:'));
890
+ console.log(chalk.white(` Total files scanned: ${totalFiles}`));
891
+ console.log(chalk.green(` Files fixed: ${totalFixed}`));
892
+ console.log(chalk.yellow(` Total issues resolved: ${totalIssues}`));
893
+
894
+ if (this.config.dryRun) {
895
+ console.log(chalk.cyan('\n💡 This was a dry run. Use without --dry-run to apply changes.'));
896
+ }
897
+
898
+ return results;
899
+
900
+ } catch (error) {
901
+ console.error(chalk.red('❌ Error during comprehensive fix:'), error.message);
902
+ throw error;
903
+ }
904
+ }
905
+
742
906
  async findHtmlFiles(directory) {
743
907
  const files = [];
744
908
 
909
+ // Check if the path is a file or directory
910
+ const stat = await fs.stat(directory);
911
+
912
+ if (stat.isFile()) {
913
+ // If it's a file, check if it's HTML
914
+ if (directory.endsWith('.html')) {
915
+ files.push(directory);
916
+ }
917
+ return files;
918
+ }
919
+
920
+ // If it's a directory, scan recursively
745
921
  async function scan(dir) {
746
922
  const entries = await fs.readdir(dir, { withFileTypes: true });
747
923
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbu-accessibility-package",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Automated accessibility fixes for HTML files - Alt attributes, Lang attributes, Role attributes. Smart context-aware alt text generation and comprehensive role attribute management.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -11,9 +11,9 @@
11
11
  "start": "node cli.js",
12
12
  "fix": "node cli.js",
13
13
  "preview": "node cli.js --dry-run",
14
- "test": "node example.js",
14
+ "test": "node test-package.js",
15
15
  "demo": "node cli.js --dry-run demo",
16
- "prepublishOnly": "npm run demo"
16
+ "prepublishOnly": "npm run test"
17
17
  },
18
18
  "keywords": [
19
19
  "accessibility",