react_on_rails 16.0.0 → 16.0.1.rc.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 +117 -77
- data/CLAUDE.md +14 -2
- data/Gemfile.lock +1 -1
- data/LICENSE.md +15 -1
- data/README.md +68 -18
- data/eslint.config.ts +3 -0
- data/knip.ts +20 -9
- data/lib/generators/react_on_rails/USAGE +65 -0
- data/lib/generators/react_on_rails/base_generator.rb +7 -7
- data/lib/generators/react_on_rails/generator_helper.rb +4 -0
- data/lib/generators/react_on_rails/generator_messages.rb +2 -2
- data/lib/generators/react_on_rails/install_generator.rb +115 -7
- data/lib/generators/react_on_rails/react_no_redux_generator.rb +16 -4
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +83 -14
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx +25 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx +5 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/dev +12 -24
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.ts +18 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx +24 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.ts +6 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.ts +20 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.ts +22 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.tsx +23 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.tsx +5 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.ts +18 -0
- data/lib/react_on_rails/configuration.rb +10 -6
- data/lib/react_on_rails/dev/server_manager.rb +185 -28
- data/lib/react_on_rails/doctor.rb +1149 -0
- data/lib/react_on_rails/helper.rb +9 -78
- data/lib/react_on_rails/pro/NOTICE +21 -0
- data/lib/react_on_rails/pro/helper.rb +122 -0
- data/lib/react_on_rails/pro/utils.rb +53 -0
- data/lib/react_on_rails/react_component/render_options.rb +6 -2
- data/lib/react_on_rails/system_checker.rb +659 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/tasks/doctor.rake +51 -0
- data/lib/tasks/generate_packs.rake +127 -4
- data/package-lock.json +11984 -0
- metadata +21 -6
- data/lib/generators/react_on_rails/bin/dev +0 -46
@@ -8,16 +8,16 @@ module ReactOnRails
|
|
8
8
|
module Dev
|
9
9
|
class ServerManager
|
10
10
|
class << self
|
11
|
-
def start(mode = :development, procfile = nil, verbose: false)
|
11
|
+
def start(mode = :development, procfile = nil, verbose: false, route: nil, rails_env: nil)
|
12
12
|
case mode
|
13
13
|
when :production_like
|
14
|
-
run_production_like(_verbose: verbose)
|
14
|
+
run_production_like(_verbose: verbose, route: route, rails_env: rails_env)
|
15
15
|
when :static
|
16
16
|
procfile ||= "Procfile.dev-static-assets"
|
17
|
-
run_static_development(procfile, verbose: verbose)
|
17
|
+
run_static_development(procfile, verbose: verbose, route: route)
|
18
18
|
when :development, :hmr
|
19
19
|
procfile ||= "Procfile.dev"
|
20
|
-
run_development(procfile, verbose: verbose)
|
20
|
+
run_development(procfile, verbose: verbose, route: route)
|
21
21
|
else
|
22
22
|
raise ArgumentError, "Unknown mode: #{mode}"
|
23
23
|
end
|
@@ -116,6 +116,50 @@ module ReactOnRails
|
|
116
116
|
puts help_troubleshooting
|
117
117
|
end
|
118
118
|
|
119
|
+
def run_from_command_line(args = ARGV)
|
120
|
+
require "optparse"
|
121
|
+
|
122
|
+
options = { route: nil, rails_env: nil }
|
123
|
+
|
124
|
+
OptionParser.new do |opts|
|
125
|
+
opts.banner = "Usage: dev [command] [options]"
|
126
|
+
|
127
|
+
opts.on("--route ROUTE", "Specify the route to display in URLs (default: root)") do |route|
|
128
|
+
options[:route] = route
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.on("--rails-env ENV", "Override RAILS_ENV for assets:precompile step only (prod mode only)") do |env|
|
132
|
+
options[:rails_env] = env
|
133
|
+
end
|
134
|
+
|
135
|
+
opts.on("-h", "--help", "Prints this help") do
|
136
|
+
show_help
|
137
|
+
exit
|
138
|
+
end
|
139
|
+
end.parse!(args)
|
140
|
+
|
141
|
+
# Get the command (anything that's not parsed as an option)
|
142
|
+
command = args[0]
|
143
|
+
|
144
|
+
# Main execution
|
145
|
+
case command
|
146
|
+
when "production-assets", "prod"
|
147
|
+
start(:production_like, nil, verbose: false, route: options[:route], rails_env: options[:rails_env])
|
148
|
+
when "static"
|
149
|
+
start(:static, "Procfile.dev-static-assets", verbose: false, route: options[:route])
|
150
|
+
when "kill"
|
151
|
+
kill_processes
|
152
|
+
when "help", "--help", "-h"
|
153
|
+
show_help
|
154
|
+
when "hmr", nil
|
155
|
+
start(:development, "Procfile.dev", verbose: false, route: options[:route])
|
156
|
+
else
|
157
|
+
puts "Unknown argument: #{command}"
|
158
|
+
puts "Run 'dev help' for usage information"
|
159
|
+
exit 1
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
119
163
|
private
|
120
164
|
|
121
165
|
def help_usage
|
@@ -142,12 +186,21 @@ module ReactOnRails
|
|
142
186
|
end
|
143
187
|
# rubocop:enable Metrics/AbcSize
|
144
188
|
|
189
|
+
# rubocop:disable Metrics/AbcSize
|
145
190
|
def help_options
|
146
191
|
<<~OPTIONS
|
147
192
|
#{Rainbow('⚙️ OPTIONS:').cyan.bold}
|
148
|
-
#{Rainbow('--
|
193
|
+
#{Rainbow('--route ROUTE').green.bold} #{Rainbow('Specify route to display in URLs (default: root)').white}
|
194
|
+
#{Rainbow('--rails-env ENV').green.bold} #{Rainbow('Override RAILS_ENV for assets:precompile step only (prod mode only)').white}
|
195
|
+
#{Rainbow('--verbose, -v').green.bold} #{Rainbow('Enable verbose output for pack generation').white}
|
196
|
+
|
197
|
+
#{Rainbow('📝 EXAMPLES:').cyan.bold}
|
198
|
+
#{Rainbow('bin/dev prod').green.bold} #{Rainbow('# NODE_ENV=production, RAILS_ENV=development').white}
|
199
|
+
#{Rainbow('bin/dev prod --rails-env=production').green.bold} #{Rainbow('# NODE_ENV=production, RAILS_ENV=production').white}
|
200
|
+
#{Rainbow('bin/dev prod --route=dashboard').green.bold} #{Rainbow('# Custom route in URLs').white}
|
149
201
|
OPTIONS
|
150
202
|
end
|
203
|
+
# rubocop:enable Metrics/AbcSize
|
151
204
|
|
152
205
|
def help_customization
|
153
206
|
<<~CUSTOMIZATION
|
@@ -172,7 +225,7 @@ module ReactOnRails
|
|
172
225
|
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
|
173
226
|
#{Rainbow('•').yellow} #{Rainbow('May have Flash of Unstyled Content (FOUC)').white}
|
174
227
|
#{Rainbow('•').yellow} #{Rainbow('Fast recompilation').white}
|
175
|
-
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000
|
228
|
+
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000/<route>').cyan.underline}
|
176
229
|
|
177
230
|
#{Rainbow('📦 Static development mode').cyan.bold} - #{Rainbow('Procfile.dev-static-assets').green}:
|
178
231
|
#{Rainbow('•').yellow} #{Rainbow('No HMR (static assets with auto-recompilation)').white}
|
@@ -181,24 +234,26 @@ module ReactOnRails
|
|
181
234
|
#{Rainbow('•').yellow} #{Rainbow('CSS extracted to separate files (no FOUC)').white}
|
182
235
|
#{Rainbow('•').yellow} #{Rainbow('Development environment (faster builds than production)').white}
|
183
236
|
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
|
184
|
-
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000
|
237
|
+
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000/<route>').cyan.underline}
|
185
238
|
|
186
239
|
#{Rainbow('🏭 Production-assets mode').cyan.bold} - #{Rainbow('Procfile.dev-prod-assets').green}:
|
187
240
|
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
|
188
|
-
#{Rainbow('•').yellow} #{Rainbow('Asset precompilation with production optimizations').white}
|
189
|
-
#{Rainbow('•').yellow} #{Rainbow('
|
190
|
-
#{Rainbow('•').yellow} #{Rainbow('
|
241
|
+
#{Rainbow('•').yellow} #{Rainbow('Asset precompilation with NODE_ENV=production (webpack optimizations)').white}
|
242
|
+
#{Rainbow('•').yellow} #{Rainbow('RAILS_ENV=development by default for assets:precompile (avoids credentials)').white}
|
243
|
+
#{Rainbow('•').yellow} #{Rainbow('Use --rails-env=production for assets:precompile only (not server processes)').white}
|
244
|
+
#{Rainbow('•').yellow} #{Rainbow('Server processes controlled by Procfile.dev-prod-assets environment').white}
|
245
|
+
#{Rainbow('•').yellow} #{Rainbow('Optimized, minified bundles with CSS extraction').white}
|
191
246
|
#{Rainbow('•').yellow} #{Rainbow('No HMR (static assets)').white}
|
192
|
-
#{Rainbow('•').yellow} #{Rainbow('
|
193
|
-
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3001/hello_world').cyan.underline}
|
247
|
+
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3001/<route>').cyan.underline}
|
194
248
|
MODES
|
195
249
|
end
|
196
250
|
# rubocop:enable Metrics/AbcSize
|
197
251
|
|
198
|
-
|
252
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
253
|
+
def run_production_like(_verbose: false, route: nil, rails_env: nil)
|
199
254
|
procfile = "Procfile.dev-prod-assets"
|
200
255
|
|
201
|
-
print_procfile_info(procfile)
|
256
|
+
print_procfile_info(procfile, route: route)
|
202
257
|
print_server_info(
|
203
258
|
"🏭 Starting production-like development server...",
|
204
259
|
[
|
@@ -208,25 +263,124 @@ module ReactOnRails
|
|
208
263
|
"No HMR (Hot Module Replacement)",
|
209
264
|
"CSS extracted to separate files (no FOUC)"
|
210
265
|
],
|
211
|
-
3001
|
266
|
+
3001,
|
267
|
+
route: route
|
212
268
|
)
|
213
269
|
|
214
|
-
# Precompile assets
|
215
|
-
|
216
|
-
|
270
|
+
# Precompile assets with production webpack optimizations (includes pack generation automatically)
|
271
|
+
env = { "NODE_ENV" => "production" }
|
272
|
+
|
273
|
+
# Validate and sanitize rails_env to prevent shell injection
|
274
|
+
if rails_env
|
275
|
+
unless rails_env.match?(/\A[a-z0-9_]+\z/i)
|
276
|
+
puts "❌ Invalid rails_env: '#{rails_env}'. Must contain only letters, numbers, and underscores."
|
277
|
+
exit 1
|
278
|
+
end
|
279
|
+
env["RAILS_ENV"] = rails_env
|
280
|
+
end
|
281
|
+
|
282
|
+
argv = ["bundle", "exec", "rails", "assets:precompile"]
|
283
|
+
|
284
|
+
puts "🔨 Precompiling assets with production webpack optimizations..."
|
285
|
+
puts ""
|
286
|
+
|
287
|
+
puts Rainbow("ℹ️ Asset Precompilation Environment:").blue
|
288
|
+
puts " • NODE_ENV=production → Webpack optimizations (minification, compression)"
|
289
|
+
if rails_env
|
290
|
+
puts " • RAILS_ENV=#{rails_env} → Custom Rails environment for assets:precompile only"
|
291
|
+
puts " • Note: RAILS_ENV=production requires credentials, database setup, etc."
|
292
|
+
puts " • Server processes will use environment from Procfile.dev-prod-assets"
|
293
|
+
else
|
294
|
+
puts " • RAILS_ENV=development → Simpler Rails setup (no credentials needed)"
|
295
|
+
puts " • Use --rails-env=production for assets:precompile step only"
|
296
|
+
puts " • Server processes will use environment from Procfile.dev-prod-assets"
|
297
|
+
puts " • Gets production webpack bundles without production Rails complexity"
|
298
|
+
end
|
299
|
+
puts ""
|
300
|
+
|
301
|
+
env_display = env.map { |k, v| "#{k}=#{v}" }.join(" ")
|
302
|
+
puts "#{Rainbow('💻 Running:').blue} #{env_display} #{argv.join(' ')}"
|
303
|
+
puts ""
|
217
304
|
|
218
|
-
|
305
|
+
# Capture both stdout and stderr
|
306
|
+
require "open3"
|
307
|
+
stdout, stderr, status = Open3.capture3(env, *argv)
|
308
|
+
|
309
|
+
if status.success?
|
219
310
|
puts "✅ Assets precompiled successfully"
|
220
311
|
ProcessManager.ensure_procfile(procfile)
|
221
312
|
ProcessManager.run_with_process_manager(procfile)
|
222
313
|
else
|
223
314
|
puts "❌ Asset precompilation failed"
|
315
|
+
puts ""
|
316
|
+
|
317
|
+
# Combine and display all output
|
318
|
+
all_output = []
|
319
|
+
all_output << stdout unless stdout.empty?
|
320
|
+
all_output << stderr unless stderr.empty?
|
321
|
+
|
322
|
+
unless all_output.empty?
|
323
|
+
puts Rainbow("📋 Full Command Output:").red.bold
|
324
|
+
puts Rainbow("─" * 60).red
|
325
|
+
all_output.each { |output| puts output }
|
326
|
+
puts Rainbow("─" * 60).red
|
327
|
+
puts ""
|
328
|
+
end
|
329
|
+
|
330
|
+
puts Rainbow("🛠️ To debug this issue:").yellow.bold
|
331
|
+
command_display = "#{env_display} #{argv.join(' ')}"
|
332
|
+
puts "#{Rainbow('1.').cyan} #{Rainbow('Run the command separately to see detailed output:').white}"
|
333
|
+
puts " #{Rainbow(command_display).cyan}"
|
334
|
+
puts ""
|
335
|
+
puts "#{Rainbow('2.').cyan} #{Rainbow('Add --trace for full stack trace:').white}"
|
336
|
+
puts " #{Rainbow("#{command_display} --trace").cyan}"
|
337
|
+
puts ""
|
338
|
+
puts "#{Rainbow('3.').cyan} #{Rainbow('Or try with development webpack (faster, less optimized):').white}"
|
339
|
+
puts " #{Rainbow('NODE_ENV=development bundle exec rails assets:precompile').cyan}"
|
340
|
+
puts ""
|
341
|
+
|
342
|
+
puts Rainbow("💡 Common fixes:").yellow.bold
|
343
|
+
|
344
|
+
# Provide specific guidance based on error content
|
345
|
+
error_content = "#{stderr} #{stdout}".downcase
|
346
|
+
|
347
|
+
if error_content.include?("secret_key_base")
|
348
|
+
puts "#{Rainbow('•').yellow} #{Rainbow('Missing secret_key_base:').white.bold} " \
|
349
|
+
"Run #{Rainbow('bin/rails credentials:edit').cyan}"
|
350
|
+
end
|
351
|
+
|
352
|
+
if error_content.include?("database") || error_content.include?("relation") ||
|
353
|
+
error_content.include?("table")
|
354
|
+
puts "#{Rainbow('•').yellow} #{Rainbow('Database issues:').white.bold} " \
|
355
|
+
"Run #{Rainbow('bin/rails db:create db:migrate').cyan}"
|
356
|
+
end
|
357
|
+
|
358
|
+
if error_content.include?("gem") || error_content.include?("bundle") || error_content.include?("load error")
|
359
|
+
puts "#{Rainbow('•').yellow} #{Rainbow('Missing dependencies:').white.bold} " \
|
360
|
+
"Run #{Rainbow('bundle install && npm install').cyan}"
|
361
|
+
end
|
362
|
+
|
363
|
+
if error_content.include?("webpack") || error_content.include?("module") ||
|
364
|
+
error_content.include?("compilation")
|
365
|
+
puts "#{Rainbow('•').yellow} #{Rainbow('Webpack compilation:').white.bold} " \
|
366
|
+
"Check JavaScript/webpack errors above"
|
367
|
+
end
|
368
|
+
|
369
|
+
# Always show these general options
|
370
|
+
puts "#{Rainbow('•').yellow} #{Rainbow('Environment config:').white} " \
|
371
|
+
"Check #{Rainbow('config/environments/production.rb').cyan}"
|
372
|
+
|
373
|
+
puts ""
|
374
|
+
puts Rainbow("ℹ️ Alternative for development:").blue
|
375
|
+
puts " #{Rainbow('bin/dev static').green} # Static assets without production optimizations"
|
376
|
+
puts ""
|
224
377
|
exit 1
|
225
378
|
end
|
226
379
|
end
|
380
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
227
381
|
|
228
|
-
def run_static_development(procfile, verbose: false)
|
229
|
-
print_procfile_info(procfile)
|
382
|
+
def run_static_development(procfile, verbose: false, route: nil)
|
383
|
+
print_procfile_info(procfile, route: route)
|
230
384
|
print_server_info(
|
231
385
|
"⚡ Starting development server with static assets...",
|
232
386
|
[
|
@@ -235,7 +389,8 @@ module ReactOnRails
|
|
235
389
|
"CSS extracted to separate files (no FOUC)",
|
236
390
|
"Development environment (source maps, faster builds)",
|
237
391
|
"Auto-recompiles on file changes"
|
238
|
-
]
|
392
|
+
],
|
393
|
+
route: route
|
239
394
|
)
|
240
395
|
|
241
396
|
PackGenerator.generate(verbose: verbose)
|
@@ -243,25 +398,27 @@ module ReactOnRails
|
|
243
398
|
ProcessManager.run_with_process_manager(procfile)
|
244
399
|
end
|
245
400
|
|
246
|
-
def run_development(procfile, verbose: false)
|
247
|
-
print_procfile_info(procfile)
|
401
|
+
def run_development(procfile, verbose: false, route: nil)
|
402
|
+
print_procfile_info(procfile, route: route)
|
248
403
|
PackGenerator.generate(verbose: verbose)
|
249
404
|
ProcessManager.ensure_procfile(procfile)
|
250
405
|
ProcessManager.run_with_process_manager(procfile)
|
251
406
|
end
|
252
407
|
|
253
|
-
def print_server_info(title, features, port = 3000)
|
408
|
+
def print_server_info(title, features, port = 3000, route: nil)
|
254
409
|
puts title
|
255
410
|
features.each { |feature| puts " - #{feature}" }
|
256
411
|
puts ""
|
257
412
|
puts ""
|
258
|
-
|
413
|
+
url = route ? "http://localhost:#{port}/#{route}" : "http://localhost:#{port}"
|
414
|
+
puts "💡 Access at: #{Rainbow(url).cyan.underline}"
|
259
415
|
puts ""
|
260
416
|
end
|
261
417
|
|
262
|
-
def print_procfile_info(procfile)
|
418
|
+
def print_procfile_info(procfile, route: nil)
|
263
419
|
port = procfile_port(procfile)
|
264
420
|
box_width = 60
|
421
|
+
url = route ? "http://localhost:#{port}/#{route}" : "http://localhost:#{port}"
|
265
422
|
|
266
423
|
puts ""
|
267
424
|
puts box_border(box_width)
|
@@ -269,7 +426,7 @@ module ReactOnRails
|
|
269
426
|
puts format_box_line("📋 Using Procfile: #{procfile}", box_width)
|
270
427
|
puts format_box_line("🔧 Customize this file for your app's needs", box_width)
|
271
428
|
puts box_empty_line(box_width)
|
272
|
-
puts format_box_line("💡 Access at: #{Rainbow(
|
429
|
+
puts format_box_line("💡 Access at: #{Rainbow(url).cyan.underline}",
|
273
430
|
box_width)
|
274
431
|
puts box_empty_line(box_width)
|
275
432
|
puts box_bottom(box_width)
|