Skip to content

Commit

Permalink
feat(textversion): migrate from textversionjs to html-to-text (#86)
Browse files Browse the repository at this point in the history
* feat(textversion): migrate from textversion to html-to-text

Resolves #85

Signed-off-by: Niloy Sikdar <niloysikdar30@gmail.com>

* refactor(textemail): refactor the headingFormatter for genearting the plain text

Create a headingFormatter function and pass the level for each heading type for formatting

Resolves #85

Signed-off-by: Niloy Sikdar <niloysikdar30@gmail.com>

Signed-off-by: Niloy Sikdar <niloysikdar30@gmail.com>
  • Loading branch information
niloysikdar committed Aug 14, 2022
1 parent b124cd2 commit 18c4b11
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 20 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
]
},
"dependencies": {
"textversionjs": "^1.1.3"
"html-to-text": "^8.2.1"
},
"peerDependencies": {
"react": ">=16.8",
Expand All @@ -72,11 +72,11 @@
"@storybook/manager-webpack4": "6.5.9",
"@storybook/react": "^6.5.9",
"@storybook/testing-library": "^0.0.13",
"@types/html-to-text": "^8.1.0",
"@types/jest": "^28.1.3",
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"@types/textversionjs": "^1.1.1",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/parser": "^5.28.0",
"babel-jest": "^28.1.1",
Expand Down
4 changes: 3 additions & 1 deletion src/components/Image/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export const Image = ({
</td>
</tr>
<tr style={styles.captionSection}>
<td style={sx(styles.caption, { textAlign: captionAlign })}>{caption}</td>
<td style={sx(styles.caption, { textAlign: captionAlign })} id="imageCaption">
{caption}
</td>
</tr>
</tbody>
</table>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Preheader/Preheader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const Preheader = ({ text, classes, className }: PreheaderProps) => {
const styles = useStyles({ classes });

return (
<div style={styles.root} className={className}>
<div style={styles.root} className={className} id="preheader">
{text}
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Quote/Quote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const Quote = ({ children, classes, className }: QuoteProps) => {
const styles = useStyles({ classes });

return (
<div style={styles.root} className={className}>
<div style={styles.root} className={className} id="quote">
{children}
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/generateTextEmail/generateTextEmail.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('generateTextEmail', () => {
</Email>,
);

expect(text.trim()).toBe('[Click Here] (https://github.com/leopardslab/react-email)');
expect(text.trim()).toBe('Click Here [https://github.com/leopardslab/react-email]');
});

test('should return expected text from the Image component', () => {
Expand All @@ -46,7 +46,7 @@ describe('generateTextEmail', () => {
</Email>,
);

const expectedText = `![Alt text] (https://images.unsplash.com/photo-1453728013993-6d66e9c9123a)\nThis is a caption`;
const expectedText = `Alt text [https://images.unsplash.com/photo-1453728013993-6d66e9c9123a]\nThis is a caption`;

expect(text.trim()).toBe(expectedText);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { generateTextEmailFromHTML } from './generateTextEmailFromHTML';

const testHTML = `<p>To know more, <a href="https://github.com/leopardslab/react-email">Click Here</a>`;
const expectedText = `To know more, [Click Here] (https://github.com/leopardslab/react-email)`;
const expectedText = `To know more, Click Here [https://github.com/leopardslab/react-email]`;

describe('generateTextEmailFromHTML', () => {
test('should return expectedText if we pass testHTML', () => {
Expand Down
52 changes: 50 additions & 2 deletions src/utils/generateTextEmailFromHTML/generateTextEmailFromHTML.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,54 @@
import htmlToPlainText from 'textversionjs';
import { convert, DomNode, FormatOptions, RecursiveCallback } from 'html-to-text';
import { BlockTextBuilder } from 'html-to-text/lib/block-text-builder';

const headingFormatter =
(level: number) =>
(
elem: DomNode,
walk: RecursiveCallback,
builder: BlockTextBuilder,
formatOptions: FormatOptions,
) => {
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks || 1 });
builder.addInline('#'.repeat(level) + ' ');
walk(elem.children, builder);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 1 });
};

export const generateTextEmailFromHTML = (html: string): string => {
const plainText = htmlToPlainText(html, { headingStyle: 'hashify' });
const plainText = convert(html, {
formatters: {
h1Formatter: headingFormatter(1),
h2Formatter: headingFormatter(2),
h3Formatter: headingFormatter(3),
h4Formatter: headingFormatter(4),
h5Formatter: headingFormatter(5),
h6Formatter: headingFormatter(6),
captionFormatter: function (elem, walk, builder) {
builder.openBlock({ leadingLineBreaks: 1 });
walk(elem.children, builder);
builder.closeBlock({ trailingLineBreaks: 1 });
},
quoteFormatter: function (elem, walk, builder, formatOptions) {
builder.openBlock({ leadingLineBreaks: formatOptions.leadingLineBreaks || 1 });
builder.addInline('> ');
walk(elem.children, builder);
builder.closeBlock({ trailingLineBreaks: formatOptions.trailingLineBreaks || 1 });
},
},
selectors: [
{ selector: '#preheader', format: 'skip' },
{ selector: 'h1', format: 'h1Formatter', options: { uppercase: false } },
{ selector: 'h2', format: 'h2Formatter', options: { uppercase: false } },
{ selector: 'h3', format: 'h3Formatter', options: { uppercase: false } },
{ selector: 'h4', format: 'h4Formatter', options: { uppercase: false } },
{ selector: 'h5', format: 'h5Formatter', options: { uppercase: false } },
{ selector: 'h6', format: 'h6Formatter', options: { uppercase: false } },
{ selector: 'table', options: { uppercaseHeaderCells: false } },
{ selector: '#imageCaption', format: 'captionFormatter' },
{ selector: '#quote', format: 'quoteFormatter' },
],
});

return plainText;
};
83 changes: 73 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2848,6 +2848,14 @@
schema-utils "^3.0.0"
source-map "^0.7.3"

"@selderee/plugin-htmlparser2@^0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz#27e994afd1c2cb647ceb5406a185a5574188069d"
integrity sha512-J3jpy002TyBjd4N/p6s+s90eX42H2eRhK3SbsZuvTDv977/E8p2U3zikdiehyJja66do7FlxLomZLPlvl2/xaA==
dependencies:
domhandler "^4.2.0"
selderee "^0.6.0"

"@semantic-release/changelog@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/@semantic-release/changelog/-/changelog-6.0.1.tgz#8dd0334fd8c7d50cda747d2591e4f18f816b3c9c"
Expand Down Expand Up @@ -3966,6 +3974,11 @@
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57"
integrity sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==

"@types/html-to-text@^8.1.0":
version "8.1.0"
resolved "https://registry.yarnpkg.com/@types/html-to-text/-/html-to-text-8.1.0.tgz#dad0bf5d199f7e3f67eae50a36c13eadb1b56d1b"
integrity sha512-54YF2fGmN4g62/w+T85uQ8n0FyBhMY5cjKZ1imsbIh4Pgbeno1mAaQktC/pv/+C2ToUYkTZis9ADgn9GRRz9nQ==

"@types/is-function@^1.0.0":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.1.tgz#2d024eace950c836d9e3335a66b97960ae41d022"
Expand Down Expand Up @@ -4131,11 +4144,6 @@
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==

"@types/textversionjs@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/textversionjs/-/textversionjs-1.1.1.tgz#f31d5283d527f8cef9f6a4ee62cadf85e95f56b0"
integrity sha512-xXa08oZ76+J2rS36guKd6zJFAbkRtUnuDb0R6OFtY93VTuXdi94k0ycsIBrsq/Av8DmiQVfTYE3k7KCEUVYsag==

"@types/uglify-js@*":
version "3.16.0"
resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.16.0.tgz#2cf74a0e6ebb6cd54c0d48e509d5bd91160a9602"
Expand Down Expand Up @@ -6750,6 +6758,11 @@ dir-glob@^3.0.0, dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"

discontinuous-range@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==

doctrine@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
Expand Down Expand Up @@ -8419,6 +8432,18 @@ html-tags@^3.1.0:
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.2.0.tgz#dbb3518d20b726524e4dd43de397eb0a95726961"
integrity sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==

html-to-text@^8.2.1:
version "8.2.1"
resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-8.2.1.tgz#4a75b8a1b646149bd71c50527adb568990bf459b"
integrity sha512-aN/3JvAk8qFsWVeE9InWAWueLXrbkoVZy0TkzaGhoRBC2gCFEeRLDDJN3/ijIGHohy6H+SZzUQWN/hcYtaPK8w==
dependencies:
"@selderee/plugin-htmlparser2" "^0.6.0"
deepmerge "^4.2.2"
he "^1.2.0"
htmlparser2 "^6.1.0"
minimist "^1.2.6"
selderee "^0.6.0"

html-void-elements@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/html-void-elements/-/html-void-elements-1.0.5.tgz#ce9159494e86d95e45795b166c2021c2cfca4483"
Expand Down Expand Up @@ -10732,6 +10757,11 @@ modify-values@^1.0.0:
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==

moo@^0.5.0, moo@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==

move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
Expand Down Expand Up @@ -10806,6 +10836,16 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==

nearley@^2.20.1:
version "2.20.1"
resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474"
integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==
dependencies:
commander "^2.19.0"
moo "^0.5.0"
railroad-diagrams "^1.0.0"
randexp "0.4.6"

negotiator@0.6.3, negotiator@^0.6.2, negotiator@^0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
Expand Down Expand Up @@ -11648,6 +11688,14 @@ parse5@^6.0.0:
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==

parseley@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.7.0.tgz#9949e3a0ed05c5072adb04f013c2810cf49171a8"
integrity sha512-xyOytsdDu077M3/46Am+2cGXEKM9U9QclBDv7fimY7e+BBlxh2JcBp2mgNsmkyA9uvgyTjVzDi7cP1v4hcFxbw==
dependencies:
moo "^0.5.1"
nearley "^2.20.1"

parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
Expand Down Expand Up @@ -12200,11 +12248,24 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==

railroad-diagrams@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==

ramda@^0.28.0:
version "0.28.0"
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.28.0.tgz#acd785690100337e8b063cab3470019be427cc97"
integrity sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==

randexp@0.4.6:
version "0.4.6"
resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==
dependencies:
discontinuous-range "1.0.0"
ret "~0.1.10"

randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
Expand Down Expand Up @@ -12952,6 +13013,13 @@ schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
ajv "^6.12.5"
ajv-keywords "^3.5.2"

selderee@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.6.0.tgz#f3bee66cfebcb6f33df98e4a1df77388b42a96f7"
integrity sha512-ibqWGV5aChDvfVdqNYuaJP/HnVBhlRGSRrlbttmlMpHcLuTqqbMH36QkSs9GEgj5M88JDYLI8eyP94JaQ8xRlg==
dependencies:
parseley "^0.7.0"

semantic-release@^19.0.3:
version "19.0.3"
resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-19.0.3.tgz#9291053ad9890052f28e7c5921d4741530d516fd"
Expand Down Expand Up @@ -13894,11 +13962,6 @@ text-table@^0.2.0, text-table@~0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==

textversionjs@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/textversionjs/-/textversionjs-1.1.3.tgz#1b700aef780467786882e28ab126f77ca326a1e8"
integrity sha512-yZbBK7+1KRkgTJFOeIkCbQSZ+jR9ojDO/KrUKN3xEA6hA/DCMJ+aMWqjZ0rpxBJDjesbP795P5NEw+j3NnWJtA==

throat@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
Expand Down

0 comments on commit 18c4b11

Please sign in to comment.