From 25bbe98d55f127c2c7e7f0554920b12d7d629262 Mon Sep 17 00:00:00 2001 From: Mendy Landa Date: Tue, 6 May 2025 21:40:46 +0300 Subject: [PATCH 1/7] feat(render): edge & cf worker support --- packages/render/src/browser/index.ts | 13 ++++--- packages/render/src/node/index.ts | 13 ++++--- .../render/src/shared/utils/edge-polyfill.ts | 37 +++++++++++++++++++ 3 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 packages/render/src/shared/utils/edge-polyfill.ts diff --git a/packages/render/src/browser/index.ts b/packages/render/src/browser/index.ts index 3d930a1cf7..d357c75a25 100644 --- a/packages/render/src/browser/index.ts +++ b/packages/render/src/browser/index.ts @@ -1,5 +1,6 @@ -import type { Options } from '../shared/options'; -import { render } from './render'; +import type { Options } from "../shared/options"; +import "../shared/utils/edge-polyfill"; +import { render } from "./render"; /** * @deprecated use {@link render} @@ -8,7 +9,7 @@ export const renderAsync = (element: React.ReactElement, options?: Options) => { return render(element, options); }; -export * from '../shared/options'; -export * from '../shared/plain-text-selectors'; -export * from '../shared/utils/pretty'; -export * from './render'; +export * from "../shared/options"; +export * from "../shared/plain-text-selectors"; +export * from "../shared/utils/pretty"; +export * from "./render"; diff --git a/packages/render/src/node/index.ts b/packages/render/src/node/index.ts index 3d930a1cf7..d357c75a25 100644 --- a/packages/render/src/node/index.ts +++ b/packages/render/src/node/index.ts @@ -1,5 +1,6 @@ -import type { Options } from '../shared/options'; -import { render } from './render'; +import type { Options } from "../shared/options"; +import "../shared/utils/edge-polyfill"; +import { render } from "./render"; /** * @deprecated use {@link render} @@ -8,7 +9,7 @@ export const renderAsync = (element: React.ReactElement, options?: Options) => { return render(element, options); }; -export * from '../shared/options'; -export * from '../shared/plain-text-selectors'; -export * from '../shared/utils/pretty'; -export * from './render'; +export * from "../shared/options"; +export * from "../shared/plain-text-selectors"; +export * from "../shared/utils/pretty"; +export * 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..eed9a0f061 --- /dev/null +++ b/packages/render/src/shared/utils/edge-polyfill.ts @@ -0,0 +1,37 @@ +/** + * 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. + */ + +if (typeof MessageChannel === "undefined") { + 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(); + } + } + + globalThis.MessageChannel = + MockMessageChannel as unknown as typeof MessageChannel; +} From 7420b736cde81231f87df201239925db188ec730 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 7 May 2025 07:13:07 -0300 Subject: [PATCH 2/7] lint --- packages/render/src/browser/index.ts | 14 +++++++------- packages/render/src/node/index.ts | 14 +++++++------- packages/render/src/shared/utils/edge-polyfill.ts | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/render/src/browser/index.ts b/packages/render/src/browser/index.ts index d357c75a25..79a24e2e3c 100644 --- a/packages/render/src/browser/index.ts +++ b/packages/render/src/browser/index.ts @@ -1,6 +1,6 @@ -import type { Options } from "../shared/options"; -import "../shared/utils/edge-polyfill"; -import { render } from "./render"; +import type { Options } from '../shared/options'; +import '../shared/utils/edge-polyfill'; +import { render } from './render'; /** * @deprecated use {@link render} @@ -9,7 +9,7 @@ export const renderAsync = (element: React.ReactElement, options?: Options) => { return render(element, options); }; -export * from "../shared/options"; -export * from "../shared/plain-text-selectors"; -export * from "../shared/utils/pretty"; -export * from "./render"; +export * from '../shared/options'; +export * from '../shared/plain-text-selectors'; +export * from '../shared/utils/pretty'; +export * from './render'; diff --git a/packages/render/src/node/index.ts b/packages/render/src/node/index.ts index d357c75a25..79a24e2e3c 100644 --- a/packages/render/src/node/index.ts +++ b/packages/render/src/node/index.ts @@ -1,6 +1,6 @@ -import type { Options } from "../shared/options"; -import "../shared/utils/edge-polyfill"; -import { render } from "./render"; +import type { Options } from '../shared/options'; +import '../shared/utils/edge-polyfill'; +import { render } from './render'; /** * @deprecated use {@link render} @@ -9,7 +9,7 @@ export const renderAsync = (element: React.ReactElement, options?: Options) => { return render(element, options); }; -export * from "../shared/options"; -export * from "../shared/plain-text-selectors"; -export * from "../shared/utils/pretty"; -export * from "./render"; +export * from '../shared/options'; +export * from '../shared/plain-text-selectors'; +export * from '../shared/utils/pretty'; +export * from './render'; diff --git a/packages/render/src/shared/utils/edge-polyfill.ts b/packages/render/src/shared/utils/edge-polyfill.ts index eed9a0f061..5e10ee6ef1 100644 --- a/packages/render/src/shared/utils/edge-polyfill.ts +++ b/packages/render/src/shared/utils/edge-polyfill.ts @@ -7,7 +7,7 @@ * We can remove this once MessageChannel is supported on all runtimes. */ -if (typeof MessageChannel === "undefined") { +if (typeof MessageChannel === 'undefined') { class MockMessagePort { onmessage: ((ev: MessageEvent) => void) | undefined; onmessageerror: ((ev: MessageEvent) => void) | undefined; From f9ab66bc310b225e761076fad4d932be9f6be9c6 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 7 May 2025 07:15:05 -0300 Subject: [PATCH 3/7] set sideEffects to true --- packages/render/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/render/package.json b/packages/render/package.json index 2dd36eefe6..b410c93a70 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -2,7 +2,7 @@ "name": "@react-email/render", "version": "1.1.0", "description": "Transform React components into HTML email templates", - "sideEffects": false, + "sideEffects": true, "main": "./dist/browser/index.js", "module": "./dist/browser/index.mjs", "types": "./dist/browser/index.d.ts", From 123c5ebf54d6c09f290d571b513d28efb881acd7 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 7 May 2025 08:37:15 -0300 Subject: [PATCH 4/7] remove sideEffects --- packages/render/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/render/package.json b/packages/render/package.json index b410c93a70..4b68da8dd0 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -2,7 +2,6 @@ "name": "@react-email/render", "version": "1.1.0", "description": "Transform React components into HTML email templates", - "sideEffects": true, "main": "./dist/browser/index.js", "module": "./dist/browser/index.mjs", "types": "./dist/browser/index.d.ts", From f3b1577a1b1f47b968d4b9b70b69b495d2aece4b Mon Sep 17 00:00:00 2001 From: Mendy Landa Date: Wed, 7 May 2025 14:50:47 +0300 Subject: [PATCH 5/7] specify polyfill sideEffect --- packages/render/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/render/package.json b/packages/render/package.json index 4b68da8dd0..862fb2895b 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -2,6 +2,9 @@ "name": "@react-email/render", "version": "1.1.0", "description": "Transform React components into HTML email templates", + "sideEffects": [ + "./src/shared/utils/edge-polyfill.ts" + ], "main": "./dist/browser/index.js", "module": "./dist/browser/index.mjs", "types": "./dist/browser/index.d.ts", From 35620af3187fc6641a7e2ba400506485952baac1 Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 7 May 2025 09:53:26 -0300 Subject: [PATCH 6/7] always polyfill in edge-light export --- packages/render/package.json | 10 +++++ packages/render/src/browser/index.ts | 7 +++- packages/render/src/edge-light/index.ts | 24 +++++++++++ packages/render/src/node/index.ts | 7 +++- .../render/src/shared/utils/edge-polyfill.ts | 41 +++++++++---------- packages/render/tsup.config.ts | 6 +++ 6 files changed, 72 insertions(+), 23 deletions(-) create mode 100644 packages/render/src/edge-light/index.ts diff --git a/packages/render/package.json b/packages/render/package.json index 862fb2895b..9eac5b9b1b 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -43,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 79a24e2e3c..a33d82a25f 100644 --- a/packages/render/src/browser/index.ts +++ b/packages/render/src/browser/index.ts @@ -1,5 +1,10 @@ +import { applyMessageChannelPolyfill } from '../shared/utils/edge-polyfill'; + +if (typeof MessageChannel === 'undefined') { + applyMessageChannelPolyfill(); +} + import type { Options } from '../shared/options'; -import '../shared/utils/edge-polyfill'; 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..0d034489f7 --- /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 79a24e2e3c..a33d82a25f 100644 --- a/packages/render/src/node/index.ts +++ b/packages/render/src/node/index.ts @@ -1,5 +1,10 @@ +import { applyMessageChannelPolyfill } from '../shared/utils/edge-polyfill'; + +if (typeof MessageChannel === 'undefined') { + applyMessageChannelPolyfill(); +} + import type { Options } from '../shared/options'; -import '../shared/utils/edge-polyfill'; import { render } from './render'; /** diff --git a/packages/render/src/shared/utils/edge-polyfill.ts b/packages/render/src/shared/utils/edge-polyfill.ts index 5e10ee6ef1..e86a0b84c6 100644 --- a/packages/render/src/shared/utils/edge-polyfill.ts +++ b/packages/render/src/shared/utils/edge-polyfill.ts @@ -6,32 +6,31 @@ * * We can remove this once MessageChannel is supported on all runtimes. */ +class MockMessagePort { + onmessage: ((ev: MessageEvent) => void) | undefined; + onmessageerror: ((ev: MessageEvent) => void) | undefined; -if (typeof MessageChannel === 'undefined') { - 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; - } + close() { } + postMessage(_message: unknown, _transfer: Transferable[] = []) { } + start() { } + addEventListener() { } + removeEventListener() { } + dispatchEvent(_event: Event): boolean { + return false; } +} - class MockMessageChannel { - port1: MockMessagePort; - port2: MockMessagePort; +class MockMessageChannel { + port1: MockMessagePort; + port2: MockMessagePort; - constructor() { - this.port1 = new MockMessagePort(); - this.port2 = new 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'], + }, ]); From 04db11fe126f875199eed6e88548e875f4a2ba3e Mon Sep 17 00:00:00 2001 From: gabriel miranda Date: Wed, 7 May 2025 09:57:57 -0300 Subject: [PATCH 7/7] lint --- packages/render/src/edge-light/index.ts | 10 +++++----- packages/render/src/shared/utils/edge-polyfill.ts | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/render/src/edge-light/index.ts b/packages/render/src/edge-light/index.ts index 0d034489f7..876d3c5dfc 100644 --- a/packages/render/src/edge-light/index.ts +++ b/packages/render/src/edge-light/index.ts @@ -1,9 +1,9 @@ /** - * 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 - */ + * 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(); diff --git a/packages/render/src/shared/utils/edge-polyfill.ts b/packages/render/src/shared/utils/edge-polyfill.ts index e86a0b84c6..f3d56a2cca 100644 --- a/packages/render/src/shared/utils/edge-polyfill.ts +++ b/packages/render/src/shared/utils/edge-polyfill.ts @@ -10,11 +10,11 @@ class MockMessagePort { onmessage: ((ev: MessageEvent) => void) | undefined; onmessageerror: ((ev: MessageEvent) => void) | undefined; - close() { } - postMessage(_message: unknown, _transfer: Transferable[] = []) { } - start() { } - addEventListener() { } - removeEventListener() { } + close() {} + postMessage(_message: unknown, _transfer: Transferable[] = []) {} + start() {} + addEventListener() {} + removeEventListener() {} dispatchEvent(_event: Event): boolean { return false; }