diff --git a/.changeset/smart-bananas-lie.md b/.changeset/smart-bananas-lie.md new file mode 100644 index 0000000000..d699dd16a7 --- /dev/null +++ b/.changeset/smart-bananas-lie.md @@ -0,0 +1,5 @@ +--- +"@react-email/render": patch +--- + +Fix unstable rendering when prettifying with the Preview component diff --git a/packages/render/src/shared/utils/__snapshots__/pretty.spec.ts.snap b/packages/render/src/shared/utils/__snapshots__/pretty.spec.ts.snap index 88ffa4bfac..46d4e12fbe 100644 --- a/packages/render/src/shared/utils/__snapshots__/pretty.spec.ts.snap +++ b/packages/render/src/shared/utils/__snapshots__/pretty.spec.ts.snap @@ -4,3 +4,158 @@ exports[`pretty > if mso syntax does not wrap 1`] = ` "<!--[if mso]><i style="mso-font-width:100%;mso-text-raise:12" hidden>  </i><![endif]--> " `; + +exports[`pretty > should prettify Preview component's complex characters correctly 1`] = ` +"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<!--$--> +<html dir="ltr" lang="en"> + <head> + <meta content="text/html; charset=UTF-8" http-equiv="Content-Type" /> + <meta name="x-apple-disable-message-reformatting" /> + </head> + <div + style="display:none;overflow:hidden;line-height:1px;opacity:0;max-height:0;max-width:0"> + You're now ready to make live transactions with Stripe! + <div> + + </div> + </div> + <body + style='background-color:#f6f9fc;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif'> + <table + align="center" + width="100%" + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="max-width:37.5em;background-color:#ffffff;margin:0 auto;padding:20px 0 48px;margin-bottom:64px"> + <tbody> + <tr style="width:100%"> + <td> + <table + align="center" + width="100%" + border="0" + cellpadding="0" + cellspacing="0" + role="presentation" + style="padding:0 48px"> + <tbody> + <tr> + <td> + <img + alt="Stripe" + height="21" + src="/static/stripe-logo.png" + style="display:block;outline:none;border:none;text-decoration:none" + width="49" /> + <hr + style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" /> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + Thanks for submitting your account information. + You're now ready to make live transactions with + Stripe! + </p> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + You can view your payments and a variety of other + information about your account right from your dashboard. + </p> + <a + href="https://dashboard.stripe.com/login" + style="line-height:100%;text-decoration:none;display:block;max-width:100%;mso-padding-alt:0px;background-color:#656ee8;border-radius:5px;color:#fff;font-size:16px;font-weight:bold;text-align:center;width:100%;padding:10px 10px 10px 10px" + target="_blank" + ><span + <!--[if mso]><i style="mso-font-width:500%;mso-text-raise:15" hidden> </i><![endif]--> + ><span + style="max-width:100%;display:inline-block;line-height:120%;mso-padding-alt:0px;mso-text-raise:7.5px" + >View your Stripe Dashboard</span + ><span + <!--[if mso]><i style="mso-font-width:500%" hidden> ​</i><![endif]--> + ></a + > + <hr + style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" /> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + If you haven't finished your integration, you might + find our<!-- --> + <a + href="https://stripe.com/docs" + style="color:#556cd6;text-decoration-line:none" + target="_blank" + >docs</a + > + <!-- -->handy. + </p> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + Once you're ready to start accepting payments, + you'll just need to use your live<!-- --> + <a + href="https://dashboard.stripe.com/login?redirect=%2Fapikeys" + style="color:#556cd6;text-decoration-line:none" + target="_blank" + >API keys</a + > + <!-- -->instead of your test API keys. Your account can + simultaneously be used for both test and live requests, so + you can continue testing while accepting live payments. + Check out our<!-- --> + <a + href="https://stripe.com/docs/dashboard" + style="color:#556cd6;text-decoration-line:none" + target="_blank" + >tutorial about account basics</a + >. + </p> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + Finally, we've put together a<!-- --> + <a + href="https://stripe.com/docs/checklist/website" + style="color:#556cd6;text-decoration-line:none" + target="_blank" + >quick checklist</a + > + <!-- -->to ensure your website conforms to card network + standards. + </p> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + We'll be here to help you with any step along the + way. You can find answers to most questions and get in + touch with us on our<!-- --> + <a + href="https://support.stripe.com/" + style="color:#556cd6;text-decoration-line:none" + target="_blank" + >support site</a + >. + </p> + <p + style="font-size:16px;line-height:24px;margin:16px 0;color:#525f7f;text-align:left"> + — The Stripe team + </p> + <hr + style="width:100%;border:none;border-top:1px solid #eaeaea;border-color:#e6ebf1;margin:20px 0" /> + <p + style="font-size:12px;line-height:16px;margin:16px 0;color:#8898aa"> + Stripe, 354 Oyster Point Blvd, South San Francisco, CA + 94080 + </p> + </td> + </tr> + </tbody> + </table> + </td> + </tr> + </tbody> + </table> + </body> +</html> +<!--/$--> +" +`; diff --git a/packages/render/src/shared/utils/pretty.spec.ts b/packages/render/src/shared/utils/pretty.spec.ts index f188d62679..e0d5ea24e7 100644 --- a/packages/render/src/shared/utils/pretty.spec.ts +++ b/packages/render/src/shared/utils/pretty.spec.ts @@ -1,6 +1,17 @@ +import { promises as fs } from 'node:fs'; +import path from 'node:path'; import { pretty } from './pretty'; describe('pretty', () => { + it("should prettify Preview component's complex characters correctly", async () => { + const stripeHTML = await fs.readFile( + path.resolve(__dirname, './stripe-email.html'), + 'utf8', + ); + + expect(await pretty(stripeHTML)).toMatchSnapshot(); + }); + test('if mso syntax does not wrap', async () => { expect( await pretty( diff --git a/packages/render/src/shared/utils/pretty.ts b/packages/render/src/shared/utils/pretty.ts index 9078dd8693..d999a84aa9 100644 --- a/packages/render/src/shared/utils/pretty.ts +++ b/packages/render/src/shared/utils/pretty.ts @@ -35,7 +35,7 @@ const defaults: Options = { }; export const pretty = (str: string, options: Options = {}) => { - return format(str, { + return format(str.replaceAll('\0', ''), { ...defaults, ...options, }); diff --git a/packages/render/src/shared/utils/stripe-email.html b/packages/render/src/shared/utils/stripe-email.html new file mode 100644 index 0000000000..6345cd7593 Binary files /dev/null and b/packages/render/src/shared/utils/stripe-email.html differ