react_on_rails 16.2.0.beta.8 → 16.2.0.beta.11
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 +19 -13
- data/CLAUDE.md +136 -5
- data/CONTRIBUTING.md +3 -1
- data/Gemfile.lock +1 -1
- data/Steepfile +4 -0
- data/analysis/rake-task-duplicate-analysis.md +149 -0
- data/bin/ci-run-failed-specs +6 -4
- data/bin/ci-switch-config +4 -3
- data/knip.ts +1 -1
- data/lib/generators/react_on_rails/base_generator.rb +5 -119
- data/lib/generators/react_on_rails/generator_helper.rb +29 -0
- data/lib/generators/react_on_rails/install_generator.rb +5 -180
- data/lib/generators/react_on_rails/js_dependency_manager.rb +354 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +19 -0
- data/lib/generators/react_on_rails/templates/base/base/config/{shakapacker.yml → shakapacker.yml.tt} +18 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +38 -4
- data/lib/react_on_rails/configuration.rb +82 -8
- data/lib/react_on_rails/dev/pack_generator.rb +1 -0
- data/lib/react_on_rails/dev/server_manager.rb +1 -0
- data/lib/react_on_rails/doctor.rb +94 -4
- data/lib/react_on_rails/engine.rb +2 -5
- data/lib/react_on_rails/system_checker.rb +7 -4
- data/lib/react_on_rails/utils.rb +54 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/react_on_rails_pro/Gemfile.lock +3 -3
- data/react_on_rails_pro/lib/react_on_rails_pro/version.rb +1 -1
- data/react_on_rails_pro/package.json +1 -1
- data/react_on_rails_pro/spec/dummy/Gemfile.lock +3 -3
- data/react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook +19 -0
- data/react_on_rails_pro/spec/dummy/config/shakapacker.yml +5 -0
- data/sig/react_on_rails/dev/file_manager.rbs +15 -0
- data/sig/react_on_rails/dev/pack_generator.rbs +19 -0
- data/sig/react_on_rails/dev/process_manager.rbs +22 -0
- data/sig/react_on_rails/dev/server_manager.rbs +39 -0
- data/sig/react_on_rails/generators/js_dependency_manager.rbs +123 -0
- metadata +11 -3
data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt
CHANGED
|
@@ -44,19 +44,53 @@ const configureServer = () => {
|
|
|
44
44
|
};
|
|
45
45
|
serverWebpackConfig.plugins.unshift(new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 }));
|
|
46
46
|
|
|
47
|
-
// Custom output for the server-bundle
|
|
48
|
-
|
|
49
|
-
//
|
|
47
|
+
// Custom output for the server-bundle
|
|
48
|
+
<% if shakapacker_version_9_or_higher? -%>
|
|
49
|
+
// Using Shakapacker 9.0+ privateOutputPath for automatic sync with shakapacker.yml
|
|
50
|
+
// This eliminates manual path configuration and keeps configs in sync.
|
|
51
|
+
// Falls back to hardcoded path if private_output_path is not configured.
|
|
52
|
+
const serverBundleOutputPath = config.privateOutputPath ||
|
|
53
|
+
require('path').resolve(__dirname, '../../ssr-generated');
|
|
54
|
+
<% else -%>
|
|
55
|
+
// Using hardcoded path (Shakapacker < 9.0)
|
|
56
|
+
// For Shakapacker 9.0+, consider using config.privateOutputPath instead
|
|
57
|
+
// to automatically sync with shakapacker.yml private_output_path.
|
|
58
|
+
const serverBundleOutputPath = require('path').resolve(__dirname, '../../ssr-generated');
|
|
59
|
+
<% end -%>
|
|
60
|
+
|
|
50
61
|
serverWebpackConfig.output = {
|
|
51
62
|
filename: 'server-bundle.js',
|
|
52
63
|
globalObject: 'this',
|
|
53
64
|
// If using the React on Rails Pro node server renderer, uncomment the next line
|
|
54
65
|
// libraryTarget: 'commonjs2',
|
|
55
|
-
path:
|
|
66
|
+
path: serverBundleOutputPath,
|
|
56
67
|
// No publicPath needed since server bundles are not served via web
|
|
57
68
|
// https://webpack.js.org/configuration/output/#outputglobalobject
|
|
58
69
|
};
|
|
59
70
|
|
|
71
|
+
// Validate server bundle output path configuration
|
|
72
|
+
<% if shakapacker_version_9_or_higher? -%>
|
|
73
|
+
// For Shakapacker 9.0+, verify privateOutputPath is configured in shakapacker.yml
|
|
74
|
+
if (!config.privateOutputPath) {
|
|
75
|
+
console.warn('⚠️ Shakapacker 9.0+ detected but private_output_path not configured in shakapacker.yml');
|
|
76
|
+
console.warn(' Add to config/shakapacker.yml:');
|
|
77
|
+
console.warn(' private_output_path: ssr-generated');
|
|
78
|
+
console.warn(' Run: rails react_on_rails:doctor to validate your configuration');
|
|
79
|
+
}
|
|
80
|
+
<% else -%>
|
|
81
|
+
// For Shakapacker < 9.0, verify hardcoded path syncs with Rails config
|
|
82
|
+
// 1. Ensure config/initializers/react_on_rails.rb has: config.server_bundle_output_path = "ssr-generated"
|
|
83
|
+
// 2. Run: rails react_on_rails:doctor to verify configuration
|
|
84
|
+
const fs = require('fs');
|
|
85
|
+
if (!fs.existsSync(serverBundleOutputPath)) {
|
|
86
|
+
console.warn(`⚠️ Server bundle output directory does not exist: ${serverBundleOutputPath}`);
|
|
87
|
+
console.warn(' It will be created during build, but ensure React on Rails is configured:');
|
|
88
|
+
console.warn(' config.server_bundle_output_path = "ssr-generated" in config/initializers/react_on_rails.rb');
|
|
89
|
+
console.warn(' Run: rails react_on_rails:doctor to validate your configuration');
|
|
90
|
+
}
|
|
91
|
+
<% end -%>
|
|
92
|
+
|
|
93
|
+
|
|
60
94
|
// Don't hash the server bundle b/c would conflict with the client manifest
|
|
61
95
|
// And no need for the MiniCssExtractPlugin
|
|
62
96
|
serverWebpackConfig.plugins = serverWebpackConfig.plugins.filter(
|
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
|
4
|
+
require "active_support/core_ext/object/blank"
|
|
5
|
+
|
|
6
|
+
# Polyfill for compact_blank (added in Rails 6.1) to support Rails 5.2-6.0
|
|
7
|
+
unless [].respond_to?(:compact_blank)
|
|
8
|
+
module Enumerable
|
|
9
|
+
def compact_blank
|
|
10
|
+
reject(&:blank?)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class Array
|
|
15
|
+
def compact_blank
|
|
16
|
+
reject(&:blank?)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
3
21
|
# rubocop:disable Metrics/ClassLength
|
|
4
22
|
|
|
5
23
|
module ReactOnRails
|
|
@@ -10,6 +28,7 @@ module ReactOnRails
|
|
|
10
28
|
|
|
11
29
|
DEFAULT_GENERATED_ASSETS_DIR = File.join(%w[public webpack], Rails.env).freeze
|
|
12
30
|
DEFAULT_COMPONENT_REGISTRY_TIMEOUT = 5000
|
|
31
|
+
DEFAULT_SERVER_BUNDLE_OUTPUT_PATH = "ssr-generated"
|
|
13
32
|
|
|
14
33
|
def self.configuration
|
|
15
34
|
@configuration ||= Configuration.new(
|
|
@@ -46,7 +65,7 @@ module ReactOnRails
|
|
|
46
65
|
# Set to 0 to disable the timeout and wait indefinitely for component registration.
|
|
47
66
|
component_registry_timeout: DEFAULT_COMPONENT_REGISTRY_TIMEOUT,
|
|
48
67
|
generated_component_packs_loading_strategy: nil,
|
|
49
|
-
server_bundle_output_path:
|
|
68
|
+
server_bundle_output_path: DEFAULT_SERVER_BUNDLE_OUTPUT_PATH,
|
|
50
69
|
enforce_private_server_bundles: false
|
|
51
70
|
)
|
|
52
71
|
end
|
|
@@ -184,6 +203,7 @@ module ReactOnRails
|
|
|
184
203
|
check_component_registry_timeout
|
|
185
204
|
validate_generated_component_packs_loading_strategy
|
|
186
205
|
validate_enforce_private_server_bundles
|
|
206
|
+
auto_detect_server_bundle_path_from_shakapacker
|
|
187
207
|
end
|
|
188
208
|
|
|
189
209
|
private
|
|
@@ -257,6 +277,57 @@ module ReactOnRails
|
|
|
257
277
|
"the public directory. Please set it to a directory outside of public."
|
|
258
278
|
end
|
|
259
279
|
|
|
280
|
+
# Auto-detect server_bundle_output_path from Shakapacker 9.0+ private_output_path
|
|
281
|
+
# Checks if user explicitly set a value and warns them to use auto-detection instead
|
|
282
|
+
def auto_detect_server_bundle_path_from_shakapacker
|
|
283
|
+
# Skip if Shakapacker is not available
|
|
284
|
+
return unless defined?(::Shakapacker)
|
|
285
|
+
|
|
286
|
+
# Check if Shakapacker config has private_output_path method (9.0+)
|
|
287
|
+
return unless ::Shakapacker.config.respond_to?(:private_output_path)
|
|
288
|
+
|
|
289
|
+
# Get the private_output_path from Shakapacker
|
|
290
|
+
private_path = ::Shakapacker.config.private_output_path
|
|
291
|
+
return unless private_path
|
|
292
|
+
|
|
293
|
+
relative_path = ReactOnRails::Utils.normalize_to_relative_path(private_path)
|
|
294
|
+
|
|
295
|
+
# Check if user explicitly configured server_bundle_output_path
|
|
296
|
+
if server_bundle_output_path != ReactOnRails::DEFAULT_SERVER_BUNDLE_OUTPUT_PATH
|
|
297
|
+
warn_about_explicit_configuration(relative_path)
|
|
298
|
+
return
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
apply_shakapacker_private_output_path(relative_path)
|
|
302
|
+
rescue StandardError => e
|
|
303
|
+
# Fail gracefully - if auto-detection fails, keep the default
|
|
304
|
+
Rails.logger&.debug("ReactOnRails: Could not auto-detect server bundle path from " \
|
|
305
|
+
"Shakapacker: #{e.message}")
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def warn_about_explicit_configuration(shakapacker_path)
|
|
309
|
+
# Normalize both paths for comparison
|
|
310
|
+
normalized_config = server_bundle_output_path.to_s.chomp("/")
|
|
311
|
+
normalized_shakapacker = shakapacker_path.to_s.chomp("/")
|
|
312
|
+
|
|
313
|
+
# Only warn if there's a mismatch
|
|
314
|
+
return if normalized_config == normalized_shakapacker
|
|
315
|
+
|
|
316
|
+
Rails.logger&.warn(
|
|
317
|
+
"ReactOnRails: server_bundle_output_path is explicitly set to '#{server_bundle_output_path}' " \
|
|
318
|
+
"but shakapacker.yml private_output_path is '#{shakapacker_path}'. " \
|
|
319
|
+
"Consider removing server_bundle_output_path from your React on Rails initializer " \
|
|
320
|
+
"to use the auto-detected value from shakapacker.yml."
|
|
321
|
+
)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def apply_shakapacker_private_output_path(relative_path)
|
|
325
|
+
self.server_bundle_output_path = relative_path
|
|
326
|
+
|
|
327
|
+
Rails.logger&.debug("ReactOnRails: Auto-detected server_bundle_output_path from " \
|
|
328
|
+
"shakapacker.yml private_output_path: '#{relative_path}'")
|
|
329
|
+
end
|
|
330
|
+
|
|
260
331
|
def check_minimum_shakapacker_version
|
|
261
332
|
ReactOnRails::PackerUtils.raise_shakapacker_version_incompatible_for_basic_pack_generation unless
|
|
262
333
|
ReactOnRails::PackerUtils.supports_basic_pack_generation?
|
|
@@ -360,13 +431,16 @@ module ReactOnRails
|
|
|
360
431
|
def ensure_webpack_generated_files_exists
|
|
361
432
|
return unless webpack_generated_files.empty?
|
|
362
433
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
434
|
+
files = ["manifest.json", server_bundle_js_file]
|
|
435
|
+
|
|
436
|
+
if ReactOnRails::Utils.react_on_rails_pro?
|
|
437
|
+
pro_config = ReactOnRailsPro.configuration
|
|
438
|
+
files << pro_config.rsc_bundle_js_file
|
|
439
|
+
files << pro_config.react_client_manifest_file
|
|
440
|
+
files << pro_config.react_server_client_manifest_file
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
self.webpack_generated_files = files.compact_blank
|
|
370
444
|
end
|
|
371
445
|
|
|
372
446
|
def configure_skip_display_none_deprecation
|
|
@@ -667,6 +667,7 @@ module ReactOnRails
|
|
|
667
667
|
end
|
|
668
668
|
end
|
|
669
669
|
|
|
670
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
670
671
|
def analyze_server_rendering_config(content)
|
|
671
672
|
checker.add_info("\n🖥️ Server Rendering:")
|
|
672
673
|
|
|
@@ -678,6 +679,19 @@ module ReactOnRails
|
|
|
678
679
|
checker.add_info(" server_bundle_js_file: server-bundle.js (default)")
|
|
679
680
|
end
|
|
680
681
|
|
|
682
|
+
# Server bundle output path
|
|
683
|
+
server_bundle_path_match = content.match(/config\.server_bundle_output_path\s*=\s*["']([^"']+)["']/)
|
|
684
|
+
default_path = ReactOnRails::DEFAULT_SERVER_BUNDLE_OUTPUT_PATH
|
|
685
|
+
rails_bundle_path = server_bundle_path_match ? server_bundle_path_match[1] : default_path
|
|
686
|
+
checker.add_info(" server_bundle_output_path: #{rails_bundle_path}")
|
|
687
|
+
|
|
688
|
+
# Enforce private server bundles
|
|
689
|
+
enforce_private_match = content.match(/config\.enforce_private_server_bundles\s*=\s*([^\s\n,]+)/)
|
|
690
|
+
checker.add_info(" enforce_private_server_bundles: #{enforce_private_match[1]}") if enforce_private_match
|
|
691
|
+
|
|
692
|
+
# Check Shakapacker integration and provide recommendations
|
|
693
|
+
check_shakapacker_private_output_path(rails_bundle_path)
|
|
694
|
+
|
|
681
695
|
# RSC bundle file (Pro feature)
|
|
682
696
|
rsc_bundle_match = content.match(/config\.rsc_bundle_js_file\s*=\s*["']([^"']+)["']/)
|
|
683
697
|
if rsc_bundle_match
|
|
@@ -702,9 +716,9 @@ module ReactOnRails
|
|
|
702
716
|
|
|
703
717
|
checker.add_info(" raise_on_prerender_error: #{raise_on_error_match[1]}")
|
|
704
718
|
end
|
|
705
|
-
# rubocop:enable Metrics/
|
|
719
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
706
720
|
|
|
707
|
-
# rubocop:disable Metrics/
|
|
721
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
708
722
|
def analyze_performance_config(content)
|
|
709
723
|
checker.add_info("\n⚡ Performance & Loading:")
|
|
710
724
|
|
|
@@ -1387,9 +1401,85 @@ module ReactOnRails
|
|
|
1387
1401
|
end
|
|
1388
1402
|
|
|
1389
1403
|
def log_debug(message)
|
|
1390
|
-
|
|
1404
|
+
Rails.logger&.debug(message)
|
|
1405
|
+
end
|
|
1406
|
+
|
|
1407
|
+
# Check Shakapacker private_output_path integration and provide recommendations
|
|
1408
|
+
def check_shakapacker_private_output_path(rails_bundle_path)
|
|
1409
|
+
return report_no_shakapacker unless defined?(::Shakapacker)
|
|
1410
|
+
return report_upgrade_shakapacker unless ::Shakapacker.config.respond_to?(:private_output_path)
|
|
1411
|
+
|
|
1412
|
+
check_shakapacker_9_private_output_path(rails_bundle_path)
|
|
1413
|
+
rescue StandardError => e
|
|
1414
|
+
checker.add_info("\n ℹ️ Could not check Shakapacker config: #{e.message}")
|
|
1415
|
+
end
|
|
1416
|
+
|
|
1417
|
+
def report_no_shakapacker
|
|
1418
|
+
checker.add_info("\n ℹ️ Shakapacker not detected - using manual configuration")
|
|
1419
|
+
end
|
|
1420
|
+
|
|
1421
|
+
def report_upgrade_shakapacker
|
|
1422
|
+
checker.add_info(<<~MSG.strip)
|
|
1423
|
+
\n 💡 Recommendation: Upgrade to Shakapacker 9.0+
|
|
1424
|
+
|
|
1425
|
+
Shakapacker 9.0+ adds 'private_output_path' in shakapacker.yml for server bundles.
|
|
1426
|
+
This eliminates the need to configure server_bundle_output_path separately.
|
|
1427
|
+
|
|
1428
|
+
Benefits:
|
|
1429
|
+
- Single source of truth in shakapacker.yml
|
|
1430
|
+
- Automatic detection by React on Rails
|
|
1431
|
+
- No configuration duplication
|
|
1432
|
+
MSG
|
|
1433
|
+
end
|
|
1434
|
+
|
|
1435
|
+
def check_shakapacker_9_private_output_path(rails_bundle_path)
|
|
1436
|
+
private_path = ::Shakapacker.config.private_output_path
|
|
1437
|
+
|
|
1438
|
+
if private_path
|
|
1439
|
+
report_shakapacker_path_status(private_path, rails_bundle_path)
|
|
1440
|
+
else
|
|
1441
|
+
report_configure_private_output_path(rails_bundle_path)
|
|
1442
|
+
end
|
|
1443
|
+
end
|
|
1444
|
+
|
|
1445
|
+
def report_shakapacker_path_status(private_path, rails_bundle_path)
|
|
1446
|
+
relative_path = ReactOnRails::Utils.normalize_to_relative_path(private_path)
|
|
1447
|
+
# Normalize both paths for comparison (remove trailing slashes)
|
|
1448
|
+
normalized_relative = relative_path.to_s.chomp("/")
|
|
1449
|
+
normalized_rails = rails_bundle_path.to_s.chomp("/")
|
|
1450
|
+
|
|
1451
|
+
if normalized_relative == normalized_rails
|
|
1452
|
+
checker.add_success("\n ✅ Using Shakapacker 9.0+ private_output_path: '#{relative_path}'")
|
|
1453
|
+
checker.add_info(" Auto-detected from shakapacker.yml - no manual config needed")
|
|
1454
|
+
else
|
|
1455
|
+
report_configuration_mismatch(relative_path, rails_bundle_path)
|
|
1456
|
+
end
|
|
1457
|
+
end
|
|
1458
|
+
|
|
1459
|
+
def report_configuration_mismatch(relative_path, rails_bundle_path)
|
|
1460
|
+
checker.add_warning(<<~MSG.strip)
|
|
1461
|
+
\n ⚠️ Configuration mismatch detected!
|
|
1462
|
+
|
|
1463
|
+
Shakapacker private_output_path: '#{relative_path}'
|
|
1464
|
+
React on Rails server_bundle_output_path: '#{rails_bundle_path}'
|
|
1465
|
+
|
|
1466
|
+
Recommendation: Remove server_bundle_output_path from your React on Rails
|
|
1467
|
+
initializer and let it auto-detect from shakapacker.yml private_output_path.
|
|
1468
|
+
MSG
|
|
1469
|
+
end
|
|
1470
|
+
|
|
1471
|
+
def report_configure_private_output_path(rails_bundle_path)
|
|
1472
|
+
checker.add_info(<<~MSG.strip)
|
|
1473
|
+
\n 💡 Recommendation: Configure private_output_path in shakapacker.yml
|
|
1474
|
+
|
|
1475
|
+
Add to config/shakapacker.yml:
|
|
1476
|
+
private_output_path: #{rails_bundle_path}
|
|
1391
1477
|
|
|
1392
|
-
|
|
1478
|
+
This will:
|
|
1479
|
+
- Keep webpack and Rails configs in sync automatically
|
|
1480
|
+
- Enable auto-detection by React on Rails
|
|
1481
|
+
- Serve as single source of truth for server bundle location
|
|
1482
|
+
MSG
|
|
1393
1483
|
end
|
|
1394
1484
|
end
|
|
1395
1485
|
# rubocop:enable Metrics/ClassLength
|
|
@@ -83,10 +83,7 @@ module ReactOnRails
|
|
|
83
83
|
ReactOnRails::ServerRenderingPool.reset_pool
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
load File.expand_path("../tasks/assets.rake", __dir__)
|
|
89
|
-
load File.expand_path("../tasks/locale.rake", __dir__)
|
|
90
|
-
end
|
|
86
|
+
# Rake tasks are automatically loaded from lib/tasks/*.rake by Rails::Engine
|
|
87
|
+
# No need to explicitly load them here to avoid duplicate loading
|
|
91
88
|
end
|
|
92
89
|
end
|
|
@@ -213,17 +213,20 @@ module ReactOnRails
|
|
|
213
213
|
|
|
214
214
|
return unless npm_version && defined?(ReactOnRails::VERSION)
|
|
215
215
|
|
|
216
|
-
#
|
|
217
|
-
|
|
216
|
+
# Normalize NPM version format to Ruby gem format for comparison
|
|
217
|
+
# Uses existing VersionSyntaxConverter to handle dash/dot differences
|
|
218
|
+
# (e.g., "16.2.0-beta.10" → "16.2.0.beta.10")
|
|
219
|
+
converter = ReactOnRails::VersionSyntaxConverter.new
|
|
220
|
+
normalized_npm_version = converter.npm_to_rubygem(npm_version)
|
|
218
221
|
gem_version = ReactOnRails::VERSION
|
|
219
222
|
|
|
220
|
-
if
|
|
223
|
+
if normalized_npm_version == gem_version
|
|
221
224
|
add_success("✅ React on Rails gem and NPM package versions match (#{gem_version})")
|
|
222
225
|
check_version_patterns(npm_version, gem_version)
|
|
223
226
|
else
|
|
224
227
|
# Check for major version differences
|
|
225
228
|
gem_major = gem_version.split(".")[0].to_i
|
|
226
|
-
npm_major =
|
|
229
|
+
npm_major = normalized_npm_version.split(".")[0].to_i
|
|
227
230
|
|
|
228
231
|
if gem_major != npm_major # rubocop:disable Style/NegatedIfElseCondition
|
|
229
232
|
add_error(<<~MSG.strip)
|
data/lib/react_on_rails/utils.rb
CHANGED
|
@@ -443,6 +443,60 @@ module ReactOnRails
|
|
|
443
443
|
end
|
|
444
444
|
end
|
|
445
445
|
|
|
446
|
+
# Converts an absolute path (String or Pathname) to a path relative to Rails.root.
|
|
447
|
+
# If the path is already relative or doesn't contain Rails.root, returns it as-is.
|
|
448
|
+
#
|
|
449
|
+
# This method is used to normalize paths from Shakapacker's privateOutputPath (which is
|
|
450
|
+
# absolute) to relative paths suitable for React on Rails configuration.
|
|
451
|
+
#
|
|
452
|
+
# Note: Absolute paths that don't start with Rails.root are intentionally passed through
|
|
453
|
+
# unchanged. While there's no known use case for server bundles outside Rails.root,
|
|
454
|
+
# this behavior preserves the original path for debugging and error messages.
|
|
455
|
+
#
|
|
456
|
+
# @param path [String, Pathname] The path to normalize
|
|
457
|
+
# @return [String, nil] The relative path as a string, or nil if path is nil
|
|
458
|
+
#
|
|
459
|
+
# @example Converting absolute paths within Rails.root
|
|
460
|
+
# # Assuming Rails.root is "/app"
|
|
461
|
+
# normalize_to_relative_path("/app/ssr-generated") # => "ssr-generated"
|
|
462
|
+
# normalize_to_relative_path("/app/foo/bar") # => "foo/bar"
|
|
463
|
+
#
|
|
464
|
+
# @example Already relative paths pass through
|
|
465
|
+
# normalize_to_relative_path("ssr-generated") # => "ssr-generated"
|
|
466
|
+
# normalize_to_relative_path("./ssr-generated") # => "./ssr-generated"
|
|
467
|
+
#
|
|
468
|
+
# @example Absolute paths outside Rails.root (edge case)
|
|
469
|
+
# normalize_to_relative_path("/other/path/bundles") # => "/other/path/bundles"
|
|
470
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
471
|
+
def self.normalize_to_relative_path(path)
|
|
472
|
+
return nil if path.nil?
|
|
473
|
+
|
|
474
|
+
path_str = path.to_s
|
|
475
|
+
rails_root_str = Rails.root.to_s.chomp("/")
|
|
476
|
+
|
|
477
|
+
# Treat as "inside Rails.root" only for exact match or a subdirectory
|
|
478
|
+
inside_rails_root = rails_root_str.present? &&
|
|
479
|
+
(path_str == rails_root_str || path_str.start_with?("#{rails_root_str}/"))
|
|
480
|
+
|
|
481
|
+
# If path is within Rails.root, remove that prefix
|
|
482
|
+
if inside_rails_root
|
|
483
|
+
# Remove Rails.root and any leading slash
|
|
484
|
+
path_str.sub(%r{^#{Regexp.escape(rails_root_str)}/?}, "")
|
|
485
|
+
else
|
|
486
|
+
# Path is already relative or outside Rails.root
|
|
487
|
+
# Warn if it's an absolute path outside Rails.root (edge case)
|
|
488
|
+
if path_str.start_with?("/") && !inside_rails_root
|
|
489
|
+
Rails.logger&.warn(
|
|
490
|
+
"ReactOnRails: Detected absolute path outside Rails.root: '#{path_str}'. " \
|
|
491
|
+
"Server bundles are typically stored within Rails.root. " \
|
|
492
|
+
"Verify this is intentional."
|
|
493
|
+
)
|
|
494
|
+
end
|
|
495
|
+
path_str
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
499
|
+
|
|
446
500
|
def self.default_troubleshooting_section
|
|
447
501
|
<<~DEFAULT
|
|
448
502
|
📞 Get Help & Support:
|
|
@@ -9,7 +9,7 @@ GIT
|
|
|
9
9
|
PATH
|
|
10
10
|
remote: ..
|
|
11
11
|
specs:
|
|
12
|
-
react_on_rails (16.2.0.beta.
|
|
12
|
+
react_on_rails (16.2.0.beta.11)
|
|
13
13
|
addressable
|
|
14
14
|
connection_pool
|
|
15
15
|
execjs (~> 2.5)
|
|
@@ -20,7 +20,7 @@ PATH
|
|
|
20
20
|
PATH
|
|
21
21
|
remote: .
|
|
22
22
|
specs:
|
|
23
|
-
react_on_rails_pro (16.2.0.beta.
|
|
23
|
+
react_on_rails_pro (16.2.0.beta.11)
|
|
24
24
|
addressable
|
|
25
25
|
async (>= 2.6)
|
|
26
26
|
connection_pool
|
|
@@ -28,7 +28,7 @@ PATH
|
|
|
28
28
|
httpx (~> 1.5)
|
|
29
29
|
jwt (~> 2.7)
|
|
30
30
|
rainbow
|
|
31
|
-
react_on_rails (= 16.2.0.beta.
|
|
31
|
+
react_on_rails (= 16.2.0.beta.11)
|
|
32
32
|
|
|
33
33
|
GEM
|
|
34
34
|
remote: https://rubygems.org/
|
|
@@ -9,7 +9,7 @@ GIT
|
|
|
9
9
|
PATH
|
|
10
10
|
remote: ../../..
|
|
11
11
|
specs:
|
|
12
|
-
react_on_rails (16.2.0.beta.
|
|
12
|
+
react_on_rails (16.2.0.beta.11)
|
|
13
13
|
addressable
|
|
14
14
|
connection_pool
|
|
15
15
|
execjs (~> 2.5)
|
|
@@ -20,7 +20,7 @@ PATH
|
|
|
20
20
|
PATH
|
|
21
21
|
remote: ../..
|
|
22
22
|
specs:
|
|
23
|
-
react_on_rails_pro (16.2.0.beta.
|
|
23
|
+
react_on_rails_pro (16.2.0.beta.11)
|
|
24
24
|
addressable
|
|
25
25
|
async (>= 2.6)
|
|
26
26
|
connection_pool
|
|
@@ -28,7 +28,7 @@ PATH
|
|
|
28
28
|
httpx (~> 1.5)
|
|
29
29
|
jwt (~> 2.7)
|
|
30
30
|
rainbow
|
|
31
|
-
react_on_rails (= 16.2.0.beta.
|
|
31
|
+
react_on_rails (= 16.2.0.beta.11)
|
|
32
32
|
|
|
33
33
|
GEM
|
|
34
34
|
remote: https://rubygems.org/
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Shakapacker precompile hook for React on Rails Pro test dummy app
|
|
5
|
+
#
|
|
6
|
+
# This script loads the shared test helper implementation.
|
|
7
|
+
# For production apps, use the generator template which includes a standalone implementation.
|
|
8
|
+
|
|
9
|
+
# Find the gem root directory (four levels up from react_on_rails_pro/spec/dummy/bin)
|
|
10
|
+
gem_root = File.expand_path("../../../..", __dir__)
|
|
11
|
+
shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb")
|
|
12
|
+
|
|
13
|
+
unless File.exist?(shared_hook)
|
|
14
|
+
warn "❌ Error: Shared precompile hook not found at #{shared_hook}"
|
|
15
|
+
exit 1
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Load and execute the shared hook
|
|
19
|
+
load shared_hook
|
|
@@ -19,6 +19,11 @@ default: &default
|
|
|
19
19
|
# Reload manifest.json on all requests so we reload latest compiled packs
|
|
20
20
|
cache_manifest: false
|
|
21
21
|
|
|
22
|
+
# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
|
|
23
|
+
# SECURITY: Only reference trusted scripts within your project. Ensure the hook path
|
|
24
|
+
# points to a file within the project root that you control.
|
|
25
|
+
precompile_hook: 'bin/shakapacker-precompile-hook'
|
|
26
|
+
|
|
22
27
|
# Extract and emit a css file
|
|
23
28
|
extract_css: true
|
|
24
29
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class FileManager
|
|
4
|
+
def self.cleanup_stale_files: () -> bool
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def self.cleanup_overmind_sockets: () -> bool
|
|
9
|
+
def self.cleanup_rails_pid_file: () -> bool
|
|
10
|
+
def self.overmind_running?: () -> bool
|
|
11
|
+
def self.process_running?: (Integer) -> bool
|
|
12
|
+
def self.remove_file_if_exists: (String, String) -> bool
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class PackGenerator
|
|
4
|
+
def self.generate: (?verbose: bool) -> void
|
|
5
|
+
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
def self.run_pack_generation: (?silent: bool) -> bool
|
|
9
|
+
def self.should_run_directly?: () -> bool
|
|
10
|
+
def self.rails_available?: () -> bool
|
|
11
|
+
def self.run_rake_task_directly: (?silent: bool) -> bool
|
|
12
|
+
def self.load_rake_tasks: () -> void
|
|
13
|
+
def self.prepare_rake_task: () -> untyped
|
|
14
|
+
def self.capture_output: (bool) { () -> bool } -> bool
|
|
15
|
+
def self.handle_rake_error: (Exception, bool) -> void
|
|
16
|
+
def self.run_via_bundle_exec: (?silent: bool) -> (bool | nil)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class ProcessManager
|
|
4
|
+
VERSION_CHECK_TIMEOUT: Integer
|
|
5
|
+
|
|
6
|
+
def self.installed?: (String) -> bool
|
|
7
|
+
def self.ensure_procfile: (String) -> void
|
|
8
|
+
def self.run_with_process_manager: (String) -> void
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def self.installed_in_current_context?: (String) -> bool
|
|
13
|
+
def self.version_flags_for: (String) -> Array[String]
|
|
14
|
+
def self.run_process_if_available: (String, Array[String]) -> bool
|
|
15
|
+
def self.run_process_outside_bundle: (String, Array[String]) -> bool
|
|
16
|
+
def self.process_available_in_system?: (String) -> bool
|
|
17
|
+
def self.with_unbundled_context: () { () -> untyped } -> untyped
|
|
18
|
+
def self.show_process_manager_installation_help: () -> void
|
|
19
|
+
def self.valid_procfile_path?: (String) -> bool
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ReactOnRails
|
|
2
|
+
module Dev
|
|
3
|
+
class ServerManager
|
|
4
|
+
type mode = :development | :production_like | :static | :hmr
|
|
5
|
+
|
|
6
|
+
def self.start: (mode, String?, ?verbose: bool, ?route: String?, ?rails_env: String?) -> void
|
|
7
|
+
def self.kill_processes: () -> void
|
|
8
|
+
def self.development_processes: () -> Hash[String, String]
|
|
9
|
+
def self.kill_running_processes: () -> bool
|
|
10
|
+
def self.find_process_pids: (String) -> Array[Integer]
|
|
11
|
+
def self.terminate_processes: (Array[Integer]) -> void
|
|
12
|
+
def self.kill_port_processes: (Array[Integer]) -> bool
|
|
13
|
+
def self.find_port_pids: (Integer) -> Array[Integer]
|
|
14
|
+
def self.cleanup_socket_files: () -> bool
|
|
15
|
+
def self.print_kill_summary: (bool) -> void
|
|
16
|
+
def self.show_help: () -> void
|
|
17
|
+
def self.run_from_command_line: (?Array[String]) -> void
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def self.help_usage: () -> String
|
|
22
|
+
def self.help_commands: () -> String
|
|
23
|
+
def self.help_options: () -> String
|
|
24
|
+
def self.help_customization: () -> String
|
|
25
|
+
def self.help_mode_details: () -> String
|
|
26
|
+
def self.help_troubleshooting: () -> String
|
|
27
|
+
def self.run_production_like: (?_verbose: bool, ?route: String?, ?rails_env: String?) -> void
|
|
28
|
+
def self.run_static_development: (String, ?verbose: bool, ?route: String?) -> void
|
|
29
|
+
def self.run_development: (String, ?verbose: bool, ?route: String?) -> void
|
|
30
|
+
def self.print_server_info: (String, Array[String], ?Integer, ?route: String?) -> void
|
|
31
|
+
def self.print_procfile_info: (String, ?route: String?) -> void
|
|
32
|
+
def self.procfile_port: (String) -> Integer
|
|
33
|
+
def self.box_border: (Integer) -> String
|
|
34
|
+
def self.box_bottom: (Integer) -> String
|
|
35
|
+
def self.box_empty_line: (Integer) -> String
|
|
36
|
+
def self.format_box_line: (String, Integer) -> String
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|