// minify.js const { minify } = require('html-minifier'); const cheerio = require('cheerio'); function sanitizeHtml(html) { try { // Remove conditional comments html = html.replace(//gs, ''); // Fix incomplete URLs html = html.replace(/src="https?:\/\/[^"]*\.{3}"/g, ''); // Remove problematic script tags html = html.replace(/)<[^<]*)*<\/script>/gi, ''); return html; } catch (err) { console.warn('HTML sanitization failed:', err); return html; } } function validateHtml(html) { try { const $ = cheerio.load(html, { xmlMode: false, decodeEntities: false }); const issues = []; if ($('html').length === 0) issues.push('Missing html tag'); if ($('head').length === 0) issues.push('Missing head tag'); if ($('body').length === 0) issues.push('Missing body tag'); return issues; } catch (err) { console.warn('HTML validation failed:', err); return ['Validation error: ' + err.message]; } } function safeMinify(html, options = {}) { const defaultOptions = { removeComments: true, collapseWhitespace: true, minifyCSS: true, minifyJS: true, conservativeCollapse: true, keepClosingSlash: true, removeAttributeQuotes: false, removeEmptyAttributes: true, removeRedundantAttributes: true, removeScriptTypeAttributes: false, removeStyleLinkTypeAttributes: false, sortAttributes: true, sortClassName: true }; const minifyOptions = { ...defaultOptions, ...options }; try { // Try full minification first return minify(html, minifyOptions); } catch (err) { console.warn('Full minification failed, trying conservative mode:', err); try { // Fall back to conservative minification return minify(html, { removeComments: true, collapseWhitespace: true, conservativeCollapse: true, keepClosingSlash: true, removeAttributeQuotes: false, removeEmptyAttributes: false, removeRedundantAttributes: false }); } catch (err2) { console.error('Conservative minification also failed:', err2); return html; } } } function minifyHtml(html, options = {}) { const result = { originalSize: html.length, minifiedHtml: '', success: false, issues: [], stats: {}, error: null }; try { // Step 1: Validate HTML const validationIssues = validateHtml(html); result.issues = validationIssues; // Step 2: Sanitize HTML const sanitized = sanitizeHtml(html); // Step 3: Minify HTML const minified = safeMinify(sanitized, options); result.minifiedHtml = minified; result.success = true; // Step 4: Calculate stats result.stats = { originalSize: html.length, minifiedSize: minified.length, reduction: ((html.length - minified.length) / html.length * 100).toFixed(2) + '%', validationIssues: validationIssues.length, timestamp: new Date().toISOString() }; } catch (err) { result.error = { message: err.message, stack: err.stack }; result.minifiedHtml = html; // Return original HTML if processing fails } return result; } module.exports = { minifyHtml, validateHtml, sanitizeHtml };