70 lines · 2.5 KB
/**
 * Turns an emailed reply into comment text: keeps what the person wrote and drops the quoted original,
 * signatures and mobile footers that mail clients append.
 */

const WROTE_LINE = /^On\b.*\bwrote:\s*$/i;
const WROTE_END = /\bwrote:\s*$/i;
const ON_START = /^On\b/i;
const ORIGINAL_MESSAGE = /^-{2,}\s*(Original|Forwarded) Message\s*-{2,}/i;
const OUTLOOK_RULE = /^_{10,}\s*$/;
const HEADER_BLOCK_START = /^From:\s/i;
const HEADER_BLOCK_FIELD = /^(Sent|Date|To|Subject|Cc):\s/i;
const SIGNATURE = /^-- ?$/;
const MOBILE_FOOTER = /^Sent from my\s/i;
const HEADER_LOOKAHEAD = 4;
const WRAPPED_LOOKAHEAD = 2;

const HTML_ENTITIES: Record<string, string> = {
  "&amp;": "&",
  "&lt;": "<",
  "&gt;": ">",
  "&quot;": '"',
  "&#39;": "'",
  "&nbsp;": " ",
};

/** Plain text for HTML-only mail; quoted blocks are dropped before tags are stripped. */
export function htmlToReplyText(html: string): string {
  return html
    .replace(/<(script|style)[\s\S]*?<\/\1>/gi, "")
    .replace(/<blockquote[\s\S]*?<\/blockquote>/gi, "")
    .replace(/<div[^>]*class="[^"]*(gmail_quote|moz-cite-prefix)[^"]*"[\s\S]*$/i, "")
    .replace(/<br\s*\/?>/gi, "\n")
    .replace(/<\/(p|div|li|h[1-6])>/gi, "\n")
    .replace(/<[^>]+>/g, "")
    .replace(/&(amp|lt|gt|quot|#39|nbsp);/g, (entity) => HTML_ENTITIES[entity] ?? entity);
}

function startsQuote(lines: string[], index: number): boolean {
  const line = lines[index].trim();