Skip to content

Commit

Permalink
fixup! Upgrade to Next.js@15 and React@19
Browse files Browse the repository at this point in the history
  • Loading branch information
Vinnl committed Jan 23, 2025
1 parent f74a142 commit 7ac7a66
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/emails/react-dom-server.edge.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// This is needed because `renderEmail.ts` needs to import from server.edge
// This is needed because `StorybookEmailRenderer.tsx` needs to import from server.edge
// directly — see the comment in that file.
declare module "react-dom/server.edge" {
export * from "react-dom/server";
Expand Down
46 changes: 32 additions & 14 deletions src/emails/renderEmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,46 @@

import "../app/functions/server/notInClientComponent";
import mjml2html from "mjml";
import { Transform } from "node:stream";
import { ReactNode } from "react";

export async function renderEmail(emailTemplate: ReactNode): Promise<string> {
return mjml2html(await renderToString(emailTemplate), {
validationLevel: "strict",
beautify: false,
minify: false,
ignoreIncludes: true,
}).html;
}

async function renderToString(node: ReactNode): Promise<string> {
// Importing react-dom/server dynamically here is a workaround for the following error:
//
// Error: react-dom/server is not supported in React Server Components.
//
// It's a bit of a pain though, as it's the only reason this needs to be an
// async function.
// https://github.com/vercel/next.js/issues/43810#issuecomment-2437931415
const { renderToPipeableStream } = await import("react-dom/server");
// It looks like we can't use `renderToStaticMarkup`, as that results in the following error:
//
// Also, react-dom/server.edge is apparently needed instead of react-dom/server
// to avoid this error:
// Internal Error: do not use legacy react-dom/server APIs. If you encountered this error, please open an issue on the Next.js repo.
//
// Uncaught ReferenceError: MessageChannel is not defined
//
// See https://github.com/facebook/react/issues/31827#issuecomment-2563094822
const { renderToStaticMarkup } = await import("react-dom/server.edge");
return mjml2html(renderToStaticMarkup(emailTemplate), {
validationLevel: "strict",
beautify: false,
minify: false,
ignoreIncludes: true,
}).html;
// Hence this `renderToPipeableStream` mess.
const { pipe } = renderToPipeableStream(node);
return new Promise((resolve, reject) => {
const parts: string[] = [];
// It's not clear to me if there's a better to `pipe` a rendered `node` to a string,
// but this works and at this point I'm fed up, so leaving it at this.
// Feel free to replace by a better way.
const transformer = new Transform({
transform: (chunk, encoding, callback) => {
callback(null, chunk.toString());
},
});
transformer.on("data", (part) => parts.push(part));
transformer.on("error", reject);
transformer.on("end", () => {
return resolve(parts.join(""));
});
pipe(transformer);
});
}

0 comments on commit 7ac7a66

Please sign in to comment.