react_on_rails 16.4.0.rc.0 → 16.4.0.rc.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/.rubocop.yml +2 -0
- data/Gemfile.lock +1 -1
- data/lib/generators/react_on_rails/base_generator.rb +30 -10
- data/lib/generators/react_on_rails/dev_tests_generator.rb +9 -1
- data/lib/generators/react_on_rails/generator_helper.rb +101 -0
- data/lib/generators/react_on_rails/generator_messages.rb +2 -2
- data/lib/generators/react_on_rails/install_generator.rb +55 -19
- data/lib/generators/react_on_rails/js_dependency_manager.rb +115 -8
- data/lib/generators/react_on_rails/pro/USAGE +21 -0
- data/lib/generators/react_on_rails/pro_generator.rb +97 -0
- data/lib/generators/react_on_rails/pro_setup.rb +314 -0
- data/lib/generators/react_on_rails/rsc/USAGE +23 -0
- data/lib/generators/react_on_rails/rsc_generator.rb +100 -0
- data/lib/generators/react_on_rails/rsc_setup.rb +471 -0
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +3 -3
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/{generateWebpackConfigs.js.tt → ServerClientOrBoth.js.tt} +28 -3
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +8 -0
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +54 -0
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/dev_tests/spec/system/hello_server_spec.rb +17 -0
- data/lib/generators/react_on_rails/templates/pro/base/client/node-renderer.js +41 -0
- data/lib/generators/react_on_rails/templates/pro/base/config/initializers/react_on_rails_pro.rb.tt +25 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/controllers/hello_server_controller.rb +25 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/components/HelloServer.jsx +80 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/components/HelloServer.tsx +90 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/components/LikeButton.jsx +54 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/components/LikeButton.tsx +54 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/ror_components/HelloServer.jsx +10 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/javascript/src/HelloServer/ror_components/HelloServer.tsx +10 -0
- data/lib/generators/react_on_rails/templates/rsc/base/app/views/hello_server/index.html.erb +37 -0
- data/lib/generators/react_on_rails/templates/rsc/base/config/webpack/rscWebpackConfig.js.tt +64 -0
- data/lib/react_on_rails/engine.rb +5 -1
- data/lib/react_on_rails/helper.rb +15 -11
- data/lib/react_on_rails/pro_helper.rb +11 -4
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +6 -1
- metadata +21 -3
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require_relative "generator_helper"
|
|
5
|
+
require_relative "generator_messages"
|
|
6
|
+
require_relative "js_dependency_manager"
|
|
7
|
+
require_relative "pro_setup"
|
|
8
|
+
|
|
9
|
+
module ReactOnRails
|
|
10
|
+
module Generators
|
|
11
|
+
class ProGenerator < Rails::Generators::Base
|
|
12
|
+
include GeneratorHelper
|
|
13
|
+
include JsDependencyManager
|
|
14
|
+
include ProSetup
|
|
15
|
+
|
|
16
|
+
source_root File.expand_path(__dir__)
|
|
17
|
+
|
|
18
|
+
def self.usage_path
|
|
19
|
+
File.expand_path("pro/USAGE", __dir__)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Hidden option for when invoked from install_generator
|
|
23
|
+
# Skips prerequisite checks and message printing (parent handles both)
|
|
24
|
+
class_option :invoked_by_install,
|
|
25
|
+
type: :boolean,
|
|
26
|
+
default: false,
|
|
27
|
+
hide: true
|
|
28
|
+
|
|
29
|
+
def run_generator
|
|
30
|
+
# When invoked by install_generator, skip prerequisites (parent already validated)
|
|
31
|
+
if options[:invoked_by_install] || prerequisites_met?
|
|
32
|
+
setup_pro
|
|
33
|
+
add_pro_npm_dependencies
|
|
34
|
+
print_success_message unless options[:invoked_by_install]
|
|
35
|
+
else
|
|
36
|
+
GeneratorMessages.add_error(<<~MSG.strip)
|
|
37
|
+
🚫 React on Rails Pro generator prerequisites not met!
|
|
38
|
+
|
|
39
|
+
Please resolve the issues listed above before continuing.
|
|
40
|
+
MSG
|
|
41
|
+
end
|
|
42
|
+
ensure
|
|
43
|
+
print_generator_messages unless options[:invoked_by_install]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def prerequisites_met?
|
|
49
|
+
!(missing_base_installation? || missing_pro_gem?(force: true))
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def missing_base_installation?
|
|
53
|
+
return false if base_react_on_rails_installed?
|
|
54
|
+
|
|
55
|
+
GeneratorMessages.add_error(<<~MSG.strip)
|
|
56
|
+
🚫 React on Rails is not installed in this application.
|
|
57
|
+
|
|
58
|
+
This generator adds Pro features to an existing React on Rails app.
|
|
59
|
+
Please run the base installer first:
|
|
60
|
+
|
|
61
|
+
rails g react_on_rails:install
|
|
62
|
+
|
|
63
|
+
Then re-run this generator to add Pro features.
|
|
64
|
+
MSG
|
|
65
|
+
true
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def base_react_on_rails_installed?
|
|
69
|
+
File.exist?(File.join(destination_root, "config/initializers/react_on_rails.rb"))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def add_pro_npm_dependencies
|
|
73
|
+
puts Rainbow("📝 Adding Pro npm dependencies...").yellow
|
|
74
|
+
add_pro_dependencies
|
|
75
|
+
puts Rainbow("✅ Pro npm dependencies added").green
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def print_success_message
|
|
79
|
+
route = if File.exist?(File.join(destination_root, "app/controllers/hello_server_controller.rb"))
|
|
80
|
+
"hello_server"
|
|
81
|
+
else
|
|
82
|
+
"hello_world"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
GeneratorMessages.add_info(<<~MSG)
|
|
86
|
+
Next steps:
|
|
87
|
+
1. Set your license: export REACT_ON_RAILS_PRO_LICENSE=your_token
|
|
88
|
+
2. Start the app: bin/dev (or foreman start -f Procfile.dev)
|
|
89
|
+
3. Visit http://localhost:3000/#{route}
|
|
90
|
+
4. The Node Renderer will start on port 3800
|
|
91
|
+
|
|
92
|
+
Documentation: https://www.shakacode.com/react-on-rails-pro/docs/
|
|
93
|
+
MSG
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rainbow"
|
|
4
|
+
require_relative "generator_messages"
|
|
5
|
+
|
|
6
|
+
module ReactOnRails
|
|
7
|
+
module Generators
|
|
8
|
+
# Provides Pro setup functionality for React on Rails generators.
|
|
9
|
+
#
|
|
10
|
+
# This module extracts Pro-specific setup methods that can be shared between:
|
|
11
|
+
# - InstallGenerator (when --pro or --rsc flags are used)
|
|
12
|
+
# - ProGenerator (standalone generator for upgrading existing apps)
|
|
13
|
+
#
|
|
14
|
+
# == Required Dependencies
|
|
15
|
+
# Including classes must provide (typically via Rails::Generators::Base):
|
|
16
|
+
# - destination_root: Path to the target Rails application
|
|
17
|
+
# - template, copy_file, append_to_file: Thor file manipulation methods
|
|
18
|
+
# - options: Generator options hash
|
|
19
|
+
#
|
|
20
|
+
# Including classes must also include GeneratorHelper which provides:
|
|
21
|
+
# - use_pro?, use_rsc?: Feature flag helpers
|
|
22
|
+
# - pro_gem_installed?: Pro gem detection
|
|
23
|
+
#
|
|
24
|
+
module ProSetup
|
|
25
|
+
# Main entry point for Pro setup.
|
|
26
|
+
# Orchestrates creation of all Pro-related files and configuration.
|
|
27
|
+
#
|
|
28
|
+
# Creates:
|
|
29
|
+
# - config/initializers/react_on_rails_pro.rb
|
|
30
|
+
# - client/node-renderer.js
|
|
31
|
+
# - Procfile.dev entry for node-renderer
|
|
32
|
+
#
|
|
33
|
+
# @note NPM dependencies are handled separately by JsDependencyManager
|
|
34
|
+
def setup_pro
|
|
35
|
+
puts Rainbow("\n#{'=' * 80}").cyan
|
|
36
|
+
puts Rainbow("🚀 REACT ON RAILS PRO SETUP").cyan.bold
|
|
37
|
+
puts Rainbow("=" * 80).cyan
|
|
38
|
+
|
|
39
|
+
create_pro_initializer
|
|
40
|
+
create_node_renderer
|
|
41
|
+
add_pro_to_procfile
|
|
42
|
+
update_webpack_config_for_pro
|
|
43
|
+
|
|
44
|
+
puts Rainbow("=" * 80).cyan
|
|
45
|
+
puts Rainbow("✅ React on Rails Pro setup complete!").green
|
|
46
|
+
puts Rainbow("=" * 80).cyan
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Check if Pro gem is missing.
|
|
50
|
+
#
|
|
51
|
+
# @param force [Boolean] When true, always performs the check.
|
|
52
|
+
# When false (default), only checks if Pro is required (use_pro? returns true).
|
|
53
|
+
# Use force: true in standalone generators where Pro is always required.
|
|
54
|
+
# @return [Boolean] true if Pro gem is missing
|
|
55
|
+
def missing_pro_gem?(force: false)
|
|
56
|
+
return false unless force || use_pro?
|
|
57
|
+
return false if pro_gem_installed?
|
|
58
|
+
|
|
59
|
+
# Detect context: install_generator defines :pro/:rsc options, standalone generators don't
|
|
60
|
+
context_line = if options.key?(:pro) || options.key?(:rsc)
|
|
61
|
+
flag = options[:rsc] ? "--rsc" : "--pro"
|
|
62
|
+
"You specified #{flag}, which requires the react_on_rails_pro gem."
|
|
63
|
+
else
|
|
64
|
+
"This generator requires the react_on_rails_pro gem."
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
GeneratorMessages.add_error(<<~MSG.strip)
|
|
68
|
+
🚫 React on Rails Pro gem is not installed.
|
|
69
|
+
|
|
70
|
+
#{context_line}
|
|
71
|
+
|
|
72
|
+
Add to your Gemfile:
|
|
73
|
+
gem 'react_on_rails_pro', '>= 16.3.0'
|
|
74
|
+
|
|
75
|
+
Then run: bundle install
|
|
76
|
+
|
|
77
|
+
Try Pro free! Email justin@shakacode.com for an evaluation license.
|
|
78
|
+
More info: https://www.shakacode.com/react-on-rails-pro/
|
|
79
|
+
MSG
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
private
|
|
84
|
+
|
|
85
|
+
def create_pro_initializer
|
|
86
|
+
initializer_path = "config/initializers/react_on_rails_pro.rb"
|
|
87
|
+
|
|
88
|
+
if File.exist?(File.join(destination_root, initializer_path))
|
|
89
|
+
puts Rainbow("ℹ️ #{initializer_path} already exists, skipping").yellow
|
|
90
|
+
return
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
puts Rainbow("📝 Creating React on Rails Pro initializer...").yellow
|
|
94
|
+
|
|
95
|
+
pro_template_path = "templates/pro/base/config/initializers/react_on_rails_pro.rb.tt"
|
|
96
|
+
template(pro_template_path, initializer_path)
|
|
97
|
+
|
|
98
|
+
puts Rainbow("✅ Created #{initializer_path}").green
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def create_node_renderer
|
|
102
|
+
node_renderer_path = "client/node-renderer.js"
|
|
103
|
+
|
|
104
|
+
if File.exist?(File.join(destination_root, node_renderer_path))
|
|
105
|
+
puts Rainbow("ℹ️ #{node_renderer_path} already exists, skipping").yellow
|
|
106
|
+
return
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
puts Rainbow("📝 Creating Node Renderer bootstrap...").yellow
|
|
110
|
+
|
|
111
|
+
# Ensure client directory exists
|
|
112
|
+
FileUtils.mkdir_p(File.join(destination_root, "client"))
|
|
113
|
+
|
|
114
|
+
template_path = "templates/pro/base/client/node-renderer.js"
|
|
115
|
+
copy_file(template_path, node_renderer_path)
|
|
116
|
+
|
|
117
|
+
puts Rainbow("✅ Created #{node_renderer_path}").green
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def add_pro_to_procfile
|
|
121
|
+
procfile_path = File.join(destination_root, "Procfile.dev")
|
|
122
|
+
|
|
123
|
+
unless File.exist?(procfile_path)
|
|
124
|
+
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
125
|
+
⚠️ Procfile.dev not found. Skipping Node Renderer process addition.
|
|
126
|
+
|
|
127
|
+
You'll need to add the Node Renderer to your process manager manually:
|
|
128
|
+
node-renderer: RENDERER_LOG_LEVEL=debug RENDERER_PORT=3800 node client/node-renderer.js
|
|
129
|
+
MSG
|
|
130
|
+
return
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if File.read(procfile_path).include?("node-renderer:")
|
|
134
|
+
puts Rainbow("ℹ️ Node Renderer already in Procfile.dev, skipping").yellow
|
|
135
|
+
return
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
puts Rainbow("📝 Adding Node Renderer to Procfile.dev...").yellow
|
|
139
|
+
|
|
140
|
+
node_renderer_line = <<~PROCFILE
|
|
141
|
+
|
|
142
|
+
# React on Rails Pro - Node Renderer for SSR
|
|
143
|
+
node-renderer: RENDERER_LOG_LEVEL=debug RENDERER_PORT=3800 node client/node-renderer.js
|
|
144
|
+
PROCFILE
|
|
145
|
+
|
|
146
|
+
append_to_file("Procfile.dev", node_renderer_line)
|
|
147
|
+
|
|
148
|
+
puts Rainbow("✅ Added Node Renderer to Procfile.dev").green
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Update webpack configs to enable Pro settings.
|
|
152
|
+
# This is needed for standalone Pro upgrades where the base install
|
|
153
|
+
# created webpack configs without Pro settings enabled.
|
|
154
|
+
#
|
|
155
|
+
# Updates serverWebpackConfig.js:
|
|
156
|
+
# - Adds extractLoader helper function (required by rscWebpackConfig.js)
|
|
157
|
+
# - Adds Babel SSR caller setup (required for correct SSR compilation)
|
|
158
|
+
# - Enables libraryTarget: 'commonjs2' (required for Node Renderer)
|
|
159
|
+
# - Enables serverWebpackConfig.target = 'node' (required for Node.js modules)
|
|
160
|
+
# - Disables Node.js polyfills via node = false (required for real __dirname)
|
|
161
|
+
# - Changes module.exports to Pro style (required by rscWebpackConfig.js)
|
|
162
|
+
#
|
|
163
|
+
# Updates ServerClientOrBoth.js:
|
|
164
|
+
# - Changes import to destructured style (required for Pro object export)
|
|
165
|
+
def update_webpack_config_for_pro
|
|
166
|
+
webpack_config_path = File.join(destination_root, "config/webpack/serverWebpackConfig.js")
|
|
167
|
+
|
|
168
|
+
unless File.exist?(webpack_config_path)
|
|
169
|
+
puts Rainbow("ℹ️ serverWebpackConfig.js not found, skipping webpack update").yellow
|
|
170
|
+
return
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
content = File.read(webpack_config_path)
|
|
174
|
+
|
|
175
|
+
# Check if Pro settings are already enabled (not commented)
|
|
176
|
+
if content.include?("libraryTarget: 'commonjs2',") &&
|
|
177
|
+
!content.include?("// libraryTarget: 'commonjs2',")
|
|
178
|
+
puts Rainbow("ℹ️ Webpack config already has Pro settings enabled, skipping").yellow
|
|
179
|
+
return
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
puts Rainbow("📝 Updating serverWebpackConfig.js for Pro...").yellow
|
|
183
|
+
|
|
184
|
+
webpack_config = "config/webpack/serverWebpackConfig.js"
|
|
185
|
+
|
|
186
|
+
# Add extractLoader helper function after bundler require
|
|
187
|
+
add_extract_loader_to_server_config(webpack_config, content)
|
|
188
|
+
|
|
189
|
+
# Add Babel SSR caller setup (uses extractLoader, so must come after)
|
|
190
|
+
add_babel_ssr_caller_to_server_config(webpack_config, content)
|
|
191
|
+
|
|
192
|
+
# Uncomment libraryTarget: 'commonjs2'
|
|
193
|
+
library_target_pattern = %r{// If using the React on Rails Pro.*\n\s*// libraryTarget: 'commonjs2',}
|
|
194
|
+
library_target_replacement = "// Required for React on Rails Pro Node Renderer\n " \
|
|
195
|
+
"libraryTarget: 'commonjs2',"
|
|
196
|
+
gsub_file(webpack_config, library_target_pattern, library_target_replacement)
|
|
197
|
+
|
|
198
|
+
# Replace stale comments and uncomment target = 'node', add node = false
|
|
199
|
+
# The base template has 4 lines: 2 explanatory comments + "uncomment" hint + commented code
|
|
200
|
+
# Replace with clean Pro output matching the template's use_pro? branch
|
|
201
|
+
# rubocop:disable Layout/LineLength
|
|
202
|
+
target_node_pattern = %r{\s*// If using the default 'web',.*\n\s*// break with SSR\..*\n\s*// If using the React on Rails Pro.*\n\s*// serverWebpackConfig\.target = 'node'}
|
|
203
|
+
# rubocop:enable Layout/LineLength
|
|
204
|
+
target_node_replacement = "\n\n " \
|
|
205
|
+
"// React on Rails Pro uses Node renderer, so target must be 'node'\n " \
|
|
206
|
+
"// This fixes issues with libraries like Emotion and loadable-components\n " \
|
|
207
|
+
"serverWebpackConfig.target = 'node';\n\n " \
|
|
208
|
+
"// Disable Node.js polyfills - not needed when targeting Node\n " \
|
|
209
|
+
"serverWebpackConfig.node = false;"
|
|
210
|
+
gsub_file(webpack_config, target_node_pattern, target_node_replacement)
|
|
211
|
+
|
|
212
|
+
# Change module.exports to Pro style (exports object with default and extractLoader)
|
|
213
|
+
update_server_config_exports(webpack_config)
|
|
214
|
+
|
|
215
|
+
# Update ServerClientOrBoth.js import style
|
|
216
|
+
update_server_client_or_both_import
|
|
217
|
+
|
|
218
|
+
verify_pro_webpack_transforms(webpack_config)
|
|
219
|
+
puts Rainbow("✅ Updated webpack configs for Pro").green
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def add_extract_loader_to_server_config(webpack_config, content)
|
|
223
|
+
# Skip if extractLoader already exists
|
|
224
|
+
return if content.include?("function extractLoader")
|
|
225
|
+
|
|
226
|
+
extract_loader_code = <<~JS.chomp
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
function extractLoader(rule, loaderName) {
|
|
230
|
+
if (!Array.isArray(rule.use)) return null;
|
|
231
|
+
return rule.use.find((item) => {
|
|
232
|
+
const testValue = typeof item === 'string' ? item : item.loader;
|
|
233
|
+
return testValue && testValue.includes(loaderName);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
JS
|
|
237
|
+
|
|
238
|
+
# Insert after bundler require line
|
|
239
|
+
gsub_file(
|
|
240
|
+
webpack_config,
|
|
241
|
+
%r{(const bundler = config\.assets_bundler.*\n.*require\('@rspack/core'\).*\n.*: require\('webpack'\);)},
|
|
242
|
+
"\\1#{extract_loader_code}"
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def add_babel_ssr_caller_to_server_config(webpack_config, content)
|
|
247
|
+
# Skip if Babel SSR caller already exists
|
|
248
|
+
return if content.include?("babelLoader.options.caller")
|
|
249
|
+
|
|
250
|
+
babel_ssr_code = "\n\n " \
|
|
251
|
+
"// Set SSR caller for Babel (if using Babel instead of SWC)\n " \
|
|
252
|
+
"const babelLoader = extractLoader(rule, 'babel-loader');\n " \
|
|
253
|
+
"if (babelLoader && babelLoader.options) {\n " \
|
|
254
|
+
"babelLoader.options.caller = { ssr: true };\n " \
|
|
255
|
+
"}"
|
|
256
|
+
|
|
257
|
+
# Insert after cssLoader.options.modules = { exportOnlyLocals: true }; block
|
|
258
|
+
gsub_file(
|
|
259
|
+
webpack_config,
|
|
260
|
+
/(cssLoader\.options\.modules = \{ exportOnlyLocals: true \};\s*\n\s*\})/,
|
|
261
|
+
"\\1#{babel_ssr_code}"
|
|
262
|
+
)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def update_server_config_exports(webpack_config)
|
|
266
|
+
# Change from: module.exports = configureServer;
|
|
267
|
+
# To: module.exports = { default: configureServer, extractLoader };
|
|
268
|
+
gsub_file(
|
|
269
|
+
webpack_config,
|
|
270
|
+
/^module\.exports = configureServer;\s*$/,
|
|
271
|
+
"module.exports = {\n default: configureServer,\n extractLoader,\n};\n"
|
|
272
|
+
)
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def verify_pro_webpack_transforms(webpack_config)
|
|
276
|
+
content = File.read(File.join(destination_root, webpack_config))
|
|
277
|
+
missing = []
|
|
278
|
+
missing << "libraryTarget: 'commonjs2'" unless content.include?("libraryTarget: 'commonjs2',")
|
|
279
|
+
missing << "function extractLoader" unless content.include?("function extractLoader")
|
|
280
|
+
missing << "serverWebpackConfig.target = 'node'" unless content.include?("serverWebpackConfig.target = 'node'")
|
|
281
|
+
missing << "module.exports = {" unless content.include?("module.exports = {")
|
|
282
|
+
return if missing.empty?
|
|
283
|
+
|
|
284
|
+
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
285
|
+
⚠️ Some Pro webpack transforms may not have applied correctly.
|
|
286
|
+
|
|
287
|
+
The following expected patterns were not found in #{webpack_config}:
|
|
288
|
+
#{missing.map { |m| " - #{m}" }.join("\n")}
|
|
289
|
+
|
|
290
|
+
This can happen if your webpack config has been customized.
|
|
291
|
+
Please verify #{webpack_config} manually.
|
|
292
|
+
MSG
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def update_server_client_or_both_import
|
|
296
|
+
server_client_path = resolve_server_client_or_both_path
|
|
297
|
+
return unless server_client_path
|
|
298
|
+
|
|
299
|
+
content = File.read(File.join(destination_root, server_client_path))
|
|
300
|
+
|
|
301
|
+
# Skip if already using destructured import
|
|
302
|
+
return if content.include?("{ default: serverWebpackConfig }")
|
|
303
|
+
|
|
304
|
+
# Change from: const serverWebpackConfig = require('./serverWebpackConfig');
|
|
305
|
+
# To: const { default: serverWebpackConfig } = require('./serverWebpackConfig');
|
|
306
|
+
gsub_file(
|
|
307
|
+
server_client_path,
|
|
308
|
+
%r{^const serverWebpackConfig = require\('\./serverWebpackConfig'\);$},
|
|
309
|
+
"const { default: serverWebpackConfig } = require('./serverWebpackConfig');"
|
|
310
|
+
)
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Add React Server Components to an existing React on Rails Pro application.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
rails generate react_on_rails:rsc
|
|
6
|
+
rails generate react_on_rails:rsc --typescript
|
|
7
|
+
|
|
8
|
+
This will add:
|
|
9
|
+
- RSC webpack configuration (config/webpack/rscWebpackConfig.js)
|
|
10
|
+
- HelloServer example component
|
|
11
|
+
- HelloServerController and view
|
|
12
|
+
- RSC routes (rsc_payload_route, hello_server)
|
|
13
|
+
- RSC bundle watcher to Procfile.dev
|
|
14
|
+
|
|
15
|
+
Modifies:
|
|
16
|
+
- config/webpack/serverWebpackConfig.js (adds RSCWebpackPlugin, rscBundle param)
|
|
17
|
+
- config/webpack/clientWebpackConfig.js (adds RSCWebpackPlugin)
|
|
18
|
+
- config/webpack/ServerClientOrBoth.js (adds rscWebpackConfig, RSC_BUNDLE_ONLY)
|
|
19
|
+
- config/initializers/react_on_rails_pro.rb (adds RSC config block)
|
|
20
|
+
- package.json (adds react-on-rails-rsc)
|
|
21
|
+
|
|
22
|
+
Prerequisites:
|
|
23
|
+
- React on Rails Pro must be installed (rails g react_on_rails:pro)
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require_relative "generator_helper"
|
|
5
|
+
require_relative "generator_messages"
|
|
6
|
+
require_relative "js_dependency_manager"
|
|
7
|
+
require_relative "rsc_setup"
|
|
8
|
+
|
|
9
|
+
module ReactOnRails
|
|
10
|
+
module Generators
|
|
11
|
+
class RscGenerator < Rails::Generators::Base
|
|
12
|
+
include GeneratorHelper
|
|
13
|
+
include JsDependencyManager
|
|
14
|
+
include RscSetup
|
|
15
|
+
|
|
16
|
+
source_root File.expand_path(__dir__)
|
|
17
|
+
|
|
18
|
+
def self.usage_path
|
|
19
|
+
File.expand_path("rsc/USAGE", __dir__)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class_option :typescript,
|
|
23
|
+
type: :boolean,
|
|
24
|
+
default: false,
|
|
25
|
+
desc: "Generate TypeScript files",
|
|
26
|
+
aliases: "-T"
|
|
27
|
+
|
|
28
|
+
# Hidden option for when invoked from install_generator
|
|
29
|
+
# Skips prerequisite checks and message printing (parent handles both)
|
|
30
|
+
class_option :invoked_by_install,
|
|
31
|
+
type: :boolean,
|
|
32
|
+
default: false,
|
|
33
|
+
hide: true
|
|
34
|
+
|
|
35
|
+
def run_generator
|
|
36
|
+
# When invoked by install_generator, skip prerequisites (parent already validated)
|
|
37
|
+
if options[:invoked_by_install] || prerequisites_met?
|
|
38
|
+
warn_about_react_version_for_rsc(force: true)
|
|
39
|
+
setup_rsc
|
|
40
|
+
add_rsc_npm_dependencies
|
|
41
|
+
print_success_message unless options[:invoked_by_install]
|
|
42
|
+
else
|
|
43
|
+
GeneratorMessages.add_error(<<~MSG.strip)
|
|
44
|
+
🚫 React on Rails RSC generator prerequisites not met!
|
|
45
|
+
|
|
46
|
+
Please resolve the issues listed above before continuing.
|
|
47
|
+
MSG
|
|
48
|
+
end
|
|
49
|
+
ensure
|
|
50
|
+
print_generator_messages unless options[:invoked_by_install]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def prerequisites_met?
|
|
56
|
+
!missing_pro_installation?
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def missing_pro_installation?
|
|
60
|
+
return false if pro_installed?
|
|
61
|
+
|
|
62
|
+
GeneratorMessages.add_error(<<~MSG.strip)
|
|
63
|
+
🚫 React on Rails Pro is not installed in this application.
|
|
64
|
+
|
|
65
|
+
RSC requires Pro. Please run the Pro generator first:
|
|
66
|
+
|
|
67
|
+
rails g react_on_rails:pro
|
|
68
|
+
|
|
69
|
+
Then re-run this generator to add RSC features.
|
|
70
|
+
MSG
|
|
71
|
+
true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def pro_installed?
|
|
75
|
+
File.exist?(File.join(destination_root, "config/initializers/react_on_rails_pro.rb"))
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def use_rsc?
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def add_rsc_npm_dependencies
|
|
83
|
+
puts Rainbow("📝 Adding RSC npm dependencies...").yellow
|
|
84
|
+
add_rsc_dependencies
|
|
85
|
+
puts Rainbow("✅ RSC npm dependencies added").green
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def print_success_message
|
|
89
|
+
GeneratorMessages.add_info(<<~MSG)
|
|
90
|
+
Next steps:
|
|
91
|
+
1. Start the app: bin/dev (or foreman start -f Procfile.dev)
|
|
92
|
+
2. Visit http://localhost:3000/hello_server to see RSC in action
|
|
93
|
+
3. The RSC bundle watcher will compile server components
|
|
94
|
+
|
|
95
|
+
Documentation: https://www.shakacode.com/react-on-rails-pro/docs/rsc/
|
|
96
|
+
MSG
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|