diff --git a/packages/shared/src/utils/html-to-text.test.ts b/packages/shared/src/utils/html-to-text.test.ts index 66ec89f4..a2e77da1 100644 --- a/packages/shared/src/utils/html-to-text.test.ts +++ b/packages/shared/src/utils/html-to-text.test.ts @@ -77,6 +77,39 @@ describe("htmlToPlainText", () => { expect(htmlToPlainText("©")).toBe("\u00A9"); // © }); + it("decodes common currency and symbol named entities", () => { + expect(htmlToPlainText("Price £5, €9, ¥100, ¢50")).toBe( + "Price £5, €9, ¥100, ¢50" + ); // pound euro yen cent + expect(htmlToPlainText("5° × ½ ÷ ±")).toBe( + "5° × ½ ÷ ±" + ); // deg times frac12 divide plusmn + expect(htmlToPlainText("§2 ¶3 µg")).toBe( + "§2 ¶3 µg" + ); // sect para micro + }); + + it("decodes typographic quote named entities", () => { + expect(htmlToPlainText("“Hi” ‘yo’")).toBe( + "“Hi” ‘yo’" + ); + }); + + it("decodes Latin-1 accented named entities", () => { + // Cafe resume naive Zoe Francois Munoz Strasse (with accents) + expect( + htmlToPlainText( + "Café résumé naïve Zoë François Muñoz Straße" + ) + ).toBe( + "Café résumé naïve Zoë François Muñoz Straße" + ); + }); + + it("leaves unknown named entities untouched", () => { + expect(htmlToPlainText("¬anentity; x")).toBe("¬anentity; x"); + }); + // Skip elements it("skips elements with data-skip-in-text attribute", () => { const html = '
Visible content
'; diff --git a/packages/shared/src/utils/html-to-text.ts b/packages/shared/src/utils/html-to-text.ts index 51be77a9..5fe0b02d 100644 --- a/packages/shared/src/utils/html-to-text.ts +++ b/packages/shared/src/utils/html-to-text.ts @@ -8,9 +8,18 @@ */ /** - * Common HTML entity map for decoding. + * Common HTML named entity map for decoding. + * + * Covers the entities that actually appear in email/marketing content — + * currency, typographic punctuation, common symbols, and the Latin-1 accented + * letters used in European names and words. Kept as a curated map (rather than + * the full HTML5 named-reference set, which is ~2000 entries) so the bundle + * stays within its size budget. Numeric entities (`{`, ``) are + * handled separately below, so anything missing here still decodes if written + * numerically. */ const HTML_ENTITIES: Record