react-email-rails 0.12.1 → 0.12.2
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 +5 -0
- data/README.md +10 -1
- data/lib/generators/react_email_rails/email_generator.rb +5 -6
- data/lib/react_email_rails/configuration.rb +14 -12
- data/lib/react_email_rails/renderer.rb +6 -7
- data/lib/react_email_rails/tasks.rb +5 -7
- data/lib/react_email_rails/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fd436260ad38b370f59dbd8cfad0b09697634624b7c56d0917eee90dc583dc07
|
|
4
|
+
data.tar.gz: 9e5f81391d9864d46903129e4f4fd2971bca156a83c933c79f8d5e520331b0d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a73846be5159e9787b96ea08813051293b75ae697d35c40a25b42cfad9fef66f5c4df2d36369e2c0ca1824b2abfe178a64d8fd39cef4a293d83355b0f5074a3e
|
|
7
|
+
data.tar.gz: af1228467e11038502e528619e3ca8de5d2b635b97e949fb617aeeb0ff7b1a9ae34ed49fc6024911244ca3ba210b45e90ee15087be0ff17d03694656c3cb8459
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.2
|
|
4
|
+
|
|
5
|
+
- The development renderer keeps its Vite cache in `node_modules/.vite/react-email-rails` and skips dependency discovery. It previously reset the app dev server's `node_modules/.vite/deps` on start, so lazily imported dependencies 504ed until that server restarted.
|
|
6
|
+
- Add `config.js_runtime` (default `"node"`) to choose the executable that runs the development renderer, the production bundle, the build task, and the generator's Vite config probe. The gem now runs the package's `bin/*.mjs` scripts directly instead of relying on `node_modules/.bin` shims and their shebangs.
|
|
7
|
+
|
|
3
8
|
## 0.12.1
|
|
4
9
|
|
|
5
10
|
- Export `CamelMailer` and `CamelMessage` TypeScript types, plus a `Register` module augmentation (`propKeys: "camel"`) so apps that camelize props can type the injected `mailer` / `message` shapes without a local mapped type. Default `Mailer` / `Message` stay snake_case.
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# react-email-rails
|
|
4
4
|
|
|
5
|
-
Build and send Action Mailer emails with [React Email](https://react.email), TypeScript, and Vite.
|
|
5
|
+
Build and send Action Mailer emails with [React Email](https://react.email), Tailwind, TypeScript, and Vite.
|
|
6
6
|
|
|
7
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
8
|
|
|
@@ -427,6 +427,7 @@ end
|
|
|
427
427
|
| `component_path_resolver` | `->(mailer:, action:) { "#{mailer}/#{action}" }` |
|
|
428
428
|
| `prop_transformer` | `->(props:) { props }` |
|
|
429
429
|
| `render_options` | `{}` |
|
|
430
|
+
| `js_runtime` | `"node"` |
|
|
430
431
|
| `render_timeout` | `30` seconds in development, `10` seconds otherwise |
|
|
431
432
|
| `render_process_max_requests` | `1_000` |
|
|
432
433
|
| `on_render_error` | `nil` |
|
|
@@ -482,6 +483,14 @@ ReactEmailRails.configure do |config|
|
|
|
482
483
|
end
|
|
483
484
|
```
|
|
484
485
|
|
|
486
|
+
`js_runtime` names the executable that runs the renderer, the build task, and the generator's Vite config probe. It defaults to `node` from `PATH`. Point it at an absolute path when Node lives behind a version manager the app process cannot see, or at another runtime such as `bun`. Only Node is tested in CI:
|
|
487
|
+
|
|
488
|
+
```ruby
|
|
489
|
+
ReactEmailRails.configure do |config|
|
|
490
|
+
config.js_runtime = "/usr/local/bin/node"
|
|
491
|
+
end
|
|
492
|
+
```
|
|
493
|
+
|
|
485
494
|
### Render Options
|
|
486
495
|
|
|
487
496
|
`render_options` is passed to [@react-email/render](https://react.email/docs/utilities/render). HTML is rendered once; plain text is derived from that markup with `toPlainText` (or `unstableToPlainText` when `unstable_text_conversion` is set). `html.pretty` is applied only to the HTML part, so it does not change the text body. `text.html_to_text_options` is forwarded to `toPlainText`. Option keys are camelized before they cross into JavaScript, so `html_to_text_options` becomes `htmlToTextOptions`.
|
|
@@ -8,7 +8,7 @@ module ReactEmailRails; end
|
|
|
8
8
|
module ReactEmailRails::Generators; end
|
|
9
9
|
|
|
10
10
|
class ReactEmailRails::Generators::EmailGenerator < Rails::Generators::NamedBase
|
|
11
|
-
|
|
11
|
+
CONFIG_SCRIPT = "node_modules/react-email-rails/bin/config.mjs"
|
|
12
12
|
|
|
13
13
|
source_root(File.expand_path("templates/email", __dir__))
|
|
14
14
|
|
|
@@ -120,7 +120,7 @@ class ReactEmailRails::Generators::EmailGenerator < Rails::Generators::NamedBase
|
|
|
120
120
|
command = vite_config_command
|
|
121
121
|
if command
|
|
122
122
|
stdout, _stderr, status = Timeout.timeout(10) do
|
|
123
|
-
Open3.capture3(command, chdir: destination_root)
|
|
123
|
+
Open3.capture3(*command, chdir: destination_root)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
status.success? ? JSON.parse(stdout) : {}
|
|
@@ -133,10 +133,9 @@ class ReactEmailRails::Generators::EmailGenerator < Rails::Generators::NamedBase
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def vite_config_command
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
].find { |path| File.exist?(File.join(destination_root, path)) }
|
|
136
|
+
return unless File.exist?(File.join(destination_root, CONFIG_SCRIPT))
|
|
137
|
+
|
|
138
|
+
[ReactEmailRails.configuration.js_runtime, CONFIG_SCRIPT]
|
|
140
139
|
end
|
|
141
140
|
|
|
142
141
|
PLUGIN_OPENING = /reactEmailRails\s*\(\s*\{.*?/m
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
class ReactEmailRails::Configuration
|
|
2
2
|
BUNDLE_PATH = "tmp/react-email-rails/emails.js"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
BUILD_SCRIPT = "node_modules/react-email-rails/bin/build.mjs"
|
|
4
|
+
CONFIG_SCRIPT = "node_modules/react-email-rails/bin/config.mjs"
|
|
5
|
+
DEV_RENDER_SCRIPT = "node_modules/react-email-rails/bin/dev.mjs"
|
|
6
6
|
|
|
7
|
+
DEFAULT_JS_RUNTIME = "node"
|
|
7
8
|
DEFAULT_RENDER_TIMEOUT = 10
|
|
8
9
|
DEVELOPMENT_RENDER_TIMEOUT = 30
|
|
9
10
|
DEFAULT_RENDER_PROCESS_MAX_REQUESTS = 1_000
|
|
@@ -11,14 +12,6 @@ class ReactEmailRails::Configuration
|
|
|
11
12
|
|
|
12
13
|
DEFAULT_PROP_TRANSFORMER = ->(props:) { props }
|
|
13
14
|
|
|
14
|
-
DEFAULT_RENDER_COMMAND = lambda do
|
|
15
|
-
if Rails.env.development?
|
|
16
|
-
[Rails.root.join(DEV_RENDER_BIN).to_s]
|
|
17
|
-
else
|
|
18
|
-
["node", Rails.root.join(BUNDLE_PATH).to_s]
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
15
|
attr_accessor(
|
|
23
16
|
:component_path_resolver,
|
|
24
17
|
:render_options,
|
|
@@ -28,6 +21,7 @@ class ReactEmailRails::Configuration
|
|
|
28
21
|
)
|
|
29
22
|
|
|
30
23
|
attr_reader(
|
|
24
|
+
:js_runtime,
|
|
31
25
|
:prop_transformer,
|
|
32
26
|
:render_timeout,
|
|
33
27
|
:render_process_max_requests,
|
|
@@ -38,6 +32,7 @@ class ReactEmailRails::Configuration
|
|
|
38
32
|
new.tap do |config|
|
|
39
33
|
config.component_path_resolver = ->(mailer:, action:) { "#{mailer}/#{action}" }
|
|
40
34
|
config.render_options = {}
|
|
35
|
+
config.js_runtime = DEFAULT_JS_RUNTIME
|
|
41
36
|
config.render_timeout = Rails.env.development? ? DEVELOPMENT_RENDER_TIMEOUT : DEFAULT_RENDER_TIMEOUT
|
|
42
37
|
config.render_process_max_requests = DEFAULT_RENDER_PROCESS_MAX_REQUESTS
|
|
43
38
|
config.prop_transformer = DEFAULT_PROP_TRANSFORMER
|
|
@@ -62,6 +57,12 @@ class ReactEmailRails::Configuration
|
|
|
62
57
|
@prop_transformer = value
|
|
63
58
|
end
|
|
64
59
|
|
|
60
|
+
def js_runtime=(value)
|
|
61
|
+
raise(ArgumentError, "react-email-rails js_runtime must be a non-empty string") unless value.is_a?(String) && value.present?
|
|
62
|
+
|
|
63
|
+
@js_runtime = value
|
|
64
|
+
end
|
|
65
|
+
|
|
65
66
|
def render_timeout=(value)
|
|
66
67
|
raise(ArgumentError, "react-email-rails render_timeout must be positive") unless value.is_a?(Numeric) && value.positive?
|
|
67
68
|
|
|
@@ -92,7 +93,8 @@ class ReactEmailRails::Configuration
|
|
|
92
93
|
private
|
|
93
94
|
|
|
94
95
|
def resolved_render_command
|
|
95
|
-
|
|
96
|
+
script = Rails.env.development? ? DEV_RENDER_SCRIPT : BUNDLE_PATH
|
|
97
|
+
[js_runtime, Rails.root.join(script).to_s]
|
|
96
98
|
end
|
|
97
99
|
|
|
98
100
|
def serialize_props(props)
|
|
@@ -106,17 +106,16 @@ class ReactEmailRails::Renderer
|
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def validate_command!
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
script = command[1].to_s
|
|
110
|
+
return if File.file?(script)
|
|
111
111
|
|
|
112
|
-
if
|
|
113
|
-
raise(render_error("development renderer not found at #{
|
|
112
|
+
if script.end_with?(ReactEmailRails::Configuration::DEV_RENDER_SCRIPT)
|
|
113
|
+
raise(render_error("development renderer not found at #{script.inspect}; install JavaScript dependencies with npm, pnpm, yarn, or bun"))
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
return unless
|
|
117
|
-
return if File.file?(bundle_path)
|
|
116
|
+
return unless script.end_with?(ReactEmailRails::Configuration::BUNDLE_PATH)
|
|
118
117
|
|
|
119
|
-
raise(render_error("email bundle not found at #{
|
|
118
|
+
raise(render_error("email bundle not found at #{script.inspect}; run react-email-rails-build before rendering React emails"))
|
|
120
119
|
end
|
|
121
120
|
|
|
122
121
|
def validate_metadata!(body)
|
|
@@ -4,9 +4,9 @@ module ReactEmailRails::Tasks
|
|
|
4
4
|
class << self
|
|
5
5
|
def build
|
|
6
6
|
command = build_command
|
|
7
|
-
raise("react-email-rails build command not found at #{command.inspect}; run JavaScript package install first") unless File.exist?(command)
|
|
7
|
+
raise("react-email-rails build command not found at #{command.last.inspect}; run JavaScript package install first") unless File.exist?(command.last)
|
|
8
8
|
|
|
9
|
-
system(command, exception: true, chdir: Rails.root.to_s)
|
|
9
|
+
system(*command, exception: true, chdir: Rails.root.to_s)
|
|
10
10
|
raise("react-email-rails build completed, but the email bundle was not found at #{bundle_path.inspect}") unless File.file?(bundle_path)
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -24,12 +24,10 @@ module ReactEmailRails::Tasks
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
def build_command
|
|
27
|
-
|
|
28
|
-
ReactEmailRails
|
|
29
|
-
|
|
27
|
+
[
|
|
28
|
+
ReactEmailRails.configuration.js_runtime,
|
|
29
|
+
Rails.root.join(ReactEmailRails::Configuration::BUILD_SCRIPT).to_s,
|
|
30
30
|
]
|
|
31
|
-
candidates.map { |path| Rails.root.join(path).to_s }.find { |path| File.exist?(path) } ||
|
|
32
|
-
Rails.root.join(ReactEmailRails::Configuration::BUILD_BIN).to_s
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
def bundle_path
|