react-email-rails 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +307 -170
- data/lib/generators/react_email_rails/email_generator.rb +0 -1
- data/lib/react_email_rails/action_mailer.rb +9 -5
- data/lib/react_email_rails/mailer_context.rb +40 -0
- data/lib/react_email_rails/version.rb +1 -1
- data/lib/react_email_rails.rb +2 -2
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58d96423ee9be99d650a3ad7d164cbb8aca228ccbcc2b36d1a9be2bac2e06172
|
|
4
|
+
data.tar.gz: 370303cb64b83fd747009f59291ddf5f4598bd5670651ed89ec23edcf4b1072e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ccee408e14a4f0b831ae58d19ce6891687b273a9f017e6b97184bdbeef7a79107a4e7ba32b4e5db551c4160e2a85b62509c9c85e1ee53bca394b77857af4e2c9
|
|
7
|
+
data.tar.gz: 262e70dd3acaf9b6bd0e8d1854649bb4a475634c4b0569c69dd2498f3a8bd9093bc33e89e35fe2c6785315247838122d081e4a26206c73d8d51da97698aa31a3
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0
|
|
4
|
+
|
|
5
|
+
- Every `react:` email now receives `mailer` and `message` props, mirroring Action Mailer's `mailer`/`message` ERB view helpers. Rendering now happens after Action Mailer assigns headers, so `message` includes subject, addressing, and default `from`/`reply_to` values. Per-mail and shared props win on conflict, serializers whose `as_json` returns a Hash receive the context, and collection props keep their original shape. The npm package exports matching `Mailer`/`Message` TypeScript types.
|
|
6
|
+
|
|
3
7
|
## 0.5.0
|
|
4
8
|
|
|
5
9
|
- Add `react_email_share` for sharing props across every `react:` email a mailer renders, mirroring inertia-rails' `inertia_share`. Supports static values, lazy lambdas and blocks (evaluated in the mailer instance), `only`/`except`/`if`/`unless` filters, and subclass inheritance. Per-mail props win over shared props.
|
data/README.md
CHANGED
|
@@ -2,15 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
# react-email-rails
|
|
4
4
|
|
|
5
|
-
Build and send
|
|
5
|
+
Build and send Action Mailer emails with [React Email](https://react.email), TypeScript, and Vite.
|
|
6
|
+
|
|
7
|
+
react-email-rails lets Rails render React Email components into HTML and plain text, then deliver them through the Action Mailer stack you already know: previews, headers, callbacks, queues, instrumentation, and delivery all keep working normally.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
HTML email is still awkward. React Email gives you a nicer component model, email-safe primitives, Tailwind support, and TypeScript. This gem connects that workflow to Rails without replacing Action Mailer.
|
|
12
|
+
|
|
13
|
+
You get:
|
|
14
|
+
|
|
15
|
+
- React Email components rendered from `mail(...)`
|
|
16
|
+
- HTML and plain-text output from the same component
|
|
17
|
+
- Rails mailer previews, tests, queues, callbacks, and delivery
|
|
18
|
+
- Vite-powered development rendering
|
|
19
|
+
- A production renderer bundle built during `assets:precompile`
|
|
20
|
+
- Optional persistent rendering for high-volume workers
|
|
21
|
+
- Optional server-side rendering for `@react-email/editor` documents
|
|
22
|
+
|
|
23
|
+
## Status
|
|
24
|
+
|
|
25
|
+
react-email-rails is pre-1.0. It was extracted from [XOXO](https://xoxo.email), which is pre-launch, so the API may still change and it has not yet been battle-tested in high-volume production.
|
|
26
|
+
|
|
27
|
+
The supported Ruby, Rails, Node, React, and Vite versions are tested in CI. Please [open an issue](https://github.com/heysupertape/react-email-rails/issues) for bugs, rough edges, or integration feedback.
|
|
6
28
|
|
|
7
29
|
## Contents
|
|
8
30
|
|
|
9
|
-
- [Why](#why)
|
|
10
|
-
- [How](#how)
|
|
11
|
-
- [Status](#status)
|
|
12
31
|
- [Requirements](#requirements)
|
|
13
|
-
- [
|
|
32
|
+
- [Installation](#installation)
|
|
33
|
+
- [Your First Email](#your-first-email)
|
|
34
|
+
- [How Rendering Works](#how-rendering-works)
|
|
14
35
|
- [Usage](#usage)
|
|
15
36
|
- [Configuration](#configuration)
|
|
16
37
|
- [Deployment](#deployment)
|
|
@@ -19,73 +40,58 @@ Build and send emails using React and Rails with [React Email](https://react.ema
|
|
|
19
40
|
- [Security](#security)
|
|
20
41
|
- [License](#license)
|
|
21
42
|
|
|
22
|
-
## Why
|
|
23
|
-
|
|
24
|
-
Building HTML emails is painfully archaic. [React Email](https://react.email) brings React, Tailwind, and TypeScript to email templates. This gem wires it into Action Mailer so React components deliver as generated HTML and text emails.
|
|
25
|
-
|
|
26
|
-
## How
|
|
27
|
-
|
|
28
|
-
**In development,** the gem renders components live through Vite's dev pipeline, so your emails get the same module resolution and transforms as the rest of your frontend.
|
|
29
|
-
|
|
30
|
-
**In production,** Rails builds a server-side renderer bundle during `assets:precompile` using `reactEmailRails()` in your Vite config for discovery and options.
|
|
31
|
-
|
|
32
|
-
react-email-rails automatically renders both HTML and plain-text versions from the same component. Delivery, headers, previews, queues, and callbacks all stay normal Action Mailer. If rendering fails, no email is sent and `ReactEmailRails::RenderError` is raised.
|
|
33
|
-
|
|
34
|
-
## Status
|
|
35
|
-
|
|
36
|
-
**react-email-rails is pre-1.0.** It was extracted from [XOXO](https://xoxo.email) which is pre-launch, so it hasn't been battle-tested in high-volume production environments yet. It's tested in CI across the supported Ruby, Rails, Node, and Vite versions, but the API may still change before 1.0. Please [share feedback and report issues](https://github.com/heysupertape/react-email-rails/issues).
|
|
37
|
-
|
|
38
43
|
## Requirements
|
|
39
44
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
| Dependency | Version |
|
|
46
|
+
|------------|---------|
|
|
47
|
+
| Ruby | >= 3.3 |
|
|
48
|
+
| Rails | Action Mailer, Active Support, and Railties >= 7.1 and < 9.0 |
|
|
49
|
+
| Node | >= 20.19 |
|
|
50
|
+
| Vite | 7 or 8 |
|
|
51
|
+
| React | 18 or 19 |
|
|
52
|
+
| `@react-email/render` | 2.x |
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
We recommend [rails_vite](https://github.com/skryukov/rails_vite/) for Vite in Rails apps.
|
|
48
55
|
|
|
49
|
-
##
|
|
56
|
+
## Installation
|
|
50
57
|
|
|
51
|
-
Add the gem:
|
|
58
|
+
Add the Ruby gem:
|
|
52
59
|
|
|
53
60
|
```ruby
|
|
54
61
|
# Gemfile
|
|
55
|
-
|
|
56
62
|
gem "react-email-rails"
|
|
57
63
|
```
|
|
58
64
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
Run the installer:
|
|
65
|
+
Then install it with Rails:
|
|
62
66
|
|
|
63
67
|
```sh
|
|
68
|
+
bundle install
|
|
64
69
|
bin/rails generate react_email_rails:install
|
|
65
70
|
```
|
|
66
71
|
|
|
67
|
-
|
|
72
|
+
The installer creates `config/initializers/react_email_rails.rb`, installs missing JavaScript dependencies when it can detect your package manager, adds `reactEmailRails()` to `vite.config.*`, and creates `app/javascript/emails`.
|
|
68
73
|
|
|
69
|
-
|
|
74
|
+
After installation, the normal Rails flow applies:
|
|
70
75
|
|
|
71
|
-
- `bin/rails generate react_email_rails:email
|
|
72
|
-
- `bin/dev`
|
|
73
|
-
- `bin/rails assets:precompile`
|
|
74
|
-
- `bin/rails react_email_rails:build`
|
|
76
|
+
- Generate mailers and components with `bin/rails generate react_email_rails:email ...`.
|
|
77
|
+
- Run `bin/dev` in development; email components render through Vite on demand.
|
|
78
|
+
- Run `bin/rails assets:precompile` for production; the renderer bundle builds automatically.
|
|
79
|
+
- Run `bin/rails react_email_rails:build` directly when CI or tests need the bundle without the full asset task.
|
|
75
80
|
|
|
76
|
-
### Manual
|
|
81
|
+
### Manual Setup
|
|
77
82
|
|
|
78
|
-
|
|
83
|
+
If you prefer to wire things up yourself, install the npm package and React Email dependencies:
|
|
79
84
|
|
|
80
85
|
```sh
|
|
81
86
|
npm i react-email-rails @react-email/render @react-email/components react react-dom
|
|
82
87
|
```
|
|
83
88
|
|
|
84
|
-
|
|
89
|
+
Use the equivalent command for pnpm, Yarn, or Bun if your app uses a different package manager.
|
|
90
|
+
|
|
91
|
+
Then add the Vite plugin:
|
|
85
92
|
|
|
86
93
|
```ts
|
|
87
94
|
// vite.config.ts
|
|
88
|
-
|
|
89
95
|
import { defineConfig } from "vite"
|
|
90
96
|
import { reactEmailRails } from "react-email-rails"
|
|
91
97
|
|
|
@@ -94,7 +100,7 @@ export default defineConfig({
|
|
|
94
100
|
})
|
|
95
101
|
```
|
|
96
102
|
|
|
97
|
-
|
|
103
|
+
## Your First Email
|
|
98
104
|
|
|
99
105
|
Generate a mailer and React Email component:
|
|
100
106
|
|
|
@@ -102,13 +108,15 @@ Generate a mailer and React Email component:
|
|
|
102
108
|
bin/rails generate react_email_rails:email Account welcome
|
|
103
109
|
```
|
|
104
110
|
|
|
105
|
-
The generator follows Rails' mailer generator shape: `NAME [method method]`. It creates
|
|
111
|
+
The generator follows Rails' mailer generator shape: `NAME [method method]`. It creates a mailer, React component, mailer preview, and test. It also reads `emails.path` and `emails.extension` from `reactEmailRails()` when available.
|
|
112
|
+
|
|
113
|
+
Pass flags when you need to override the detected component directory or extension:
|
|
106
114
|
|
|
107
115
|
```sh
|
|
108
116
|
bin/rails generate react_email_rails:email Account welcome --emails-path=app/emails --extension=jsx
|
|
109
117
|
```
|
|
110
118
|
|
|
111
|
-
|
|
119
|
+
Update the generated mailer to pass props into the React component:
|
|
112
120
|
|
|
113
121
|
```ruby
|
|
114
122
|
class AccountMailer < ApplicationMailer
|
|
@@ -128,11 +136,10 @@ class AccountMailer < ApplicationMailer
|
|
|
128
136
|
end
|
|
129
137
|
```
|
|
130
138
|
|
|
131
|
-
|
|
139
|
+
Then edit the generated component:
|
|
132
140
|
|
|
133
141
|
```tsx
|
|
134
142
|
// app/javascript/emails/account_mailer/welcome.tsx
|
|
135
|
-
|
|
136
143
|
import { Body, Container, Html, Text } from "@react-email/components"
|
|
137
144
|
|
|
138
145
|
type WelcomeProps = {
|
|
@@ -154,29 +161,70 @@ export default function Welcome({ account }: WelcomeProps) {
|
|
|
154
161
|
}
|
|
155
162
|
```
|
|
156
163
|
|
|
157
|
-
|
|
164
|
+
Deliver it like any other Action Mailer email:
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
AccountMailer.with(account: current_account).welcome.deliver_later
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
React Email also provides primitives like [`<Button>`, `<Heading>`, `<Tailwind>`, and more](https://react.email/docs/components/html).
|
|
171
|
+
|
|
172
|
+
## How Rendering Works
|
|
173
|
+
|
|
174
|
+
In development, react-email-rails renders components through Vite's dev pipeline. Your email components get the same module resolution and transforms as the rest of your frontend.
|
|
175
|
+
|
|
176
|
+
In production, `assets:precompile` builds a server-side renderer bundle from your Vite config. Rails runs that bundle with Node whenever an email needs to render.
|
|
158
177
|
|
|
159
|
-
|
|
178
|
+
Every `react:` email renders HTML and plain text from the same component. If rendering fails, the email is not sent and `ReactEmailRails::RenderError` is raised.
|
|
160
179
|
|
|
161
180
|
## Usage
|
|
162
181
|
|
|
163
|
-
|
|
182
|
+
### Passing Props
|
|
183
|
+
|
|
184
|
+
Top-level keys passed to `react:` become props on the component's default export. The API is intentionally close to [inertia-rails](https://inertia-rails.dev), so apps using both libraries should feel consistent.
|
|
164
185
|
|
|
165
186
|
```ruby
|
|
166
|
-
mail
|
|
187
|
+
mail(
|
|
188
|
+
to: account.email,
|
|
189
|
+
subject: "Welcome",
|
|
190
|
+
react: {
|
|
191
|
+
account: {
|
|
192
|
+
name: account.name,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
)
|
|
167
196
|
```
|
|
168
197
|
|
|
169
198
|
```tsx
|
|
170
|
-
|
|
199
|
+
type WelcomeProps = {
|
|
200
|
+
account: {
|
|
201
|
+
name: string
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export default function Welcome({ account }: WelcomeProps) {
|
|
171
206
|
// ...
|
|
172
207
|
}
|
|
173
208
|
```
|
|
174
209
|
|
|
175
|
-
###
|
|
210
|
+
### Component Inference
|
|
176
211
|
|
|
177
|
-
|
|
212
|
+
By default, react-email-rails infers the component from the mailer and action:
|
|
178
213
|
|
|
179
|
-
|
|
214
|
+
| Mailer action | Component |
|
|
215
|
+
|---------------|-----------|
|
|
216
|
+
| `AccountMailer#welcome` | `account_mailer/welcome` |
|
|
217
|
+
| `Users::InviteMailer#new_invite` | `users/invite_mailer/new_invite` |
|
|
218
|
+
|
|
219
|
+
`AccountMailer#welcome` resolves to `app/javascript/emails/account_mailer/welcome.tsx` or `.jsx` with the default Vite options.
|
|
220
|
+
|
|
221
|
+
Use `react: true` to render the inferred component. By default it renders with no props, which is handy for emails that take none:
|
|
222
|
+
|
|
223
|
+
```ruby
|
|
224
|
+
mail(to: account.email, subject: "Welcome", react: true)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
To have `react: true` build props automatically, enable `use_react_instance_props`. The component then receives the mailer's instance variables as props:
|
|
180
228
|
|
|
181
229
|
```ruby
|
|
182
230
|
class AccountMailer < ApplicationMailer
|
|
@@ -184,33 +232,22 @@ class AccountMailer < ApplicationMailer
|
|
|
184
232
|
|
|
185
233
|
def welcome
|
|
186
234
|
@account = params.fetch(:account)
|
|
187
|
-
|
|
235
|
+
|
|
236
|
+
mail(to: @account.email, subject: "Welcome", react: true)
|
|
188
237
|
end
|
|
189
238
|
end
|
|
190
239
|
```
|
|
191
240
|
|
|
192
|
-
Action Mailer's framework assigns
|
|
193
|
-
|
|
194
|
-
Without `use_react_instance_props`, `react: true` still infers the component and renders it with no props, which is handy for emails that take no props at all.
|
|
195
|
-
|
|
196
|
-
#### Implicit Component, Explicit Props
|
|
241
|
+
Action Mailer's framework assigns, including `params` and `rendered_format`, are excluded from instance props.
|
|
197
242
|
|
|
198
|
-
|
|
199
|
-
mail(
|
|
200
|
-
...
|
|
201
|
-
react: {
|
|
202
|
-
account: {
|
|
203
|
-
name: account.name,
|
|
204
|
-
},
|
|
205
|
-
},
|
|
206
|
-
)
|
|
207
|
-
```
|
|
243
|
+
### Explicit Components
|
|
208
244
|
|
|
209
|
-
|
|
245
|
+
Pass a component name when the mailer action and component path do not line up:
|
|
210
246
|
|
|
211
247
|
```ruby
|
|
212
248
|
mail(
|
|
213
|
-
|
|
249
|
+
to: account.email,
|
|
250
|
+
subject: "Welcome",
|
|
214
251
|
react: "accounts/welcome",
|
|
215
252
|
props: {
|
|
216
253
|
account: {
|
|
@@ -220,110 +257,168 @@ mail(
|
|
|
220
257
|
)
|
|
221
258
|
```
|
|
222
259
|
|
|
260
|
+
To change naming globally, override `component_path_resolver` in your [Rails configuration](#rails-configuration).
|
|
261
|
+
|
|
223
262
|
### Shared Props
|
|
224
263
|
|
|
225
|
-
Use `react_email_share` to
|
|
264
|
+
Use `react_email_share` to merge props into every `react:` email for a mailer and its subclasses:
|
|
226
265
|
|
|
227
266
|
```ruby
|
|
228
267
|
class MarketingMailer < ApplicationMailer
|
|
229
|
-
# Static value
|
|
230
268
|
react_email_share app_name: "Acme"
|
|
231
269
|
|
|
232
|
-
|
|
233
|
-
react_email_share unread_count: -> { current_user&.unread_count }
|
|
270
|
+
react_email_share unread_count: -> { params[:account]&.unread_count }
|
|
234
271
|
|
|
235
|
-
# Block, evaluated lazily in the mailer instance
|
|
236
272
|
react_email_share do
|
|
237
273
|
{ brand: { name: "Acme", url: marketing_url } }
|
|
238
274
|
end
|
|
239
275
|
end
|
|
240
276
|
```
|
|
241
277
|
|
|
242
|
-
Per-mail props win over shared props of the same name
|
|
278
|
+
Per-mail props win over shared props of the same name:
|
|
243
279
|
|
|
244
280
|
```ruby
|
|
245
|
-
mail
|
|
281
|
+
mail(
|
|
282
|
+
to: account.email,
|
|
283
|
+
subject: "Welcome",
|
|
284
|
+
react: {
|
|
285
|
+
app_name: "Acme Pro",
|
|
286
|
+
},
|
|
287
|
+
)
|
|
246
288
|
```
|
|
247
289
|
|
|
248
|
-
Shared props
|
|
290
|
+
Shared props work with `react:` hashes, `react: true`, and explicit `react: "component", props: ...` calls.
|
|
249
291
|
|
|
250
|
-
|
|
292
|
+
### Conditional Shared Props
|
|
251
293
|
|
|
252
|
-
|
|
294
|
+
`react_email_share` accepts the same filter options as `before_action`: `only`, `except`, `if`, and `unless`.
|
|
253
295
|
|
|
254
296
|
```ruby
|
|
255
297
|
react_email_share only: [:welcome, :reactivation] do
|
|
256
|
-
{ promotion:
|
|
298
|
+
{ promotion: params.fetch(:promotion) }
|
|
257
299
|
end
|
|
258
300
|
|
|
259
|
-
react_email_share if: :
|
|
260
|
-
{
|
|
301
|
+
react_email_share if: :account_active? do
|
|
302
|
+
{ account: { name: params.fetch(:account).name } }
|
|
261
303
|
end
|
|
262
304
|
```
|
|
263
305
|
|
|
264
|
-
You can also share
|
|
306
|
+
You can also share props inside an action before calling `mail`:
|
|
265
307
|
|
|
266
308
|
```ruby
|
|
267
309
|
def welcome
|
|
310
|
+
account = params.fetch(:account)
|
|
311
|
+
|
|
268
312
|
react_email_share notice: "Thanks for joining!"
|
|
269
|
-
|
|
313
|
+
|
|
314
|
+
mail(
|
|
315
|
+
to: account.email,
|
|
316
|
+
subject: "Welcome",
|
|
317
|
+
react: { account: account.as_json(only: [:name]) },
|
|
318
|
+
)
|
|
270
319
|
end
|
|
271
320
|
```
|
|
272
321
|
|
|
273
|
-
|
|
322
|
+
### Deep Merging Shared Props
|
|
323
|
+
|
|
324
|
+
Shared props are merged shallowly by default. That means a per-mail prop replaces a shared prop with the same name.
|
|
274
325
|
|
|
275
|
-
|
|
326
|
+
Pass `deep_merge: true` to merge nested hashes instead:
|
|
276
327
|
|
|
277
328
|
```ruby
|
|
278
329
|
react_email_share do
|
|
279
330
|
{ settings: { theme: "light", locale: "en" } }
|
|
280
331
|
end
|
|
281
332
|
|
|
282
|
-
|
|
283
|
-
|
|
333
|
+
mail(
|
|
334
|
+
to: account.email,
|
|
335
|
+
subject: "Welcome",
|
|
336
|
+
react: {
|
|
337
|
+
settings: {
|
|
338
|
+
theme: "dark",
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
deep_merge: true,
|
|
342
|
+
)
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The component receives:
|
|
284
346
|
|
|
285
|
-
|
|
286
|
-
|
|
347
|
+
```ruby
|
|
348
|
+
{ settings: { theme: "dark", locale: "en" } }
|
|
287
349
|
```
|
|
288
350
|
|
|
289
|
-
Set `config.deep_merge_shared_props = true` to make deep merging the default for every email.
|
|
351
|
+
Set `config.deep_merge_shared_props = true` to make deep merging the default for every email.
|
|
290
352
|
|
|
291
|
-
###
|
|
353
|
+
### Mailer and Message Props
|
|
292
354
|
|
|
293
|
-
|
|
355
|
+
Every `react:` email receives `mailer` and `message` props, mirroring the [`mailer` and `message` view helpers](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-view-helpers) available to Action Mailer ERB views.
|
|
294
356
|
|
|
295
|
-
|
|
357
|
+
`mailer` identifies the mailer action. `message` reflects the email after Action Mailer has assigned headers and defaults, including default `from` and `reply_to` values. With the default prop transform, import the `Mailer` and `Message` types to annotate them:
|
|
296
358
|
|
|
297
|
-
|
|
359
|
+
```tsx
|
|
360
|
+
import type { Mailer, Message } from "react-email-rails"
|
|
361
|
+
import { Body, Container, Html, Text } from "@react-email/components"
|
|
298
362
|
|
|
299
|
-
|
|
363
|
+
type WelcomeProps = {
|
|
364
|
+
account: { name: string }
|
|
365
|
+
mailer: Mailer
|
|
366
|
+
message: Message
|
|
367
|
+
}
|
|
300
368
|
|
|
301
|
-
|
|
369
|
+
export default function Welcome({ account, mailer, message }: WelcomeProps) {
|
|
370
|
+
return (
|
|
371
|
+
<Html>
|
|
372
|
+
<Body>
|
|
373
|
+
<Container>
|
|
374
|
+
<Text>Welcome, {account.name}</Text>
|
|
375
|
+
<Text>Re: {message.subject}</Text>
|
|
376
|
+
</Container>
|
|
377
|
+
</Body>
|
|
378
|
+
</Html>
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
```
|
|
302
382
|
|
|
303
|
-
|
|
|
304
|
-
|
|
305
|
-
| `
|
|
306
|
-
| `
|
|
383
|
+
| Prop | Example |
|
|
384
|
+
|------|---------|
|
|
385
|
+
| `mailer.mailerName` | `"account_mailer"` |
|
|
386
|
+
| `mailer.actionName` | `"welcome"` |
|
|
387
|
+
| `message.subject` | `"Welcome"` |
|
|
388
|
+
| `message.to` | `["account@example.com"]` |
|
|
389
|
+
| `message.cc`, `message.bcc` | `["…"]` or `null` |
|
|
390
|
+
| `message.from`, `message.replyTo` | `["app@example.com"]` |
|
|
391
|
+
|
|
392
|
+
Context is merged before prop serialization, so keys follow `config.transform_props` just like your own props. The exported TypeScript types describe the default `:lower_camel` shape.
|
|
307
393
|
|
|
308
|
-
|
|
394
|
+
Per-mail and shared props win on conflict, so a prop named `mailer` or `message` overrides the injected context. Serializer props receive the context when `as_json` returns a hash. Collections, arrays, and other non-object values pass through unchanged so their top-level shape is preserved.
|
|
309
395
|
|
|
310
|
-
|
|
396
|
+
### Prop Serialization
|
|
311
397
|
|
|
312
|
-
|
|
398
|
+
Props are serialized with `as_json`, just like `render json:`. You can pass hashes, arrays, Active Model objects, and serializer output from libraries such as [Alba](https://github.com/okuramasafumi/alba) or [ActiveModel::Serializer](https://github.com/rails-api/active_model_serializers).
|
|
313
399
|
|
|
314
|
-
|
|
315
|
-
|
|
400
|
+
Prop keys are camelized by default, so `plan_name` arrives in React as `planName`. See [Prop Transformation](#prop-transformation) to change that behavior.
|
|
401
|
+
|
|
402
|
+
### Component Files
|
|
403
|
+
|
|
404
|
+
Files and directories starting with `_` are ignored as renderable email entries by default. Use them for shared components, layouts, and helpers:
|
|
405
|
+
|
|
406
|
+
```text
|
|
407
|
+
app/javascript/emails/
|
|
408
|
+
account_mailer/
|
|
409
|
+
welcome.tsx
|
|
410
|
+
_components/
|
|
411
|
+
email_layout.tsx
|
|
316
412
|
```
|
|
317
413
|
|
|
318
|
-
|
|
414
|
+
Ignored files can still be imported by email components.
|
|
319
415
|
|
|
320
416
|
### Layouts
|
|
321
417
|
|
|
322
|
-
Action Mailer layouts
|
|
418
|
+
Action Mailer layouts are not applied to `react:` emails. In React Email, layouts are normal React components:
|
|
323
419
|
|
|
324
420
|
```tsx
|
|
325
421
|
// app/javascript/emails/_components/email_layout.tsx
|
|
326
|
-
|
|
327
422
|
import { Body, Container, Html } from "@react-email/components"
|
|
328
423
|
import type { ReactNode } from "react"
|
|
329
424
|
|
|
@@ -344,7 +439,6 @@ export function EmailLayout({ children }: EmailLayoutProps) {
|
|
|
344
439
|
|
|
345
440
|
```tsx
|
|
346
441
|
// app/javascript/emails/account_mailer/welcome.tsx
|
|
347
|
-
|
|
348
442
|
import { Text } from "@react-email/components"
|
|
349
443
|
import { EmailLayout } from "../_components/email_layout"
|
|
350
444
|
|
|
@@ -357,21 +451,28 @@ export default function Welcome() {
|
|
|
357
451
|
}
|
|
358
452
|
```
|
|
359
453
|
|
|
360
|
-
|
|
454
|
+
### Editor Documents
|
|
361
455
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
If you're also using [@react-email/editor](https://react.email/docs/editor) to let users compose emails inside your app, `ReactEmailRails.compose` can render those stored documents on the server.
|
|
456
|
+
If your app uses [@react-email/editor](https://react.email/docs/editor) to let users compose emails visually, `ReactEmailRails.compose` can render stored editor documents on the server.
|
|
365
457
|
|
|
366
458
|
See [Editor rendering](docs/editor.md) for setup and usage.
|
|
367
459
|
|
|
368
460
|
## Configuration
|
|
369
461
|
|
|
370
|
-
Configuration
|
|
462
|
+
Configuration lives in two places:
|
|
463
|
+
|
|
464
|
+
- Rails configuration controls mailer behavior, prop handling, rendering mode, timeouts, and error hooks.
|
|
465
|
+
- Vite configuration controls email component discovery and the renderer bundle.
|
|
371
466
|
|
|
372
467
|
### Rails Configuration
|
|
373
468
|
|
|
374
|
-
|
|
469
|
+
Override Rails-side defaults in `config/initializers/react_email_rails.rb`:
|
|
470
|
+
|
|
471
|
+
```ruby
|
|
472
|
+
ReactEmailRails.configure do |config|
|
|
473
|
+
# config.render_mode = :persistent
|
|
474
|
+
end
|
|
475
|
+
```
|
|
375
476
|
|
|
376
477
|
| Option | Default |
|
|
377
478
|
|--------|---------|
|
|
@@ -384,14 +485,14 @@ If the defaults don't fit, override them in `config/initializers/react_email_rai
|
|
|
384
485
|
| `on_render_error` | `nil` |
|
|
385
486
|
| `deep_merge_shared_props` | `false` |
|
|
386
487
|
|
|
387
|
-
|
|
488
|
+
### Prop Transformation
|
|
388
489
|
|
|
389
|
-
Set `transform_props` to
|
|
490
|
+
Set `transform_props` to choose how Ruby prop keys are exposed to React:
|
|
390
491
|
|
|
391
492
|
| Value | Example |
|
|
392
493
|
|-------|---------|
|
|
393
494
|
| `:camel` | `AccountName` |
|
|
394
|
-
| `:lower_camel`
|
|
495
|
+
| `:lower_camel` | `accountName` |
|
|
395
496
|
| `:dash` | `account-name` |
|
|
396
497
|
| `:snake` | `account_name` |
|
|
397
498
|
| `:none` | preserves serialized keys |
|
|
@@ -402,15 +503,15 @@ ReactEmailRails.configure do |config|
|
|
|
402
503
|
end
|
|
403
504
|
```
|
|
404
505
|
|
|
405
|
-
|
|
506
|
+
Only prop keys are transformed. Values are always serialized with `as_json`.
|
|
406
507
|
|
|
407
|
-
|
|
508
|
+
### Render Modes
|
|
408
509
|
|
|
409
|
-
`:subprocess` starts a fresh Node process for each render. It
|
|
510
|
+
The default `:subprocess` mode starts a fresh Node process for each render. It is simple, isolated, and always uses the latest bundle, but it pays Node startup and bundle load time for each email.
|
|
410
511
|
|
|
411
|
-
`:persistent`
|
|
512
|
+
`:persistent` mode keeps one long-lived Node child process per Ruby process. It is faster for render-heavy workers, but uses more memory and can serve a stale component until the child is recycled.
|
|
412
513
|
|
|
413
|
-
|
|
514
|
+
Switch to persistent mode when Node startup shows up in traces or when a worker renders many emails from the same bundle:
|
|
414
515
|
|
|
415
516
|
```ruby
|
|
416
517
|
ReactEmailRails.configure do |config|
|
|
@@ -418,21 +519,22 @@ ReactEmailRails.configure do |config|
|
|
|
418
519
|
end
|
|
419
520
|
```
|
|
420
521
|
|
|
421
|
-
Persistent mode
|
|
522
|
+
Persistent mode details:
|
|
422
523
|
|
|
423
|
-
- Renders are newline-delimited JSON and processed one at a time.
|
|
424
|
-
-
|
|
425
|
-
-
|
|
524
|
+
- Renders are newline-delimited JSON and processed one at a time.
|
|
525
|
+
- Throughput scales with more worker processes.
|
|
526
|
+
- It is fork-safe; clustered Puma and forking job runners spawn one Node child per worker.
|
|
527
|
+
- The child recycles after `render_process_max_requests` renders. Set that option to `nil` to disable recycling.
|
|
426
528
|
|
|
427
|
-
|
|
529
|
+
### Render Options
|
|
428
530
|
|
|
429
|
-
`render_options` is passed to [@react-email/render](https://react.email/docs/utilities/render). Use `html` and `text` keys
|
|
531
|
+
`render_options` is passed to [@react-email/render](https://react.email/docs/utilities/render). Use `html` and `text` keys to configure each output. Option keys are camelized before they cross into JavaScript.
|
|
430
532
|
|
|
431
533
|
```ruby
|
|
432
534
|
ReactEmailRails.configure do |config|
|
|
433
535
|
config.render_options = {
|
|
434
536
|
html: {
|
|
435
|
-
pretty: Rails.env.development
|
|
537
|
+
pretty: Rails.env.development?,
|
|
436
538
|
},
|
|
437
539
|
text: {
|
|
438
540
|
html_to_text_options: {
|
|
@@ -443,9 +545,11 @@ ReactEmailRails.configure do |config|
|
|
|
443
545
|
end
|
|
444
546
|
```
|
|
445
547
|
|
|
446
|
-
|
|
548
|
+
`render_options` can also be a callable. When rendering from a mailer, it is evaluated in the mailer instance so it can use helpers or per-email state.
|
|
549
|
+
|
|
550
|
+
### Error Reporting
|
|
447
551
|
|
|
448
|
-
Use `on_render_error` to report failures before the exception is re-raised
|
|
552
|
+
Use `on_render_error` to report failures before the exception is re-raised:
|
|
449
553
|
|
|
450
554
|
```ruby
|
|
451
555
|
ReactEmailRails.configure do |config|
|
|
@@ -455,33 +559,50 @@ ReactEmailRails.configure do |config|
|
|
|
455
559
|
end
|
|
456
560
|
```
|
|
457
561
|
|
|
458
|
-
|
|
562
|
+
The callback receives the error plus render context such as `kind:` and `component:`.
|
|
459
563
|
|
|
460
|
-
|
|
564
|
+
### Instrumentation
|
|
565
|
+
|
|
566
|
+
Every render emits `render.react-email-rails` through [ActiveSupport::Notifications](https://guides.rubyonrails.org/active_support_instrumentation.html). Email payloads include `kind`, `component`, and successful HTML size in `html_bytes`.
|
|
461
567
|
|
|
462
568
|
```ruby
|
|
463
569
|
ActiveSupport::Notifications.subscribe("render.react-email-rails") do |event|
|
|
464
|
-
Rails.logger.info(
|
|
570
|
+
Rails.logger.info(
|
|
571
|
+
"[react-email-rails] Rendered #{event.payload[:component]} " \
|
|
572
|
+
"(#{event.duration.round}ms, #{event.payload[:html_bytes]} bytes)",
|
|
573
|
+
)
|
|
465
574
|
end
|
|
466
575
|
```
|
|
467
576
|
|
|
468
577
|
### Vite Configuration
|
|
469
578
|
|
|
470
|
-
Most apps only need the
|
|
579
|
+
Most apps only need the plugin from [Installation](#installation):
|
|
580
|
+
|
|
581
|
+
```ts
|
|
582
|
+
import { defineConfig } from "vite"
|
|
583
|
+
import { reactEmailRails } from "react-email-rails"
|
|
584
|
+
|
|
585
|
+
export default defineConfig({
|
|
586
|
+
plugins: [reactEmailRails()],
|
|
587
|
+
})
|
|
588
|
+
```
|
|
471
589
|
|
|
472
|
-
|
|
590
|
+
The isolated renderer loads `reactEmailRails()`, JSX support, and component-facing Vite config such as `resolve`, `define`, `css`, `json`, `assetsInclude`, `esbuild`, and `oxc`. It does not load your other app plugins unless you explicitly add them with `vite.plugins`.
|
|
473
591
|
|
|
474
|
-
|
|
592
|
+
Server, preview, dependency optimization, and build output settings stay owned by react-email-rails so Rails can always find the renderer bundle.
|
|
593
|
+
|
|
594
|
+
### Vite Plugin Options
|
|
475
595
|
|
|
476
596
|
| Option | Default | Description |
|
|
477
597
|
|--------|---------|-------------|
|
|
478
598
|
| `emails.path` | `"app/javascript/emails"` | Directory containing email components |
|
|
479
599
|
| `emails.extension` | `[".tsx", ".jsx"]` | Component extension, or an array of extensions |
|
|
480
600
|
| `emails.ignore` | `["**/_*", "**/_*/**"]` | Glob patterns ignored under `emails.path` |
|
|
601
|
+
| `documents` | `false` | Optional `@react-email/editor` document renderer discovery; see [Editor rendering](docs/editor.md) |
|
|
481
602
|
| `standalone` | `true` | Inline production renderer bundle dependencies |
|
|
482
603
|
| `vite` | `{}` | Extra email-only Vite config for compilation and resolution |
|
|
483
604
|
|
|
484
|
-
Use a custom directory:
|
|
605
|
+
Use a custom email directory:
|
|
485
606
|
|
|
486
607
|
```ts
|
|
487
608
|
reactEmailRails({
|
|
@@ -489,7 +610,7 @@ reactEmailRails({
|
|
|
489
610
|
})
|
|
490
611
|
```
|
|
491
612
|
|
|
492
|
-
Use
|
|
613
|
+
Use custom extensions:
|
|
493
614
|
|
|
494
615
|
```ts
|
|
495
616
|
reactEmailRails({
|
|
@@ -499,11 +620,11 @@ reactEmailRails({
|
|
|
499
620
|
})
|
|
500
621
|
```
|
|
501
622
|
|
|
502
|
-
Component names come from the Vite directory layout
|
|
623
|
+
Component names come from the Vite directory layout. To map mailer actions to a different layout, override `component_path_resolver` on the Ruby side so both halves stay in sync.
|
|
503
624
|
|
|
504
|
-
|
|
625
|
+
### Email-Only Vite Plugins
|
|
505
626
|
|
|
506
|
-
Most apps
|
|
627
|
+
Most apps do not need extra email plugins. If email components need a transform that is not part of Vite's default pipeline, add it to the isolated renderer:
|
|
507
628
|
|
|
508
629
|
```ts
|
|
509
630
|
import mdx from "@mdx-js/rollup"
|
|
@@ -521,13 +642,13 @@ export default defineConfig({
|
|
|
521
642
|
})
|
|
522
643
|
```
|
|
523
644
|
|
|
524
|
-
|
|
645
|
+
Only `assetsInclude`, `css`, `define`, `esbuild`, `json`, `oxc`, `plugins`, and `resolve` are accepted inside `vite`. Output settings such as `build.outDir` and `build.rollupOptions` are ignored.
|
|
525
646
|
|
|
526
|
-
|
|
647
|
+
### Standalone Builds
|
|
527
648
|
|
|
528
|
-
By default the production renderer bundle inlines React, `@react-email/render`, and other Node dependencies. This works well for Rails deploys that build assets in one stage and run without `node_modules` in the final image.
|
|
649
|
+
By default, the production renderer bundle inlines React, `@react-email/render`, and other Node dependencies. This works well for Rails deploys that build assets in one stage and run without `node_modules` in the final image.
|
|
529
650
|
|
|
530
|
-
Set `standalone: false` when your runtime
|
|
651
|
+
Set `standalone: false` when your runtime ships `node_modules` and you prefer a smaller SSR-style bundle:
|
|
531
652
|
|
|
532
653
|
```ts
|
|
533
654
|
reactEmailRails({
|
|
@@ -535,45 +656,43 @@ reactEmailRails({
|
|
|
535
656
|
})
|
|
536
657
|
```
|
|
537
658
|
|
|
538
|
-
Externalized bundles
|
|
659
|
+
Externalized bundles can be smaller and may build faster, but the externalized packages must be available at runtime.
|
|
539
660
|
|
|
540
661
|
## Deployment
|
|
541
662
|
|
|
542
|
-
|
|
663
|
+
Production deploys should run the normal Rails asset task:
|
|
543
664
|
|
|
544
665
|
```sh
|
|
545
666
|
bin/rails assets:precompile
|
|
546
667
|
```
|
|
547
668
|
|
|
548
|
-
|
|
669
|
+
react-email-rails hooks `react_email_rails:build` into `assets:precompile`. The build task loads `reactEmailRails()` options from your Vite config and writes `tmp/react-email-rails/emails.js` with the email component registry. If [Editor rendering](docs/editor.md) is enabled, the bundle also includes document renderers.
|
|
549
670
|
|
|
550
|
-
You can run
|
|
671
|
+
You can run the renderer build directly:
|
|
551
672
|
|
|
552
673
|
```sh
|
|
553
674
|
bin/rails react_email_rails:build
|
|
554
675
|
```
|
|
555
676
|
|
|
556
|
-
Production rendering
|
|
557
|
-
|
|
558
|
-
The npm package, Vite, React, and `@react-email/render` must be available when Rails runs `assets:precompile`. If you enable [Editor document rendering](docs/editor.md), its peer dependencies must be available too.
|
|
677
|
+
Production rendering requires that bundle. If it is missing, rendering raises `ReactEmailRails::RenderError` and Action Mailer does not send the email.
|
|
559
678
|
|
|
560
|
-
|
|
679
|
+
Set `SKIP_REACT_EMAIL_RAILS_BUILD=1` to skip the automatic asset hook. Directly running `bin/rails react_email_rails:build` always attempts the build.
|
|
561
680
|
|
|
562
|
-
The
|
|
681
|
+
The npm package, Vite, React, and `@react-email/render` must be available when Rails runs `assets:precompile`. If [Editor rendering](docs/editor.md) is enabled, its peer dependencies must be available too.
|
|
563
682
|
|
|
564
|
-
The
|
|
683
|
+
The Ruby gem and npm package must stay on the same version. A protocol/version handshake catches mismatched installs and raises an actionable `ReactEmailRails::RenderError`.
|
|
565
684
|
|
|
566
|
-
### Renderer
|
|
685
|
+
### Verify the Renderer
|
|
567
686
|
|
|
568
|
-
|
|
687
|
+
Run this before relying on the production renderer:
|
|
569
688
|
|
|
570
689
|
```sh
|
|
571
690
|
bin/rails react_email_rails:verify
|
|
572
691
|
```
|
|
573
692
|
|
|
574
|
-
It checks that the render command runs and that the npm package version matches the gem
|
|
693
|
+
It checks that the render command runs and that the npm package version matches the gem. It exits non-zero with an actionable message on failure, so it is safe to wire into CI or release steps.
|
|
575
694
|
|
|
576
|
-
For programmatic checks
|
|
695
|
+
For programmatic checks, `ReactEmailRails.healthy?` returns a boolean:
|
|
577
696
|
|
|
578
697
|
```ruby
|
|
579
698
|
Rails.application.config.after_initialize do
|
|
@@ -583,13 +702,31 @@ Rails.application.config.after_initialize do
|
|
|
583
702
|
end
|
|
584
703
|
```
|
|
585
704
|
|
|
705
|
+
If you check at boot, scope it to processes that send mail so the rest of the app does not pay the cost.
|
|
706
|
+
|
|
586
707
|
## Development
|
|
587
708
|
|
|
588
709
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for local setup, checks, formatting, and release verification.
|
|
589
710
|
|
|
711
|
+
The short version:
|
|
712
|
+
|
|
713
|
+
```sh
|
|
714
|
+
bundle install
|
|
715
|
+
cd vite && pnpm install
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Run the core checks before opening a pull request:
|
|
719
|
+
|
|
720
|
+
```sh
|
|
721
|
+
ruby scripts/check_version_sync.rb
|
|
722
|
+
bin/test
|
|
723
|
+
bin/lint
|
|
724
|
+
cd vite && pnpm run build
|
|
725
|
+
```
|
|
726
|
+
|
|
590
727
|
## Contributing
|
|
591
728
|
|
|
592
|
-
Bug reports and pull requests are welcome
|
|
729
|
+
Bug reports and pull requests are welcome. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request so the local checks and release expectations are clear.
|
|
593
730
|
|
|
594
731
|
## Security
|
|
595
732
|
|
|
@@ -597,4 +734,4 @@ Please report vulnerabilities privately. See [SECURITY.md](SECURITY.md) for deta
|
|
|
597
734
|
|
|
598
735
|
## License
|
|
599
736
|
|
|
600
|
-
The gem and npm package are available as open source under the terms of the [MIT License](LICENSE.md).
|
|
737
|
+
The Ruby gem and npm package are available as open source under the terms of the [MIT License](LICENSE.md).
|
|
@@ -164,7 +164,6 @@ class ReactEmailRails::Generators::EmailGenerator < Rails::Generators::NamedBase
|
|
|
164
164
|
)
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
-
# First capture-group match across an ordered list of patterns, or nil.
|
|
168
167
|
def first_capture(source, *patterns)
|
|
169
168
|
patterns.each do |pattern|
|
|
170
169
|
match = source[pattern, 1]
|
|
@@ -13,8 +13,6 @@ module ReactEmailRails::ActionMailer
|
|
|
13
13
|
self.react_email_use_instance_props = true
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
# Share props with every `react:` email this mailer and its subclasses render.
|
|
17
|
-
# Mirrors inertia-rails `inertia_share`; `only`/`except`/`if`/`unless` go to `before_action`.
|
|
18
16
|
def react_email_share(hash = nil, **props, &block)
|
|
19
17
|
options = props.slice(*SHARED_FILTER_OPTIONS)
|
|
20
18
|
data = hash || props.except(*SHARED_FILTER_OPTIONS)
|
|
@@ -25,7 +23,6 @@ module ReactEmailRails::ActionMailer
|
|
|
25
23
|
end
|
|
26
24
|
end
|
|
27
25
|
|
|
28
|
-
# Share props from within an action, e.g. conditionally before calling `mail`.
|
|
29
26
|
def react_email_share(hash = nil, **props, &block)
|
|
30
27
|
react_email_append_shared(hash || props, block)
|
|
31
28
|
end
|
|
@@ -43,18 +40,25 @@ module ReactEmailRails::ActionMailer
|
|
|
43
40
|
resolved_props,
|
|
44
41
|
deep_merge: react_email_deep_merge?(deep_merge),
|
|
45
42
|
)
|
|
46
|
-
render_options = ReactEmailRails.configuration.resolve_render_options(self)
|
|
47
|
-
rendered = ReactEmailRails.render(component:, props: resolved_props, render_options:)
|
|
48
43
|
|
|
49
44
|
super(headers) do |format|
|
|
45
|
+
rendered = react_email_render(component, resolved_props)
|
|
46
|
+
|
|
50
47
|
format.html { rendered.html }
|
|
51
48
|
format.text { rendered.text } if rendered.text.present?
|
|
49
|
+
|
|
52
50
|
yield(format) if block
|
|
53
51
|
end
|
|
54
52
|
end
|
|
55
53
|
|
|
56
54
|
private
|
|
57
55
|
|
|
56
|
+
def react_email_render(component, props)
|
|
57
|
+
props = ReactEmailRails::MailerContext.new(self).merge_into(props)
|
|
58
|
+
render_options = ReactEmailRails.configuration.resolve_render_options(self)
|
|
59
|
+
ReactEmailRails.render(component:, props:, render_options:)
|
|
60
|
+
end
|
|
61
|
+
|
|
58
62
|
def react_email_append_shared(data, block)
|
|
59
63
|
store = (@_react_email_shared ||= [])
|
|
60
64
|
store << data.dup.freeze if data.present?
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class ReactEmailRails::MailerContext
|
|
2
|
+
MESSAGE_FIELDS = [:subject, :to, :cc, :bcc, :from, :reply_to].freeze
|
|
3
|
+
|
|
4
|
+
def initialize(mailer)
|
|
5
|
+
@mailer = mailer
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def merge_into(props)
|
|
9
|
+
serialized_props = props.as_json
|
|
10
|
+
return props unless serialized_props.is_a?(Hash)
|
|
11
|
+
|
|
12
|
+
to_h.merge(serialized_props)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_h
|
|
16
|
+
{
|
|
17
|
+
"mailer" => mailer_context,
|
|
18
|
+
"message" => message_context,
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
attr_reader(:mailer)
|
|
25
|
+
|
|
26
|
+
def mailer_context
|
|
27
|
+
{
|
|
28
|
+
"mailer_name" => mailer.class.mailer_name,
|
|
29
|
+
"action_name" => mailer.action_name,
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def message_context
|
|
34
|
+
message = mailer.message
|
|
35
|
+
|
|
36
|
+
MESSAGE_FIELDS.each_with_object({}) do |field, context|
|
|
37
|
+
context[field.to_s] = message.public_send(field)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
data/lib/react_email_rails.rb
CHANGED
|
@@ -26,6 +26,7 @@ require_relative("react_email_rails/configuration")
|
|
|
26
26
|
require_relative("react_email_rails/tasks")
|
|
27
27
|
require_relative("react_email_rails/props_resolver")
|
|
28
28
|
require_relative("react_email_rails/shared_props")
|
|
29
|
+
require_relative("react_email_rails/mailer_context")
|
|
29
30
|
require_relative("react_email_rails/railtie")
|
|
30
31
|
|
|
31
32
|
module ReactEmailRails
|
|
@@ -58,8 +59,7 @@ module ReactEmailRails
|
|
|
58
59
|
perform(payload:, label: type, kind: "document", type:)
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
#
|
|
62
|
-
# extensions. Pass exactly one of `html:` or `markdown:`.
|
|
62
|
+
# Returns an editor document Hash, built via the renderer's extensions. Pass exactly one of `html:`/`markdown:`.
|
|
63
63
|
def parse(type:, html: nil, markdown: nil, context: {})
|
|
64
64
|
payload = {
|
|
65
65
|
kind: "parse",
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: react-email-rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Supertape
|
|
@@ -153,6 +153,7 @@ files:
|
|
|
153
153
|
- lib/react_email_rails.rb
|
|
154
154
|
- lib/react_email_rails/action_mailer.rb
|
|
155
155
|
- lib/react_email_rails/configuration.rb
|
|
156
|
+
- lib/react_email_rails/mailer_context.rb
|
|
156
157
|
- lib/react_email_rails/props_resolver.rb
|
|
157
158
|
- lib/react_email_rails/railtie.rb
|
|
158
159
|
- lib/react_email_rails/render_error.rb
|