/** * Email utility functions shared across all email services */ /** * Convert HTML to plain text for email fallback * Strips HTML tags and formats content for plain text email clients * @param {string} html - HTML content to convert * @returns {string} Plain text version of the HTML */ function htmlToPlainText(html) { return ( html // Remove style and script tags and their content .replace(/]*>[\s\S]*?<\/style>/gi, "") .replace(/]*>[\s\S]*?<\/script>/gi, "") // Convert common HTML elements to text equivalents .replace(//gi, "\n") .replace(/<\/p>/gi, "\n\n") .replace(/<\/div>/gi, "\n") .replace(/<\/li>/gi, "\n") .replace(/<\/h[1-6]>/gi, "\n\n") .replace(/
  • /gi, "• ") // Remove remaining HTML tags .replace(/<[^>]+>/g, "") // Decode HTML entities .replace(/ /g, " ") .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, '"') .replace(/'/g, "'") // Remove emojis and special characters that don't render well in plain text .replace(/[\u{1F600}-\u{1F64F}]/gu, "") // Emoticons .replace(/[\u{1F300}-\u{1F5FF}]/gu, "") // Misc Symbols and Pictographs .replace(/[\u{1F680}-\u{1F6FF}]/gu, "") // Transport and Map .replace(/[\u{2600}-\u{26FF}]/gu, "") // Misc symbols .replace(/[\u{2700}-\u{27BF}]/gu, "") // Dingbats .replace(/[\u{FE00}-\u{FE0F}]/gu, "") // Variation Selectors .replace(/[\u{1F900}-\u{1F9FF}]/gu, "") // Supplemental Symbols and Pictographs .replace(/[\u{1FA70}-\u{1FAFF}]/gu, "") // Symbols and Pictographs Extended-A // Clean up excessive whitespace .replace(/\n\s*\n\s*\n/g, "\n\n") .trim() ); } /** * Format a date consistently for email display * @param {Date|string} date - Date to format * @returns {string} Formatted date string */ function formatEmailDate(date) { const dateObj = typeof date === "string" ? new Date(date) : date; return dateObj.toLocaleString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", hour12: true, }); } /** * Format a date as a short date (no time) * @param {Date|string} date - Date to format * @returns {string} Formatted date string */ function formatShortDate(date) { const dateObj = typeof date === "string" ? new Date(date) : date; return dateObj.toLocaleString("en-US", { year: "numeric", month: "long", day: "numeric", }); } /** * Format currency for email display * @param {number} amount - Amount in cents or smallest currency unit * @param {string} currency - Currency code (default: USD) * @returns {string} Formatted currency string */ function formatCurrency(amount, currency = "USD") { return new Intl.NumberFormat("en-US", { style: "currency", currency: currency, }).format(amount / 100); } /** * Escape HTML special characters to prevent XSS attacks * Converts characters that could be interpreted as HTML into safe entities * @param {*} str - Value to escape (will be converted to string) * @returns {string} HTML-escaped string safe for insertion into HTML */ function escapeHtml(str) { if (str === null || str === undefined) return ""; return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } module.exports = { htmlToPlainText, formatEmailDate, formatShortDate, formatCurrency, escapeHtml, };