react_on_rails 16.1.2 → 16.2.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/.rspec +2 -0
- data/.rubocop.yml +85 -0
- data/Gemfile.development_dependencies +8 -7
- data/Gemfile.lock +158 -119
- data/Steepfile +56 -0
- data/lib/generators/react_on_rails/base_generator.rb +43 -120
- data/lib/generators/react_on_rails/dev_tests_generator.rb +2 -1
- data/lib/generators/react_on_rails/generator_helper.rb +102 -2
- data/lib/generators/react_on_rails/install_generator.rb +36 -156
- data/lib/generators/react_on_rails/js_dependency_manager.rb +383 -0
- data/lib/generators/react_on_rails/templates/base/base/.dev-services.yml.example +76 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/shakapacker-precompile-hook +30 -0
- data/lib/generators/react_on_rails/templates/base/base/bin/switch-bundler +141 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +44 -45
- data/lib/generators/react_on_rails/templates/base/base/config/{shakapacker.yml → shakapacker.yml.tt} +28 -3
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +15 -9
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +42 -6
- data/lib/react_on_rails/configuration.rb +149 -32
- data/lib/react_on_rails/controller.rb +3 -3
- data/lib/react_on_rails/dev/pack_generator.rb +168 -2
- data/lib/react_on_rails/dev/process_manager.rb +136 -14
- data/lib/react_on_rails/dev/server_manager.rb +194 -26
- data/lib/react_on_rails/dev/service_checker.rb +200 -0
- data/lib/react_on_rails/doctor.rb +341 -12
- data/lib/react_on_rails/engine.rb +75 -1
- data/lib/react_on_rails/git_utils.rb +3 -1
- data/lib/react_on_rails/helper.rb +70 -192
- data/lib/react_on_rails/locales/base.rb +17 -5
- data/lib/react_on_rails/packer_utils.rb +79 -2
- data/lib/react_on_rails/packs_generator.rb +57 -39
- data/lib/react_on_rails/prerender_error.rb +74 -17
- data/lib/react_on_rails/pro_helper.rb +64 -0
- data/lib/react_on_rails/react_component/render_options.rb +7 -7
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +2 -5
- data/lib/react_on_rails/smart_error.rb +326 -0
- data/lib/react_on_rails/system_checker.rb +8 -9
- data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +16 -7
- data/lib/react_on_rails/utils.rb +241 -55
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +383 -35
- data/lib/tasks/generate_packs.rake +12 -6
- data/lib/tasks/locale.rake +6 -1
- data/rakelib/docker.rake +26 -0
- data/rakelib/dummy_apps.rake +30 -0
- data/rakelib/example_type.rb +121 -0
- data/rakelib/examples_config.yml +52 -0
- data/rakelib/lint.rake +52 -0
- data/rakelib/node_package.rake +15 -0
- data/rakelib/rbs.rake +70 -0
- data/rakelib/run_rspec.rake +223 -0
- data/rakelib/shakapacker_examples.rake +171 -0
- data/rakelib/task_helpers.rb +134 -0
- data/rakelib/update_changelog.rake +73 -0
- data/react_on_rails.gemspec +4 -3
- data/sig/README.md +52 -0
- data/sig/react_on_rails/configuration.rbs +96 -0
- data/sig/react_on_rails/controller.rbs +15 -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/dev/service_checker.rbs +22 -0
- data/sig/react_on_rails/error.rbs +4 -0
- data/sig/react_on_rails/generators/js_dependency_manager.rbs +123 -0
- data/sig/react_on_rails/git_utils.rbs +8 -0
- data/sig/react_on_rails/helper.rbs +65 -0
- data/sig/react_on_rails/json_parse_error.rbs +10 -0
- data/sig/react_on_rails/locales.rbs +46 -0
- data/sig/react_on_rails/packer_utils.rbs +15 -0
- data/sig/react_on_rails/prerender_error.rbs +21 -0
- data/sig/react_on_rails/server_rendering_pool.rbs +12 -0
- data/sig/react_on_rails/smart_error.rbs +28 -0
- data/sig/react_on_rails/test_helper.rbs +11 -0
- data/sig/react_on_rails/utils.rbs +34 -0
- data/sig/react_on_rails/version_checker.rbs +12 -0
- data/sig/react_on_rails.rbs +17 -0
- metadata +49 -32
- data/AI_AGENT_INSTRUCTIONS.md +0 -63
- data/CHANGELOG.md +0 -1836
- data/CLAUDE.md +0 -135
- data/CODING_AGENTS.md +0 -313
- data/CONTRIBUTING.md +0 -668
- data/Dockerfile_tests +0 -12
- data/KUDOS.md +0 -114
- data/LICENSE.md +0 -47
- data/LICENSES/README.md +0 -14
- data/NEWS.md +0 -62
- data/PROJECTS.md +0 -63
- data/REACT-ON-RAILS-PRO-LICENSE.md +0 -129
- data/README.md +0 -217
- data/SUMMARY.md +0 -88
- data/TODO.md +0 -135
- data/bin/lefthook/check-trailing-newlines +0 -38
- data/bin/lefthook/get-changed-files +0 -26
- data/bin/lefthook/prettier-format +0 -26
- data/bin/lefthook/ruby-autofix +0 -26
- data/bin/lefthook/ruby-lint +0 -27
- data/docker-compose.yml +0 -11
- data/eslint.config.ts +0 -232
- data/knip.ts +0 -114
- data/lib/react_on_rails/pro/NOTICE +0 -21
- data/lib/react_on_rails/pro/helper.rb +0 -122
- data/lib/react_on_rails/pro/utils.rb +0 -53
- data/tsconfig.eslint.json +0 -6
- data/tsconfig.json +0 -19
data/Steepfile
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Steepfile - Configuration for Steep type checker
|
|
4
|
+
# See https://github.com/soutaro/steep for documentation
|
|
5
|
+
#
|
|
6
|
+
# IMPORTANT: This file lists only the files that are ready for type checking.
|
|
7
|
+
# We use a positive list (explicit check statements) rather than checking all files
|
|
8
|
+
# because not all files have RBS signatures yet.
|
|
9
|
+
#
|
|
10
|
+
# Files/directories intentionally excluded (no RBS signatures yet):
|
|
11
|
+
# - lib/generators/**/* - Rails generators (complex Rails integration)
|
|
12
|
+
# - lib/react_on_rails/engine.rb - Rails engine setup
|
|
13
|
+
# - lib/react_on_rails/doctor.rb - Diagnostic tool
|
|
14
|
+
# - lib/react_on_rails/locales/**/* - I18n files
|
|
15
|
+
# - lib/react_on_rails/props_js_builder.rb - TODO: Add signature
|
|
16
|
+
# - lib/react_on_rails/shakapacker/**/* - Shakapacker integration (complex)
|
|
17
|
+
#
|
|
18
|
+
# To add a new file to type checking:
|
|
19
|
+
# 1. Create corresponding RBS signature in sig/react_on_rails/filename.rbs
|
|
20
|
+
# 2. Add `check "lib/react_on_rails/filename.rb"` below
|
|
21
|
+
# 3. Run `bundle exec rake rbs:steep` to verify
|
|
22
|
+
# 4. Fix any type errors before committing
|
|
23
|
+
|
|
24
|
+
D = Steep::Diagnostic
|
|
25
|
+
|
|
26
|
+
target :lib do
|
|
27
|
+
# Core files with RBS signatures (alphabetically ordered for easy maintenance)
|
|
28
|
+
check "lib/react_on_rails.rb"
|
|
29
|
+
check "lib/react_on_rails/configuration.rb"
|
|
30
|
+
check "lib/react_on_rails/controller.rb"
|
|
31
|
+
check "lib/react_on_rails/dev/file_manager.rb"
|
|
32
|
+
check "lib/react_on_rails/dev/pack_generator.rb"
|
|
33
|
+
check "lib/react_on_rails/dev/process_manager.rb"
|
|
34
|
+
check "lib/react_on_rails/dev/server_manager.rb"
|
|
35
|
+
check "lib/react_on_rails/dev/service_checker.rb"
|
|
36
|
+
check "lib/react_on_rails/git_utils.rb"
|
|
37
|
+
check "lib/react_on_rails/helper.rb"
|
|
38
|
+
check "lib/react_on_rails/packer_utils.rb"
|
|
39
|
+
check "lib/react_on_rails/server_rendering_pool.rb"
|
|
40
|
+
check "lib/react_on_rails/test_helper.rb"
|
|
41
|
+
check "lib/react_on_rails/utils.rb"
|
|
42
|
+
check "lib/react_on_rails/version_checker.rb"
|
|
43
|
+
|
|
44
|
+
# Specify RBS signature directories
|
|
45
|
+
signature "sig"
|
|
46
|
+
|
|
47
|
+
# Configure libraries (gems) - Steep will load their RBS signatures
|
|
48
|
+
configure_code_diagnostics(D::Ruby.default)
|
|
49
|
+
|
|
50
|
+
# Library configuration - standard library gems used by checked files
|
|
51
|
+
library "pathname"
|
|
52
|
+
library "singleton"
|
|
53
|
+
library "logger"
|
|
54
|
+
library "monitor"
|
|
55
|
+
library "securerandom"
|
|
56
|
+
end
|
|
@@ -4,10 +4,12 @@ require "rails/generators"
|
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require_relative "generator_messages"
|
|
6
6
|
require_relative "generator_helper"
|
|
7
|
+
require_relative "js_dependency_manager"
|
|
7
8
|
module ReactOnRails
|
|
8
9
|
module Generators
|
|
9
10
|
class BaseGenerator < Rails::Generators::Base
|
|
10
11
|
include GeneratorHelper
|
|
12
|
+
include JsDependencyManager
|
|
11
13
|
|
|
12
14
|
Rails::Generators.hide_namespace(namespace)
|
|
13
15
|
source_root(File.expand_path("templates", __dir__))
|
|
@@ -19,6 +21,12 @@ module ReactOnRails
|
|
|
19
21
|
desc: "Install Redux package and Redux version of Hello World Example",
|
|
20
22
|
aliases: "-R"
|
|
21
23
|
|
|
24
|
+
# --rspack
|
|
25
|
+
class_option :rspack,
|
|
26
|
+
type: :boolean,
|
|
27
|
+
default: false,
|
|
28
|
+
desc: "Use Rspack instead of Webpack as the bundler"
|
|
29
|
+
|
|
22
30
|
def add_hello_world_route
|
|
23
31
|
route "get 'hello_world', to: 'hello_world#index'"
|
|
24
32
|
end
|
|
@@ -37,12 +45,17 @@ module ReactOnRails
|
|
|
37
45
|
app/views/layouts/hello_world.html.erb
|
|
38
46
|
Procfile.dev
|
|
39
47
|
Procfile.dev-static-assets
|
|
40
|
-
Procfile.dev-prod-assets
|
|
48
|
+
Procfile.dev-prod-assets
|
|
49
|
+
.dev-services.yml.example
|
|
50
|
+
bin/shakapacker-precompile-hook]
|
|
41
51
|
base_templates = %w[config/initializers/react_on_rails.rb]
|
|
42
52
|
base_files.each { |file| copy_file("#{base_path}#{file}", file) }
|
|
43
53
|
base_templates.each do |file|
|
|
44
54
|
template("#{base_path}/#{file}.tt", file)
|
|
45
55
|
end
|
|
56
|
+
|
|
57
|
+
# Make the hook script executable (copy_file guarantees it exists)
|
|
58
|
+
File.chmod(0o755, File.join(destination_root, "bin/shakapacker-precompile-hook"))
|
|
46
59
|
end
|
|
47
60
|
|
|
48
61
|
def copy_js_bundle_files
|
|
@@ -82,20 +95,23 @@ module ReactOnRails
|
|
|
82
95
|
if File.exist?(".shakapacker_just_installed")
|
|
83
96
|
puts "Skipping Shakapacker config copy (already installed by Shakapacker installer)"
|
|
84
97
|
File.delete(".shakapacker_just_installed") # Clean up marker
|
|
98
|
+
configure_rspack_in_shakapacker if options.rspack?
|
|
85
99
|
return
|
|
86
100
|
end
|
|
87
101
|
|
|
88
102
|
puts "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
|
|
89
103
|
base_path = "base/base/"
|
|
90
104
|
config = "config/shakapacker.yml"
|
|
91
|
-
|
|
105
|
+
# Use template to enable version-aware configuration
|
|
106
|
+
template("#{base_path}#{config}.tt", config)
|
|
107
|
+
configure_rspack_in_shakapacker if options.rspack?
|
|
92
108
|
end
|
|
93
109
|
|
|
94
110
|
def add_base_gems_to_gemfile
|
|
95
111
|
run "bundle"
|
|
96
112
|
end
|
|
97
113
|
|
|
98
|
-
def
|
|
114
|
+
def update_gitignore_for_auto_registration
|
|
99
115
|
gitignore_path = File.join(destination_root, ".gitignore")
|
|
100
116
|
return unless File.exist?(gitignore_path)
|
|
101
117
|
|
|
@@ -134,123 +150,6 @@ module ReactOnRails
|
|
|
134
150
|
|
|
135
151
|
private
|
|
136
152
|
|
|
137
|
-
def setup_js_dependencies
|
|
138
|
-
add_js_dependencies
|
|
139
|
-
install_js_dependencies
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def add_js_dependencies
|
|
143
|
-
add_react_on_rails_package
|
|
144
|
-
add_react_dependencies
|
|
145
|
-
add_css_dependencies
|
|
146
|
-
add_dev_dependencies
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
def add_react_on_rails_package
|
|
150
|
-
major_minor_patch_only = /\A\d+\.\d+\.\d+\z/
|
|
151
|
-
|
|
152
|
-
# Try to use package_json gem first, fall back to direct npm commands
|
|
153
|
-
react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only)
|
|
154
|
-
["react-on-rails@#{ReactOnRails::VERSION}"]
|
|
155
|
-
else
|
|
156
|
-
puts "Adding the latest react-on-rails NPM module. " \
|
|
157
|
-
"Double check this is correct in package.json"
|
|
158
|
-
["react-on-rails"]
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
puts "Installing React on Rails package..."
|
|
162
|
-
return if add_npm_dependencies(react_on_rails_pkg)
|
|
163
|
-
|
|
164
|
-
puts "Using direct npm commands as fallback"
|
|
165
|
-
success = system("npm", "install", *react_on_rails_pkg)
|
|
166
|
-
handle_npm_failure("react-on-rails package", react_on_rails_pkg) unless success
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
def add_react_dependencies
|
|
170
|
-
puts "Installing React dependencies..."
|
|
171
|
-
react_deps = %w[
|
|
172
|
-
react
|
|
173
|
-
react-dom
|
|
174
|
-
@babel/preset-react
|
|
175
|
-
prop-types
|
|
176
|
-
babel-plugin-transform-react-remove-prop-types
|
|
177
|
-
babel-plugin-macros
|
|
178
|
-
]
|
|
179
|
-
return if add_npm_dependencies(react_deps)
|
|
180
|
-
|
|
181
|
-
success = system("npm", "install", *react_deps)
|
|
182
|
-
handle_npm_failure("React dependencies", react_deps) unless success
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def add_css_dependencies
|
|
186
|
-
puts "Installing CSS handling dependencies..."
|
|
187
|
-
css_deps = %w[
|
|
188
|
-
css-loader
|
|
189
|
-
css-minimizer-webpack-plugin
|
|
190
|
-
mini-css-extract-plugin
|
|
191
|
-
style-loader
|
|
192
|
-
]
|
|
193
|
-
return if add_npm_dependencies(css_deps)
|
|
194
|
-
|
|
195
|
-
success = system("npm", "install", *css_deps)
|
|
196
|
-
handle_npm_failure("CSS dependencies", css_deps) unless success
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
def add_dev_dependencies
|
|
200
|
-
puts "Installing development dependencies..."
|
|
201
|
-
dev_deps = %w[
|
|
202
|
-
@pmmmwh/react-refresh-webpack-plugin
|
|
203
|
-
react-refresh
|
|
204
|
-
]
|
|
205
|
-
return if add_npm_dependencies(dev_deps, dev: true)
|
|
206
|
-
|
|
207
|
-
success = system("npm", "install", "--save-dev", *dev_deps)
|
|
208
|
-
handle_npm_failure("development dependencies", dev_deps, dev: true) unless success
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
def install_js_dependencies
|
|
212
|
-
# Detect which package manager to use
|
|
213
|
-
success = if File.exist?(File.join(destination_root, "yarn.lock"))
|
|
214
|
-
system("yarn", "install")
|
|
215
|
-
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
|
|
216
|
-
system("pnpm", "install")
|
|
217
|
-
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
|
|
218
|
-
File.exist?(File.join(destination_root, "package.json"))
|
|
219
|
-
# Use npm for package-lock.json or as default fallback
|
|
220
|
-
system("npm", "install")
|
|
221
|
-
else
|
|
222
|
-
true # No package manager detected, skip
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
unless success
|
|
226
|
-
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
227
|
-
⚠️ JavaScript dependencies installation failed.
|
|
228
|
-
|
|
229
|
-
This could be due to network issues or missing package manager.
|
|
230
|
-
You can install dependencies manually later by running:
|
|
231
|
-
• npm install (if using npm)
|
|
232
|
-
• yarn install (if using yarn)
|
|
233
|
-
• pnpm install (if using pnpm)
|
|
234
|
-
MSG
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
success
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
def handle_npm_failure(dependency_type, packages, dev: false)
|
|
241
|
-
install_command = dev ? "npm install --save-dev" : "npm install"
|
|
242
|
-
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
243
|
-
⚠️ Failed to install #{dependency_type}.
|
|
244
|
-
|
|
245
|
-
The following packages could not be installed automatically:
|
|
246
|
-
#{packages.map { |pkg| " • #{pkg}" }.join("\n")}
|
|
247
|
-
|
|
248
|
-
This could be due to network issues or missing package manager.
|
|
249
|
-
You can install them manually later by running:
|
|
250
|
-
#{install_command} #{packages.join(' ')}
|
|
251
|
-
MSG
|
|
252
|
-
end
|
|
253
|
-
|
|
254
153
|
def copy_webpack_main_config(base_path, config)
|
|
255
154
|
webpack_config_path = "config/webpack/webpack.config.js"
|
|
256
155
|
|
|
@@ -392,6 +291,30 @@ module ReactOnRails
|
|
|
392
291
|
search_str = "RSpec.configure do |config|"
|
|
393
292
|
gsub_file(helper_file, search_str, CONFIGURE_RSPEC_TO_COMPILE_ASSETS)
|
|
394
293
|
end
|
|
294
|
+
|
|
295
|
+
def configure_rspack_in_shakapacker
|
|
296
|
+
shakapacker_config_path = "config/shakapacker.yml"
|
|
297
|
+
return unless File.exist?(shakapacker_config_path)
|
|
298
|
+
|
|
299
|
+
puts Rainbow("🔧 Configuring Shakapacker for Rspack...").yellow
|
|
300
|
+
|
|
301
|
+
# Parse YAML config properly to avoid fragile regex manipulation
|
|
302
|
+
# Support both old and new Psych versions
|
|
303
|
+
config = begin
|
|
304
|
+
YAML.load_file(shakapacker_config_path, aliases: true)
|
|
305
|
+
rescue ArgumentError
|
|
306
|
+
# Older Psych versions don't support the aliases parameter
|
|
307
|
+
YAML.load_file(shakapacker_config_path)
|
|
308
|
+
end
|
|
309
|
+
# Update default section
|
|
310
|
+
config["default"] ||= {}
|
|
311
|
+
config["default"]["assets_bundler"] = "rspack"
|
|
312
|
+
config["default"]["webpack_loader"] = "swc"
|
|
313
|
+
|
|
314
|
+
# Write back as YAML
|
|
315
|
+
File.write(shakapacker_config_path, YAML.dump(config))
|
|
316
|
+
puts Rainbow("✅ Updated shakapacker.yml for Rspack").green
|
|
317
|
+
end
|
|
395
318
|
end
|
|
396
319
|
end
|
|
397
320
|
end
|
|
@@ -31,7 +31,8 @@ module ReactOnRails
|
|
|
31
31
|
|
|
32
32
|
def add_test_related_gems_to_gemfile
|
|
33
33
|
gem("rspec-rails", group: :test)
|
|
34
|
-
|
|
34
|
+
# NOTE: chromedriver-helper was deprecated in 2019. Modern selenium-webdriver (4.x)
|
|
35
|
+
# and GitHub Actions have built-in driver management, so no driver helper is needed.
|
|
35
36
|
gem("coveralls", require: false)
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -26,9 +26,9 @@ module GeneratorHelper
|
|
|
26
26
|
|
|
27
27
|
begin
|
|
28
28
|
if dev
|
|
29
|
-
pj.manager.add(packages, type: :dev)
|
|
29
|
+
pj.manager.add(packages, type: :dev, exact: true)
|
|
30
30
|
else
|
|
31
|
-
pj.manager.add(packages)
|
|
31
|
+
pj.manager.add(packages, exact: true)
|
|
32
32
|
end
|
|
33
33
|
true
|
|
34
34
|
rescue StandardError => e
|
|
@@ -95,4 +95,104 @@ module GeneratorHelper
|
|
|
95
95
|
def component_extension(options)
|
|
96
96
|
options.typescript? ? "tsx" : "jsx"
|
|
97
97
|
end
|
|
98
|
+
|
|
99
|
+
# Check if Shakapacker 9.0 or higher is available
|
|
100
|
+
# Returns true if Shakapacker >= 9.0, false otherwise
|
|
101
|
+
#
|
|
102
|
+
# This method is used during code generation to determine which configuration
|
|
103
|
+
# patterns to use in generated files (e.g., config.privateOutputPath vs hardcoded paths).
|
|
104
|
+
#
|
|
105
|
+
# @return [Boolean] true if Shakapacker 9.0+ is available or likely to be installed
|
|
106
|
+
#
|
|
107
|
+
# @note Default behavior: Returns true when Shakapacker is not yet installed
|
|
108
|
+
# Rationale: During fresh installations, we optimistically assume users will install
|
|
109
|
+
# the latest Shakapacker version. This ensures new projects get best-practice configs.
|
|
110
|
+
# If users later install an older version, the generated webpack config includes
|
|
111
|
+
# fallback logic (e.g., `config.privateOutputPath || hardcodedPath`) that prevents
|
|
112
|
+
# breakage, and validation warnings guide them to fix any misconfigurations.
|
|
113
|
+
def shakapacker_version_9_or_higher?
|
|
114
|
+
return @shakapacker_version_9_or_higher if defined?(@shakapacker_version_9_or_higher)
|
|
115
|
+
|
|
116
|
+
@shakapacker_version_9_or_higher = begin
|
|
117
|
+
# If Shakapacker is not available yet (fresh install), default to true
|
|
118
|
+
# since we're likely installing the latest version
|
|
119
|
+
return true unless defined?(ReactOnRails::PackerUtils)
|
|
120
|
+
|
|
121
|
+
ReactOnRails::PackerUtils.shakapacker_version_requirement_met?("9.0.0")
|
|
122
|
+
rescue StandardError
|
|
123
|
+
# If we can't determine version, assume latest
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Check if SWC is configured as the JavaScript transpiler in shakapacker.yml
|
|
129
|
+
#
|
|
130
|
+
# @return [Boolean] true if SWC is configured or should be used by default
|
|
131
|
+
#
|
|
132
|
+
# Detection logic:
|
|
133
|
+
# 1. If shakapacker.yml exists and specifies javascript_transpiler: parse it
|
|
134
|
+
# 2. For Shakapacker 9.3.0+, SWC is the default if not specified
|
|
135
|
+
# 3. Returns true for fresh installations (SWC is recommended default)
|
|
136
|
+
#
|
|
137
|
+
# @note This method is used to determine whether to install SWC dependencies
|
|
138
|
+
# (@swc/core, swc-loader) instead of Babel dependencies during generation.
|
|
139
|
+
#
|
|
140
|
+
# @note Caching: The result is memoized for the lifetime of the generator instance.
|
|
141
|
+
# If shakapacker.yml changes during generator execution (unlikely), the cached
|
|
142
|
+
# value will not update. This is acceptable since generators run quickly.
|
|
143
|
+
def using_swc?
|
|
144
|
+
return @using_swc if defined?(@using_swc)
|
|
145
|
+
|
|
146
|
+
@using_swc = detect_swc_configuration
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
def detect_swc_configuration
|
|
152
|
+
shakapacker_yml_path = File.join(destination_root, "config/shakapacker.yml")
|
|
153
|
+
|
|
154
|
+
if File.exist?(shakapacker_yml_path)
|
|
155
|
+
config = parse_shakapacker_yml(shakapacker_yml_path)
|
|
156
|
+
transpiler = config.dig("default", "javascript_transpiler")
|
|
157
|
+
|
|
158
|
+
# Explicit configuration takes precedence
|
|
159
|
+
return transpiler == "swc" if transpiler
|
|
160
|
+
|
|
161
|
+
# For Shakapacker 9.3.0+, SWC is the default
|
|
162
|
+
return shakapacker_version_9_3_or_higher?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Fresh install: SWC is recommended default for Shakapacker 9.3.0+
|
|
166
|
+
shakapacker_version_9_3_or_higher?
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def parse_shakapacker_yml(path)
|
|
170
|
+
require "yaml"
|
|
171
|
+
# Use safe_load_file for security (defense-in-depth, even though this is user's own config)
|
|
172
|
+
# permitted_classes: [Symbol] allows symbol keys which shakapacker.yml may use
|
|
173
|
+
# aliases: true allows YAML anchors (&default, *default) commonly used in Rails configs
|
|
174
|
+
YAML.safe_load_file(path, permitted_classes: [Symbol], aliases: true)
|
|
175
|
+
rescue ArgumentError
|
|
176
|
+
# Older Psych versions don't support all parameters - try without aliases
|
|
177
|
+
begin
|
|
178
|
+
YAML.safe_load_file(path, permitted_classes: [Symbol])
|
|
179
|
+
rescue ArgumentError
|
|
180
|
+
# Very old Psych - fall back to safe_load with File.read
|
|
181
|
+
YAML.safe_load(File.read(path), permitted_classes: [Symbol]) # rubocop:disable Style/YAMLFileRead
|
|
182
|
+
end
|
|
183
|
+
rescue StandardError
|
|
184
|
+
# If we can't parse the file, return empty config
|
|
185
|
+
{}
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Check if Shakapacker 9.3.0 or higher is available
|
|
189
|
+
# This version made SWC the default JavaScript transpiler
|
|
190
|
+
def shakapacker_version_9_3_or_higher?
|
|
191
|
+
return true unless defined?(ReactOnRails::PackerUtils)
|
|
192
|
+
|
|
193
|
+
ReactOnRails::PackerUtils.shakapacker_version_requirement_met?("9.3.0")
|
|
194
|
+
rescue StandardError
|
|
195
|
+
# If we can't determine version, assume latest (which uses SWC)
|
|
196
|
+
true
|
|
197
|
+
end
|
|
98
198
|
end
|
|
@@ -4,12 +4,14 @@ require "rails/generators"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require_relative "generator_helper"
|
|
6
6
|
require_relative "generator_messages"
|
|
7
|
+
require_relative "js_dependency_manager"
|
|
7
8
|
|
|
8
9
|
module ReactOnRails
|
|
9
10
|
module Generators
|
|
10
11
|
# rubocop:disable Metrics/ClassLength
|
|
11
12
|
class InstallGenerator < Rails::Generators::Base
|
|
12
13
|
include GeneratorHelper
|
|
14
|
+
include JsDependencyManager
|
|
13
15
|
|
|
14
16
|
# fetch USAGE file for details generator description
|
|
15
17
|
source_root(File.expand_path(__dir__))
|
|
@@ -28,6 +30,12 @@ module ReactOnRails
|
|
|
28
30
|
desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
|
|
29
31
|
aliases: "-T"
|
|
30
32
|
|
|
33
|
+
# --rspack
|
|
34
|
+
class_option :rspack,
|
|
35
|
+
type: :boolean,
|
|
36
|
+
default: false,
|
|
37
|
+
desc: "Use Rspack instead of Webpack as the bundler. Default: false"
|
|
38
|
+
|
|
31
39
|
# --ignore-warnings
|
|
32
40
|
class_option :ignore_warnings,
|
|
33
41
|
type: :boolean,
|
|
@@ -36,7 +44,25 @@ module ReactOnRails
|
|
|
36
44
|
|
|
37
45
|
# Removed: --skip-shakapacker-install (Shakapacker is now a required dependency)
|
|
38
46
|
|
|
47
|
+
# Main generator entry point
|
|
48
|
+
#
|
|
49
|
+
# Sets up React on Rails in a Rails application by:
|
|
50
|
+
# 1. Validating prerequisites
|
|
51
|
+
# 2. Installing required packages
|
|
52
|
+
# 3. Generating configuration files
|
|
53
|
+
# 4. Setting up example components
|
|
54
|
+
#
|
|
55
|
+
# @note Validation Skipping: Sets ENV["REACT_ON_RAILS_SKIP_VALIDATION"] to prevent
|
|
56
|
+
# version validation from running during generator execution. The npm package
|
|
57
|
+
# isn't installed until midway through the generator, so validation would fail
|
|
58
|
+
# if run during Rails initialization. The ensure block guarantees cleanup even
|
|
59
|
+
# if the generator fails.
|
|
39
60
|
def run_generators
|
|
61
|
+
# Set environment variable to skip validation during generator run
|
|
62
|
+
# This is inherited by all invoked generators and persists through Rails initialization
|
|
63
|
+
# See lib/react_on_rails/engine.rb for the validation skip logic
|
|
64
|
+
ENV["REACT_ON_RAILS_SKIP_VALIDATION"] = "true"
|
|
65
|
+
|
|
40
66
|
if installation_prerequisites_met? || options.ignore_warnings?
|
|
41
67
|
invoke_generators
|
|
42
68
|
add_bin_scripts
|
|
@@ -55,6 +81,11 @@ module ReactOnRails
|
|
|
55
81
|
GeneratorMessages.add_error(error)
|
|
56
82
|
end
|
|
57
83
|
ensure
|
|
84
|
+
# Always clean up ENV variable, even if generator fails
|
|
85
|
+
# CRITICAL: ENV cleanup must come first to ensure it executes even if
|
|
86
|
+
# print_generator_messages raises an exception. This prevents ENV pollution
|
|
87
|
+
# that could affect subsequent processes.
|
|
88
|
+
ENV.delete("REACT_ON_RAILS_SKIP_VALIDATION")
|
|
58
89
|
print_generator_messages
|
|
59
90
|
end
|
|
60
91
|
|
|
@@ -73,7 +104,8 @@ module ReactOnRails
|
|
|
73
104
|
create_css_module_types
|
|
74
105
|
create_typescript_config
|
|
75
106
|
end
|
|
76
|
-
invoke "react_on_rails:base", [],
|
|
107
|
+
invoke "react_on_rails:base", [],
|
|
108
|
+
{ typescript: options.typescript?, redux: options.redux?, rspack: options.rspack? }
|
|
77
109
|
if options.redux?
|
|
78
110
|
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
|
|
79
111
|
else
|
|
@@ -83,10 +115,7 @@ module ReactOnRails
|
|
|
83
115
|
end
|
|
84
116
|
|
|
85
117
|
def setup_react_dependencies
|
|
86
|
-
|
|
87
|
-
@ran_direct_installs ||= false
|
|
88
|
-
add_js_dependencies
|
|
89
|
-
install_js_dependencies if @added_dependencies_to_package_json && !@ran_direct_installs
|
|
118
|
+
setup_js_dependencies
|
|
90
119
|
end
|
|
91
120
|
|
|
92
121
|
# NOTE: other requirements for existing files such as .gitignore or application.
|
|
@@ -336,29 +365,8 @@ module ReactOnRails
|
|
|
336
365
|
|
|
337
366
|
def install_typescript_dependencies
|
|
338
367
|
puts Rainbow("📝 Installing TypeScript dependencies...").yellow
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
typescript_packages = %w[
|
|
342
|
-
typescript
|
|
343
|
-
@types/react
|
|
344
|
-
@types/react-dom
|
|
345
|
-
@babel/preset-typescript
|
|
346
|
-
]
|
|
347
|
-
|
|
348
|
-
# Try using GeneratorHelper first (package manager agnostic)
|
|
349
|
-
return if add_npm_dependencies(typescript_packages, dev: true)
|
|
350
|
-
|
|
351
|
-
# Fallback to npm if GeneratorHelper fails
|
|
352
|
-
success = system("npm", "install", "--save-dev", *typescript_packages)
|
|
353
|
-
return if success
|
|
354
|
-
|
|
355
|
-
warning = <<~MSG.strip
|
|
356
|
-
⚠️ Failed to install TypeScript dependencies automatically.
|
|
357
|
-
|
|
358
|
-
Please run manually:
|
|
359
|
-
npm install --save-dev #{typescript_packages.join(' ')}
|
|
360
|
-
MSG
|
|
361
|
-
GeneratorMessages.add_warning(warning)
|
|
368
|
+
# Delegate to shared module for consistent dependency management
|
|
369
|
+
add_typescript_dependencies
|
|
362
370
|
end
|
|
363
371
|
|
|
364
372
|
def create_css_module_types
|
|
@@ -420,134 +428,6 @@ module ReactOnRails
|
|
|
420
428
|
puts Rainbow("✅ Created tsconfig.json").green
|
|
421
429
|
end
|
|
422
430
|
|
|
423
|
-
def add_js_dependencies
|
|
424
|
-
add_react_on_rails_package
|
|
425
|
-
add_react_dependencies
|
|
426
|
-
add_css_dependencies
|
|
427
|
-
add_dev_dependencies
|
|
428
|
-
end
|
|
429
|
-
|
|
430
|
-
def add_react_on_rails_package
|
|
431
|
-
major_minor_patch_only = /\A\d+\.\d+\.\d+\z/
|
|
432
|
-
|
|
433
|
-
# Try to use package_json gem first, fall back to direct npm commands
|
|
434
|
-
react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only)
|
|
435
|
-
["react-on-rails@#{ReactOnRails::VERSION}"]
|
|
436
|
-
else
|
|
437
|
-
puts "Adding the latest react-on-rails NPM module. " \
|
|
438
|
-
"Double check this is correct in package.json"
|
|
439
|
-
["react-on-rails"]
|
|
440
|
-
end
|
|
441
|
-
|
|
442
|
-
puts "Installing React on Rails package..."
|
|
443
|
-
if add_npm_dependencies(react_on_rails_pkg)
|
|
444
|
-
@added_dependencies_to_package_json = true
|
|
445
|
-
return
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
puts "Using direct npm commands as fallback"
|
|
449
|
-
success = system("npm", "install", *react_on_rails_pkg)
|
|
450
|
-
@ran_direct_installs = true if success
|
|
451
|
-
handle_npm_failure("react-on-rails package", react_on_rails_pkg) unless success
|
|
452
|
-
end
|
|
453
|
-
|
|
454
|
-
def add_react_dependencies
|
|
455
|
-
puts "Installing React dependencies..."
|
|
456
|
-
react_deps = %w[
|
|
457
|
-
react
|
|
458
|
-
react-dom
|
|
459
|
-
@babel/preset-react
|
|
460
|
-
prop-types
|
|
461
|
-
babel-plugin-transform-react-remove-prop-types
|
|
462
|
-
babel-plugin-macros
|
|
463
|
-
]
|
|
464
|
-
if add_npm_dependencies(react_deps)
|
|
465
|
-
@added_dependencies_to_package_json = true
|
|
466
|
-
return
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
success = system("npm", "install", *react_deps)
|
|
470
|
-
@ran_direct_installs = true if success
|
|
471
|
-
handle_npm_failure("React dependencies", react_deps) unless success
|
|
472
|
-
end
|
|
473
|
-
|
|
474
|
-
def add_css_dependencies
|
|
475
|
-
puts "Installing CSS handling dependencies..."
|
|
476
|
-
css_deps = %w[
|
|
477
|
-
css-loader
|
|
478
|
-
css-minimizer-webpack-plugin
|
|
479
|
-
mini-css-extract-plugin
|
|
480
|
-
style-loader
|
|
481
|
-
]
|
|
482
|
-
if add_npm_dependencies(css_deps)
|
|
483
|
-
@added_dependencies_to_package_json = true
|
|
484
|
-
return
|
|
485
|
-
end
|
|
486
|
-
|
|
487
|
-
success = system("npm", "install", *css_deps)
|
|
488
|
-
@ran_direct_installs = true if success
|
|
489
|
-
handle_npm_failure("CSS dependencies", css_deps) unless success
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
def add_dev_dependencies
|
|
493
|
-
puts "Installing development dependencies..."
|
|
494
|
-
dev_deps = %w[
|
|
495
|
-
@pmmmwh/react-refresh-webpack-plugin
|
|
496
|
-
react-refresh
|
|
497
|
-
]
|
|
498
|
-
if add_npm_dependencies(dev_deps, dev: true)
|
|
499
|
-
@added_dependencies_to_package_json = true
|
|
500
|
-
return
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
success = system("npm", "install", "--save-dev", *dev_deps)
|
|
504
|
-
@ran_direct_installs = true if success
|
|
505
|
-
handle_npm_failure("development dependencies", dev_deps, dev: true) unless success
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
def install_js_dependencies
|
|
509
|
-
# Detect which package manager to use
|
|
510
|
-
success = if File.exist?(File.join(destination_root, "yarn.lock"))
|
|
511
|
-
system("yarn", "install")
|
|
512
|
-
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
|
|
513
|
-
system("pnpm", "install")
|
|
514
|
-
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
|
|
515
|
-
File.exist?(File.join(destination_root, "package.json"))
|
|
516
|
-
# Use npm for package-lock.json or as default fallback
|
|
517
|
-
system("npm", "install")
|
|
518
|
-
else
|
|
519
|
-
true # No package manager detected, skip
|
|
520
|
-
end
|
|
521
|
-
|
|
522
|
-
unless success
|
|
523
|
-
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
524
|
-
⚠️ JavaScript dependencies installation failed.
|
|
525
|
-
|
|
526
|
-
This could be due to network issues or missing package manager.
|
|
527
|
-
You can install dependencies manually later by running:
|
|
528
|
-
• npm install (if using npm)
|
|
529
|
-
• yarn install (if using yarn)
|
|
530
|
-
• pnpm install (if using pnpm)
|
|
531
|
-
MSG
|
|
532
|
-
end
|
|
533
|
-
|
|
534
|
-
success
|
|
535
|
-
end
|
|
536
|
-
|
|
537
|
-
def handle_npm_failure(dependency_type, packages, dev: false)
|
|
538
|
-
install_command = dev ? "npm install --save-dev" : "npm install"
|
|
539
|
-
GeneratorMessages.add_warning(<<~MSG.strip)
|
|
540
|
-
⚠️ Failed to install #{dependency_type}.
|
|
541
|
-
|
|
542
|
-
The following packages could not be installed automatically:
|
|
543
|
-
#{packages.map { |pkg| " • #{pkg}" }.join("\n")}
|
|
544
|
-
|
|
545
|
-
This could be due to network issues or missing package manager.
|
|
546
|
-
You can install them manually later by running:
|
|
547
|
-
#{install_command} #{packages.join(' ')}
|
|
548
|
-
MSG
|
|
549
|
-
end
|
|
550
|
-
|
|
551
431
|
# Removed: Shakapacker auto-installation logic (now explicit dependency)
|
|
552
432
|
|
|
553
433
|
# Removed: Shakapacker 8+ is now required as explicit dependency
|