diff --git a/packages/render/package.json b/packages/render/package.json index 2dd36eefe6..9eac5b9b1b 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -2,7 +2,9 @@ "name": "@react-email/render", "version": "1.1.0", "description": "Transform React components into HTML email templates", - "sideEffects": false, + "sideEffects": [ + "./src/shared/utils/edge-polyfill.ts" + ], "main": "./dist/browser/index.js", "module": "./dist/browser/index.mjs", "types": "./dist/browser/index.d.ts", @@ -41,6 +43,16 @@ "default": "./dist/browser/index.js" } }, + "edge-light": { + "import": { + "types": "./dist/edge-light/index.d.mts", + "default": "./dist/edge-light/index.mjs" + }, + "require": { + "types": "./dist/edge-light/index.d.ts", + "default": "./dist/edge-light/index.js" + } + }, "browser": { "import": { "types": "./dist/browser/index.d.mts", diff --git a/packages/render/src/browser/index.ts b/packages/render/src/browser/index.ts index 3d930a1cf7..a33d82a25f 100644 --- a/packages/render/src/browser/index.ts +++ b/packages/render/src/browser/index.ts @@ -1,3 +1,9 @@ +import { applyMessageChannelPolyfill } from '../shared/utils/edge-polyfill'; + +if (typeof MessageChannel === 'undefined') { + applyMessageChannelPolyfill(); +} + import type { Options } from '../shared/options'; import { render } from './render'; diff --git a/packages/render/src/edge-light/index.ts b/packages/render/src/edge-light/index.ts new file mode 100644 index 0000000000..876d3c5dfc --- /dev/null +++ b/packages/render/src/edge-light/index.ts @@ -0,0 +1,24 @@ +/** + * This is the export type for NextJS's edge runtime. We always apply the polyfill here + * since NextJS patches `MessageChannel` to be a function that throws an error when called. + * + * @see https://github.com/resend/react-email/pull/2222#issuecomment-2858463529 + */ +import { applyMessageChannelPolyfill } from '../shared/utils/edge-polyfill'; + +applyMessageChannelPolyfill(); + +import { render } from '../browser/render'; +import type { Options } from '../shared/options'; + +/** + * @deprecated use {@link render} + */ +export const renderAsync = (element: React.ReactElement, options?: Options) => { + return render(element, options); +}; + +export * from '../browser/render'; +export * from '../shared/options'; +export * from '../shared/plain-text-selectors'; +export * from '../shared/utils/pretty'; diff --git a/packages/render/src/node/index.ts b/packages/render/src/node/index.ts index 3d930a1cf7..a33d82a25f 100644 --- a/packages/render/src/node/index.ts +++ b/packages/render/src/node/index.ts @@ -1,3 +1,9 @@ +import { applyMessageChannelPolyfill } from '../shared/utils/edge-polyfill'; + +if (typeof MessageChannel === 'undefined') { + applyMessageChannelPolyfill(); +} + import type { Options } from '../shared/options'; import { render } from './render'; diff --git a/packages/render/src/shared/utils/edge-polyfill.ts b/packages/render/src/shared/utils/edge-polyfill.ts new file mode 100644 index 0000000000..f3d56a2cca --- /dev/null +++ b/packages/render/src/shared/utils/edge-polyfill.ts @@ -0,0 +1,36 @@ +/** + * MessageChannel is not supported in Edge, but it's not needed to successfully render. + * This polyfill is used to avoid errors when importing the package in an vercel edge and cf worker runtimes. + * + * @see https://github.com/resend/react-email/issues/1630#issuecomment-2773421899 + * + * We can remove this once MessageChannel is supported on all runtimes. + */ +class MockMessagePort { + onmessage: ((ev: MessageEvent) => void) | undefined; + onmessageerror: ((ev: MessageEvent) => void) | undefined; + + close() {} + postMessage(_message: unknown, _transfer: Transferable[] = []) {} + start() {} + addEventListener() {} + removeEventListener() {} + dispatchEvent(_event: Event): boolean { + return false; + } +} + +class MockMessageChannel { + port1: MockMessagePort; + port2: MockMessagePort; + + constructor() { + this.port1 = new MockMessagePort(); + this.port2 = new MockMessagePort(); + } +} + +export const applyMessageChannelPolyfill = () => { + globalThis.MessageChannel = + MockMessageChannel as unknown as typeof MessageChannel; +}; diff --git a/packages/render/tsup.config.ts b/packages/render/tsup.config.ts index da400f8668..1ba6f86223 100644 --- a/packages/render/tsup.config.ts +++ b/packages/render/tsup.config.ts @@ -13,4 +13,10 @@ export default defineConfig([ outDir: './dist/browser', format: ['cjs', 'esm'], }, + { + dts: true, + entry: ['./src/edge-light/index.ts'], + outDir: './dist/edge-light', + format: ['cjs', 'esm'], + }, ]);