Skip to main content
Glama
enhanced-compatibility-checker.js9.91 kB
import { CanIUseClient } from './caniuse-client.js'; import { ProjectScanner } from './project-scanner.js'; import { ConfigManager } from './config-manager.js'; export class EnhancedCompatibilityChecker { constructor(projectPath = '.') { this.client = new CanIUseClient(projectPath); this.scanner = new ProjectScanner(); this.configManager = new ConfigManager(projectPath); } async checkProjectCompatibility(projectPath, options = {}) { const config = await this.configManager.loadConfig(); const { targets = [config.defaultBaseline], scanOptions = {}, includeRecommendations = true } = options; // Scan project for features const scanResult = await this.scanner.scanDirectory(projectPath, scanOptions); if (scanResult.featuresArray.length === 0) { return { status: 'no-features-detected', message: 'No detectable CSS/JS features found in project', scannedFiles: scanResult.summary.totalFiles, suggestions: [ 'Ensure files have supported extensions (.js, .jsx, .css, etc.)', 'Check if files contain recognizable feature patterns', 'Try scanning specific files with scan_files tool' ] }; } // Check compatibility for all detected features across all targets const compatibilityResults = {}; for (const target of targets) { try { const results = await this._checkFeaturesForTarget(scanResult.featuresArray, target); const browserConfig = await this.configManager.resolveTargetVersion(target); compatibilityResults[target] = { ...results, browserInfo: browserConfig }; } catch (error) { console.warn(`Error checking target ${target}: ${error.message}`); compatibilityResults[target] = { error: true, message: error.message, browserInfo: { browser: 'unknown', version: 'unknown' } }; } } const summary = this._generateCompatibilitySummary(compatibilityResults, targets); return { projectScan: { totalFiles: scanResult.summary.totalFiles, jsFiles: scanResult.summary.jsFiles, cssFiles: scanResult.summary.cssFiles, featuresDetected: scanResult.featuresArray.length }, features: scanResult.featuresArray, featureDetails: scanResult.features, compatibility: compatibilityResults, summary, recommendations: includeRecommendations ? this._generateRecommendations(summary, scanResult) : null, nextSteps: this._generateNextSteps(summary) }; } async checkSpecificFeatures(features, options = {}) { const config = await this.configManager.loadConfig(); const { targets = [config.defaultBaseline] } = options; const results = {}; for (const target of targets) { try { results[target] = await this._checkFeaturesForTarget(features, target); } catch (error) { console.warn(`Error checking target ${target}: ${error.message}`); results[target] = { error: true, message: error.message, total: features.length, supported: 0, unsupported: features.length, errors: features.length }; } } return { features, compatibility: results, summary: this._generateCompatibilitySummary(results, targets) }; } async _checkFeaturesForTarget(features, targetString) { const results = await Promise.all( features.map(async (feature) => { try { const result = await this.client.checkFeatureSupportForTarget(feature, targetString); return { feature, supported: result.supported, status: result.type, description: result.description, source: result.source, rawValue: result.rawValue, originalSupport: result.originalSupport // For polyfilled features }; } catch (error) { return { feature, supported: false, status: 'error', description: `Could not check feature: ${error.message}`, source: 'error', error: true }; } }) ); const supported = results.filter(r => r.supported && !r.error); const unsupported = results.filter(r => !r.supported && !r.error); const errors = results.filter(r => r.error); const polyfilled = results.filter(r => r.source === 'polyfill'); const overridden = results.filter(r => r.source === 'config-override'); return { total: results.length, supported: supported.length, unsupported: unsupported.length, errors: errors.length, polyfilled: polyfilled.length, overridden: overridden.length, supportedFeatures: supported.map(r => r.feature), unsupportedFeatures: unsupported.map(r => r.feature), errorFeatures: errors.map(r => r.feature), polyfilledFeatures: polyfilled.map(r => r.feature), overriddenFeatures: overridden.map(r => r.feature), details: results }; } _generateCompatibilitySummary(compatibilityResults, targets) { const summary = { overallScore: 0, targets: {}, criticalIssues: [], commonUnsupported: [] }; let totalSupported = 0; let totalFeatures = 0; for (const target of targets) { const result = compatibilityResults[target]; if (!result) continue; const score = result.total > 0 ? Math.round((result.supported / result.total) * 100) : 100; summary.targets[target] = { score, supported: result.supported, unsupported: result.unsupported, issues: result.unsupportedFeatures }; totalSupported += result.supported; totalFeatures += result.total; // Track features unsupported in this target result.unsupportedFeatures.forEach(feature => { const existing = summary.criticalIssues.find(issue => issue.feature === feature); if (existing) { existing.targets.push(target); } else { summary.criticalIssues.push({ feature, targets: [target] }); } }); } summary.overallScore = totalFeatures > 0 ? Math.round((totalSupported / totalFeatures / targets.length) * 100) : 100; // Find features unsupported in all targets summary.commonUnsupported = summary.criticalIssues .filter(issue => issue.targets.length === targets.length) .map(issue => issue.feature); return summary; } _generateRecommendations(summary, scanResult) { const recommendations = []; // Overall score recommendations if (summary.overallScore < 70) { recommendations.push({ type: 'critical', title: 'Low Compatibility Score', message: `${summary.overallScore}% compatibility. Consider using build tools to transpile/polyfill unsupported features.`, action: 'Run get_fixes tool for specific remediation steps' }); } else if (summary.overallScore < 90) { recommendations.push({ type: 'warning', title: 'Moderate Compatibility Issues', message: `${summary.overallScore}% compatibility. Some features may need polyfills or fallbacks.`, action: 'Review unsupported features and implement fallbacks' }); } // Feature-specific recommendations const categorized = this.scanner.categorizeFeatures(summary.commonUnsupported); if (categorized.critical.length > 0) { recommendations.push({ type: 'critical', title: 'Critical Features Unsupported', message: `These essential features are unsupported: ${categorized.critical.join(', ')}`, action: 'Implement polyfills or use alternative approaches immediately' }); } if (categorized.high.length > 0) { recommendations.push({ type: 'warning', title: 'Important Features Need Attention', message: `These features need fallbacks: ${categorized.high.join(', ')}`, action: 'Add vendor prefixes and fallback implementations' }); } return recommendations; } _generateNextSteps(summary) { const steps = []; if (summary.commonUnsupported.length > 0) { steps.push({ step: 1, title: 'Get specific fixes', command: `Use get_fixes tool with features: ${summary.commonUnsupported.slice(0, 3).join(', ')}`, priority: 'high' }); } if (summary.overallScore < 80) { steps.push({ step: 2, title: 'Generate build configuration', command: 'Use generate_configs tool to create Babel/PostCSS configs with polyfills', priority: 'high' }); } steps.push({ step: steps.length + 1, title: 'Set up development workflow', command: 'Use generate_configs tool with type="workflow" for CI/CD and Git hooks', priority: 'medium' }); return steps; } async getSupportedBrowserTargets() { const targets = await this.configManager.getBrowserTargets(); return Object.keys(targets).map(key => ({ id: key, ...targets[key], description: this._getBrowserDescription(key) })); } _getBrowserDescription(targetId) { const descriptions = { 'chrome-37': 'Chrome 37 (Legacy Android support)', 'chrome-latest': 'Latest Chrome (Modern features)', 'firefox-esr': 'Firefox ESR (Enterprise support)', 'safari-12': 'Safari 12 (iOS 12+ compatibility)', 'ie-11': 'Internet Explorer 11 (Legacy Windows)', 'edge-legacy': 'Edge Legacy (Pre-Chromium Edge)' }; return descriptions[targetId] || targetId; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Amirmahdi-Kaheh/caniuse-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server