- Add 26 documentation files including test reports, deployment guides, and troubleshooting documentation - Include 3 CSV data files for trainer imports and user registration tracking - Add 43 JavaScript test files covering mobile optimization, Safari compatibility, and E2E testing - Include 18 PHP utility files for debugging, geocoding, and data analysis - Add 12 shell scripts for deployment verification, user management, and database operations - Update .gitignore with whitelist patterns for development files, documentation, and CSV data 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
355 lines
No EOL
13 KiB
JavaScript
355 lines
No EOL
13 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
class CSSAnalyzer {
|
||
constructor() {
|
||
this.issues = [];
|
||
this.warnings = [];
|
||
this.recommendations = [];
|
||
this.browserSupport = {
|
||
customProperties: { ie: false, edge: '16+', chrome: '49+', firefox: '31+', safari: '9.1+' },
|
||
gridLayout: { ie: '10+ (partial)', edge: '16+', chrome: '57+', firefox: '52+', safari: '10.1+' },
|
||
flexbox: { ie: '11+', edge: '12+', chrome: '29+', firefox: '28+', safari: '9+' },
|
||
transforms: { ie: '9+', edge: '12+', chrome: '36+', firefox: '16+', safari: '9+' },
|
||
transitions: { ie: '10+', edge: '12+', chrome: '26+', firefox: '16+', safari: '6.1+' },
|
||
calc: { ie: '9+', edge: '12+', chrome: '26+', firefox: '16+', safari: '7+' }
|
||
};
|
||
}
|
||
|
||
analyzeFile(filePath) {
|
||
console.log(`\n🔍 Analyzing: ${path.basename(filePath)}`);
|
||
console.log('='.repeat(50));
|
||
|
||
try {
|
||
const content = fs.readFileSync(filePath, 'utf8');
|
||
const lines = content.split('\n');
|
||
|
||
this.checkCustomProperties(content, filePath);
|
||
this.checkResponsiveDesign(content, filePath);
|
||
this.checkAccessibility(content, filePath);
|
||
this.checkBrowserCompatibility(content, filePath);
|
||
this.checkPerformance(content, filePath);
|
||
this.checkCodeQuality(content, filePath);
|
||
|
||
} catch (error) {
|
||
console.error(`❌ Error reading ${filePath}:`, error.message);
|
||
}
|
||
}
|
||
|
||
checkCustomProperties(content, filePath) {
|
||
const customProps = content.match(/--[\w-]+:/g);
|
||
const useCustomProps = content.match(/var\(--[\w-]+\)/g);
|
||
|
||
if (customProps) {
|
||
console.log(`✅ CSS Custom Properties: ${customProps.length} defined`);
|
||
|
||
// Check for fallbacks
|
||
const withFallbacks = content.match(/var\(--[\w-]+,\s*[^)]+\)/g);
|
||
if (withFallbacks) {
|
||
console.log(`✅ Fallbacks provided: ${withFallbacks.length}`);
|
||
} else if (useCustomProps) {
|
||
this.warnings.push({
|
||
file: filePath,
|
||
issue: 'CSS Custom Properties used without fallbacks',
|
||
impact: 'Will break in IE and older browsers',
|
||
recommendation: 'Add fallback values: var(--color-primary, #0073aa)'
|
||
});
|
||
console.log(`⚠️ No fallbacks for custom properties (${useCustomProps.length} uses)`);
|
||
}
|
||
}
|
||
}
|
||
|
||
checkResponsiveDesign(content, filePath) {
|
||
const mediaQueries = content.match(/@media[^{]*{/g);
|
||
const breakpoints = content.match(/\(max-width:\s*(\d+(?:\.\d+)?)(px|em|rem)\)/g);
|
||
|
||
if (mediaQueries) {
|
||
console.log(`✅ Media Queries: ${mediaQueries.length} found`);
|
||
|
||
if (breakpoints) {
|
||
const uniqueBreakpoints = [...new Set(breakpoints)];
|
||
console.log(`📱 Breakpoints: ${uniqueBreakpoints.join(', ')}`);
|
||
|
||
// Check for common breakpoints
|
||
const commonBreakpoints = ['768px', '480px', '1024px', '767px'];
|
||
const hasStandardBreakpoints = commonBreakpoints.some(bp =>
|
||
content.includes(bp)
|
||
);
|
||
|
||
if (hasStandardBreakpoints) {
|
||
console.log(`✅ Uses standard breakpoints`);
|
||
} else {
|
||
this.recommendations.push({
|
||
file: filePath,
|
||
recommendation: 'Consider using standard breakpoints (768px, 1024px) for better consistency'
|
||
});
|
||
}
|
||
}
|
||
} else {
|
||
this.warnings.push({
|
||
file: filePath,
|
||
issue: 'No responsive design detected',
|
||
impact: 'May not work well on mobile devices'
|
||
});
|
||
console.log(`⚠️ No media queries found`);
|
||
}
|
||
}
|
||
|
||
checkAccessibility(content, filePath) {
|
||
const focusStyles = content.match(/:focus[^{]*{/g);
|
||
const skipLinks = content.includes('skip-link');
|
||
const highContrastSupport = content.includes('prefers-color-scheme') ||
|
||
content.includes('high-contrast');
|
||
|
||
if (focusStyles) {
|
||
console.log(`✅ Focus Styles: ${focusStyles.length} found`);
|
||
} else {
|
||
this.issues.push({
|
||
file: filePath,
|
||
issue: 'No focus styles defined',
|
||
impact: 'Poor keyboard navigation accessibility',
|
||
severity: 'high'
|
||
});
|
||
console.log(`❌ No focus styles found`);
|
||
}
|
||
|
||
if (skipLinks) {
|
||
console.log(`✅ Skip links implemented`);
|
||
}
|
||
|
||
if (highContrastSupport) {
|
||
console.log(`✅ High contrast support detected`);
|
||
}
|
||
|
||
// Check for sufficient color contrast (basic check)
|
||
const colorValues = content.match(/#[0-9a-fA-F]{3,6}/g);
|
||
if (colorValues) {
|
||
console.log(`🎨 Color values: ${[...new Set(colorValues)].length} unique colors`);
|
||
}
|
||
}
|
||
|
||
checkBrowserCompatibility(content, filePath) {
|
||
const features = {
|
||
'grid': /display:\s*grid/g,
|
||
'flexbox': /display:\s*flex/g,
|
||
'transforms': /transform:/g,
|
||
'transitions': /transition:/g,
|
||
'calc': /calc\(/g,
|
||
'customProperties': /var\(/g
|
||
};
|
||
|
||
console.log(`🌐 Browser Compatibility Check:`);
|
||
|
||
Object.entries(features).forEach(([feature, regex]) => {
|
||
const matches = content.match(regex);
|
||
if (matches) {
|
||
const support = this.browserSupport[feature];
|
||
console.log(` ${feature}: ${matches.length} uses - ${JSON.stringify(support)}`);
|
||
|
||
if (feature === 'customProperties' && !content.includes('var(--')) {
|
||
// This is a false positive, skip
|
||
return;
|
||
}
|
||
|
||
if (support.ie === false || support.ie.includes('partial')) {
|
||
this.warnings.push({
|
||
file: filePath,
|
||
issue: `${feature} has limited IE support`,
|
||
recommendation: 'Consider progressive enhancement or fallbacks'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
checkPerformance(content, filePath) {
|
||
const selectors = content.match(/[^{}]+{/g);
|
||
const universalSelectors = content.match(/\*[^{]*/g);
|
||
const deepSelectors = content.match(/[^{]+>\s*[^{]+>\s*[^{]+{/g);
|
||
|
||
if (selectors) {
|
||
console.log(`⚡ Performance Analysis:`);
|
||
console.log(` Total selectors: ${selectors.length}`);
|
||
|
||
if (universalSelectors) {
|
||
console.log(` ⚠️ Universal selectors: ${universalSelectors.length}`);
|
||
this.warnings.push({
|
||
file: filePath,
|
||
issue: 'Universal selectors detected',
|
||
impact: 'May impact performance',
|
||
recommendation: 'Use more specific selectors when possible'
|
||
});
|
||
}
|
||
|
||
if (deepSelectors) {
|
||
console.log(` Deep selectors (3+ levels): ${deepSelectors.length}`);
|
||
if (deepSelectors.length > 10) {
|
||
this.recommendations.push({
|
||
file: filePath,
|
||
recommendation: 'Consider reducing selector depth for better performance'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Check file size
|
||
const sizeKB = Math.round(content.length / 1024);
|
||
console.log(` File size: ${sizeKB}KB`);
|
||
|
||
if (sizeKB > 50) {
|
||
this.recommendations.push({
|
||
file: filePath,
|
||
recommendation: 'Consider minification and compression for production'
|
||
});
|
||
}
|
||
}
|
||
|
||
checkCodeQuality(content, filePath) {
|
||
const duplicateProperties = this.findDuplicateProperties(content);
|
||
const unusedVariables = this.findUnusedVariables(content);
|
||
const inconsistentNaming = this.checkNamingConsistency(content);
|
||
|
||
console.log(`🧹 Code Quality:`);
|
||
|
||
if (duplicateProperties.length > 0) {
|
||
console.log(` ⚠️ Duplicate properties: ${duplicateProperties.length}`);
|
||
this.issues.push({
|
||
file: filePath,
|
||
issue: 'Duplicate CSS properties found',
|
||
details: duplicateProperties
|
||
});
|
||
} else {
|
||
console.log(` ✅ No duplicate properties`);
|
||
}
|
||
|
||
if (unusedVariables.length > 0) {
|
||
console.log(` ⚠️ Potentially unused variables: ${unusedVariables.length}`);
|
||
}
|
||
|
||
// Check for vendor prefixes
|
||
const vendorPrefixes = content.match(/-webkit-|-moz-|-ms-|-o-/g);
|
||
if (vendorPrefixes) {
|
||
console.log(` 🔧 Vendor prefixes: ${vendorPrefixes.length}`);
|
||
}
|
||
}
|
||
|
||
findDuplicateProperties(content) {
|
||
// This is a simplified check - in reality, we'd need a CSS parser
|
||
const rules = content.match(/{[^}]+}/g) || [];
|
||
const duplicates = [];
|
||
|
||
rules.forEach(rule => {
|
||
const properties = rule.match(/[\w-]+\s*:/g) || [];
|
||
const propertyNames = properties.map(p => p.replace(/\s*:$/, ''));
|
||
const seen = new Set();
|
||
|
||
propertyNames.forEach(prop => {
|
||
if (seen.has(prop)) {
|
||
duplicates.push(prop);
|
||
}
|
||
seen.add(prop);
|
||
});
|
||
});
|
||
|
||
return [...new Set(duplicates)];
|
||
}
|
||
|
||
findUnusedVariables(content) {
|
||
const defined = content.match(/--[\w-]+/g) || [];
|
||
const used = content.match(/var\(--[\w-]+/g) || [];
|
||
|
||
const definedClean = defined.map(v => v.replace('--', ''));
|
||
const usedClean = used.map(v => v.replace('var(--', ''));
|
||
|
||
return definedClean.filter(v => !usedClean.includes(v));
|
||
}
|
||
|
||
checkNamingConsistency(content) {
|
||
const variables = content.match(/--[\w-]+/g) || [];
|
||
const classes = content.match(/\.[\w-]+/g) || [];
|
||
|
||
// Check for consistent naming patterns
|
||
const kebabCase = /^[a-z][a-z0-9-]*$/;
|
||
const camelCase = /^[a-z][a-zA-Z0-9]*$/;
|
||
|
||
const inconsistent = [];
|
||
[...variables, ...classes].forEach(name => {
|
||
const clean = name.replace(/^(--|\.)/g, '');
|
||
if (!kebabCase.test(clean) && !camelCase.test(clean)) {
|
||
inconsistent.push(name);
|
||
}
|
||
});
|
||
|
||
return inconsistent;
|
||
}
|
||
|
||
generateReport() {
|
||
console.log('\n' + '='.repeat(80));
|
||
console.log('📋 COMPREHENSIVE CSS ANALYSIS REPORT');
|
||
console.log('='.repeat(80));
|
||
|
||
if (this.issues.length > 0) {
|
||
console.log('\n❌ CRITICAL ISSUES:');
|
||
this.issues.forEach((issue, i) => {
|
||
console.log(`${i + 1}. ${issue.issue}`);
|
||
console.log(` File: ${path.basename(issue.file)}`);
|
||
if (issue.impact) console.log(` Impact: ${issue.impact}`);
|
||
if (issue.recommendation) console.log(` Fix: ${issue.recommendation}`);
|
||
console.log('');
|
||
});
|
||
}
|
||
|
||
if (this.warnings.length > 0) {
|
||
console.log('\n⚠️ WARNINGS:');
|
||
this.warnings.forEach((warning, i) => {
|
||
console.log(`${i + 1}. ${warning.issue}`);
|
||
console.log(` File: ${path.basename(warning.file)}`);
|
||
if (warning.impact) console.log(` Impact: ${warning.impact}`);
|
||
if (warning.recommendation) console.log(` Recommendation: ${warning.recommendation}`);
|
||
console.log('');
|
||
});
|
||
}
|
||
|
||
if (this.recommendations.length > 0) {
|
||
console.log('\n💡 RECOMMENDATIONS:');
|
||
this.recommendations.forEach((rec, i) => {
|
||
console.log(`${i + 1}. ${rec.recommendation}`);
|
||
console.log(` File: ${path.basename(rec.file)}`);
|
||
console.log('');
|
||
});
|
||
}
|
||
|
||
console.log('\n✅ SUMMARY:');
|
||
console.log(`Total Issues: ${this.issues.length}`);
|
||
console.log(`Total Warnings: ${this.warnings.length}`);
|
||
console.log(`Total Recommendations: ${this.recommendations.length}`);
|
||
}
|
||
}
|
||
|
||
// Main execution
|
||
const analyzer = new CSSAnalyzer();
|
||
const cssFiles = [
|
||
'./assets/css/hvac-common.css',
|
||
'./assets/css/hvac-dashboard.css',
|
||
'./assets/css/hvac-registration.css',
|
||
'./assets/css/hvac-certificates.css',
|
||
'./assets/css/hvac-email-attendees.css',
|
||
'./assets/css/hvac-event-summary.css',
|
||
'./assets/css/hvac-attendee-profile.css',
|
||
'./assets/css/hvac-mobile-nav.css',
|
||
'./assets/css/hvac-animations.css',
|
||
'./assets/css/hvac-print.css'
|
||
];
|
||
|
||
console.log('🔬 HVAC COMMUNITY EVENTS - CSS DEEP ANALYSIS');
|
||
console.log('Starting comprehensive CSS investigation...\n');
|
||
|
||
cssFiles.forEach(file => {
|
||
if (fs.existsSync(file)) {
|
||
analyzer.analyzeFile(file);
|
||
} else {
|
||
console.log(`⚠️ File not found: ${file}`);
|
||
}
|
||
});
|
||
|
||
analyzer.generateReport(); |