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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 281158df961cccec48c34e03628715fda9a9c9cf16bbda4cae3097f380498a48
4
- data.tar.gz: 68d882e14c9a5ed7281b5e1bb39a046e7ecb77314b25667cb27a289509aac319
3
+ metadata.gz: fd436260ad38b370f59dbd8cfad0b09697634624b7c56d0917eee90dc583dc07
4
+ data.tar.gz: 9e5f81391d9864d46903129e4f4fd2971bca156a83c933c79f8d5e520331b0d8
5
5
  SHA512:
6
- metadata.gz: 7a679b532deae816d2337e38810077045b85a38d0e0e86089d516ba70d8e9060a06b4c9e9af5a8fa4a7aaa310f101c2817c0f246e559f97c1baaf829f19db91c
7
- data.tar.gz: 7117f2c68aacaa3a4683d2272384a8ee5171d3684c3ad2a7b809bb833e7b85b4a19df738de66de3c45ce234d7e87a78537db3d1c8b24c733fcb25b26b8fa3d60
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
- CONFIG_BIN = "node_modules/.bin/react-email-rails-config"
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
- CONFIG_BIN,
138
- "#{CONFIG_BIN}.cmd",
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
- BUILD_BIN = "node_modules/.bin/react-email-rails-build"
4
- CONFIG_BIN = "node_modules/.bin/react-email-rails-config"
5
- DEV_RENDER_BIN = "node_modules/.bin/react-email-rails-dev"
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
- DEFAULT_RENDER_COMMAND.call
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
- command_path = command.first.to_s
110
- bundle_path = command[1].to_s
109
+ script = command[1].to_s
110
+ return if File.file?(script)
111
111
 
112
- if command_path.end_with?(ReactEmailRails::Configuration::DEV_RENDER_BIN) && !File.exist?(command_path)
113
- raise(render_error("development renderer not found at #{command_path.inspect}; install JavaScript dependencies with npm, pnpm, yarn, or bun"))
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 command_path == "node" && bundle_path.end_with?(ReactEmailRails::Configuration::BUNDLE_PATH)
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 #{bundle_path.inspect}; run react-email-rails-build before rendering React emails"))
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
- candidates = [
28
- ReactEmailRails::Configuration::BUILD_BIN,
29
- "#{ReactEmailRails::Configuration::BUILD_BIN}.cmd",
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
@@ -1,3 +1,3 @@
1
1
  module ReactEmailRails
2
- VERSION = "0.12.1"
2
+ VERSION = "0.12.2"
3
3
  end
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.12.1
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Supertape