UNPKG

eol-check

Version:

CLI tool to check End-of-Life (EOL) status of Node.js, package managers, operating systems, dependencies, and databases. Supports HTML reports and GitHub Actions.

343 lines (302 loc) 8.02 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateHtmlReport = generateHtmlReport; const fs_1 = __importDefault(require("fs")); const evaluator_1 = require("./evaluator"); function generateHtmlReport(results, outputPath, options = {}) { const { title = 'EOL Check Report', includeTimestamp = true } = options; const timestamp = new Date().toLocaleString('en-US', { dateStyle: 'full', timeStyle: 'short', }); // Calculate statistics const stats = { total: results.length, ok: results.filter((r) => r.status === evaluator_1.Status.OK).length, warn: results.filter((r) => r.status === evaluator_1.Status.WARN).length, err: results.filter((r) => r.status === evaluator_1.Status.ERR).length, }; const html = `<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>${title}</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 2rem; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 16px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); overflow: hidden; } header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2.5rem 2rem; text-align: center; } header h1 { font-size: 2.5rem; margin-bottom: 0.5rem; font-weight: 700; } header .timestamp { font-size: 0.95rem; opacity: 0.9; font-weight: 400; } .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1.5rem; padding: 2rem; background: #f8f9fa; border-bottom: 1px solid #e9ecef; } .stat-card { background: white; padding: 1.5rem; border-radius: 12px; text-align: center; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); transition: transform 0.2s; } .stat-card:hover { transform: translateY(-4px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12); } .stat-card .number { font-size: 2.5rem; font-weight: 700; margin-bottom: 0.5rem; } .stat-card .label { font-size: 0.9rem; color: #6c757d; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; } .stat-card.total .number { color: #495057; } .stat-card.ok .number { color: #28a745; } .stat-card.warn .number { color: #ffc107; } .stat-card.err .number { color: #dc3545; } .results { padding: 2rem; } .results h2 { font-size: 1.5rem; margin-bottom: 1.5rem; color: #212529; } table { width: 100%; border-collapse: separate; border-spacing: 0; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); border-radius: 8px; overflow: hidden; } thead { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; } th { padding: 1rem; text-align: left; font-weight: 600; text-transform: uppercase; font-size: 0.85rem; letter-spacing: 0.5px; } tbody tr { background: white; border-bottom: 1px solid #e9ecef; transition: background-color 0.2s; } tbody tr:last-child { border-bottom: none; } tbody tr:hover { background: #f8f9fa; } td { padding: 1rem; color: #495057; } .badge { display: inline-block; padding: 0.4rem 0.8rem; border-radius: 20px; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; min-width: 60px; text-align: center; } .badge.ok { background: #d4edda; color: #155724; } .badge.warn { background: #fff3cd; color: #856404; } .badge.err { background: #f8d7da; color: #721c24; } .version-badge { font-family: 'Courier New', monospace; background: #e9ecef; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.85rem; font-weight: 600; color: #495057; } footer { padding: 1.5rem 2rem; background: #f8f9fa; text-align: center; color: #6c757d; font-size: 0.85rem; border-top: 1px solid #e9ecef; } footer a { color: #667eea; text-decoration: none; font-weight: 600; } footer a:hover { text-decoration: underline; } @media (max-width: 768px) { body { padding: 1rem; } header h1 { font-size: 1.75rem; } .stats { grid-template-columns: 1fr; padding: 1rem; } .results { padding: 1rem; } table { font-size: 0.85rem; } th, td { padding: 0.75rem 0.5rem; } } @media print { body { background: white; padding: 0; } .container { box-shadow: none; } .stat-card:hover, tbody tr:hover { transform: none; background: white; } } </style> </head> <body> <div class="container"> <header> <h1>${title}</h1> ${includeTimestamp ? `<p class="timestamp">Generated on ${timestamp}</p>` : ''} </header> <div class="stats"> <div class="stat-card total"> <div class="number">${stats.total}</div> <div class="label">Total Checks</div> </div> <div class="stat-card ok"> <div class="number">${stats.ok}</div> <div class="label">Supported</div> </div> <div class="stat-card warn"> <div class="number">${stats.warn}</div> <div class="label">Warnings</div> </div> <div class="stat-card err"> <div class="number">${stats.err}</div> <div class="label">EOL / Errors</div> </div> </div> <div class="results"> <h2>Detailed Results</h2> <table> <thead> <tr> <th>Status</th> <th>Component</th> <th>Version</th> <th>Message</th> </tr> </thead> <tbody> ${results .map((result) => ` <tr> <td><span class="badge ${result.status.toLowerCase()}">${result.status}</span></td> <td><strong>${escapeHtml(result.component)}</strong></td> <td><span class="version-badge">${escapeHtml(result.version)}</span></td> <td>${escapeHtml(result.message)}</td> </tr> `) .join('')} </tbody> </table> </div> <footer> <p> Generated by <a href="https://github.com/abhishekpanda0620/eol-check" target="_blank">eol-check</a> | Data from <a href="https://endoflife.date" target="_blank">endoflife.date</a> </p> </footer> </div> </body> </html>`; try { fs_1.default.writeFileSync(outputPath, html, 'utf-8'); } catch (error) { throw new Error(`Failed to write HTML report to ${outputPath}: ${error}`); } } function escapeHtml(text) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;', }; return text.replace(/[&<>"']/g, (char) => map[char]); }