gbu-accessibility-package
Version:
Comprehensive accessibility fixes for HTML files. Smart context-aware alt text generation, form labels, button names, link names, landmarks, heading analysis, and WCAG-compliant role attributes. Covers major axe DevTools issues with individual fix modes.
444 lines (380 loc) โข 19.2 kB
JavaScript
/**
* Accessibility Fixer CLI
* Command line interface for the accessibility fixer tool
*/
const AccessibilityFixer = require('./lib/fixer.js');
const chalk = require('chalk');
const path = require('path');
// Parse command line arguments
const args = process.argv.slice(2);
const options = {
directory: '.',
language: 'ja',
backupFiles: false, // Default to false for faster processing
dryRun: false,
help: false,
cleanupOnly: false,
comprehensive: false, // Keep for backward compatibility
altOnly: false,
langOnly: false,
roleOnly: false,
formsOnly: false,
nestedOnly: false,
buttonsOnly: false,
linksOnly: false,
landmarksOnly: false,
headingsOnly: false,
brokenLinksOnly: false,
// Enhanced alt options
enhancedAlt: false,
altCreativity: 'balanced', // conservative, balanced, creative
includeEmotions: false,
strictAltChecking: false
};
// Parse arguments
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '--help':
case '-h':
options.help = true;
break;
case '--directory':
case '-d':
options.directory = args[++i];
break;
case '--language':
case '-l':
options.language = args[++i];
break;
case '--backup':
options.backupFiles = true;
break;
case '--no-backup':
options.backupFiles = false;
break;
case '--dry-run':
options.dryRun = true;
break;
case '--cleanup-only':
options.cleanupOnly = true;
break;
case '--comprehensive':
case '--all':
options.comprehensive = true; // Keep for backward compatibility
break;
case '--alt-only':
options.altOnly = true;
break;
case '--lang-only':
options.langOnly = true;
break;
case '--role-only':
options.roleOnly = true;
break;
case '--forms-only':
options.formsOnly = true;
break;
case '--nested-only':
options.nestedOnly = true;
break;
case '--buttons-only':
options.buttonsOnly = true;
break;
case '--links-only':
options.linksOnly = true;
break;
case '--landmarks-only':
options.landmarksOnly = true;
break;
case '--headings-only':
options.headingsOnly = true;
break;
case '--links-check':
case '--broken-links':
options.brokenLinksOnly = true;
break;
case '--enhanced-alt':
options.enhancedAlt = true;
break;
case '--alt-creativity':
options.altCreativity = args[++i];
break;
case '--include-emotions':
options.includeEmotions = true;
break;
case '--strict-alt':
options.strictAltChecking = true;
break;
default:
if (!arg.startsWith('-')) {
options.directory = arg;
}
}
}
// Show help
if (options.help) {
console.log(chalk.blue(`
๐ง Accessibility Fixer CLI
Usage: node cli.js [options] [directory]
Options:
-d, --directory <path> Target directory (default: current directory)
-l, --language <lang> Language for lang attribute (default: ja)
--backup Create backup files
--no-backup Don't create backup files (default)
--dry-run Preview changes without applying
--comprehensive, --all Run comprehensive fixes (same as default)
--cleanup-only Only cleanup duplicate role attributes
--alt-only Fix alt attributes + cleanup
--lang-only Fix HTML lang attributes + cleanup
--role-only Fix role attributes + cleanup
--forms-only Fix form labels + cleanup
--buttons-only Fix button names + cleanup
--links-only Fix link names + cleanup
--landmarks-only Fix landmarks + cleanup
--headings-only Analyze heading structure (no auto-fix)
--links-check Check for broken links and 404 resources (no auto-fix)
--enhanced-alt Use enhanced alt attribute analysis and generation
--alt-creativity <mode> Alt text creativity: conservative, balanced, creative (default: balanced)
--include-emotions Include emotional descriptors in alt text
--strict-alt Enable strict alt attribute quality checking
-h, --help Show this help message
Enhanced Alt Features:
--enhanced-alt Comprehensive alt attribute analysis with:
โข Image type classification (decorative, functional, complex, etc.)
โข Content quality checking (length, redundancy, generic text)
โข Context-aware alt text generation
โข Multi-language vocabulary support
โข Brand and emotional context integration
โข Technical image description (charts, graphs)
Alt Creativity Modes:
conservative Simple, factual descriptions
balanced Context-aware with moderate creativity (default)
creative Rich descriptions with emotions and brand context
Examples:
node cli.js # Comprehensive fixes (no backup by default)
node cli.js --comprehensive # Comprehensive fixes (same as default)
node cli.js --alt-only # Fix alt attributes + cleanup
node cli.js --forms-only # Fix form labels + cleanup
node cli.js --buttons-only # Fix button names + cleanup
node cli.js --links-only # Fix link names + cleanup
node cli.js --landmarks-only # Fix landmarks + cleanup
node cli.js --headings-only # Analyze heading structure only
node cli.js --links-check # Check for broken links and 404s
node cli.js --cleanup-only # Only cleanup duplicate roles
node cli.js ./src # Fix src directory (comprehensive)
node cli.js -l en --dry-run ./dist # Preview comprehensive fixes in English
node cli.js --backup ./public # Comprehensive fixes with backups
node cli.js --enhanced-alt # Use enhanced alt attribute analysis
node cli.js --enhanced-alt --alt-creativity creative # Creative alt text generation
node cli.js --enhanced-alt --include-emotions # Include emotional context
node cli.js --strict-alt --enhanced-alt # Strict quality checking
Features:
โ
Alt attributes for images
โ
Lang attributes for HTML
โ
Role attributes for accessibility
โ
Context-aware text generation
โ
Automatic backups
`));
process.exit(0);
}
// Helper function to show completion message with backup info
function showCompletionMessage(options, mode = 'fixes') {
if (options.dryRun) {
console.log(chalk.cyan('\n๐ก This was a dry run. Use without --dry-run to apply changes.'));
} else {
console.log(chalk.green(`\n๐ ${mode} completed successfully!`));
if (options.backupFiles) {
console.log(chalk.gray(' ๐ Backup files created with .backup extension'));
console.log(chalk.gray(' ๐ก Use --no-backup to disable backups in future runs'));
} else {
console.log(chalk.blue(' โก No backup files created (default behavior for faster processing)'));
console.log(chalk.gray(' ๐ก Use --backup to enable backups for safety'));
}
}
}
// Main function
async function main() {
console.log(chalk.blue('๐ Starting Accessibility Fixer...'));
console.log(chalk.gray(`Directory: ${path.resolve(options.directory)}`));
console.log(chalk.gray(`Language: ${options.language}`));
console.log(chalk.gray(`Backup: ${options.backupFiles ? 'Yes' : 'No'}`));
console.log(chalk.gray(`Mode: ${options.dryRun ? 'Dry Run (Preview)' : 'Apply Changes'}`));
console.log('');
const fixer = new AccessibilityFixer({
language: options.language,
backupFiles: options.backupFiles,
dryRun: options.dryRun,
enhancedAltMode: options.enhancedAlt,
altCreativity: options.altCreativity,
includeEmotions: options.includeEmotions,
strictAltChecking: options.strictAltChecking
});
try {
// Handle different modes - All modes now include cleanup
if (options.cleanupOnly || options.altOnly || options.langOnly || options.roleOnly ||
options.formsOnly || options.nestedOnly || options.buttonsOnly || options.linksOnly || options.landmarksOnly ||
options.headingsOnly || options.brokenLinksOnly) {
// Individual modes - handle each separately, then run cleanup
} else {
// Default mode: Run comprehensive fix (all fixes including cleanup)
console.log(chalk.blue('๐ฏ Running comprehensive accessibility fixes...'));
const results = await fixer.fixAllAccessibilityIssues(options.directory);
// Results already logged in the method
return;
}
// Individual modes
if (options.cleanupOnly) {
// Only cleanup duplicate roles
console.log(chalk.blue('๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\nโ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Cleanup');
return;
} else if (options.cleanupOnly) {
// Only cleanup duplicate roles
console.log(chalk.blue('๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\nโ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Cleanup');
return;
} else if (options.altOnly) {
// Fix alt attributes + cleanup
console.log(chalk.blue('๐ผ๏ธ Running alt attribute fixes + cleanup...'));
const altResults = await fixer.fixEmptyAltAttributes(options.directory);
const altFixed = altResults.filter(r => r.status === 'fixed').length;
const totalAltIssues = altResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed alt attributes in ${altFixed} files (${totalAltIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Alt attribute fixes + cleanup');
return;
} else if (options.langOnly) {
// Fix lang attributes + cleanup
console.log(chalk.blue('๐ Running HTML lang attribute fixes + cleanup...'));
const langResults = await fixer.fixHtmlLang(options.directory);
const langFixed = langResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`\nโ
Fixed lang attributes in ${langFixed} files`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Lang attribute fixes + cleanup');
return;
} else if (options.roleOnly) {
// Fix role attributes + cleanup
console.log(chalk.blue('๐ญ Running role attribute fixes + cleanup...'));
const roleResults = await fixer.fixRoleAttributes(options.directory);
const roleFixed = roleResults.filter(r => r.status === 'fixed').length;
const totalRoleIssues = roleResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed role attributes in ${roleFixed} files (${totalRoleIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Role attribute fixes + cleanup');
return;
} else if (options.formsOnly) {
// Fix form labels + cleanup
console.log(chalk.blue('๐ Running form label fixes + cleanup...'));
const formResults = await fixer.fixFormLabels(options.directory);
const formFixed = formResults.filter(r => r.status === 'fixed').length;
const totalFormIssues = formResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed form labels in ${formFixed} files (${totalFormIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Form label fixes + cleanup');
return;
} else if (options.nestedOnly) {
// Fix nested interactive controls + cleanup
console.log(chalk.blue('๐ฏ Running nested interactive controls fixes + cleanup...'));
const nestedResults = await fixer.fixNestedInteractiveControls(options.directory);
const nestedFixed = nestedResults.filter(r => r.status === 'fixed').length;
const totalNestedIssues = nestedResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed nested interactive controls in ${nestedFixed} files (${totalNestedIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Nested interactive controls fixes + cleanup');
return;
} else if (options.buttonsOnly) {
// Fix button names + cleanup
console.log(chalk.blue('๐ Running button name fixes + cleanup...'));
const buttonResults = await fixer.fixButtonNames(options.directory);
const buttonFixed = buttonResults.filter(r => r.status === 'fixed').length;
const totalButtonIssues = buttonResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed button names in ${buttonFixed} files (${totalButtonIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Button name fixes + cleanup');
return;
} else if (options.linksOnly) {
// Fix link names + cleanup
console.log(chalk.blue('๐ Running link name fixes + cleanup...'));
const linkResults = await fixer.fixLinkNames(options.directory);
const linkFixed = linkResults.filter(r => r.status === 'fixed').length;
const totalLinkIssues = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed link names in ${linkFixed} files (${totalLinkIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Link name fixes + cleanup');
return;
} else if (options.landmarksOnly) {
// Fix landmarks + cleanup
console.log(chalk.blue('๐๏ธ Running landmark fixes + cleanup...'));
const landmarkResults = await fixer.fixLandmarks(options.directory);
const landmarkFixed = landmarkResults.filter(r => r.status === 'fixed').length;
const totalLandmarkIssues = landmarkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Fixed landmarks in ${landmarkFixed} files (${totalLandmarkIssues} issues)`));
// Run cleanup
console.log(chalk.blue('\n๐งน Running cleanup for duplicate role attributes...'));
const cleanupResults = await fixer.cleanupDuplicateRoles(options.directory);
const cleanupFixed = cleanupResults.filter(r => r.status === 'fixed').length;
console.log(chalk.green(`โ
Cleaned duplicate roles in ${cleanupFixed} files`));
showCompletionMessage(options, 'Landmark fixes + cleanup');
return;
} else if (options.headingsOnly) {
// Analyze headings only (no fixes, no cleanup)
console.log(chalk.blue('๐ Running heading analysis only...'));
const headingResults = await fixer.analyzeHeadings(options.directory);
const totalSuggestions = headingResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Analyzed headings in ${headingResults.length} files (${totalSuggestions} suggestions)`));
console.log(chalk.gray('๐ก Heading issues require manual review and cannot be auto-fixed'));
showCompletionMessage(options, 'Heading analysis');
return;
} else if (options.brokenLinksOnly) {
// Check broken links only (no fixes, no cleanup)
console.log(chalk.blue('๐ Running broken links check only...'));
const linkResults = await fixer.checkBrokenLinks(options.directory);
const totalBrokenLinks = linkResults.reduce((sum, r) => sum + (r.issues || 0), 0);
console.log(chalk.green(`\nโ
Checked links in ${linkResults.length} files (${totalBrokenLinks} issues found)`));
console.log(chalk.gray('๐ก Broken link issues require manual review and cannot be auto-fixed'));
showCompletionMessage(options, 'Broken links check');
return;
}
} catch (error) {
console.error(chalk.red('โ Error occurred:'), error.message);
process.exit(1);
}
}
// Run the CLI
main();