Decode common named HTML entities in renderToPlainText output - #54
Merged
ivoIturrieta merged 1 commit intoAug 13, 2026
Merged
Conversation
htmlToPlainText (behind the public renderToPlainText, which builds the text/plain MIME part of an email) only mapped ~17 named entities, so common ones such as £, €, é, ° and × leaked into the plaintext as literal entity text (e.g. "Price £5" instead of "Price £5"). Expand the named-entity map to cover currency, typographic punctuation, common symbols, and the Latin-1 accented letters used in European names/words. Kept as a curated map (not the full HTML5 named-reference set) so the ESM bundle stays within its CI size budget; anything omitted still decodes when written as a numeric entity. Unknown entities are still left untouched.
lucasbesen
approved these changes
Aug 13, 2026
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #54 +/- ##
=======================================
Coverage 95.50% 95.50%
=======================================
Files 31 31
Lines 1937 1937
Branches 382 382
=======================================
Hits 1850 1850
Misses 86 86
Partials 1 1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR proposes decoding the common named HTML entities (currency, typographic punctuation, symbols, and Latin-1 accented letters) that
renderToPlainTextcurrently leaks into the text/plain output as literal entity text. We include this PR work along with a full history of your repo at https://eastagiletracker.com/projects/280. You can sign in with your GitHub ID to claim ownership of the project.What this fixes
renderToPlainTextbuilds thetext/plainMIME part of an email — the parthtml-to-text.tsitself calls out as "critical for email deliverability: spam filters penalize HTML-only emails." Its decoder only mapped ~17 named entities, so common ones written in thehtmlprop or the<Html>component —£,€,¥,é,ç,°,×,½, smart quotes — were passed straight through as literal entity text. The plaintext part of the email then showedTotal £42.50instead ofTotal £42.50.Reproduced on
mainat HEAD (401e8bd) through the public API:The fix expands the named-entity map in
packages/shared/src/utils/html-to-text.tsto cover currency, typographic punctuation, common symbols, and the Latin-1 accented letters used in European names and words. It is kept as a curated map rather than the full HTML5 named-reference set (~2000 entries) so the shipped ESM bundle stays under the CI size budget; anything omitted still decodes when written as a numeric entity (£), and the numeric-entity path is untouched.How I verified it
The change is additive: unknown entities are still returned untouched, numeric entities are unaffected, and every previously-decoded entity is unchanged — so existing callers keep their output.
packages/shared/src/utils/html-to-text.test.tsthat fail onmainand pass with the fix (currency/symbols, typographic quotes, and Latin-1 accents), plus a null-case control asserting unknown entities are left as-is.pnpm test→ react 450 passed, shared 92 passed;pnpm --filter @unlayer/react-elements typecheckand the CSP safety gate both pass.packages/react/dist/index.jsis 74,629 bytes, within the 75,000-byte CI budget.How this was managed
This work was tracked on a live agile board imported from this repository's own issues and pull requests, on the story https://eastagiletracker.com/projects/280/stories/174647, with the board itself at https://eastagiletracker.com/projects/280.
If you'd rather not receive contributions like this, reply
no-more-prson this pull request and we won't open any further ones on your repositories.Lawrence W. Sinclair
CEO / East Agile
linkedin.com/in/lwsinclair/
eastagile.com