UNPKG

lwc-linter

Version:

A comprehensive CLI tool for linting Lightning Web Components v8.0.0+ with modern LWC patterns, decorators, lifecycle hooks, and Salesforce platform integration

1,536 lines • 67.6 kB
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutputFormatter = void 0;
const chalk_1 = __importDefault(require("chalk"));
const table_1 = require("table");
const html_minifier_terser_1 = require("html-minifier-terser");
class OutputFormatter {
    constructor(format = 'cli') {
        this.outputFormat = format;
    }
    async format(results) {
        switch (this.outputFormat) {
            case 'json':
                return this.formatJSON(results);
            case 'html':
                return await this.formatHTML(results);
            case 'cli':
            default:
                return this.formatCLI(results);
        }
    }
    async formatWithServer(results, port) {
        return await this.formatHTML(results, port);
    }
    formatCLI(results) {
        if (results.length === 0) {
            return chalk_1.default.green('\nāœ… No issues found! Your LWC code looks great.\n');
        }
        let output = '\n';
        let totalIssues = 0;
        let totalErrors = 0;
        let totalWarnings = 0;
        let totalFixed = 0;
        results.forEach(result => {
            if (result.issues.length > 0) {
                output += chalk_1.default.bold.underline(`\nšŸ“ ${result.filePath}\n`);
                result.issues.forEach(issue => {
                    const icon = this.getSeverityIcon(issue.severity);
                    const location = issue.line ? ` (line ${issue.line}${issue.column ? `:${issue.column}` : ''})` : '';
                    const fixableText = issue.fixable ? chalk_1.default.dim(' [fixable]') : '';
                    output += `  ${icon} ${issue.message}${location}${fixableText}\n`;
                    output += `    ${chalk_1.default.dim(`Rule: ${issue.rule} | Category: ${issue.category}`)}\n`;
                    totalIssues++;
                    if (issue.severity === 'error')
                        totalErrors++;
                    if (issue.severity === 'warn')
                        totalWarnings++;
                });
            }
            if (result.fixedCount && result.fixedCount > 0) {
                totalFixed += result.fixedCount;
            }
        });
        // Summary
        output += '\n' + chalk_1.default.bold('šŸ“Š Summary:\n');
        const summaryData = [
            ['Files processed', results.length.toString()],
            ['Total issues', totalIssues.toString()],
            ['Errors', chalk_1.default.red(totalErrors.toString())],
            ['Warnings', chalk_1.default.yellow(totalWarnings.toString())],
            ['Info', chalk_1.default.gray((totalIssues - totalErrors - totalWarnings).toString())]
        ];
        if (totalFixed > 0) {
            summaryData.push(['Fixed', chalk_1.default.green(totalFixed.toString())]);
        }
        output += (0, table_1.table)(summaryData, {
            border: {
                topBody: '',
                topJoin: '',
                topLeft: '',
                topRight: '',
                bottomBody: '',
                bottomJoin: '',
                bottomLeft: '',
                bottomRight: '',
                bodyLeft: '  ',
                bodyRight: '',
                bodyJoin: ': '
            },
            columnDefault: {
                paddingLeft: 0,
                paddingRight: 1
            },
            drawHorizontalLine: () => false
        });
        if (totalErrors > 0) {
            output += chalk_1.default.red('\nāŒ Linting completed with errors.\n');
        }
        else if (totalWarnings > 0) {
            output += chalk_1.default.yellow('\nāš ļø  Linting completed with warnings.\n');
        }
        else {
            output += chalk_1.default.green('\nāœ… Linting completed successfully.\n');
        }
        return output;
    }
    formatJSON(results) {
        const summary = this.generateSummary(results);
        return JSON.stringify({
            summary,
            results: results.map(result => ({
                filePath: result.filePath,
                issueCount: result.issues.length,
                fixedCount: result.fixedCount || 0,
                issues: result.issues
            }))
        }, null, 2);
    }
    async formatHTML(results, serverPort) {
        const isServerMode = !!serverPort;
        const timestamp = new Date().toISOString();
        const summary = this.generateSummary(results);
        let html = `<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LWC Linter Report</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { 
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            line-height: 1.6;
        }
        .container { 
            max-width: 1200px; 
            margin: 0 auto; 
            background: white; 
            min-height: 100vh;
            box-shadow: 0 0 20px rgba(0,0,0,0.1);
        }
        .header { 
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white; 
            padding: 40px 30px; 
            text-align: center;
            position: relative;
        }
        .header h1 { 
            font-size: 2.5em; 
            margin-bottom: 10px;
            text-shadow: 0 2px 4px rgba(0,0,0,0.3);
        }
        .header p { 
            opacity: 0.9; 
            font-size: 1.1em;
        }
        
        .controls { 
            padding: 30px; 
            background: #f8f9fa; 
            border-bottom: 1px solid #e9ecef;
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            align-items: center;
            justify-content: space-between;
        }
        .control-group { display: flex; gap: 10px; align-items: center; }
        .control-group label { font-weight: 500; color: #495057; }
        select, button { 
            padding: 8px 12px; 
            border: 1px solid #ced4da; 
            border-radius: 6px; 
            background: white;
            font-size: 14px;
            cursor: pointer;
        }
        button { 
            background: #007bff; 
            color: white; 
            border: none; 
            cursor: pointer;
            font-weight: 500;
            transition: background 0.2s;
        }
        button:hover { background: #0056b3; }
        button.export { background: #28a745; }
        button.export:hover { background: #1e7e34; }
        button.refresh { background: #17a2b8; }
        button.refresh:hover { background: #138496; }
        
        .summary { 
            display: grid; 
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); 
            gap: 20px; 
            padding: 30px;
            background: white;
        }
        .stat-card { 
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            padding: 25px; 
            border-radius: 12px; 
            text-align: center;
            border: 1px solid #dee2e6;
            transition: transform 0.2s, box-shadow 0.3s;
            position: relative;
            cursor: pointer;
        }
        .stat-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
        .stat-number { font-size: 2.5em; font-weight: bold; margin-bottom: 8px; transition: all 0.3s ease; }
        .stat-label { color: #6c757d; font-size: 0.9em; font-weight: 500; }
        .error { color: #dc3545; }
        .warning { color: #ffc107; }
        .info { color: #17a2b8; }
        .success { color: #28a745; }
        
        .files-container { padding: 0 30px 30px 30px; }
        .file-section { 
            margin-bottom: 25px; 
            border: 1px solid #e9ecef; 
            border-radius: 12px; 
            overflow: hidden;
            background: white;
            box-shadow: 0 2px 4px rgba(0,0,0,0.05);
            transition: box-shadow 0.3s;
        }
        .file-header { 
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            padding: 20px; 
            border-bottom: 1px solid #e9ecef; 
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
            transition: background 0.2s;
        }
        .file-header:hover { background: linear-gradient(135deg, #e9ecef 0%, #dee2e6 100%); }
        .file-header h3 { margin: 0; font-size: 1.1em; color: #495057; }
        .file-stats { display: flex; gap: 15px; font-size: 0.9em; }
        .expand-icon { 
            font-size: 1.2em; 
            transition: transform 0.3s;
            color: #6c757d;
        }
        .expanded .expand-icon { transform: rotate(180deg); }
        
        .issues-container { 
            max-height: 0; 
            overflow: hidden; 
            transition: max-height 0.3s ease-out;
        }
        .expanded .issues-container { max-height: 2000px; }
        
        .issue { 
            padding: 20px; 
            border-bottom: 1px solid #f8f9fa;
            transition: background 0.2s;
        }
        .issue:hover { background: #f8f9fa; }
        .issue:last-child { border-bottom: none; }
        .issue-header { display: flex; justify-content: between; align-items: flex-start; gap: 15px; }
        .issue-severity { 
            padding: 4px 8px; 
            border-radius: 12px; 
            font-size: 0.75em; 
            font-weight: bold; 
            text-transform: uppercase;
            min-width: 60px;
            text-align: center;
        }
        .severity-error { background: #f8d7da; color: #721c24; }
        .severity-warn { background: #fff3cd; color: #856404; }
        .severity-info { background: #d1ecf1; color: #0c5460; }
        .issue-content { flex: 1; }
        .issue-message { 
            font-weight: 500; 
            margin-bottom: 8px; 
            color: #495057;
            font-size: 1.05em;
        }
        .issue-meta { 
            font-size: 0.85em; 
            color: #6c757d; 
            margin-bottom: 15px;
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
        }
        .meta-item { 
            background: #f8f9fa; 
            padding: 4px 8px; 
            border-radius: 6px;
            border: 1px solid #e9ecef;
        }
        .fixable-tag { 
            background: #d4edda; 
            color: #155724; 
            font-weight: 500;
        }
        
        .code-snippet { 
            margin-top: 15px;
            border-radius: 8px;
            overflow: hidden;
            background: #f8f9fa;
            border: 1px solid #e9ecef;
        }
        .snippet-header { 
            background: #e9ecef; 
            padding: 10px 15px; 
            font-weight: 500; 
            color: #495057;
            font-size: 0.9em;
        }
        .snippet-content { 
            padding: 15px; 
            background: #2d3748; 
            color: #e2e8f0;
            font-family: 'Monaco', 'Consolas', monospace;
            font-size: 0.85em;
            line-height: 1.5;
            overflow-x: auto;
        }
        .code-before { color: #fed7d7; }
        .code-after { color: #c6f6d5; }
        .code-line-number { color: #a0aec0; margin-right: 15px; }
        
        .no-issues { 
            text-align: center; 
            padding: 60px 20px;
            color: #28a745; 
            font-size: 1.3em;
            font-weight: 500;
        }
        .no-issues-icon { font-size: 3em; margin-bottom: 20px; }
        
        .hidden { display: none !important; }
        
        .toast {
            position: fixed;
            top: 20px;
            right: 20px;
            background: #28a745;
            color: white;
            padding: 15px 20px;
            border-radius: 8px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.15);
            z-index: 1000;
            opacity: 0;
            transform: translateX(100%);
            transition: all 0.3s ease;
            max-width: 350px;
            font-weight: 500;
        }
        .toast.show { opacity: 1; transform: translateX(0); }
        .toast.success { background: #28a745; }
        .toast.error { background: #dc3545; }
        .toast.warning { background: #ffc107; color: #212529; }
        .toast.info { background: #17a2b8; }

        /* Loading Spinner Styles */
        .loading-spinner {
            display: inline-block;
            width: 16px;
            height: 16px;
            border: 2px solid #f3f3f3;
            border-top: 2px solid #007bff;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-right: 8px;
        }

        .loading-spinner-large {
            width: 32px;
            height: 32px;
            border-width: 3px;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }

        .btn-loading {
            position: relative;
            pointer-events: none;
            opacity: 0.7;
        }

        .btn-loading::before {
            content: "";
            position: absolute;
            left: 50%;
            top: 50%;
            width: 16px;
            height: 16px;
            margin: -8px 0 0 -8px;
            border: 2px solid #ffffff;
            border-top: 2px solid transparent;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }

        .processing-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.3);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
            visibility: hidden;
            opacity: 0;
            transition: all 0.3s ease;
        }

        .processing-overlay.show {
            visibility: visible;
            opacity: 1;
        }

        .processing-content {
            background: white;
            padding: 30px;
            border-radius: 10px;
            text-align: center;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
        }

        .processing-text {
            margin-top: 15px;
            font-size: 16px;
            color: #333;
        }
        
        @media (max-width: 768px) {
            .controls { flex-direction: column; align-items: stretch; }
            .control-group { justify-content: space-between; }
            .summary { grid-template-columns: repeat(2, 1fr); }
            .stat-number { font-size: 2em; }
            .file-header { flex-direction: column; align-items: flex-start; gap: 10px; }
            .issue-header { flex-direction: column; align-items: flex-start; }
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>šŸ” LWC Linter Report</h1>
            <p id="lastUpdated">Generated on ${new Date().toLocaleString()}</p>
        </div>
        
        <div class="controls">
            <div class="control-group">
                <label for="severityFilter">Filter by Severity:</label>
                <select id="severityFilter" onchange="filterBySeverity()">
                    <option value="all">All Issues</option>
                    <option value="error">Errors Only</option>
                    <option value="warn">Warnings Only</option>
                    <option value="info">Info Only</option>
                </select>
            </div>
            <div class="control-group">
                <label for="categoryFilter">Filter by Category:</label>
                <select id="categoryFilter" onchange="filterByCategory()">
                    <option value="all">All Categories</option>
                    <option value="code-quality">Code Quality</option>
                    <option value="accessibility">Accessibility</option>
                    <option value="performance">Performance</option>
                    <option value="security">Security</option>
                </select>
            </div>
            <div class="control-group">
                <button onclick="expandAll()">Expand All</button>
                <button onclick="collapseAll()">Collapse All</button>
            </div>
            ${isServerMode ? `
            <div class="control-group">
                <button class="refresh" onclick="manualRefresh()">šŸ”„ Refresh Now</button>
            </div>` : ''}
            <div class="control-group">
                <button class="export" onclick="exportToExcel()">šŸ“Š Export to Excel</button>
            </div>
        </div>
        
        <div class="summary" id="summaryContainer">
            <div class="stat-card" onclick="filterBySeverity('all')">
                <div class="stat-number" id="totalFiles">${summary.totalFiles}</div>
                <div class="stat-label">Files Processed</div>
            </div>
            <div class="stat-card" onclick="filterBySeverity('error')">
                <div class="stat-number error" id="totalErrors">${summary.totalErrors}</div>
                <div class="stat-label">Errors</div>
            </div>
            <div class="stat-card" onclick="filterBySeverity('warn')">
                <div class="stat-number warning" id="totalWarnings">${summary.totalWarnings}</div>
                <div class="stat-label">Warnings</div>
            </div>
            <div class="stat-card" onclick="filterBySeverity('info')">
                <div class="stat-number info" id="totalInfo">${summary.totalInfo}</div>
                <div class="stat-label">Info</div>
            </div>
        </div>
        
        <div class="files-container" id="filesContainer">`;
        if (results.length === 0) {
            html += `
            <div class="no-issues">
                <div class="no-issues-icon">āœ…</div>
                <div>No issues found! Your LWC code looks great.</div>
            </div>`;
        }
        else {
            results.forEach((result, index) => {
                if (result.issues.length > 0) {
                    const errorCount = result.issues.filter(i => i.severity === 'error').length;
                    const warnCount = result.issues.filter(i => i.severity === 'warn').length;
                    const infoCount = result.issues.filter(i => i.severity === 'info').length;
                    html += `
            <div class="file-section" data-file-index="${index}" data-file-path="${result.filePath}">
                <div class="file-header" onclick="toggleFile(${index})">
                    <h3>šŸ“ ${result.filePath}</h3>
                    <div class="file-stats">
                        <span class="error">${errorCount} errors</span>
                        <span class="warning">${warnCount} warnings</span>
                        <span class="info">${infoCount} info</span>
                        <span class="expand-icon">ā–¼</span>
                    </div>
                </div>
                <div class="issues-container">`;
                    result.issues.forEach((issue, issueIndex) => {
                        const location = issue.line ? ` (line ${issue.line}${issue.column ? `:${issue.column}` : ''})` : '';
                        const fixableClass = issue.fixable ? ' fixable-tag' : '';
                        const codeSnippet = this.generateCodeSnippet(issue);
                        html += `
                    <div class="issue" data-severity="${issue.severity}" data-category="${issue.category}">
                        <div class="issue-header">
                            <div class="issue-severity severity-${issue.severity}">${issue.severity}</div>
                            <div class="issue-content">
                                <div class="issue-message">${this.escapeHtml(issue.message)}${location}</div>
                                <div class="issue-meta">
                                    <span class="meta-item">Rule: ${issue.rule}</span>
                                    <span class="meta-item">Category: ${issue.category}</span>
                                    ${issue.fixable ? '<span class="meta-item fixable-tag">Fixable</span>' : ''}
                                </div>
                                ${codeSnippet}
                            </div>
                        </div>
                    </div>`;
                    });
                    html += `
                </div>
            </div>`;
                }
            });
        }
        html += `
        </div>
    </div>

    <!-- Toast notification -->
    <div class="toast" id="toast"></div>

    <!-- Processing overlay -->
    <div class="processing-overlay" id="processingOverlay">
        <div class="processing-content">
            <div class="loading-spinner loading-spinner-large"></div>
            <div class="processing-text" id="processingText">Processing...</div>
        </div>
    </div>

    <!-- Safe JSON data embedding -->
    <script type="application/json" id="lint-data">${JSON.stringify(results)}</script>

    <script>
        // Global error handler for server logging
        window.addEventListener('error', function(event) {
            const errorData = {
                message: event.message,
                line: event.lineno,
                column: event.colno,
                error: event.error ? event.error.toString() : '',
                stack: event.error ? event.error.stack : '',
                userAgent: navigator.userAgent
            };
            
            // Log to server if available
            if (${isServerMode} && fetch) {
                fetch('/api/log-error', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(errorData)
                }).catch(() => {}); // Fail silently
            }
        });
        
        try {
            // Debug logging
            console.log('šŸ” LWC Linter Dashboard Loading...');
            console.log('Server Mode:', ${isServerMode});
            
            // Global data and state with error handling
            try {
                const lintDataElement = document.getElementById('lint-data');
                if (lintDataElement) {
                    window.lintData = JSON.parse(lintDataElement.textContent);
                } else {
                    console.warn('āš ļø Lint data element not found, using empty array');
                    window.lintData = [];
                }
            } catch (jsonError) {
                console.error('āŒ Failed to parse lint data:', jsonError);
                window.lintData = [];
            }
            
            window.lastUpdate = "${timestamp}";
            window.serverMode = ${isServerMode};
            window.userState = {
                expandedFiles: new Set(),
                currentSeverityFilter: 'all',
                currentCategoryFilter: 'all'
            };
        
        console.log('āœ… Global variables initialized');
        
        // Manual refresh with error handling and loading state
        function manualRefresh() {
            try {
                console.log('šŸ”„ Manual refresh triggered');
                
                if (!window.serverMode) {
                    showToast('Manual refresh requires server mode', 'warning');
                    return;
                }
                
                // Show loading state
                const refreshBtn = document.querySelector('.btn:contains("šŸ”„ Refresh Now")') || document.querySelector('[onclick*="manualRefresh"]');
                showProcessingOverlay('Refreshing data...');
                setButtonLoading(refreshBtn, true);
                
                fetch('/api/lint-results')
                    .then(response => response.json())
                    .then(data => {
                        try {
                            window.lintData = data.results;
                            window.lastUpdate = data.timestamp;
                            document.getElementById('lastUpdated').textContent = 'Last updated: ' + new Date().toLocaleString();
                            regenerateFilesDisplay(data.results);
                            showToast('Manual refresh completed!', 'success');
                        } catch (updateError) {
                            console.error('āŒ Error updating content:', updateError);
                            showToast('Error updating content: ' + updateError.message, 'error');
                        }
                    })
                    .catch(error => {
                        console.error('āŒ Manual refresh failed:', error);
                        showToast('Manual refresh failed: ' + error.message, 'error');
                    })
                    .finally(() => {
                        hideProcessingOverlay();
                        setButtonLoading(refreshBtn, false);
                    });
            } catch (error) {
                console.error('āŒ Error in manualRefresh:', error);
                showToast('Manual refresh error: ' + error.message, 'error');
                hideProcessingOverlay();
            }
        }
        
        // Show toast notification
        function showToast(message, type = 'success') {
            try {
                const toast = document.getElementById('toast');
                toast.textContent = message;
                toast.className = 'toast ' + type + ' show';
                
                setTimeout(() => {
                    toast.classList.remove('show');
                }, 3000);
            } catch (error) {
                console.error('āŒ Error showing toast:', error);
            }
        }
        
        // Lazy loading configuration
        const ITEMS_PER_BATCH = 10; // Load 10 files at a time
        let currentlyLoadedFiles = 0;
        let allResults = [];
        
        // Regenerate the files display with lazy loading
        function regenerateFilesDisplay(results) {
            try {
                console.log('šŸ”„ Regenerating files display with lazy loading...');
                const container = document.getElementById('filesContainer');
                
                if (results.length === 0) {
                    container.innerHTML = '<div class="no-issues"><div class="no-issues-icon">āœ…</div><div>No issues found! Your LWC code looks great.</div></div>';
                    return;
                }
                
                // Store results globally for lazy loading
                allResults = results.filter(result => result.issues.length > 0);
                currentlyLoadedFiles = 0;
                
                // Clear container and load initial batch
                container.innerHTML = '';
                loadNextBatch();
                
                // Setup scroll listener for lazy loading
                setupLazyScrolling();
                
                // Update statistics with full data
                const newStats = calculateStats(results);
                document.getElementById('totalFiles').textContent = newStats.totalFiles;
                document.getElementById('totalErrors').textContent = newStats.totalErrors;
                document.getElementById('totalWarnings').textContent = newStats.totalWarnings;
                document.getElementById('totalInfo').textContent = newStats.totalInfo;
                
                console.log('āœ… Files display regenerated with lazy loading');
            } catch (error) {
                console.error('āŒ Error regenerating files display:', error);
                showToast('Error updating display: ' + error.message, 'error');
            }
        }
        
        // Load the next batch of files
        function loadNextBatch() {
            try {
                if (currentlyLoadedFiles >= allResults.length) {
                    return; // All files loaded
                }
                
                const container = document.getElementById('filesContainer');
                const endIndex = Math.min(currentlyLoadedFiles + ITEMS_PER_BATCH, allResults.length);
                
                for (let i = currentlyLoadedFiles; i < endIndex; i++) {
                    const result = allResults[i];
                    const fileElement = createFileElement(result, i);
                    container.appendChild(fileElement);
                }
                
                currentlyLoadedFiles = endIndex;
                
                // Show loading indicator if more files to load
                if (currentlyLoadedFiles < allResults.length) {
                    showLoadingIndicator();
                } else {
                    hideLoadingIndicator();
                }
                
                console.log('āœ… Loaded batch:', currentlyLoadedFiles, '/', allResults.length);
            } catch (error) {
                console.error('āŒ Error loading next batch:', error);
            }
        }
        
        // Create a file element
        function createFileElement(result, index) {
            const errorCount = result.issues.filter(i => i.severity === 'error').length;
            const warnCount = result.issues.filter(i => i.severity === 'warn').length;
            const infoCount = result.issues.filter(i => i.severity === 'info').length;
            
            const fileDiv = document.createElement('div');
            fileDiv.className = 'file-section';
            fileDiv.setAttribute('data-file-index', index);
            fileDiv.setAttribute('data-file-path', result.filePath);
            
            let html = '<div class="file-header" onclick="toggleFile(' + index + ')">';
            html += '<h3>šŸ“ ' + result.filePath + '</h3>';
            html += '<div class="file-stats">';
            html += '<span class="error">' + errorCount + ' errors</span>';
            html += '<span class="warning">' + warnCount + ' warnings</span>';
            html += '<span class="info">' + infoCount + ' info</span>';
            html += '<span class="expand-icon">ā–¼</span>';
            html += '</div></div>';
            html += '<div class="issues-container">';
            
            result.issues.forEach((issue, issueIndex) => {
                const location = issue.line ? ' (line ' + issue.line + (issue.column ? ':' + issue.column : '') + ')' : '';
                const codeSnippet = generateCodeSnippetForIssue(issue);
                
                html += '<div class="issue" data-severity="' + issue.severity + '" data-category="' + issue.category + '">';
                html += '<div class="issue-header">';
                html += '<div class="issue-severity severity-' + issue.severity + '">' + issue.severity + '</div>';
                html += '<div class="issue-content">';
                html += '<div class="issue-message">' + escapeHtml(issue.message) + location + '</div>';
                html += '<div class="issue-meta">';
                html += '<span class="meta-item">Rule: ' + issue.rule + '</span>';
                html += '<span class="meta-item">Category: ' + issue.category + '</span>';
                if (issue.fixable) {
                    html += '<span class="meta-item fixable-tag">Fixable</span>';
                }
                html += '</div>';
                html += codeSnippet;
                html += '</div></div></div>';
            });
            
            html += '</div></div>';
            fileDiv.innerHTML = html;
            
            return fileDiv;
        }
        
        // Setup lazy scrolling
        function setupLazyScrolling() {
            // Remove existing listener if any
            if (window.lazyScrollHandler) {
                window.removeEventListener('scroll', window.lazyScrollHandler);
            }
            
            window.lazyScrollHandler = function() {
                if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - 1000) {
                    // User is near bottom, load more
                    if (currentlyLoadedFiles < allResults.length) {
                        loadNextBatch();
                    }
                }
            };
            
            window.addEventListener('scroll', window.lazyScrollHandler);
        }
        
        // Show loading indicator
        function showLoadingIndicator() {
            let indicator = document.getElementById('loadingIndicator');
            if (!indicator) {
                indicator = document.createElement('div');
                indicator.id = 'loadingIndicator';
                indicator.innerHTML = '<div class="loading-spinner">šŸ”„ Loading more files...</div>';
                indicator.style.cssText = 'text-align: center; padding: 20px; color: #6c757d; font-style: italic;';
                document.getElementById('filesContainer').appendChild(indicator);
            }
        }
        
        // Hide loading indicator
        function hideLoadingIndicator() {
            const indicator = document.getElementById('loadingIndicator');
            if (indicator) {
                indicator.remove();
            }
        }
        
        // Calculate statistics
        function calculateStats(results) {
            try {
                let totalErrors = 0, totalWarnings = 0, totalInfo = 0;
                
                results.forEach(result => {
                    result.issues.forEach(issue => {
                        switch (issue.severity) {
                            case 'error': totalErrors++; break;
                            case 'warn': totalWarnings++; break;
                            case 'info': totalInfo++; break;
                        }
                    });
                });
                
                return {
                    totalFiles: results.length,
                    totalErrors,
                    totalWarnings,
                    totalInfo
                };
            } catch (error) {
                console.error('āŒ Error calculating stats:', error);
                return { totalFiles: 0, totalErrors: 0, totalWarnings: 0, totalInfo: 0 };
            }
        }
        
        // Generate code snippet for an issue
        function generateCodeSnippetForIssue(issue) {
            try {
                const codeExamples = {
                    'no-var': { before: 'var userName = "john";', after: 'const userName = "john";' },
                    'prefer-const': { before: 'let config = { debug: true };', after: 'const config = { debug: true };' },
                    'no-console': { before: 'console.log("Debug message");', after: '// console.log("Debug message"); // Remove in production' },
                    'eslint-integration': { 
                        before: issue.message.includes('quotes') ? 'const message = "Hello World";' : 'const result = data',
                        after: issue.message.includes('quotes') ? "const message = 'Hello World';" : 'const result = data;'
                    },
                    'prettier-formatting': { before: 'const obj={name:"test",value:123};', after: 'const obj = { name: "test", value: 123 };' },
                    'aria-required': { before: '<button onclick={handleClick}></button>', after: '<button onclick={handleClick} aria-label="Submit form"></button>' },
                    'alt-text-required': { before: '<img src="logo.png">', after: '<img src="logo.png" alt="Company logo">' },
                    'no-innerhtml': { before: 'element.innerHTML = userInput;', after: 'element.textContent = userInput; // Safer approach' },
                    'no-eval': { before: 'eval(userCode);', after: '// Use safer alternatives like JSON.parse() or specific parsers' },
                    'no-unused-vars': { before: 'let unusedVar = "test";', after: '// Remove unused variables or prefix with _ if intentional' },
                    'import-organization': { before: 'import { c, a, b } from "module";', after: 'import { a, b, c } from "module";' }
                };
                
                const example = codeExamples[issue.rule];
                if (!example) return '';
                
                return '<div class="code-snippet">' +
                       '<div class="snippet-header">šŸ’” Suggested Fix</div>' +
                       '<div class="snippet-content">' +
                       '<div class="code-before"><span class="code-line-number">-</span>' + escapeHtml(example.before) + '</div>' +
                       '<div class="code-after"><span class="code-line-number">+</span>' + escapeHtml(example.after) + '</div>' +
                       '</div></div>';
            } catch (error) {
                console.error('āŒ Error generating code snippet:', error);
                return '';
            }
        }
        
        // Escape HTML
        function escapeHtml(text) {
            try {
                return text
                    .replace(/&/g, '&amp;')
                    .replace(/</g, '&lt;')
                    .replace(/>/g, '&gt;')
                    .replace(/"/g, '&quot;')
                    .replace(/'/g, '&#39;');
            } catch (error) {
                console.error('āŒ Error escaping HTML:', error);
                return text || '';
            }
        }
        
        // Toggle file expansion with loading state
        function toggleFile(index) {
            try {
                const section = document.querySelector('[data-file-index="' + index + '"]');
                if (section) {
                    const header = section.querySelector('.file-header');
                    const expandIcon = section.querySelector('.expand-icon');
                    
                    // Show mini loading
                    if (expandIcon) {
                        const originalText = expandIcon.textContent;
                        expandIcon.innerHTML = '<span class="loading-spinner"></span>';
                        
                        setTimeout(() => {
                            section.classList.toggle('expanded');
                            expandIcon.textContent = section.classList.contains('expanded') ? 'ā–²' : 'ā–¼';
                        }, 150);
                    } else {
                        section.classList.toggle('expanded');
                    }
                }
            } catch (error) {
                console.error('āŒ Error toggling file:', error);
            }
        }
        
        // Expand all files with loading state
        function expandAll() {
            try {
                const expandBtn = document.querySelector('[onclick*="expandAll"]');
                setButtonLoading(expandBtn, true);
                showProcessingOverlay('Expanding all files...');
                
                setTimeout(() => {
                    document.querySelectorAll('.file-section').forEach(section => {
                        section.classList.add('expanded');
                    });
                    // Update all expand icons
                    document.querySelectorAll('.expand-icon').forEach(icon => {
                        icon.textContent = 'ā–²';
                    });
                    showToast('All files expanded', 'success');
                    hideProcessingOverlay();
                    setButtonLoading(expandBtn, false);
                }, 300);
            } catch (error) {
                console.error('āŒ Error expanding all:', error);
                showToast('Error expanding files', 'error');
                hideProcessingOverlay();
            }
        }
        
        // Collapse all files with loading state
        function collapseAll() {
            try {
                const collapseBtn = document.querySelector('[onclick*="collapseAll"]');
                setButtonLoading(collapseBtn, true);
                showProcessingOverlay('Collapsing all files...');
                
                setTimeout(() => {
                    document.querySelectorAll('.file-section').forEach(section => {
                        section.classList.remove('expanded');
                    });
                    // Update all expand icons
                    document.querySelectorAll('.expand-icon').forEach(icon => {
                        icon.textContent = 'ā–¼';
                    });
                    showToast('All files collapsed', 'success');
                    hideProcessingOverlay();
                    setButtonLoading(collapseBtn, false);
                }, 300);
            } catch (error) {
                console.error('āŒ Error collapsing all:', error);
                showToast('Error collapsing files', 'error');
                hideProcessingOverlay();
            }
        }
        
        // Filter by severity with loading state
        function filterBySeverity() {
            try {
                const filterSelect = document.getElementById('severityFilter');
                showMiniSpinner(filterSelect);
                
                setTimeout(() => {
                    const filter = filterSelect.value;
                    document.querySelectorAll('.issue').forEach(issue => {
                        const severity = issue.dataset.severity;
                        if (filter === 'all' || severity === filter) {
                            issue.classList.remove('hidden');
                        } else {
                            issue.classList.add('hidden');
                        }
                    });
                    updateFileVisibility();
                    updateDashboardCounts(); // Update counts after filtering
                    hideMiniSpinner(filterSelect);
                    showToast('Filter applied successfully', 'success');
                }, 100); // Small delay to show spinner
            } catch (error) {
                console.error('āŒ Error filtering by severity:', error);
                showToast('Error applying filter', 'error');
            }
        }
        
        // Filter by category with loading state
        function filterByCategory() {
            try {
                const filterSelect = document.getElementById('categoryFilter');
                showMiniSpinner(filterSelect);
                
                setTimeout(() => {
                    const filter = filterSelect.value;
                    document.querySelectorAll('.issue').forEach(issue => {
                        const category = issue.dataset.category;
                        if (filter === 'all' || category === filter) {
                            issue.classList.remove('hidden');
                        } else {
                            issue.classList.add('hidden');
                        }
                    });
                    updateFileVisibility();
                    updateDashboardCounts(); // Update counts after filtering
                    hideMiniSpinner(filterSelect);
                    showToast('Filter applied successfully', 'success');
                }, 100); // Small delay to show spinner
            } catch (error) {
                console.error('āŒ Error filtering by category:', error);
                showToast('Error applying filter', 'error');
            }
        }
        
        // Update file visibility based on visible issues
        function updateFileVisibility() {
            try {
                document.querySelectorAll('.file-section').forEach(fileSection => {
                    const visibleIssues = fileSection.querySelectorAll('.issue:not(.hidden)');
                    if (visibleIssues.length === 0) {
                        fileSection.classList.add('hidden');
                    } else {
                        fileSection.classList.remove('hidden');
                    }
                });
            } catch (error) {
                console.error('āŒ Error updating file visibility:', error);
            }
        }
        
        // Update dashboard counts based on visible/filtered items
        function updateDashboardCounts() {
            try {
                let visibleFiles = 0;
                let visibleErrors = 0;
                let visibleWarnings = 0;
                let visibleInfo = 0;
                
                document.querySelectorAll('.file-section:not(.hidden)').forEach(fileSection => {
                    visibleFiles++;
                    fileSection.querySelectorAll('.issue:not(.hidden)').forEach(issue => {
                        const severity = issue.dataset.severity;
                        switch (severity) {
                            case 'error': visibleErrors++; break;
                            case 'warn': visibleWarnings++; break;
                            case 'info': visibleInfo++; break;
                        }
                    });
                });
                
                // Update the dashboard counts
                document.getElementById('totalFiles').textContent = visibleFiles;
                document.getElementById('totalErrors').textContent = visibleErrors;
                document.getElementById('totalWarnings').textContent = visibleWarnings;
                document.getElementById('totalInfo').textContent = visibleInfo;
                
                console.log('āœ… Dashboard counts updated:', { visibleFiles, visibleErrors, visibleWarnings, visibleInfo });
            } catch (error) {
                console.error('āŒ Error updating dashboard counts:', error);
            }
        }
        
        // Export to Excel with loading state
        function exportToExcel() {
            try {
                if (typeof XLSX === 'undefined') {
                    showToast('Excel export library not loaded', 'error');
                    return;
                }
                
                const exportBtn = document.querySelector('[onclick*="exportToExcel"]');
                showProcessingOverlay('Preparing Excel export...');
                setButtonLoading(exportBtn, true);
                
                setTimeout(() => {
                    try {
                        const data = [];
                        data.push(['File', 'Rule', 'Severity', 'Category', 'Message', 'Line', 'Column', 'Fixable']);
                        
                        window.lintData.forEach(result => {
                            result.issues.forEach(issue => {
                                data.push([
                                    result.filePath,
                                    issue.rule,
                                    issue.severity,
                                    issue.category,
                                    issue.message,
                                    issue.line || '',
                                    issue.column || '',
                                    issue.fixable ? 'Yes' : 'No'
                                ]);
                            });
                        });
                        
                        const wb = XLSX.utils.book_new();
                        const ws = XLSX.utils.aoa_to_sheet(data);
                        XLSX.utils.book_append_sheet(wb, ws, 'LWC Lint Results');
                        XLSX.writeFile(wb, 'lwc-lint-results-' + new Date().toISOString().split('T')[0] + '.xlsx');
                        
                        showToast('Excel file downloaded successfully!', 'success');
                    } catch (exportError) {
                        console.error('āŒ Error during export:', exportError);
                        showToast('Export failed: ' + exportError.message, 'error');
                    } finally {
                        hideProcessingOverlay();
                        setButtonLoading(exportBtn, false);
                    }
                }, 500); // Show loading for at least 500ms
            } catch (error) {
                console.error('āŒ Error exporting to Excel:', error);
                showToast('Export failed: ' + error.message, 'error');
                hideProcessingOverlay();
            }
        }

        } catch (error) {
            console.error('āŒ Critical dashboard error:', error);
            
            // Show user-friendly error message
            document.body.innerHTML = '<div style="padding: 40px; text-align: center; font-family: Arial, sans-serif;">' +
                '<h2 style="color: #dc3545;">āš ļø Dashboard Error</h2>' +
                '<p>There was an error loading the dashboard. Please refresh the page or check the console for details.</p>' +
                '<p style="font-size: 0.9em; color: #6c757d;">Error: ' + error.message + '</p>' +
                '</div>';
                }
        
        // Loading state helper functions
        function showProcessingOverlay(message = 'Processing...') {
            try {
                const overlay = document.getElementById('processingOverlay');
                const text = document.getElementById('processingText');
                if (overlay && text) {
                    text.textContent = message;
                    overlay.classList.add('show');
                }
            } catch (error) {
                console.error('āŒ Error showing processing overlay:', error);
            }
        }
        
        function hideProcessingOverlay() {
            try {
                const overlay = document.getElementById('processingOverlay');
                if (overlay) {
                    overlay.classList.remove('show');
                }
            } catch (error) {
                console.error('āŒ Error hiding processing overlay:', error);
            }
        }
        
        function setButtonLoading(button, isLoading) {
            try {
                if (!button) return;
                
                if (isLoading) {
                    button.classList.add('btn-loading');
                    button.disabled = true;
                    if (!button.dataset.originalText) {
                        button.dataset.originalText = button.textContent;
                    }
                } else {
                    button.classList.remove('btn-loading');
                    button.disabled = false;
                    if (button.dataset.originalText) {
                        button.textContent = button.dataset.originalText;
                    }
                }
            } catch (error) {
                console.error('āŒ Error setting button loading state:', error);
            }
        }
        
        function showMiniSpinner(element) {
            try {
                if (!element) return;
                element.style.position = 'relative';
                const spinner = document.createElement('div');
                spinner.className = 'loading-spinner';
                spinner.style.cssText = 'position: absolute; right: 10px; top: 50%; transform: translateY(-50%); z-index: 10;';
                element.parentNode.appendChild(spinner);
                element.dataset.hasSpinner = 'true';
            } catch (error) {
                console.error('āŒ Error showing mini spinner:', error);
            }
        }
        
        function hideMiniSpinner(element) {
            try {
                if (!element || !element.dataset.hasSpinner) return;
                const spinner = element.parentNode.querySelector('.loading-spinner');
                if (spinner) {
                    spinner.remove();
                }
                delete element.dataset.hasSpinner;
            } catch (error) {
                console.error('āŒ Error hiding mini spinner:', error);
            }
        }

        // Attach functions to global window object for HTML onclick handlers
        window.toggleFile = toggleFile;
        window.expandAll = expandAll;
        window.collapseAll = collapseAll;
        window.filterBySeverity = filterBySeverity;
        window.filterByCategory = filterByCategory;
        window.exportToExcel = exportToExcel;
        window.showToast = showToast;
        window.updateDashboardCounts = updateDashboardCounts;
        window.loadNextBatch = loadNextBatch;
        window.showProcessingOverlay = showProcessingOverlay;
        window.hideProcessingOverlay = hideProcessingOverlay;
        window.setButtonLoading = setButtonLoading;
        window.showMiniSpinner = showMiniSpinner;
        window.hideMiniSpinner = hideMiniSpinner;
        
        if (${isServerMode}) {
            window.manualRefresh = manualRefresh;
        }
        
        console.log('āœ… Functions attached to window object');
        
        // Function validation on load
        document.addEventListener('DOMContentLoaded', function() {
            try {
                const requiredFunctions = ['expandAll', 'collapseAll', 'filterBySeverity', 'filterByCategory', 'exportToExcel', 'toggleFile'];
                if (${isServerMode}) {
                    requiredFunctions.push('manualRefresh');
                }
                
                const missingFunctions = requiredFunctions.filter(func => typeof window[func] !== 'function');
                
                if (missingFunctions.length > 0) {
                    console.error('āŒ Missing functions:', missingFunctions);
                    showToast('Some dashboard features may not work: ' + missingFunctions.join(', '), 'warning');
                } else {
                    console.log('āœ… All required functions are available');
                }
                
                console.log('šŸŽ‰ LWC Linter Dashboard loaded successfully!');
                showToast('Dashboard loaded successfully!', 'success');
            } catch (error) {
                console.error('āŒ Error during DOMContentLoaded:', error);
            }
        });
    </script>
</body>
</html>`;
        // Minify and compress the HTML for faster loading
        try {
            const minifiedHTML = await this.compressHTML(html);
            return minifiedHTML;
        }
        catch (error) {
            console.warn('āš ļø HTML minification failed, returning uncompressed:', error.message);
            return html;
        }
    }
    async compressHTML(html) {
        try {
            const minifiedHTML = await (0, html_minifier_terser_1.minify)(html, {
                removeComments: true,
                removeRedundantAttributes: true,
                removeEmptyAttributes: true,
                removeOptionalTags: true,
                removeEmptyElements: false, // Keep for dynamic content
                useShortDoctype: true,
                collapseWhitespace: true,
                conservativeCollapse: true,
                collapseBooleanAttributes: true,
                caseSensitive: false,
                minifyCSS: {
                    level: 2,
                    inline: ['all'],
                    compatibility: '*'
                },
                minifyJS: true,
                processConditionalComments: true,
                removeAttributeQuotes: true,
                removeScriptTypeAttributes: true,
                removeStyleLinkTypeAttributes: true,
                sortAttributes: true,
                sortClassName: true
            });
            const originalSize = Buffer.byteLength(html, 'utf8');
            const compressedSize = Buffer.byteLength(minifiedHTML, 'utf8');
            const compressionRatio = ((originalSize - compressedSize) / originalSize * 100).toFixed(1);
            console.log(`šŸ“¦ HTML compressed: ${originalSize} → ${compressedSize} bytes (${compressionRatio}% reduction)`);
            return minifiedHTML;
        }
        catch (error) {
            console.warn('āš ļø HTML compression failed:', error.message);
            return html;
        }
    }
    generateCodeSnippet(issue) {
        const codeExamples = {
            // ESLint Integration Rules
            'no-var': {
                before: 'var userName = "john";\nvar config = { debug: true };',
                after: 'const userName = "john";\nconst config = { debug: true };'
            },
            'prefer-const': {
                before: 'let config = { debug: true };\nlet API_URL = "https://api.example.com";',
                after: 'const config = { debug: true };\nconst API_URL = "https://api.example.com";'
            },
            'no-console': {
                before: 'console.log("Debug message");\nconsole.error("Error occurred");',
                after: '// console.log("Debug message"); // Remove in production\n// Use proper logging framework instead'
            },
            'no-unused-vars': {
                before: 'let unusedVar = "test";\nconst data = fetchData();\nreturn data.results;',
                after: '// Remove unused variables\nconst data = fetchData();\nreturn data.results;'
            },
            'quotes': {
                before: 'const message = "Hello World";\nconst name = "John";',
                after: "const message = 'Hello World';\nconst name = 'John';"
            },
            'semi': {
                before: 'const result = getData()\nconst user = { name: "John" }',
                after: 'const result = getData();\nconst user = { name: "John" };'
            },
            // Prettier Integration Rules
            'prettier-formatting': {
                before: 'const obj={name:"test",value:123,active:true};\nfunction process(a,b,c){return a+b+c;}',
                after: 'const obj = { name: "test", value: 123, active: true };\nfunction process(a, b, c) {\n  return a + b + c;\n}'
            },
            'eslint-integration': {
                before: issue.message.includes('quotes') ?
                    'const message = "Hello World";\nconst error = "Something went wrong";' :
                    'const result=data;const items=array.map(item=>item.id)',
                after: issue.message.includes('quotes') ?
                    "const message = 'Hello World';\nconst error = 'Something went wrong';" :
                    'const result = data;\nconst items = array.map(item => item.id);'
            },
            'import-organization': {
                before: 'import { getUserData, processData, validateInput } from "utils";\nimport { z, a, m } from "helpers";',
                after: 'import { getUserData, processData, validateInput } from "utils";\nimport { a, m, z } from "helpers";'
            },
            // Accessibility Rules
            'aria-required': {
                before: '<button onclick={handleClick}>Submit</button>\n<input type="text" placeholder="Enter name" />',
                after: '<button onclick={handleClick} aria-label="Submit form">Submit</button>\n<input type="text" placeholder="Enter name" aria-label="User name" />'
            },
            'alt-text-required': {
                before: '<img src="logo.png" />\n<img src="chart.jpg" />',
                after: '<img src="logo.png" alt="Company logo" />\n<img src="chart.jpg" alt="Sales performance chart" />'
            },
            'keyboard-navigation': {
                before: '<div onclick={handleClick}>Click me</div>\n<span onclick={handleAction}>Action</span>',
                after: '<button onclick={handleClick}>Click me</button>\n<button onclick={handleAction}>Action</button>'
            },
            'color-contrast': {
                before: '.text { color: #ccc; background: #fff; }\n.warning { color: #ffff00; }',
                after: '.text { color: #666; background: #fff; }\n.warning { color: #b8860b; /* Better contrast */ }'
            },
            // Security Rules  
            'no-innerhtml': {
                before: 'element.innerHTML = userInput;\ndiv.innerHTML = `<p>${data}</p>`;',
                after: 'element.textContent = userInput; // Safer approach\n// Use template literals with sanitization instead'
            },
            'no-eval': {
                before: 'eval(userCode);\nconst result = eval(`return ${expression}`);',
                after: '// Use safer alternatives like JSON.parse() or specific parsers\n// Consider using Function constructor with validation'
            },
            'validate-inputs': {
                before: 'function processUser(data) {\n  return database.save(data);\n}',
                after: 'function processUser(data) {\n  if (!data || !data.email) throw new Error("Invalid input");\n  return database.save(sanitize(data));\n}'
            },
            'xss-prevention': {
                before: 'const html = `<div>${userInput}</div>`;\ndocument.body.innerHTML = html;',
                after: 'const html = `<div>${escapeHtml(userInput)}</div>`;\n// Use safe DOM manipulation methods'
            },
            // Performance Rules
            'avoid-dom-queries': {
                before: 'for (let i = 0; i < items.length; i++) {\n  document.getElementById("list").appendChild(item);\n}',
                after: 'const list = document.getElementById("list");\nfor (let i = 0; i < items.length; i++) {\n  list.appendChild(item);\n}'
            },
            'lazy-loading': {
                before: 'import { heavyLibrary } from "heavy-lib";\nconst result = heavyLibrary.process();',
                after: '// Lazy load heavy dependencies\nconst { heavyLibrary } = await import("heavy-lib");\nconst result = heavyLibrary.process();'
            },
            'memory-leaks': {
                before: 'window.addEventListener("resize", handleResize);\n// Event listener never removed',
                after: 'window.addEventListener("resize", handleResize);\n// Remember to cleanup:\n// window.removeEventListener("resize", handleResize);'
            },
            'efficient-loops': {
                before: 'for (let i = 0; i < array.length; i++) {\n  if (array[i].heavy_operation()) { /* process */ }\n}',
                after: 'const length = array.length;\nfor (let i = 0; i < length; i++) {\n  const item = array[i];\n  if (item.heavy_operation()) { /* process */ }\n}'
            },
            // Code Quality Rules
            'use-strict-equality': {
                before: 'if (value == null) { return; }\nif (count != 0) { process(); }',
                after: 'if (value === null || value === undefined) { return; }\nif (count !== 0) { process(); }'
            },
            'error-handling': {
                before: 'function fetchData() {\n  const result = api.call();\n  return result.data;\n}',
                after: 'function fetchData() {\n  try {\n    const result = api.call();\n    return result.data;\n  } catch (error) {\n    console.error("API call failed:", error);\n    throw error;\n  }\n}'
            },
            'consistent-naming': {
                before: 'const user_name = "john";\nconst UserAge = 25;\nconst ISACTIVE = true;',
                after: 'const userName = "john";\nconst userAge = 25;\nconst isActive = true;'
            },
            'function-complexity': {
                before: 'function complexFunction(a, b, c, d, e) {\n  if (a) { if (b) { if (c) { /* nested logic */ } } }\n  return result;\n}',
                after: 'function processData(data) {\n  validateInput(data);\n  return transformData(data);\n}\n\nfunction validateInput(data) { /* validation */ }\nfunction transformData(data) { /* transformation */ }'
            },
            // LWC Specific Rules
            'lwc-component-naming': {
                before: 'export default class myComponent extends LightningElement {\n  // component logic\n}',
                after: 'export default class MyComponent extends LightningElement {\n  // component logic\n}'
            },
            'lwc-property-decorators': {
                before: 'export default class MyComponent extends LightningElement {\n  userInfo;\n  @api recordId;\n}',
                after: 'export default class MyComponent extends LightningElement {\n  @api recordId;\n  userInfo; // Private properties after public ones\n}'
            },
            'lwc-event-handling': {
                before: '<button onclick="handleClick()">Click</button>',
                after: '<button onclick={handleClick}>Click</button>'
            },
            // CSS Rules
            'css-organization': {
                before: '.component { font-size: 14px; color: red; margin: 10px; font-size: 16px; }',
                after: '.component {\n  margin: 10px;\n  font-size: 16px; /* Remove duplicate */\n  color: red;\n}'
            },
            'css-vendor-prefixes': {
                before: '.box { transform: scale(1.1); transition: all 0.3s; }',
                after: '.box {\n  -webkit-transform: scale(1.1);\n  transform: scale(1.1);\n  -webkit-transition: all 0.3s;\n  transition: all 0.3s;\n}'
            }
        };
        const example = codeExamples[issue.rule];
        if (!example)
            return '';
        return `
      <div class="code-snippet">
        <div class="snippet-header">šŸ’” Suggested Fix for "${issue.rule}"</div>
        <div class="snippet-content">
          <div class="code-before">
            <span class="code-line-number">-</span>${this.escapeHtml(example.before)}
          </div>
          <div class="code-after">
            <span class="code-line-number">+</span>${this.escapeHtml(example.after)}
          </div>
        </div>
      </div>`;
    }
    getSeverityIcon(severity) {
        switch (severity) {
            case 'error':
                return chalk_1.default.red('āŒ');
            case 'warn':
                return chalk_1.default.yellow('āš ļø ');
            case 'info':
            default:
                return chalk_1.default.blue('ā„¹ļø ');
        }
    }
    generateSummary(results) {
        let totalErrors = 0;
        let totalWarnings = 0;
        let totalInfo = 0;
        let totalFixed = 0;
        results.forEach(result => {
            result.issues.forEach(issue => {
                switch (issue.severity) {
                    case 'error':
                        totalErrors++;
                        break;
                    case 'warn':
                        totalWarnings++;
                        break;
                    case 'info':
                        totalInfo++;
                        break;
                }
            });
            if (result.fixedCount) {
                totalFixed += result.fixedCount;
            }
        });
        return {
            totalFiles: results.length,
            totalIssues: totalErrors + totalWarnings + totalInfo,
            totalErrors,
            totalWarnings,
            totalInfo
        };
    }
    escapeHtml(text) {
        return text
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;');
    }
}
exports.OutputFormatter = OutputFormatter;
//# sourceMappingURL=output-formatter.js.map