UNPKG

postal-mime

Version:

Email parser for Node.js and browser environments

285 lines (282 loc) 10.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var text_format_exports = {}; __export(text_format_exports, { decodeHTMLEntities: () => decodeHTMLEntities, escapeHtml: () => escapeHtml, formatHtmlHeader: () => formatHtmlHeader, formatTextHeader: () => formatTextHeader, htmlToText: () => htmlToText, textToHtml: () => textToHtml }); module.exports = __toCommonJS(text_format_exports); var import_html_entities = __toESM(require("./html-entities.cjs"), 1); function decodeHTMLEntities(str) { return str.replace(/&(#\d+|#x[a-f0-9]+|[a-z]+\d*);?/gi, (match, entity) => { if (typeof import_html_entities.default[match] === "string") { return import_html_entities.default[match]; } if (entity.charAt(0) !== "#" || match.charAt(match.length - 1) !== ";") { return match; } let codePoint; if (entity.charAt(1) === "x") { codePoint = parseInt(entity.substr(2), 16); } else { codePoint = parseInt(entity.substr(1), 10); } let output = ""; if (codePoint >= 55296 && codePoint <= 57343 || codePoint > 1114111) { return "\uFFFD"; } if (codePoint > 65535) { codePoint -= 65536; output += String.fromCharCode(codePoint >>> 10 & 1023 | 55296); codePoint = 56320 | codePoint & 1023; } output += String.fromCharCode(codePoint); return output; }); } function escapeHtml(str) { return str.trim().replace(/[<>"'?&]/g, (c) => { let hex = c.charCodeAt(0).toString(16); if (hex.length < 2) { hex = "0" + hex; } return "&#x" + hex.toUpperCase() + ";"; }); } function textToHtml(str) { let html = escapeHtml(str).replace(/\n/g, "<br />"); return "<div>" + html + "</div>"; } function htmlToText(str) { str = str.replace(/\r?\n/g, "").replace(/<\!\-\-.*?\-\->/gi, " ").replace(/<br\b[^>]*>/gi, "\n").replace(/<\/?(p|div|table|tr|td|th)\b[^>]*>/gi, "\n\n").replace(/<script\b[^>]*>.*?<\/script\b[^>]*>/gi, " ").replace(/^.*<body\b[^>]*>/i, "").replace(/^.*<\/head\b[^>]*>/i, "").replace(/^.*<\!doctype\b[^>]*>/i, "").replace(/<\/body\b[^>]*>.*$/i, "").replace(/<\/html\b[^>]*>.*$/i, "").replace(/<a\b[^>]*href\s*=\s*["']?([^\s"']+)[^>]*>/gi, " ($1) ").replace(/<\/?(span|em|i|strong|b|u|a)\b[^>]*>/gi, "").replace(/<li\b[^>]*>[\n\u0001\s]*/gi, "* ").replace(/<hr\b[^>]*>/g, "\n-------------\n").replace(/<[^>]*>/g, " ").replace(/\u0001/g, "\n").replace(/[ \t]+/g, " ").replace(/^\s+$/gm, "").replace(/\n\n+/g, "\n\n").replace(/^\n+/, "\n").replace(/\n+$/, "\n"); str = decodeHTMLEntities(str); return str; } function formatTextAddress(address) { return [].concat(address.name || []).concat(address.name ? `<${address.address}>` : address.address).join(" "); } function formatTextAddresses(addresses) { let parts = []; let processAddress = (address, partCounter) => { if (partCounter) { parts.push(", "); } if (address.group) { let groupStart = `${address.name}:`; let groupEnd = `;`; parts.push(groupStart); address.group.forEach(processAddress); parts.push(groupEnd); } else { parts.push(formatTextAddress(address)); } }; addresses.forEach(processAddress); return parts.join(""); } function formatHtmlAddress(address) { return `<a href="mailto:${escapeHtml(address.address)}" class="postal-email-address">${escapeHtml(address.name || `<${address.address}>`)}</a>`; } function formatHtmlAddresses(addresses) { let parts = []; let processAddress = (address, partCounter) => { if (partCounter) { parts.push('<span class="postal-email-address-separator">, </span>'); } if (address.group) { let groupStart = `<span class="postal-email-address-group">${escapeHtml(address.name)}:</span>`; let groupEnd = `<span class="postal-email-address-group">;</span>`; parts.push(groupStart); address.group.forEach(processAddress); parts.push(groupEnd); } else { parts.push(formatHtmlAddress(address)); } }; addresses.forEach(processAddress); return parts.join(" "); } function foldLines(str, lineLength, afterSpace) { str = (str || "").toString(); lineLength = lineLength || 76; let pos = 0, len = str.length, result = "", line, match; while (pos < len) { line = str.substr(pos, lineLength); if (line.length < lineLength) { result += line; break; } if (match = line.match(/^[^\n\r]*(\r?\n|\r)/)) { line = match[0]; result += line; pos += line.length; continue; } else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || "").length : 0) < line.length) { line = line.substr(0, line.length - (match[0].length - (afterSpace ? (match[1] || "").length : 0))); } else if (match = str.substr(pos + line.length).match(/^[^\s]+(\s*)/)) { line = line + match[0].substr(0, match[0].length - (!afterSpace ? (match[1] || "").length : 0)); } result += line; pos += line.length; if (pos < len) { result += "\r\n"; } } return result; } function formatTextHeader(message) { let rows = []; if (message.from) { rows.push({ key: "From", val: formatTextAddress(message.from) }); } if (message.subject) { rows.push({ key: "Subject", val: message.subject }); } if (message.date) { let dateOptions = { year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false }; let dateStr = typeof Intl === "undefined" ? message.date : new Intl.DateTimeFormat("default", dateOptions).format(new Date(message.date)); rows.push({ key: "Date", val: dateStr }); } if (message.to && message.to.length) { rows.push({ key: "To", val: formatTextAddresses(message.to) }); } if (message.cc && message.cc.length) { rows.push({ key: "Cc", val: formatTextAddresses(message.cc) }); } if (message.bcc && message.bcc.length) { rows.push({ key: "Bcc", val: formatTextAddresses(message.bcc) }); } let maxKeyLength = rows.map((r) => r.key.length).reduce((acc, cur) => { return cur > acc ? cur : acc; }, 0); rows = rows.flatMap((row) => { let sepLen = maxKeyLength - row.key.length; let prefix = `${row.key}: ${" ".repeat(sepLen)}`; let emptyPrefix = `${" ".repeat(row.key.length + 1)} ${" ".repeat(sepLen)}`; let foldedLines = foldLines(row.val, 80, true).split(/\r?\n/).map((line) => line.trim()); return foldedLines.map((line, i) => `${i ? emptyPrefix : prefix}${line}`); }); let maxLineLength = rows.map((r) => r.length).reduce((acc, cur) => { return cur > acc ? cur : acc; }, 0); let lineMarker = "-".repeat(maxLineLength); let template = ` ${lineMarker} ${rows.join("\n")} ${lineMarker} `; return template; } function formatHtmlHeader(message) { let rows = []; if (message.from) { rows.push( `<div class="postal-email-header-key">From</div><div class="postal-email-header-value">${formatHtmlAddress(message.from)}</div>` ); } if (message.subject) { rows.push( `<div class="postal-email-header-key">Subject</div><div class="postal-email-header-value postal-email-header-subject">${escapeHtml( message.subject )}</div>` ); } if (message.date) { let dateOptions = { year: "numeric", month: "numeric", day: "numeric", hour: "numeric", minute: "numeric", second: "numeric", hour12: false }; let dateStr = typeof Intl === "undefined" ? message.date : new Intl.DateTimeFormat("default", dateOptions).format(new Date(message.date)); rows.push( `<div class="postal-email-header-key">Date</div><div class="postal-email-header-value postal-email-header-date" data-date="${escapeHtml( message.date )}">${escapeHtml(dateStr)}</div>` ); } if (message.to && message.to.length) { rows.push( `<div class="postal-email-header-key">To</div><div class="postal-email-header-value">${formatHtmlAddresses(message.to)}</div>` ); } if (message.cc && message.cc.length) { rows.push( `<div class="postal-email-header-key">Cc</div><div class="postal-email-header-value">${formatHtmlAddresses(message.cc)}</div>` ); } if (message.bcc && message.bcc.length) { rows.push( `<div class="postal-email-header-key">Bcc</div><div class="postal-email-header-value">${formatHtmlAddresses(message.bcc)}</div>` ); } let template = `<div class="postal-email-header">${rows.length ? '<div class="postal-email-header-row">' : ""}${rows.join( '</div>\n<div class="postal-email-header-row">' )}${rows.length ? "</div>" : ""}</div>`; return template; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { decodeHTMLEntities, escapeHtml, formatHtmlHeader, formatTextHeader, htmlToText, textToHtml }); // Make default export work naturally with require() if (module.exports.default) { var defaultExport = module.exports.default; var namedExports = {}; for (var key in module.exports) { if (key !== 'default' && key !== '__esModule') { namedExports[key] = module.exports[key]; } } module.exports = defaultExport; Object.assign(module.exports, namedExports); // Preserve __esModule and .default for bundler/transpiler interop Object.defineProperty(module.exports, '__esModule', { value: true }); module.exports.default = defaultExport; }