react_on_rails 17.0.0.rc.0 → 17.0.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 +2 -34
- data/Rakefile +1 -1
- data/lib/generators/react_on_rails/base_generator.rb +47 -12
- data/lib/generators/react_on_rails/demo_page_config.rb +6 -6
- data/lib/generators/react_on_rails/generator_helper.rb +121 -28
- data/lib/generators/react_on_rails/generator_messages/ci_section.rb +10 -2
- data/lib/generators/react_on_rails/generator_messages/package_manager_detection.rb +9 -9
- data/lib/generators/react_on_rails/generator_messages/shakapacker_status_section.rb +1 -1
- data/lib/generators/react_on_rails/generator_messages.rb +7 -7
- data/lib/generators/react_on_rails/install_generator.rb +55 -28
- data/lib/generators/react_on_rails/js_dependency_manager.rb +161 -64
- data/lib/generators/react_on_rails/pro_generator.rb +13 -13
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +1 -1
- data/lib/generators/react_on_rails/rsc_setup/client_references.rb +590 -101
- data/lib/generators/react_on_rails/rsc_setup.rb +64 -26
- data/lib/generators/react_on_rails/shakapacker_precompile_hook_helper.rb +476 -0
- data/lib/generators/react_on_rails/templates/base/base/.github/workflows/ci.yml.tt +7 -1
- data/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook +88 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +5 -5
- data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +1 -1
- data/lib/generators/react_on_rails/templates/rsc/base/config/webpack/rscWebpackConfig.js.tt +133 -13
- data/lib/react_on_rails/config_path_resolver.rb +0 -2
- data/lib/react_on_rails/controller.rb +2 -2
- data/lib/react_on_rails/dev/pack_generator.rb +5 -5
- data/lib/react_on_rails/dev/port_selector.rb +4 -4
- data/lib/react_on_rails/dev/process_manager.rb +3 -3
- data/lib/react_on_rails/dev/server_manager.rb +208 -92
- data/lib/react_on_rails/dev/server_mode.rb +211 -0
- data/lib/react_on_rails/dev/service_checker.rb +1 -1
- data/lib/react_on_rails/dev.rb +1 -0
- data/lib/react_on_rails/doctor.rb +81 -35
- data/lib/react_on_rails/engine.rb +9 -2
- data/lib/react_on_rails/error.rb +3 -0
- data/lib/react_on_rails/git_utils.rb +3 -3
- data/lib/react_on_rails/helper.rb +33 -32
- data/lib/react_on_rails/length_prefixed_parser.rb +5 -4
- data/lib/react_on_rails/locales/base.rb +2 -2
- data/lib/react_on_rails/packer_utils.rb +3 -3
- data/lib/react_on_rails/packs_generator.rb +331 -62
- data/lib/react_on_rails/prerender_error.rb +5 -5
- data/lib/react_on_rails/pro_helper.rb +4 -2
- data/lib/react_on_rails/pro_migration.rb +2 -2
- data/lib/react_on_rails/react_component/render_options.rb +1 -1
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +200 -14
- data/lib/react_on_rails/shakapacker_config_helpers.rb +139 -0
- data/lib/react_on_rails/system_checker.rb +33 -50
- data/lib/react_on_rails/test_helper/dev_assets_detector.rb +23 -21
- data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +2 -2
- data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +4 -4
- data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +2 -2
- data/lib/react_on_rails/test_helper.rb +11 -11
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_synchronizer.rb +22 -22
- data/lib/tasks/doctor.rake +1 -1
- data/lib/tasks/locale.rake +1 -1
- data/lib/tasks/sync_versions.rake +1 -1
- data/rakelib/lint.rake +15 -2
- data/rakelib/run_rspec.rake +1 -1
- data/rakelib/shakapacker_version.rake +4 -1
- data/rakelib/task_helpers.rb +1 -0
- data/rakelib/update_changelog.rake +2 -2
- data/react_on_rails.gemspec +2 -2
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5610a42464b29a2f8066e2a91d342838cdd1f3734c3965864e0a5bd0d18e4c8a
|
|
4
|
+
data.tar.gz: 551fc429af6eea0798b929d0aece8b417b4646dfed702c1dcd39ae26d52084ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4b1df35fc94bc088e6a9afe96ca54ec651951149ae82415e55d1db023ef582089fb58f9536db698d0971633678b3d2d70188c0ca355217fd9f6a854ead1f177c
|
|
7
|
+
data.tar.gz: c1371c43729036f9f9dbb78d68b85be81919a98a16fb8d3d4185b94c0f48c92506404bedb6e1a7a804b52596ae784b4f60487cc4bad8333f0933e38aa005551a
|
data/.rubocop.yml
CHANGED
|
@@ -11,6 +11,8 @@ AllCops:
|
|
|
11
11
|
|
|
12
12
|
Exclude:
|
|
13
13
|
- 'spec/dummy/bin/*'
|
|
14
|
+
- 'spec/react_on_rails/dummy-for-generators/**/*' # Generated fixture contains intentionally invalid Ruby
|
|
15
|
+
- 'spike/**/*' # Exploratory spike code outside lib/ — not part of the production surface
|
|
14
16
|
|
|
15
17
|
Naming/FileName:
|
|
16
18
|
Exclude:
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
react_on_rails (17.0.0.rc.
|
|
4
|
+
react_on_rails (17.0.0.rc.2)
|
|
5
5
|
addressable
|
|
6
6
|
connection_pool
|
|
7
7
|
execjs (~> 2.5)
|
|
@@ -207,7 +207,6 @@ GEM
|
|
|
207
207
|
racc (~> 1.4)
|
|
208
208
|
ostruct (0.6.1)
|
|
209
209
|
package_json (0.2.0)
|
|
210
|
-
parallel (1.24.0)
|
|
211
210
|
parser (3.3.1.0)
|
|
212
211
|
ast (~> 2.4.1)
|
|
213
212
|
racc
|
|
@@ -313,34 +312,6 @@ GEM
|
|
|
313
312
|
rspec-support (3.13.1)
|
|
314
313
|
rspec_junit_formatter (0.6.0)
|
|
315
314
|
rspec-core (>= 2, < 4, != 2.12.0)
|
|
316
|
-
rubocop (1.61.0)
|
|
317
|
-
json (~> 2.3)
|
|
318
|
-
language_server-protocol (>= 3.17.0)
|
|
319
|
-
parallel (~> 1.10)
|
|
320
|
-
parser (>= 3.3.0.2)
|
|
321
|
-
rainbow (>= 2.2.2, < 4.0)
|
|
322
|
-
regexp_parser (>= 1.8, < 3.0)
|
|
323
|
-
rexml (>= 3.2.5, < 4.0)
|
|
324
|
-
rubocop-ast (>= 1.30.0, < 2.0)
|
|
325
|
-
ruby-progressbar (~> 1.7)
|
|
326
|
-
unicode-display_width (>= 2.4.0, < 3.0)
|
|
327
|
-
rubocop-ast (1.31.3)
|
|
328
|
-
parser (>= 3.3.1.0)
|
|
329
|
-
rubocop-capybara (2.20.0)
|
|
330
|
-
rubocop (~> 1.41)
|
|
331
|
-
rubocop-factory_bot (2.25.1)
|
|
332
|
-
rubocop (~> 1.41)
|
|
333
|
-
rubocop-performance (1.20.2)
|
|
334
|
-
rubocop (>= 1.48.1, < 2.0)
|
|
335
|
-
rubocop-ast (>= 1.30.0, < 2.0)
|
|
336
|
-
rubocop-rspec (2.29.2)
|
|
337
|
-
rubocop (~> 1.40)
|
|
338
|
-
rubocop-capybara (~> 2.17)
|
|
339
|
-
rubocop-factory_bot (~> 2.22)
|
|
340
|
-
rubocop-rspec_rails (~> 2.28)
|
|
341
|
-
rubocop-rspec_rails (2.28.3)
|
|
342
|
-
rubocop (~> 1.40)
|
|
343
|
-
ruby-progressbar (1.13.0)
|
|
344
315
|
rubyzip (2.3.2)
|
|
345
316
|
sass-rails (6.0.0)
|
|
346
317
|
sassc-rails (~> 2.1, >= 2.1.1)
|
|
@@ -469,9 +440,6 @@ DEPENDENCIES
|
|
|
469
440
|
rspec-rails
|
|
470
441
|
rspec-retry
|
|
471
442
|
rspec_junit_formatter
|
|
472
|
-
rubocop (= 1.61.0)
|
|
473
|
-
rubocop-performance (~> 1.20.0)
|
|
474
|
-
rubocop-rspec (~> 2.26)
|
|
475
443
|
sass-rails (~> 6.0)
|
|
476
444
|
sdoc
|
|
477
445
|
selenium-webdriver (= 4.9.0)
|
|
@@ -487,4 +455,4 @@ DEPENDENCIES
|
|
|
487
455
|
webdrivers (= 5.3.0)
|
|
488
456
|
|
|
489
457
|
BUNDLED WITH
|
|
490
|
-
|
|
458
|
+
4.0.10
|
data/Rakefile
CHANGED
|
@@ -13,7 +13,7 @@ desc "All actions but no examples, good for local developer run."
|
|
|
13
13
|
task all_but_examples: ["run_rspec:all_but_examples", "lint"]
|
|
14
14
|
|
|
15
15
|
desc "Prepare for ci, including node_package, dummy app, and generator examples"
|
|
16
|
-
task
|
|
16
|
+
task(prepare_for_ci:)
|
|
17
17
|
|
|
18
18
|
desc "Runs prepare_for_ci and tasks"
|
|
19
19
|
task ci: [:prepare_for_ci, *tasks]
|
|
@@ -6,11 +6,13 @@ require "erb"
|
|
|
6
6
|
require_relative "generator_messages"
|
|
7
7
|
require_relative "generator_helper"
|
|
8
8
|
require_relative "js_dependency_manager"
|
|
9
|
+
require_relative "shakapacker_precompile_hook_helper"
|
|
9
10
|
module ReactOnRails
|
|
10
11
|
module Generators
|
|
11
12
|
class BaseGenerator < Rails::Generators::Base
|
|
12
13
|
include GeneratorHelper
|
|
13
14
|
include JsDependencyManager
|
|
15
|
+
include ShakapackerPrecompileHookHelper
|
|
14
16
|
|
|
15
17
|
Rails::Generators.hide_namespace(namespace)
|
|
16
18
|
source_root(File.expand_path("templates", __dir__))
|
|
@@ -22,11 +24,22 @@ module ReactOnRails
|
|
|
22
24
|
desc: "Install Redux package and Redux version of Hello World Example",
|
|
23
25
|
aliases: "-R"
|
|
24
26
|
|
|
25
|
-
# --rspack
|
|
27
|
+
# --rspack / --no-rspack (Rspack is the default on fresh installs; --no-rspack selects Webpack)
|
|
28
|
+
# IMPORTANT: do NOT add a `default:` here. The absence of a default is load-bearing — Thor
|
|
29
|
+
# only includes :rspack in the options hash when the flag is explicitly passed, which is how
|
|
30
|
+
# GeneratorHelper#using_rspack? tells an explicit choice from "no flag given" (the latter
|
|
31
|
+
# falls back to rspack_bundler_default). Adding `default: false` would make
|
|
32
|
+
# options.key?(:rspack) always true and silently break the fresh-install Rspack default.
|
|
33
|
+
# (Thor's omit-when-no-default behavior verified against Thor 1.5.0; see Gemfile.lock.)
|
|
26
34
|
class_option :rspack,
|
|
27
35
|
type: :boolean,
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
desc: "Use Rspack (default) as the bundler; pass --no-rspack to use Webpack"
|
|
37
|
+
|
|
38
|
+
# --webpack: friendly alias for --no-rspack (reconciled in GeneratorHelper#explicit_bundler_choice).
|
|
39
|
+
# No `default:` here either — same load-bearing reason as --rspack above.
|
|
40
|
+
class_option :webpack,
|
|
41
|
+
type: :boolean,
|
|
42
|
+
desc: "Use Webpack as the bundler (alias for --no-rspack; --no-webpack is equivalent to --rspack)"
|
|
30
43
|
|
|
31
44
|
# --pro
|
|
32
45
|
class_option :pro,
|
|
@@ -96,6 +109,14 @@ module ReactOnRails
|
|
|
96
109
|
def shakapacker_version_9_or_higher?
|
|
97
110
|
generator.__send__(:shakapacker_version_9_or_higher?)
|
|
98
111
|
end
|
|
112
|
+
|
|
113
|
+
def rsc_plugin_class_name
|
|
114
|
+
generator.__send__(:rsc_plugin_class_name)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def rsc_plugin_import_path
|
|
118
|
+
generator.__send__(:rsc_plugin_import_path)
|
|
119
|
+
end
|
|
99
120
|
end
|
|
100
121
|
|
|
101
122
|
REMOVABLE_WEBPACK_FILES = (MANAGED_WEBPACK_FILE_TEMPLATES.keys +
|
|
@@ -313,6 +334,18 @@ module ReactOnRails
|
|
|
313
334
|
|
|
314
335
|
private
|
|
315
336
|
|
|
337
|
+
# Fresh-install context: default to Rspack (when Shakapacker supports it) unless the
|
|
338
|
+
# app already declares a bundler. See GeneratorHelper#fresh_install_rspack_default.
|
|
339
|
+
# NOTE: InstallGenerator#rspack_bundler_default is an intentional twin of this override
|
|
340
|
+
# (both generators are independently CLI-invocable); keep the two in sync.
|
|
341
|
+
def rspack_bundler_default
|
|
342
|
+
fresh_install_rspack_default
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def generated_build_test_command
|
|
346
|
+
shakapacker_build_command(env: "RAILS_ENV=test NODE_ENV=test", environment: "test")
|
|
347
|
+
end
|
|
348
|
+
|
|
316
349
|
def generate_new_app_home_page?
|
|
317
350
|
options.new_app? && new_app_root_route_added?
|
|
318
351
|
end
|
|
@@ -347,8 +380,8 @@ module ReactOnRails
|
|
|
347
380
|
end
|
|
348
381
|
|
|
349
382
|
{
|
|
350
|
-
app_name
|
|
351
|
-
docs_url
|
|
383
|
+
app_name:,
|
|
384
|
+
docs_url:,
|
|
352
385
|
examples: home_page_examples,
|
|
353
386
|
file_hints: home_page_file_hints,
|
|
354
387
|
stack_badges: home_page_stack_badges,
|
|
@@ -938,8 +971,9 @@ module ReactOnRails
|
|
|
938
971
|
# current run omits those options; in that case, we preserve the directory.
|
|
939
972
|
# Templates rely on config[:message] plus a small helper subset exposed by
|
|
940
973
|
# TemplateRenderContext (add_documentation_reference, use_pro?, use_rsc?,
|
|
941
|
-
# shakapacker_version_9_or_higher
|
|
942
|
-
# NoMethodError and are caught below, treating the
|
|
974
|
+
# shakapacker_version_9_or_higher?, rsc_plugin_class_name, rsc_plugin_import_path).
|
|
975
|
+
# Missing method delegates raise NoMethodError and are caught below, treating the
|
|
976
|
+
# file as non-removable.
|
|
943
977
|
# Missing config hash keys return nil silently, so any new config key
|
|
944
978
|
# required by templates must be added to template_doc_config above.
|
|
945
979
|
# Use TemplateRenderContext#erb_binding to avoid leaking method-local
|
|
@@ -984,13 +1018,13 @@ module ReactOnRails
|
|
|
984
1018
|
expected_configs = shakapacker_default_configs
|
|
985
1019
|
|
|
986
1020
|
# Check if the content matches any of the known default configurations
|
|
987
|
-
expected_configs.any? { |config| content_matches_template?(content, config, strip_comments:
|
|
1021
|
+
expected_configs.any? { |config| content_matches_template?(content, config, strip_comments:) }
|
|
988
1022
|
end
|
|
989
1023
|
|
|
990
1024
|
def content_matches_template?(content, template, strip_comments: false)
|
|
991
1025
|
# Normalize whitespace and compare
|
|
992
|
-
normalize_config_content(content, strip_comments:
|
|
993
|
-
normalize_config_content(template, strip_comments:
|
|
1026
|
+
normalize_config_content(content, strip_comments:) ==
|
|
1027
|
+
normalize_config_content(template, strip_comments:)
|
|
994
1028
|
end
|
|
995
1029
|
|
|
996
1030
|
def normalize_config_content(content, strip_comments: false)
|
|
@@ -1196,8 +1230,9 @@ module ReactOnRails
|
|
|
1196
1230
|
|
|
1197
1231
|
content = File.read(shakapacker_config_path)
|
|
1198
1232
|
|
|
1199
|
-
#
|
|
1200
|
-
|
|
1233
|
+
# Don't materialize placeholders when any placeholder section already has
|
|
1234
|
+
# a direct or inherited active precompile_hook.
|
|
1235
|
+
return if active_precompile_hook_configured?(content)
|
|
1201
1236
|
|
|
1202
1237
|
# Replace the commented placeholder with the actual value
|
|
1203
1238
|
# Shakapacker 9.x default config has: # precompile_hook: ~
|
|
@@ -5,12 +5,12 @@ module ReactOnRails
|
|
|
5
5
|
module DemoPageConfig # rubocop:disable Metrics/ModuleLength
|
|
6
6
|
def build_hello_world_view_config(component_name:, source_path:, landing_page:, redux:, rsc_demo:)
|
|
7
7
|
{
|
|
8
|
-
component_name
|
|
8
|
+
component_name:,
|
|
9
9
|
title: redux ? "Redux SSR Demo" : "React SSR Demo",
|
|
10
|
-
intro: hello_world_intro(redux:
|
|
11
|
-
highlights: hello_world_highlights(redux:
|
|
12
|
-
file_hints: hello_world_file_hints(source_path
|
|
13
|
-
quick_links: hello_world_quick_links(landing_page
|
|
10
|
+
intro: hello_world_intro(redux:),
|
|
11
|
+
highlights: hello_world_highlights(redux:),
|
|
12
|
+
file_hints: hello_world_file_hints(source_path:, redux:),
|
|
13
|
+
quick_links: hello_world_quick_links(landing_page:, rsc_demo:),
|
|
14
14
|
learning_links: hello_world_learning_links
|
|
15
15
|
}
|
|
16
16
|
end
|
|
@@ -22,7 +22,7 @@ module ReactOnRails
|
|
|
22
22
|
"component response while only client islands ship JavaScript to the browser.",
|
|
23
23
|
highlights: hello_server_highlights,
|
|
24
24
|
file_hints: hello_server_file_hints,
|
|
25
|
-
quick_links: hello_server_quick_links(landing_page
|
|
25
|
+
quick_links: hello_server_quick_links(landing_page:, redux_demo:),
|
|
26
26
|
learning_links: hello_server_learning_links
|
|
27
27
|
}
|
|
28
28
|
end
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require_relative "shakapacker_precompile_hook_helper"
|
|
4
5
|
|
|
6
|
+
# rubocop:disable Metrics/ModuleLength
|
|
5
7
|
module GeneratorHelper
|
|
8
|
+
include ReactOnRails::Generators::ShakapackerPrecompileHookHelper
|
|
9
|
+
|
|
6
10
|
def package_json
|
|
7
11
|
# Lazy load package_json gem only when actually needed for dependency management
|
|
8
12
|
|
|
@@ -37,7 +41,7 @@ module GeneratorHelper
|
|
|
37
41
|
result != false
|
|
38
42
|
rescue StandardError => e
|
|
39
43
|
say_status :warning, "Could not add packages via package_json gem: #{e.message}", :yellow
|
|
40
|
-
say_status :warning, "Will fall back to direct
|
|
44
|
+
say_status :warning, "Will fall back to direct package manager commands.", :yellow
|
|
41
45
|
false
|
|
42
46
|
end
|
|
43
47
|
end
|
|
@@ -128,11 +132,57 @@ module GeneratorHelper
|
|
|
128
132
|
def using_rspack?
|
|
129
133
|
return @using_rspack if defined?(@using_rspack)
|
|
130
134
|
|
|
131
|
-
#
|
|
132
|
-
#
|
|
133
|
-
#
|
|
134
|
-
|
|
135
|
-
@using_rspack =
|
|
135
|
+
# An explicit bundler flag always wins. When none was passed (or the generator doesn't
|
|
136
|
+
# declare the flags, e.g. RscGenerator/ProGenerator), fall back to the bundler default,
|
|
137
|
+
# which each generator defines for its own context.
|
|
138
|
+
explicit = explicit_bundler_choice
|
|
139
|
+
@using_rspack = explicit.nil? ? rspack_bundler_default : explicit
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# Resolve the explicit bundler flags into a single choice.
|
|
143
|
+
#
|
|
144
|
+
# --rspack selects Rspack; --no-rspack and --webpack select Webpack (--webpack is a friendly
|
|
145
|
+
# alias for --no-rspack, and the auto-generated --no-webpack mirrors --rspack). Returns true
|
|
146
|
+
# for Rspack, false for Webpack, or nil when no bundler flag was passed (so the caller falls
|
|
147
|
+
# back to rspack_bundler_default).
|
|
148
|
+
#
|
|
149
|
+
# IMPORTANT: this relies on Thor NOT including a nil-defaulted option in the hash when the flag
|
|
150
|
+
# is absent — options.key?(:rspack)/(:webpack) is true only when the user passed that flag.
|
|
151
|
+
# Re-adding `default:` to either class_option would make the key always present and break both
|
|
152
|
+
# the "no flag given" fallback and the conflict detection here.
|
|
153
|
+
# (Thor's omit-when-no-default behavior verified against Thor 1.5.0; see Gemfile.lock.)
|
|
154
|
+
#
|
|
155
|
+
# Passing contradictory flags (e.g. --rspack --webpack) raises a Thor::Error.
|
|
156
|
+
def explicit_bundler_choice
|
|
157
|
+
choices = []
|
|
158
|
+
choices << options[:rspack] if options.key?(:rspack)
|
|
159
|
+
# --webpack means "use Webpack" (rspack = false); --no-webpack means "use Rspack".
|
|
160
|
+
# Name the inverted webpack flag so the rspack-boolean intent reads directly.
|
|
161
|
+
rspack_via_webpack_flag = !options[:webpack]
|
|
162
|
+
choices << rspack_via_webpack_flag if options.key?(:webpack)
|
|
163
|
+
return nil if choices.empty?
|
|
164
|
+
|
|
165
|
+
if choices.uniq.length > 1
|
|
166
|
+
raise Thor::Error,
|
|
167
|
+
"Conflicting bundler flags: pass either Rspack (--rspack) or Webpack " \
|
|
168
|
+
"(--webpack / --no-rspack), not both."
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
choices.first
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# True when the user passed any explicit bundler flag
|
|
175
|
+
# (--rspack/--no-rspack/--webpack/--no-webpack).
|
|
176
|
+
def bundler_flag_given?
|
|
177
|
+
options.key?(:rspack) || options.key?(:webpack)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Bundler to use when no explicit bundler flag was passed.
|
|
181
|
+
# Default (standalone generators like RscGenerator/ProGenerator): respect the existing
|
|
182
|
+
# project's shakapacker.yml and never impose a bundler. InstallGenerator/BaseGenerator
|
|
183
|
+
# override this to default fresh installs to Rspack.
|
|
184
|
+
def rspack_bundler_default
|
|
185
|
+
rspack_configured_in_project?
|
|
136
186
|
end
|
|
137
187
|
|
|
138
188
|
# Remap a config path from config/webpack/ to config/rspack/ when using rspack.
|
|
@@ -147,6 +197,26 @@ module GeneratorHelper
|
|
|
147
197
|
path.sub(%r{\Aconfig/webpack/}, "config/rspack/")
|
|
148
198
|
end
|
|
149
199
|
|
|
200
|
+
# RSC client-manifest plugin class name for the active bundler.
|
|
201
|
+
# Rspack uses the native `RSCRspackPlugin`; webpack uses `RSCWebpackPlugin`.
|
|
202
|
+
# Both expose the same `{ isServer, clientReferences }` API and emit the same
|
|
203
|
+
# manifest schema, so only the import path and class name differ.
|
|
204
|
+
# Shared by the base webpack-config templates and the standalone RSC migration
|
|
205
|
+
# so both paths scaffold the bundler-correct plugin from one source of truth.
|
|
206
|
+
#
|
|
207
|
+
# @return [String] "RSCRspackPlugin" when rspack, "RSCWebpackPlugin" otherwise
|
|
208
|
+
def rsc_plugin_class_name
|
|
209
|
+
using_rspack? ? "RSCRspackPlugin" : "RSCWebpackPlugin"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# `react-on-rails-rsc` subpath that exports {#rsc_plugin_class_name}.
|
|
213
|
+
#
|
|
214
|
+
# @return [String] "react-on-rails-rsc/RspackPlugin" when rspack,
|
|
215
|
+
# "react-on-rails-rsc/WebpackPlugin" otherwise
|
|
216
|
+
def rsc_plugin_import_path
|
|
217
|
+
using_rspack? ? "react-on-rails-rsc/RspackPlugin" : "react-on-rails-rsc/WebpackPlugin"
|
|
218
|
+
end
|
|
219
|
+
|
|
150
220
|
# Detect the installed React version from package.json
|
|
151
221
|
# Uses VERSION_PARTS_REGEX pattern from VersionChecker for consistency
|
|
152
222
|
#
|
|
@@ -269,6 +339,24 @@ module GeneratorHelper
|
|
|
269
339
|
@pro_gem_install_deferred = true
|
|
270
340
|
end
|
|
271
341
|
|
|
342
|
+
# The other bundler's plugin class name — the one this project should NOT be using.
|
|
343
|
+
# Used to detect a config left in a mixed state (e.g. a legacy `RSCWebpackPlugin` surviving
|
|
344
|
+
# in an rspack project) so diagnostics can say "wrong bundler plugin" rather than "missing".
|
|
345
|
+
#
|
|
346
|
+
# @return [String] "RSCWebpackPlugin" when rspack, "RSCRspackPlugin" otherwise
|
|
347
|
+
def inactive_rsc_plugin_class_name
|
|
348
|
+
using_rspack? ? "RSCWebpackPlugin" : "RSCRspackPlugin"
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
# Import path for the inactive bundler's plugin — the counterpart to {#rsc_plugin_import_path},
|
|
352
|
+
# used when migrating a legacy config to the active bundler's plugin.
|
|
353
|
+
#
|
|
354
|
+
# @return [String] "react-on-rails-rsc/WebpackPlugin" when rspack,
|
|
355
|
+
# "react-on-rails-rsc/RspackPlugin" otherwise
|
|
356
|
+
def inactive_rsc_plugin_import_path
|
|
357
|
+
using_rspack? ? "react-on-rails-rsc/WebpackPlugin" : "react-on-rails-rsc/RspackPlugin"
|
|
358
|
+
end
|
|
359
|
+
|
|
272
360
|
# NOTE: only the `default:` section is inspected — same assumption as
|
|
273
361
|
# rspack_configured_in_project?. Projects that set `javascript_transpiler`
|
|
274
362
|
# only in per-environment sections (without a `default:` block) will not be
|
|
@@ -291,25 +379,6 @@ module GeneratorHelper
|
|
|
291
379
|
shakapacker_version_9_3_or_higher?
|
|
292
380
|
end
|
|
293
381
|
|
|
294
|
-
def parse_shakapacker_yml(path)
|
|
295
|
-
require "yaml"
|
|
296
|
-
# Use safe_load_file for security (defense-in-depth, even though this is user's own config)
|
|
297
|
-
# permitted_classes: [Symbol] allows symbol keys which shakapacker.yml may use
|
|
298
|
-
# aliases: true allows YAML anchors (&default, *default) commonly used in Rails configs
|
|
299
|
-
YAML.safe_load_file(path, permitted_classes: [Symbol], aliases: true)
|
|
300
|
-
rescue ArgumentError
|
|
301
|
-
# Older Psych versions don't support all parameters - try without aliases
|
|
302
|
-
begin
|
|
303
|
-
YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
304
|
-
rescue ArgumentError
|
|
305
|
-
# Very old Psych - fall back to safe_load with File.read
|
|
306
|
-
YAML.safe_load(File.read(path), permitted_classes: [Symbol]) # rubocop:disable Style/YAMLFileRead
|
|
307
|
-
end
|
|
308
|
-
rescue StandardError
|
|
309
|
-
# If we can't parse the file, return empty config
|
|
310
|
-
{}
|
|
311
|
-
end
|
|
312
|
-
|
|
313
382
|
# Check if Shakapacker 9.3.0 or higher is available
|
|
314
383
|
# This version made SWC the default JavaScript transpiler
|
|
315
384
|
def shakapacker_version_9_3_or_higher?
|
|
@@ -330,10 +399,34 @@ module GeneratorHelper
|
|
|
330
399
|
# `assets_bundler` inside the `default: &default` block, and our generator writes
|
|
331
400
|
# it there too via configure_rspack_in_shakapacker.
|
|
332
401
|
def rspack_configured_in_project?
|
|
402
|
+
shakapacker_assets_bundler_value == "rspack"
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# Fresh-install bundler default used by InstallGenerator/BaseGenerator: prefer Rspack
|
|
406
|
+
# when Shakapacker supports it (Rspack landed in Shakapacker 9.0), but never override an
|
|
407
|
+
# existing app's explicit assets_bundler choice. On a brand-new install where Shakapacker
|
|
408
|
+
# isn't loaded yet, shakapacker_version_9_or_higher? optimistically returns true.
|
|
409
|
+
def fresh_install_rspack_default
|
|
410
|
+
return rspack_configured_in_project? if project_declares_assets_bundler?
|
|
411
|
+
|
|
412
|
+
shakapacker_version_9_or_higher?
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# True when config/shakapacker.yml exists and its default: section declares an
|
|
416
|
+
# assets_bundler (i.e., the project has already made an explicit bundler choice).
|
|
417
|
+
def project_declares_assets_bundler?
|
|
418
|
+
!shakapacker_assets_bundler_value.nil?
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# Single source for the config/shakapacker.yml default-section read shared by
|
|
422
|
+
# rspack_configured_in_project? and project_declares_assets_bundler?. Returns the
|
|
423
|
+
# assets_bundler value (e.g. "rspack"), or nil when the file is absent or the key is unset.
|
|
424
|
+
# Only the default: section is inspected (see rspack_configured_in_project? for the rationale).
|
|
425
|
+
def shakapacker_assets_bundler_value
|
|
333
426
|
shakapacker_yml_path = File.join(destination_root, "config/shakapacker.yml")
|
|
334
|
-
return
|
|
427
|
+
return nil unless File.exist?(shakapacker_yml_path)
|
|
335
428
|
|
|
336
|
-
|
|
337
|
-
config.dig("default", "assets_bundler") == "rspack"
|
|
429
|
+
parse_shakapacker_yml(shakapacker_yml_path).dig("default", "assets_bundler")
|
|
338
430
|
end
|
|
339
431
|
end
|
|
432
|
+
# rubocop:enable Metrics/ModuleLength
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "rainbow"
|
|
4
|
+
require_relative "../shakapacker_precompile_hook_helper"
|
|
4
5
|
|
|
5
6
|
module GeneratorMessages
|
|
6
7
|
module CiSection
|
|
8
|
+
include ReactOnRails::Generators::ShakapackerPrecompileHookHelper
|
|
9
|
+
|
|
7
10
|
private
|
|
8
11
|
|
|
9
12
|
def build_ci_section(app_root: Dir.pwd, ci_workflow_generated: false)
|
|
@@ -12,7 +15,7 @@ module GeneratorMessages
|
|
|
12
15
|
# Read package.json once and reuse for both package-manager detection and the
|
|
13
16
|
# build:test script presence check to avoid a second I/O pass.
|
|
14
17
|
package_json = read_package_json(app_root)
|
|
15
|
-
package_manager = detect_package_manager(app_root
|
|
18
|
+
package_manager = detect_package_manager(app_root:, package_json:)
|
|
16
19
|
ci_status = if ci_workflow_generated
|
|
17
20
|
"A GitHub Actions workflow has been generated at .github/workflows/ci.yml."
|
|
18
21
|
else
|
|
@@ -25,6 +28,11 @@ module GeneratorMessages
|
|
|
25
28
|
else
|
|
26
29
|
""
|
|
27
30
|
end
|
|
31
|
+
manual_build_command = shakapacker_build_command(
|
|
32
|
+
env: "RAILS_ENV=test NODE_ENV=test",
|
|
33
|
+
app_root:,
|
|
34
|
+
environment: "test"
|
|
35
|
+
)
|
|
28
36
|
|
|
29
37
|
<<~CI
|
|
30
38
|
|
|
@@ -35,7 +43,7 @@ module GeneratorMessages
|
|
|
35
43
|
#{ci_status}
|
|
36
44
|
|
|
37
45
|
To build bundles manually before tests:
|
|
38
|
-
#{Rainbow(
|
|
46
|
+
#{Rainbow(manual_build_command).cyan}#{build_test_hint}
|
|
39
47
|
CI
|
|
40
48
|
end
|
|
41
49
|
end
|
|
@@ -37,8 +37,8 @@ module GeneratorMessages
|
|
|
37
37
|
# wants detection to fall through directly to lockfile heuristics.
|
|
38
38
|
def detect_package_manager(app_root: Dir.pwd, package_json: PACKAGE_JSON_UNSET)
|
|
39
39
|
detect_package_manager_with_source(
|
|
40
|
-
app_root
|
|
41
|
-
package_json:
|
|
40
|
+
app_root:,
|
|
41
|
+
package_json:
|
|
42
42
|
).first
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -52,13 +52,13 @@ module GeneratorMessages
|
|
|
52
52
|
return [env_package_manager, :env] if supported_package_manager?(env_package_manager)
|
|
53
53
|
|
|
54
54
|
content = package_json_content(
|
|
55
|
-
app_root
|
|
56
|
-
package_json:
|
|
55
|
+
app_root:,
|
|
56
|
+
package_json:
|
|
57
57
|
)
|
|
58
58
|
pm_from_json = content ? package_manager_name_from_content(content) : nil
|
|
59
59
|
return [pm_from_json, :package_json] if pm_from_json
|
|
60
60
|
|
|
61
|
-
pm_from_lockfile = detect_package_manager_from_lockfiles(app_root:
|
|
61
|
+
pm_from_lockfile = detect_package_manager_from_lockfiles(app_root:)
|
|
62
62
|
return [pm_from_lockfile, :lockfile] if pm_from_lockfile
|
|
63
63
|
|
|
64
64
|
["npm", :default]
|
|
@@ -84,8 +84,8 @@ module GeneratorMessages
|
|
|
84
84
|
# package_json: nil to preserve a cached missing/unreadable read.
|
|
85
85
|
def package_manager_declared?(manager:, app_root: Dir.pwd, package_json: PACKAGE_JSON_UNSET)
|
|
86
86
|
content = package_json_content(
|
|
87
|
-
app_root
|
|
88
|
-
package_json:
|
|
87
|
+
app_root:,
|
|
88
|
+
package_json:
|
|
89
89
|
)
|
|
90
90
|
return false unless content
|
|
91
91
|
|
|
@@ -99,12 +99,12 @@ module GeneratorMessages
|
|
|
99
99
|
# that's not on disk (e.g. `packageManager: pnpm` without `pnpm-lock.yaml`, which
|
|
100
100
|
# breaks `actions/setup-node`'s cache step).
|
|
101
101
|
def lockfile_for_manager?(package_manager, app_root: Dir.pwd)
|
|
102
|
-
!lockfile_filename_for(package_manager, app_root:
|
|
102
|
+
!lockfile_filename_for(package_manager, app_root:).nil?
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
def detect_package_manager_from_lockfiles(app_root: Dir.pwd)
|
|
106
106
|
LOCKFILE_CANDIDATES_BY_MANAGER.keys.find do |pm|
|
|
107
|
-
lockfile_for_manager?(pm, app_root:
|
|
107
|
+
lockfile_for_manager?(pm, app_root:)
|
|
108
108
|
end
|
|
109
109
|
end
|
|
110
110
|
|
|
@@ -7,7 +7,7 @@ module GeneratorMessages
|
|
|
7
7
|
private
|
|
8
8
|
|
|
9
9
|
def build_shakapacker_status_section(shakapacker_just_installed: false, app_root: Dir.pwd)
|
|
10
|
-
version_warning = check_shakapacker_version_warning(app_root:
|
|
10
|
+
version_warning = check_shakapacker_version_warning(app_root:)
|
|
11
11
|
if shakapacker_just_installed
|
|
12
12
|
base = <<~SHAKAPACKER
|
|
13
13
|
|
|
@@ -58,13 +58,13 @@ module GeneratorMessages
|
|
|
58
58
|
rsc: false, shakapacker_just_installed: false, landing_page: false,
|
|
59
59
|
ci_workflow_generated: false, app_root: Dir.pwd)
|
|
60
60
|
process_manager_section = build_process_manager_section
|
|
61
|
-
testing_section = build_testing_section(app_root:
|
|
62
|
-
ci_section = build_ci_section(app_root
|
|
63
|
-
package_manager = detect_package_manager(app_root:
|
|
64
|
-
shakapacker_status = build_shakapacker_status_section(shakapacker_just_installed
|
|
65
|
-
app_root:
|
|
66
|
-
render_example = build_render_example(component_name
|
|
67
|
-
render_label = build_render_label(route
|
|
61
|
+
testing_section = build_testing_section(app_root:)
|
|
62
|
+
ci_section = build_ci_section(app_root:, ci_workflow_generated:)
|
|
63
|
+
package_manager = detect_package_manager(app_root:)
|
|
64
|
+
shakapacker_status = build_shakapacker_status_section(shakapacker_just_installed:,
|
|
65
|
+
app_root:)
|
|
66
|
+
render_example = build_render_example(component_name:, route:, rsc:)
|
|
67
|
+
render_label = build_render_label(route:, rsc:)
|
|
68
68
|
normalized_route = route.to_s.sub(%r{\A/+}, "")
|
|
69
69
|
visit_url = if landing_page || normalized_route.empty?
|
|
70
70
|
"http://localhost:3000"
|