react_on_rails 17.1.0 → 17.2.0.rc.1
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/Gemfile +1 -1
- data/Gemfile.development_dependencies +2 -2
- data/Gemfile.lock +65 -73
- data/Steepfile +1 -0
- data/docs/agent/install-and-upgrade.md +8 -3
- data/docs/agent/rsc-adoption.md +3 -0
- data/lib/generators/react_on_rails/base_generator.rb +2 -1
- data/lib/generators/react_on_rails/generated_webpack_config_pair.rb +84 -0
- data/lib/generators/react_on_rails/generator_helper.rb +2 -4
- data/lib/generators/react_on_rails/generator_messages.rb +1 -1
- data/lib/generators/react_on_rails/install_generator.rb +2 -1
- data/lib/generators/react_on_rails/js_dependency_manager.rb +14 -14
- data/lib/generators/react_on_rails/pro/USAGE +14 -3
- data/lib/generators/react_on_rails/pro_generator.rb +1 -1
- data/lib/generators/react_on_rails/pro_setup.rb +41 -213
- data/lib/generators/react_on_rails/rsc_setup/client_references.rb +100 -30
- data/lib/generators/react_on_rails/rsc_setup.rb +7 -16
- data/lib/generators/react_on_rails/templates/pro/base/config/initializers/react_on_rails_pro.rb.tt +1 -1
- data/lib/react_on_rails/agent_guardrails.rb +217 -39
- data/lib/react_on_rails/dev/process_manager.rb +14 -0
- data/lib/react_on_rails/dev/server_manager.rb +1727 -103
- data/lib/react_on_rails/doctor.rb +875 -44
- data/lib/react_on_rails/helper.rb +3 -3
- data/lib/react_on_rails/react_component/render_options.rb +5 -7
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +27 -116
- data/lib/react_on_rails/utils.rb +65 -0
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker/lockfile_resolution.rb +170 -0
- data/lib/react_on_rails/version_checker.rb +43 -22
- data/rakelib/run_rspec.rake +6 -1
- data/sig/react_on_rails/configuration.rbs +28 -26
- data/sig/react_on_rails/controller.rbs +2 -1
- data/sig/react_on_rails/dev/process_manager.rbs +1 -0
- data/sig/react_on_rails/dev/server_manager.rbs +150 -10
- data/sig/react_on_rails/git_utils.rbs +5 -1
- data/sig/react_on_rails/locales.rbs +4 -4
- data/sig/react_on_rails/prerender_error.rbs +2 -2
- data/sig/react_on_rails/test_helper.rbs +15 -1
- data/sig/react_on_rails/utils.rbs +2 -2
- data/sig/react_on_rails/version_checker/lockfile_resolution.rbs +31 -0
- data/sig/react_on_rails/version_checker.rbs +30 -7
- metadata +4 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "securerandom"
|
|
4
4
|
require_relative "generator_messages"
|
|
5
|
+
require_relative "generated_webpack_config_pair"
|
|
5
6
|
require "react_on_rails/node_renderer_procfile"
|
|
6
7
|
require "react_on_rails/pro_migration"
|
|
7
8
|
|
|
@@ -36,9 +37,8 @@ module ReactOnRails
|
|
|
36
37
|
# Loader helpers emitted by
|
|
37
38
|
# templates/base/base/config/webpack/serverWebpackConfig.js.tt.
|
|
38
39
|
#
|
|
39
|
-
# Keep
|
|
40
|
-
#
|
|
41
|
-
# the two must produce the same source text so drift is detectable (issue #4786).
|
|
40
|
+
# Keep these parity-test anchors byte-identical to the template (issue #4786).
|
|
41
|
+
# Migration recognition uses the entire rendered pair, never these snippets.
|
|
42
42
|
GET_LOADER_PATH_JS = <<~JS
|
|
43
43
|
// Normalizes an entry of a webpack/rspack `rule.use` array to its loader path.
|
|
44
44
|
// Entries may be a bare string, a `{ loader, options }` object, or null.
|
|
@@ -56,15 +56,6 @@ module ReactOnRails
|
|
|
56
56
|
}
|
|
57
57
|
JS
|
|
58
58
|
|
|
59
|
-
BUNDLER_REQUIRE_PATTERN =
|
|
60
|
-
%r{(const bundler = config\.assets_bundler.*\n.*require\('@rspack/core'\).*\n.*: require\('webpack'\);)}
|
|
61
|
-
|
|
62
|
-
# Matches any declaration of the getLoaderPath symbol, however it is written. The
|
|
63
|
-
# emitted extractLoader calls getLoaderPath, so we must never add a second
|
|
64
|
-
# declaration: `function` next to an existing `const` is a SyntaxError, not a
|
|
65
|
-
# silent shadow, and the generated config would fail to parse in Node.
|
|
66
|
-
GET_LOADER_PATH_DECLARATION = /(?:function\s+getLoaderPath\s*\(|(?:const|let|var)\s+getLoaderPath\s*=)/
|
|
67
|
-
|
|
68
59
|
# Main entry point for Pro setup.
|
|
69
60
|
# Orchestrates creation of all Pro-related files and configuration.
|
|
70
61
|
#
|
|
@@ -157,8 +148,8 @@ module ReactOnRails
|
|
|
157
148
|
|
|
158
149
|
Then run: bundle install
|
|
159
150
|
|
|
160
|
-
No license needed for
|
|
161
|
-
|
|
151
|
+
No license key is needed to run Pro. Production is free for small organizations, charities, schools, and hospitals;
|
|
152
|
+
larger organizations subscribe: https://reactonrails.com/pricing/
|
|
162
153
|
See the upgrade guide: https://reactonrails.com/docs/pro/upgrading-to-pro/
|
|
163
154
|
MSG
|
|
164
155
|
true
|
|
@@ -439,228 +430,65 @@ module ReactOnRails
|
|
|
439
430
|
say "✅ Added Node Renderer to #{procfile}", :green
|
|
440
431
|
end
|
|
441
432
|
|
|
442
|
-
#
|
|
443
|
-
#
|
|
444
|
-
# created webpack configs without Pro settings enabled.
|
|
445
|
-
#
|
|
446
|
-
# Updates serverWebpackConfig.js:
|
|
447
|
-
# - Adds extractLoader helper function (required by rscWebpackConfig.js)
|
|
448
|
-
# - Adds Babel SSR caller setup (required for correct SSR compilation)
|
|
449
|
-
# - Enables libraryTarget: 'commonjs2' (required for Node Renderer)
|
|
450
|
-
# - Enables serverWebpackConfig.target = 'node' (required for Node.js modules)
|
|
451
|
-
# - Disables Node.js polyfills via node = false (required for real __dirname)
|
|
452
|
-
# - Changes module.exports to Pro style (required by rscWebpackConfig.js)
|
|
453
|
-
#
|
|
454
|
-
# Updates ServerClientOrBoth.js:
|
|
455
|
-
# - Changes import to destructured style (required for Pro object export)
|
|
433
|
+
# Only replace complete, unchanged generated pairs. Inspecting declarations or
|
|
434
|
+
# individual markers cannot establish that customized JavaScript is safe to rewrite.
|
|
456
435
|
def update_webpack_config_for_pro
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
436
|
+
paths = generated_webpack_pair_paths
|
|
437
|
+
contents = read_generated_webpack_pair(paths) if paths
|
|
438
|
+
replacement = GeneratedWebpackConfigPair.pro_upgrade(contents) if contents
|
|
439
|
+
unless replacement
|
|
440
|
+
warn_unknown_webpack_pair
|
|
461
441
|
return
|
|
462
442
|
end
|
|
463
443
|
|
|
464
|
-
|
|
465
|
-
server_config_ready = pro_server_config_ready?(content)
|
|
466
|
-
import_ready = server_client_import_ready?
|
|
467
|
-
|
|
468
|
-
# Skip only when both server config and import style are already updated.
|
|
469
|
-
if server_config_ready && import_ready
|
|
444
|
+
if replacement == contents
|
|
470
445
|
say "ℹ️ Webpack config already has Pro settings enabled, skipping", :yellow
|
|
471
446
|
return
|
|
472
447
|
end
|
|
473
448
|
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
unless server_config_ready
|
|
477
|
-
# Add extractLoader helper function after bundler require
|
|
478
|
-
add_extract_loader_to_server_config(webpack_config, content)
|
|
479
|
-
|
|
480
|
-
# Add Babel SSR caller setup (uses extractLoader, so must come after)
|
|
481
|
-
add_babel_ssr_caller_to_server_config(webpack_config, content)
|
|
482
|
-
|
|
483
|
-
# Uncomment libraryTarget: 'commonjs2'
|
|
484
|
-
library_target_pattern = %r{// If using the React on Rails Pro.*\n\s*// libraryTarget: 'commonjs2',}
|
|
485
|
-
library_target_replacement = "// Required for React on Rails Pro Node Renderer\n " \
|
|
486
|
-
"libraryTarget: 'commonjs2',"
|
|
487
|
-
gsub_file(webpack_config, library_target_pattern, library_target_replacement)
|
|
488
|
-
|
|
489
|
-
# Replace stale comments and uncomment target = 'node', add node = false
|
|
490
|
-
# The base template has 4 lines: 2 explanatory comments + "uncomment" hint + commented code
|
|
491
|
-
# Replace with clean Pro output matching the template's use_pro? branch
|
|
492
|
-
# rubocop:disable Layout/LineLength
|
|
493
|
-
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'}
|
|
494
|
-
# rubocop:enable Layout/LineLength
|
|
495
|
-
target_node_replacement = "\n\n " \
|
|
496
|
-
"// React on Rails Pro uses Node renderer, so target must be 'node'\n " \
|
|
497
|
-
"// This fixes issues with libraries like Emotion and loadable-components\n " \
|
|
498
|
-
"serverWebpackConfig.target = 'node';\n\n " \
|
|
499
|
-
"// Disable Node.js polyfills - not needed when targeting Node\n " \
|
|
500
|
-
"serverWebpackConfig.node = false;"
|
|
501
|
-
gsub_file(webpack_config, target_node_pattern, target_node_replacement)
|
|
502
|
-
|
|
503
|
-
# Change module.exports to Pro style (exports object with default and extractLoader)
|
|
504
|
-
update_server_config_exports(webpack_config)
|
|
449
|
+
paths.zip(replacement).each do |path, content|
|
|
450
|
+
create_file(path, content, force: true)
|
|
505
451
|
end
|
|
506
|
-
|
|
507
|
-
# Update ServerClientOrBoth.js import style
|
|
508
|
-
update_server_client_or_both_import
|
|
509
|
-
|
|
510
|
-
verify_pro_webpack_transforms(webpack_config)
|
|
511
452
|
say "✅ Updated webpack configs for Pro", :green
|
|
512
453
|
end
|
|
513
454
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
# Skip if extractLoader already exists
|
|
521
|
-
return if content.include?("function extractLoader")
|
|
522
|
-
|
|
523
|
-
if content.include?(GET_LOADER_PATH_JS)
|
|
524
|
-
# Config rendered by the current base template: append extractLoader directly after
|
|
525
|
-
# the shared getLoaderPath helper so the result matches the template's Pro output.
|
|
526
|
-
gsub_file(webpack_config, GET_LOADER_PATH_JS, "#{GET_LOADER_PATH_JS}\n#{EXTRACT_LOADER_JS}")
|
|
527
|
-
elsif content.match?(GET_LOADER_PATH_DECLARATION)
|
|
528
|
-
# The app already declares getLoaderPath but has customized it (reformatted, recommented,
|
|
529
|
-
# or rewritten as an arrow function). Reuse whatever is there and emit extractLoader only.
|
|
530
|
-
# Emitting our own copy would redeclare the identifier, which is a SyntaxError next to an
|
|
531
|
-
# existing const/let and silent shadowing next to another function declaration.
|
|
532
|
-
gsub_file(webpack_config, BUNDLER_REQUIRE_PATTERN, "\\1\n\n#{EXTRACT_LOADER_JS}".chomp)
|
|
533
|
-
else
|
|
534
|
-
# Base config generated before getLoaderPath existed: emit both helpers after the
|
|
535
|
-
# bundler require so extractLoader's dependency is present.
|
|
536
|
-
gsub_file(
|
|
537
|
-
webpack_config,
|
|
538
|
-
BUNDLER_REQUIRE_PATTERN,
|
|
539
|
-
"\\1\n\n#{GET_LOADER_PATH_JS}\n#{EXTRACT_LOADER_JS}".chomp
|
|
540
|
-
)
|
|
455
|
+
# Unlike resolve_server_client_or_both_path, discovery must not rename files
|
|
456
|
+
# or edit environment imports before the entire pair has been recognized.
|
|
457
|
+
def generated_webpack_pair_paths
|
|
458
|
+
server = destination_config_path("config/webpack/serverWebpackConfig.js")
|
|
459
|
+
companions = %w[ServerClientOrBoth.js generateWebpackConfigs.js].map do |name|
|
|
460
|
+
destination_config_path("config/webpack/#{name}")
|
|
541
461
|
end
|
|
542
|
-
|
|
462
|
+
existing = companions.select do |path|
|
|
463
|
+
absolute = File.join(destination_root, path)
|
|
464
|
+
File.exist?(absolute) || File.symlink?(absolute)
|
|
465
|
+
end
|
|
466
|
+
return unless existing.one?
|
|
543
467
|
|
|
544
|
-
|
|
545
|
-
return
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
"const babelLoader = extractLoader(rule, 'babel-loader');\n " \
|
|
550
|
-
"if (babelLoader && babelLoader.options) {\n " \
|
|
551
|
-
"babelLoader.options.caller = { ssr: true };\n " \
|
|
552
|
-
"}"
|
|
553
|
-
|
|
554
|
-
# Insert after cssLoader.options.modules; [\s\S]*? covers both single-line and spread syntax patterns.
|
|
555
|
-
gsub_file(
|
|
556
|
-
webpack_config,
|
|
557
|
-
/(cssLoader\.options\.modules = \{[\s\S]*?exportOnlyLocals: true[\s\S]*?\};\s*\n\s*\})/,
|
|
558
|
-
"\\1#{babel_ssr_code}"
|
|
559
|
-
)
|
|
560
|
-
new_content = File.read(File.join(destination_root, webpack_config))
|
|
561
|
-
return if new_content.include?("babelLoader.options.caller")
|
|
468
|
+
paths = [server, existing.first]
|
|
469
|
+
return unless paths.all? do |path|
|
|
470
|
+
absolute = File.join(destination_root, path)
|
|
471
|
+
File.file?(absolute) && !File.symlink?(absolute)
|
|
472
|
+
end
|
|
562
473
|
|
|
563
|
-
|
|
474
|
+
paths
|
|
564
475
|
end
|
|
565
476
|
|
|
566
|
-
def
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
webpack_config,
|
|
571
|
-
/^module\.exports = configureServer;\s*$/,
|
|
572
|
-
"module.exports = {\n default: configureServer,\n extractLoader,\n};\n"
|
|
573
|
-
)
|
|
477
|
+
def read_generated_webpack_pair(paths)
|
|
478
|
+
paths.map { |path| File.binread(File.join(destination_root, path)) }
|
|
479
|
+
rescue SystemCallError, IOError
|
|
480
|
+
nil
|
|
574
481
|
end
|
|
575
482
|
|
|
576
|
-
def
|
|
577
|
-
content = File.read(File.join(destination_root, webpack_config))
|
|
578
|
-
missing = missing_server_config_transforms(content)
|
|
579
|
-
missing.concat(missing_server_client_import_transform)
|
|
580
|
-
return if missing.empty?
|
|
581
|
-
|
|
483
|
+
def warn_unknown_webpack_pair
|
|
582
484
|
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
This can happen if your webpack config has been customized.
|
|
589
|
-
Please verify #{webpack_config} manually.
|
|
485
|
+
Skipped Pro webpack updates: the config pair is missing, ambiguous, customized,
|
|
486
|
+
or does not match a supported current generated pair.
|
|
487
|
+
Both webpack configs and their paths were left unchanged. Update them manually using the Pro setup guide:
|
|
488
|
+
https://reactonrails.com/docs/pro/upgrading-to-pro/
|
|
590
489
|
MSG
|
|
591
490
|
end
|
|
592
491
|
|
|
593
|
-
def missing_server_config_transforms(content)
|
|
594
|
-
checks = [
|
|
595
|
-
"libraryTarget: 'commonjs2',",
|
|
596
|
-
"function extractLoader",
|
|
597
|
-
"babelLoader.options.caller = { ssr: true }",
|
|
598
|
-
"serverWebpackConfig.target = 'node'",
|
|
599
|
-
"serverWebpackConfig.node = false",
|
|
600
|
-
"default: configureServer",
|
|
601
|
-
"extractLoader,"
|
|
602
|
-
]
|
|
603
|
-
|
|
604
|
-
checks.reject { |pattern| content.include?(pattern) }
|
|
605
|
-
end
|
|
606
|
-
|
|
607
|
-
def missing_server_client_import_transform
|
|
608
|
-
server_client_path = resolve_server_client_or_both_path
|
|
609
|
-
return [] unless server_client_path
|
|
610
|
-
|
|
611
|
-
content = File.read(File.join(destination_root, server_client_path))
|
|
612
|
-
return [] if content.include?("{ default: serverWebpackConfig }")
|
|
613
|
-
|
|
614
|
-
["{ default: serverWebpackConfig }"]
|
|
615
|
-
end
|
|
616
|
-
|
|
617
|
-
def update_server_client_or_both_import
|
|
618
|
-
server_client_path = resolve_server_client_or_both_path
|
|
619
|
-
return unless server_client_path
|
|
620
|
-
|
|
621
|
-
content = File.read(File.join(destination_root, server_client_path))
|
|
622
|
-
|
|
623
|
-
# Skip if already using destructured import
|
|
624
|
-
return if content.include?("{ default: serverWebpackConfig }")
|
|
625
|
-
|
|
626
|
-
# Change from: const serverWebpackConfig = require('./serverWebpackConfig');
|
|
627
|
-
# To: const { default: serverWebpackConfig } = require('./serverWebpackConfig');
|
|
628
|
-
gsub_file(
|
|
629
|
-
server_client_path,
|
|
630
|
-
%r{^const serverWebpackConfig = require\('\./serverWebpackConfig'\);$},
|
|
631
|
-
"const { default: serverWebpackConfig } = require('./serverWebpackConfig');"
|
|
632
|
-
)
|
|
633
|
-
|
|
634
|
-
new_content = File.read(File.join(destination_root, server_client_path))
|
|
635
|
-
return if new_content.include?("{ default: serverWebpackConfig }")
|
|
636
|
-
|
|
637
|
-
say_status(
|
|
638
|
-
:warning,
|
|
639
|
-
"ServerClientOrBoth import update failed in #{server_client_path}; manual edit required.",
|
|
640
|
-
:yellow
|
|
641
|
-
)
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
def pro_server_config_ready?(content)
|
|
645
|
-
# Check for the Pro-specific comment marker (written by the transform) to avoid
|
|
646
|
-
# false-negatives when commented-out lines also contain the pattern string.
|
|
647
|
-
content.include?("// Required for React on Rails Pro Node Renderer") &&
|
|
648
|
-
content.include?("function extractLoader") &&
|
|
649
|
-
content.include?("babelLoader.options.caller = { ssr: true }") &&
|
|
650
|
-
content.include?("serverWebpackConfig.target = 'node'") &&
|
|
651
|
-
content.include?("serverWebpackConfig.node = false") &&
|
|
652
|
-
content.include?("default: configureServer") &&
|
|
653
|
-
content.include?("extractLoader,")
|
|
654
|
-
end
|
|
655
|
-
|
|
656
|
-
def server_client_import_ready?
|
|
657
|
-
server_client_path = resolve_server_client_or_both_path
|
|
658
|
-
return true unless server_client_path
|
|
659
|
-
|
|
660
|
-
content = File.read(File.join(destination_root, server_client_path))
|
|
661
|
-
content.include?("{ default: serverWebpackConfig }")
|
|
662
|
-
end
|
|
663
|
-
|
|
664
492
|
def pro_gem_auto_install_command
|
|
665
493
|
"bundle add #{PRO_GEM_NAME} --version='#{pro_gem_version_requirement}' --strict"
|
|
666
494
|
end
|
|
@@ -43,8 +43,10 @@ module ReactOnRails
|
|
|
43
43
|
// (which re-runs the hook). Running bin/shakapacker directly after changing server
|
|
44
44
|
// components is covered by the best-effort staleness warning below.
|
|
45
45
|
//
|
|
46
|
-
// The resolution cascade below
|
|
47
|
-
//
|
|
46
|
+
// The resolution cascade below — including the react-on-rails-pro client-component
|
|
47
|
+
// registrations appended to every branch's result (issue #5079) — is mirrored, branch
|
|
48
|
+
// for branch, by the Pro dummy's hand-written rscManifestClientReferences.js and pinned
|
|
49
|
+
// on both sides by contract tests.
|
|
48
50
|
const rscClientReferences = (() => {
|
|
49
51
|
// Required inside the IIFE rather than at the top of the file because the module-scope
|
|
50
52
|
// bindings 'resolve' (from 'path') and 'config' (from 'shakapacker') are already
|
|
@@ -168,40 +170,73 @@ module ReactOnRails
|
|
|
168
170
|
);
|
|
169
171
|
};
|
|
170
172
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
const resolveDiscoveredClientReferences = () => {
|
|
174
|
+
if (configuredRefsJson) {
|
|
175
|
+
const resolvedRefsJson = resolve(configuredRefsJson);
|
|
176
|
+
if (!existsSync(resolvedRefsJson)) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`RSC_MANIFEST_CLIENT_REFERENCES_JSON is set but the file does not exist: ${resolvedRefsJson}`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
warnIfManifestStale(resolvedRefsJson);
|
|
182
|
+
return readManifestReferences(resolvedRefsJson);
|
|
177
183
|
}
|
|
178
|
-
warnIfManifestStale(resolvedRefsJson);
|
|
179
|
-
return readManifestReferences(resolvedRefsJson);
|
|
180
|
-
}
|
|
181
184
|
|
|
182
|
-
|
|
183
|
-
return [fallbackRscClientReferences];
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (existsSync(defaultRefsJson)) {
|
|
187
|
-
warnIfManifestStale(defaultRefsJson);
|
|
188
|
-
return readManifestReferences(defaultRefsJson);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (existsSync(serverComponentRegistrationEntry)) {
|
|
192
|
-
if (!rscConfigSupportsDiscovery()) {
|
|
193
|
-
console.warn(
|
|
194
|
-
`[react_on_rails] Missing ${defaultRefsJson}, but this app's RSC webpack config ` +
|
|
195
|
-
'or precompile hook does not support manifest discovery yet; falling back to broad client ' +
|
|
196
|
-
'reference scan. Re-run rails g react_on_rails:rsc to update generated configs.',
|
|
197
|
-
);
|
|
185
|
+
if (process.env.RSC_REFERENCE_DISCOVERY_BUILD === 'true' || process.env.RSC_BUNDLE_ONLY === 'true') {
|
|
198
186
|
return [fallbackRscClientReferences];
|
|
199
187
|
}
|
|
200
188
|
|
|
201
|
-
|
|
202
|
-
|
|
189
|
+
if (existsSync(defaultRefsJson)) {
|
|
190
|
+
warnIfManifestStale(defaultRefsJson);
|
|
191
|
+
return readManifestReferences(defaultRefsJson);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (existsSync(serverComponentRegistrationEntry)) {
|
|
195
|
+
if (!rscConfigSupportsDiscovery()) {
|
|
196
|
+
console.warn(
|
|
197
|
+
`[react_on_rails] Missing ${defaultRefsJson}, but this app's RSC webpack config ` +
|
|
198
|
+
'or precompile hook does not support manifest discovery yet; falling back to broad client ' +
|
|
199
|
+
'reference scan. Re-run rails g react_on_rails:rsc to update generated configs.',
|
|
200
|
+
);
|
|
201
|
+
return [fallbackRscClientReferences];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
throw new Error(`Missing ${defaultRefsJson}. Run bin/shakapacker-precompile-hook before bin/shakapacker.`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return [fallbackRscClientReferences];
|
|
208
|
+
};
|
|
203
209
|
|
|
204
|
-
|
|
210
|
+
// react-on-rails-pro ships its own 'use client' components (RSCRoute, RSCProvider, and
|
|
211
|
+
// the default RSC provider registration). Both the discovery build and the fallback
|
|
212
|
+
// directory scan only cover app source — node_modules is never scanned — so these can
|
|
213
|
+
// never be discovered and, without explicit registration, never reach
|
|
214
|
+
// react-client-manifest.json. Server components rendering them (e.g. a nested
|
|
215
|
+
// <RSCRoute> for section-level refetching) then fail the render-time manifest lookup
|
|
216
|
+
// even when the RSC bundle compiles (issue #5079). require.resolve returns the
|
|
217
|
+
// absolute realpath, which matches the module resource webpack records
|
|
218
|
+
// (resolve.symlinks defaults to true), so the manifest keys line up with the file URLs
|
|
219
|
+
// the RSC bundle's client references carry even when react-on-rails-pro is installed
|
|
220
|
+
// through a symlink (pnpm, workspaces). A function (invoked in the return below, after
|
|
221
|
+
// the discovered references resolve) so both this resolver and its Pro dummy mirror
|
|
222
|
+
// surface the same first error when both steps fail.
|
|
223
|
+
const reactOnRailsProClientReferences = () =>
|
|
224
|
+
[
|
|
225
|
+
'react-on-rails-pro/RSCRoute',
|
|
226
|
+
'react-on-rails-pro/RSCProvider',
|
|
227
|
+
'react-on-rails-pro/registerDefaultRSCProvider/client',
|
|
228
|
+
].map((subpath) => {
|
|
229
|
+
try {
|
|
230
|
+
return require.resolve(subpath);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`Failed to resolve the react-on-rails-pro client component "${subpath}" for RSC ` +
|
|
234
|
+
`client-reference registration: ${err.message}`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
return [...new Set([...resolveDiscoveredClientReferences(), ...reactOnRailsProClientReferences()])];
|
|
205
240
|
})();
|
|
206
241
|
JS
|
|
207
242
|
end
|
|
@@ -277,6 +312,10 @@ module ReactOnRails
|
|
|
277
312
|
|
|
278
313
|
content = read_current_rsc_config(config_path, fallback: content)
|
|
279
314
|
end
|
|
315
|
+
# A previously generated IIFE (no react-on-rails-pro registrations — issue #5079) also
|
|
316
|
+
# counts as generated below; warn so the user registers the components manually.
|
|
317
|
+
warn_outdated_rsc_client_references_helper(config_path) if
|
|
318
|
+
outdated_generated_rsc_client_references_defined?(content)
|
|
280
319
|
return :scoped if generated_rsc_client_references_defined?(content)
|
|
281
320
|
|
|
282
321
|
warn_unscoped_rsc_client_references_helper(config_path)
|
|
@@ -312,6 +351,12 @@ module ReactOnRails
|
|
|
312
351
|
content = normalized_content
|
|
313
352
|
end
|
|
314
353
|
|
|
354
|
+
# A resolver emitted by a previous generator version (no react-on-rails-pro
|
|
355
|
+
# client-component registrations — issue #5079) is deliberately not rewritten; warn
|
|
356
|
+
# before the scoped-references short-circuit below treats it as already current.
|
|
357
|
+
warn_outdated_rsc_client_references_helper(config_path) if
|
|
358
|
+
outdated_generated_rsc_client_references_defined?(content)
|
|
359
|
+
|
|
315
360
|
return unless rsc_plugin_sections_safe_to_rewrite?(config_path, content, is_server:)
|
|
316
361
|
|
|
317
362
|
return if rsc_plugin_uses_scoped_client_references?(content, is_server:)
|
|
@@ -1405,6 +1450,31 @@ module ReactOnRails
|
|
|
1405
1450
|
write_existing_rsc_config(config_path, updated_content, action: :rewrite)
|
|
1406
1451
|
end
|
|
1407
1452
|
|
|
1453
|
+
# True when the file carries a manifest-backed generated resolver IIFE that does not
|
|
1454
|
+
# register the react-on-rails-pro client components (issue #5079) — either the resolver a
|
|
1455
|
+
# previous generator version emitted, or a user customization of one. The generator never
|
|
1456
|
+
# rewrites such a resolver; it warns with manual registration instructions instead.
|
|
1457
|
+
def outdated_generated_rsc_client_references_defined?(content)
|
|
1458
|
+
return false unless generated_rsc_client_references_defined?(content)
|
|
1459
|
+
|
|
1460
|
+
iife_body = generated_rsc_client_references_iife_body(content)
|
|
1461
|
+
return false unless iife_body
|
|
1462
|
+
|
|
1463
|
+
!js_code_matches?(iife_body, /\breactOnRailsProClientReferences\b/)
|
|
1464
|
+
end
|
|
1465
|
+
|
|
1466
|
+
def warn_outdated_rsc_client_references_helper(config_path)
|
|
1467
|
+
GeneratorMessages.add_warning(
|
|
1468
|
+
"#{config_path} contains a rscClientReferences resolver from a previous generator " \
|
|
1469
|
+
"version: it does not register the react-on-rails-pro package's own client components " \
|
|
1470
|
+
"(RSCRoute, RSCProvider, registerDefaultRSCProvider/client — issue #5079). The file was " \
|
|
1471
|
+
"left unchanged. Add require.resolve entries for those components to your " \
|
|
1472
|
+
"clientReferences (see the RSC setup docs at " \
|
|
1473
|
+
"https://reactonrails.com/docs/pro/react-server-components/create-without-ssr), or " \
|
|
1474
|
+
"remove the generated resolver block and re-run this generator to emit the current one."
|
|
1475
|
+
)
|
|
1476
|
+
end
|
|
1477
|
+
|
|
1408
1478
|
def read_current_rsc_config(config_path, fallback:)
|
|
1409
1479
|
return fallback if options[:pretend] || options[:skip]
|
|
1410
1480
|
|
|
@@ -82,17 +82,9 @@ module ReactOnRails
|
|
|
82
82
|
react_version = detect_react_version
|
|
83
83
|
return if react_version.nil? # React not installed yet, will be installed by generator
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
major, minor, patch = version.segments
|
|
85
|
+
major, minor, patch = react_version.split(".").map(&:to_i)
|
|
87
86
|
|
|
88
|
-
if
|
|
89
|
-
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
90
|
-
⚠️ RSC requires stable React (detected: #{react_version}).
|
|
91
|
-
|
|
92
|
-
React prereleases are not supported. Install matching stable React/React DOM versions:
|
|
93
|
-
#{manual_add_packages_command(["react@#{RSC_REACT_VERSION_RANGE}", "react-dom@#{RSC_REACT_VERSION_RANGE}"])}
|
|
94
|
-
MSG
|
|
95
|
-
elsif major != RSC_SUPPORTED_REACT_MAJOR || minor != RSC_SUPPORTED_REACT_MINOR
|
|
87
|
+
if major != RSC_SUPPORTED_REACT_MAJOR || minor != RSC_SUPPORTED_REACT_MINOR
|
|
96
88
|
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
97
89
|
⚠️ RSC requires React #{RSC_SUPPORTED_REACT_LINE} (detected: #{react_version})
|
|
98
90
|
|
|
@@ -105,14 +97,13 @@ module ReactOnRails
|
|
|
105
97
|
MSG
|
|
106
98
|
elsif patch < RSC_MINIMUM_REACT_PATCH
|
|
107
99
|
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
108
|
-
⚠️ React #{react_version} is below the
|
|
100
|
+
⚠️ React #{react_version} is below the recommended minimum for RSC.
|
|
109
101
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
The Node Renderer refuses startup until React and React DOM are upgraded together:
|
|
113
|
-
#{manual_add_packages_command(["react@#{RSC_REACT_VERSION_RANGE}", "react-dom@#{RSC_REACT_VERSION_RANGE}"])}
|
|
102
|
+
Please upgrade to at least React #{RSC_MINIMUM_REACT_VERSION}:
|
|
103
|
+
#{manual_add_packages_command(["react@#{RSC_MINIMUM_REACT_VERSION}", "react-dom@#{RSC_MINIMUM_REACT_VERSION}"])}
|
|
114
104
|
|
|
115
|
-
|
|
105
|
+
react-on-rails-rsc 19.2.x with patch >= 19.2.1 is coordinated with
|
|
106
|
+
React/React DOM #{RSC_MINIMUM_REACT_VERSION}+ for the React on Rails Pro 17 RSC runtime.
|
|
116
107
|
MSG
|
|
117
108
|
end
|
|
118
109
|
end
|
data/lib/generators/react_on_rails/templates/pro/base/config/initializers/react_on_rails_pro.rb.tt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# See https://reactonrails.com/docs/configuration/configuration-pro
|
|
4
|
-
#
|
|
4
|
+
# Optional license key from your subscription: set REACT_ON_RAILS_PRO_LICENSE to mark your pages Licensed.
|
|
5
5
|
ReactOnRailsPro.configure do |config|
|
|
6
6
|
config.server_renderer = "NodeRenderer"
|
|
7
7
|
config.renderer_url = ENV.fetch("REACT_RENDERER_URL", "http://localhost:3800")
|