react_on_rails 14.2.1 → 16.1.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/AI_AGENT_INSTRUCTIONS.md +63 -0
- data/CHANGELOG.md +564 -85
- data/CLAUDE.md +135 -0
- data/CODING_AGENTS.md +313 -0
- data/CONTRIBUTING.md +448 -37
- data/Gemfile.development_dependencies +6 -1
- data/Gemfile.lock +13 -4
- data/KUDOS.md +22 -1
- data/LICENSE.md +30 -4
- data/LICENSES/README.md +14 -0
- data/NEWS.md +48 -48
- data/PROJECTS.md +45 -40
- data/REACT-ON-RAILS-PRO-LICENSE.md +129 -0
- data/README.md +113 -42
- data/SUMMARY.md +62 -52
- data/TODO.md +135 -0
- data/bin/lefthook/check-trailing-newlines +38 -0
- data/bin/lefthook/get-changed-files +26 -0
- data/bin/lefthook/prettier-format +26 -0
- data/bin/lefthook/ruby-autofix +26 -0
- data/bin/lefthook/ruby-lint +27 -0
- data/eslint.config.ts +232 -0
- data/knip.ts +40 -6
- data/lib/generators/USAGE +4 -5
- data/lib/generators/react_on_rails/USAGE +65 -0
- data/lib/generators/react_on_rails/base_generator.rb +276 -62
- data/lib/generators/react_on_rails/dev_tests_generator.rb +1 -0
- data/lib/generators/react_on_rails/generator_helper.rb +35 -1
- data/lib/generators/react_on_rails/generator_messages.rb +138 -17
- data/lib/generators/react_on_rails/install_generator.rb +474 -26
- data/lib/generators/react_on_rails/react_no_redux_generator.rb +19 -6
- data/lib/generators/react_on_rails/react_with_redux_generator.rb +110 -18
- data/lib/generators/react_on_rails/templates/.eslintrc +1 -1
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +5 -0
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-prod-assets +8 -0
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static-assets +2 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +0 -5
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +2 -2
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +1 -1
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +1 -8
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx +21 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.tsx +25 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css +4 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.jsx +5 -0
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.server.tsx +5 -0
- data/lib/generators/react_on_rails/templates/base/base/app/views/hello_world/index.html.erb.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb +4 -2
- data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +5 -2
- data/lib/generators/react_on_rails/templates/base/base/bin/dev +34 -0
- data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb.tt +14 -5
- data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +76 -7
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +1 -1
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +6 -10
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +3 -2
- data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +2 -2
- data/lib/generators/react_on_rails/templates/dev_tests/spec/system/hello_world_spec.rb +0 -2
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/actions/helloWorldActionCreators.ts +18 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +0 -6
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +4 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.tsx +24 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/constants/helloWorldConstants.ts +6 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/containers/HelloWorldContainer.ts +20 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.js +1 -1
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/reducers/helloWorldReducer.ts +22 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.client.tsx +23 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.jsx +5 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/HelloWorldApp.server.tsx +5 -0
- data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.ts +18 -0
- data/lib/react_on_rails/configuration.rb +141 -57
- data/lib/react_on_rails/controller.rb +6 -2
- data/lib/react_on_rails/dev/file_manager.rb +78 -0
- data/lib/react_on_rails/dev/pack_generator.rb +27 -0
- data/lib/react_on_rails/dev/process_manager.rb +61 -0
- data/lib/react_on_rails/dev/server_manager.rb +487 -0
- data/lib/react_on_rails/dev.rb +20 -0
- data/lib/react_on_rails/doctor.rb +1149 -0
- data/lib/react_on_rails/engine.rb +6 -0
- data/lib/react_on_rails/git_utils.rb +12 -2
- data/lib/react_on_rails/helper.rb +176 -74
- data/lib/react_on_rails/json_parse_error.rb +6 -1
- data/lib/react_on_rails/packer_utils.rb +61 -71
- data/lib/react_on_rails/packs_generator.rb +221 -19
- data/lib/react_on_rails/prerender_error.rb +4 -0
- data/lib/react_on_rails/pro/NOTICE +21 -0
- data/lib/react_on_rails/pro/helper.rb +122 -0
- data/lib/react_on_rails/pro/utils.rb +53 -0
- data/lib/react_on_rails/react_component/render_options.rb +38 -6
- data/lib/react_on_rails/server_rendering_js_code.rb +0 -1
- data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +12 -5
- data/lib/react_on_rails/system_checker.rb +659 -0
- data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +1 -1
- data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +6 -4
- data/lib/react_on_rails/test_helper.rb +2 -3
- data/lib/react_on_rails/utils.rb +139 -43
- data/lib/react_on_rails/version.rb +1 -1
- data/lib/react_on_rails/version_checker.rb +14 -20
- data/lib/react_on_rails/version_syntax_converter.rb +1 -1
- data/lib/react_on_rails.rb +1 -0
- data/lib/tasks/assets.rake +1 -1
- data/lib/tasks/doctor.rake +48 -0
- data/lib/tasks/generate_packs.rake +158 -1
- data/react_on_rails.gemspec +1 -0
- data/tsconfig.eslint.json +6 -0
- data/tsconfig.json +5 -3
- metadata +63 -14
- data/REACT-ON-RAILS-PRO-LICENSE +0 -95
- data/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb +0 -41
- data/lib/generators/react_on_rails/bin/dev +0 -30
- data/lib/generators/react_on_rails/bin/dev-static +0 -30
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static.tt +0 -9
- data/lib/generators/react_on_rails/templates/base/base/Procfile.dev.tt +0 -5
- data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/registration.js.tt +0 -8
- /data/lib/generators/react_on_rails/templates/base/base/config/webpack/{webpackConfig.js.tt → generateWebpackConfigs.js.tt} +0 -0
- /data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/startup/{HelloWorldApp.jsx → HelloWorldApp.client.jsx} +0 -0
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "rails/generators"
|
4
|
+
require "json"
|
4
5
|
require_relative "generator_helper"
|
5
6
|
require_relative "generator_messages"
|
6
7
|
|
7
8
|
module ReactOnRails
|
8
9
|
module Generators
|
10
|
+
# rubocop:disable Metrics/ClassLength
|
9
11
|
class InstallGenerator < Rails::Generators::Base
|
10
12
|
include GeneratorHelper
|
11
13
|
|
@@ -16,22 +18,40 @@ module ReactOnRails
|
|
16
18
|
class_option :redux,
|
17
19
|
type: :boolean,
|
18
20
|
default: false,
|
19
|
-
desc: "Install Redux
|
21
|
+
desc: "Install Redux package and Redux version of Hello World Example. Default: false",
|
20
22
|
aliases: "-R"
|
21
23
|
|
24
|
+
# --typescript
|
25
|
+
class_option :typescript,
|
26
|
+
type: :boolean,
|
27
|
+
default: false,
|
28
|
+
desc: "Generate TypeScript files and install TypeScript dependencies. Default: false",
|
29
|
+
aliases: "-T"
|
30
|
+
|
22
31
|
# --ignore-warnings
|
23
32
|
class_option :ignore_warnings,
|
24
33
|
type: :boolean,
|
25
34
|
default: false,
|
26
35
|
desc: "Skip warnings. Default: false"
|
27
36
|
|
37
|
+
# Removed: --skip-shakapacker-install (Shakapacker is now a required dependency)
|
38
|
+
|
28
39
|
def run_generators
|
29
40
|
if installation_prerequisites_met? || options.ignore_warnings?
|
30
41
|
invoke_generators
|
31
42
|
add_bin_scripts
|
32
|
-
|
43
|
+
# Only add the post install message if not using Redux
|
44
|
+
# Redux generator handles its own messages
|
45
|
+
add_post_install_message unless options.redux?
|
33
46
|
else
|
34
|
-
error =
|
47
|
+
error = <<~MSG.strip
|
48
|
+
🚫 React on Rails generator prerequisites not met!
|
49
|
+
|
50
|
+
Please resolve the issues listed above before continuing.
|
51
|
+
All prerequisites must be satisfied for a successful installation.
|
52
|
+
|
53
|
+
Use --ignore-warnings to bypass checks (not recommended).
|
54
|
+
MSG
|
35
55
|
GeneratorMessages.add_error(error)
|
36
56
|
end
|
37
57
|
ensure
|
@@ -47,45 +67,105 @@ module ReactOnRails
|
|
47
67
|
end
|
48
68
|
|
49
69
|
def invoke_generators
|
50
|
-
|
70
|
+
ensure_shakapacker_installed
|
71
|
+
if options.typescript?
|
72
|
+
install_typescript_dependencies
|
73
|
+
create_css_module_types
|
74
|
+
create_typescript_config
|
75
|
+
end
|
76
|
+
invoke "react_on_rails:base", [], { typescript: options.typescript? }
|
51
77
|
if options.redux?
|
52
|
-
invoke "react_on_rails:react_with_redux"
|
78
|
+
invoke "react_on_rails:react_with_redux", [], { typescript: options.typescript? }
|
53
79
|
else
|
54
|
-
invoke "react_on_rails:react_no_redux"
|
80
|
+
invoke "react_on_rails:react_no_redux", [], { typescript: options.typescript? }
|
55
81
|
end
|
82
|
+
setup_react_dependencies
|
83
|
+
end
|
56
84
|
|
57
|
-
|
85
|
+
def setup_react_dependencies
|
86
|
+
@added_dependencies_to_package_json ||= false
|
87
|
+
@ran_direct_installs ||= false
|
88
|
+
add_js_dependencies
|
89
|
+
install_js_dependencies if @added_dependencies_to_package_json && !@ran_direct_installs
|
58
90
|
end
|
59
91
|
|
60
92
|
# NOTE: other requirements for existing files such as .gitignore or application.
|
61
93
|
# js(.coffee) are not checked by this method, but instead produce warning messages
|
62
94
|
# and allow the build to continue
|
63
95
|
def installation_prerequisites_met?
|
64
|
-
!(missing_node? ||
|
96
|
+
!(missing_node? || missing_package_manager? || ReactOnRails::GitUtils.uncommitted_changes?(GeneratorMessages))
|
65
97
|
end
|
66
98
|
|
67
|
-
def
|
68
|
-
|
99
|
+
def missing_node?
|
100
|
+
node_missing = ReactOnRails::Utils.running_on_windows? ? `where node`.blank? : `which node`.blank?
|
69
101
|
|
70
|
-
|
71
|
-
|
72
|
-
|
102
|
+
if node_missing
|
103
|
+
error = <<~MSG.strip
|
104
|
+
🚫 Node.js is required but not found on your system.
|
105
|
+
|
106
|
+
Please install Node.js before continuing:
|
107
|
+
• Download from: https://nodejs.org/en/
|
108
|
+
• Recommended: Use a version manager like nvm, fnm, or volta
|
109
|
+
• Minimum required version: Node.js 18+
|
110
|
+
|
111
|
+
After installation, restart your terminal and try again.
|
112
|
+
MSG
|
113
|
+
GeneratorMessages.add_error(error)
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
|
117
|
+
# Check Node.js version if available
|
118
|
+
check_node_version
|
119
|
+
false
|
73
120
|
end
|
74
121
|
|
75
|
-
def
|
76
|
-
|
122
|
+
def check_node_version
|
123
|
+
node_version = `node --version 2>/dev/null`.strip
|
124
|
+
return if node_version.blank?
|
77
125
|
|
78
|
-
|
79
|
-
|
80
|
-
|
126
|
+
# Extract major version number (e.g., "v18.17.0" -> 18)
|
127
|
+
major_version = node_version[/v(\d+)/, 1]&.to_i
|
128
|
+
return unless major_version
|
129
|
+
|
130
|
+
return unless major_version < 18
|
131
|
+
|
132
|
+
warning = <<~MSG.strip
|
133
|
+
⚠️ Node.js version #{node_version} detected.
|
134
|
+
|
135
|
+
React on Rails recommends Node.js 18+ for best compatibility.
|
136
|
+
You may experience issues with older versions.
|
137
|
+
|
138
|
+
Consider upgrading: https://nodejs.org/en/
|
139
|
+
MSG
|
140
|
+
GeneratorMessages.add_warning(warning)
|
141
|
+
end
|
142
|
+
|
143
|
+
def ensure_shakapacker_installed
|
144
|
+
return if shakapacker_configured?
|
145
|
+
|
146
|
+
print_shakapacker_setup_banner
|
147
|
+
ensure_shakapacker_in_gemfile
|
148
|
+
install_shakapacker
|
149
|
+
finalize_shakapacker_setup
|
150
|
+
end
|
151
|
+
|
152
|
+
# Checks whether "shakapacker" is explicitly declared in this project's Gemfile.
|
153
|
+
# We only check the Gemfile text, not lockfile or dependencies, because
|
154
|
+
# shakapacker might be present as a dependency of react_on_rails but not
|
155
|
+
# properly configured for this specific Rails application.
|
156
|
+
def shakapacker_in_gemfile?
|
157
|
+
gem_name = "shakapacker"
|
158
|
+
shakapacker_in_gemfile_text?(gem_name)
|
81
159
|
end
|
82
160
|
|
83
161
|
def add_bin_scripts
|
84
|
-
|
162
|
+
# Copy bin scripts from templates
|
163
|
+
template_bin_path = "#{__dir__}/templates/base/base/bin"
|
164
|
+
directory template_bin_path, "bin"
|
85
165
|
|
86
166
|
# Make these and only these files executable
|
87
167
|
files_to_copy = []
|
88
|
-
Dir.chdir(
|
168
|
+
Dir.chdir(template_bin_path) do
|
89
169
|
files_to_copy.concat(Dir.glob("*"))
|
90
170
|
end
|
91
171
|
files_to_become_executable = files_to_copy.map { |filename| "bin/#{filename}" }
|
@@ -94,16 +174,384 @@ module ReactOnRails
|
|
94
174
|
end
|
95
175
|
|
96
176
|
def add_post_install_message
|
97
|
-
|
177
|
+
# Determine what route will be created by the generator
|
178
|
+
route = "hello_world" # This is the hardcoded route from base_generator.rb
|
179
|
+
component_name = options.redux? ? "HelloWorldApp" : "HelloWorld"
|
180
|
+
|
181
|
+
GeneratorMessages.add_info(GeneratorMessages.helpful_message_after_installation(
|
182
|
+
component_name: component_name,
|
183
|
+
route: route
|
184
|
+
))
|
185
|
+
end
|
186
|
+
|
187
|
+
def shakapacker_loaded_in_process?(gem_name)
|
188
|
+
Gem.loaded_specs.key?(gem_name)
|
189
|
+
end
|
190
|
+
|
191
|
+
def shakapacker_in_lockfile?(gem_name)
|
192
|
+
gemfile = ENV["BUNDLE_GEMFILE"] || "Gemfile"
|
193
|
+
lockfile = File.join(File.dirname(gemfile), "Gemfile.lock")
|
194
|
+
|
195
|
+
File.file?(lockfile) && File.foreach(lockfile).any? { |l| l.match?(/^\s{4}#{Regexp.escape(gem_name)}\s\(/) }
|
196
|
+
end
|
197
|
+
|
198
|
+
def shakapacker_in_bundler_specs?(gem_name)
|
199
|
+
require "bundler"
|
200
|
+
Bundler.load.specs.any? { |s| s.name == gem_name }
|
201
|
+
rescue StandardError
|
202
|
+
false
|
203
|
+
end
|
204
|
+
|
205
|
+
def shakapacker_in_gemfile_text?(gem_name)
|
206
|
+
gemfile = ENV["BUNDLE_GEMFILE"] || "Gemfile"
|
207
|
+
|
208
|
+
File.file?(gemfile) &&
|
209
|
+
File.foreach(gemfile).any? { |l| l.match?(/^\s*gem\s+['"]#{Regexp.escape(gem_name)}['"]/) }
|
210
|
+
end
|
211
|
+
|
212
|
+
def cli_exists?(command)
|
213
|
+
system("which #{command} > /dev/null 2>&1")
|
214
|
+
end
|
215
|
+
|
216
|
+
def shakapacker_binaries_exist?
|
217
|
+
File.exist?("bin/shakapacker") && File.exist?("bin/shakapacker-dev-server")
|
218
|
+
end
|
219
|
+
|
220
|
+
def shakapacker_configured?
|
221
|
+
# Check for essential shakapacker configuration files and binaries
|
222
|
+
shakapacker_binaries_exist? &&
|
223
|
+
File.exist?("config/shakapacker.yml") &&
|
224
|
+
File.exist?("config/webpack/webpack.config.js")
|
225
|
+
end
|
226
|
+
|
227
|
+
def print_shakapacker_setup_banner
|
228
|
+
puts Rainbow("\n#{'=' * 80}").cyan
|
229
|
+
puts Rainbow("🔧 SHAKAPACKER SETUP").cyan.bold
|
230
|
+
puts Rainbow("=" * 80).cyan
|
231
|
+
end
|
232
|
+
|
233
|
+
def ensure_shakapacker_in_gemfile
|
234
|
+
return if shakapacker_in_gemfile?
|
235
|
+
|
236
|
+
puts Rainbow("📝 Adding Shakapacker to Gemfile...").yellow
|
237
|
+
success = system("bundle add shakapacker --strict")
|
238
|
+
return if success
|
239
|
+
|
240
|
+
handle_shakapacker_gemfile_error
|
241
|
+
end
|
242
|
+
|
243
|
+
def install_shakapacker
|
244
|
+
puts Rainbow("⚙️ Installing Shakapacker (required for webpack integration)...").yellow
|
245
|
+
|
246
|
+
# First run bundle install to make shakapacker available
|
247
|
+
puts Rainbow("📦 Running bundle install...").yellow
|
248
|
+
bundle_success = system("bundle install")
|
249
|
+
unless bundle_success
|
250
|
+
handle_shakapacker_install_error
|
251
|
+
return
|
252
|
+
end
|
253
|
+
|
254
|
+
# Then run the shakapacker installer
|
255
|
+
success = system("bundle exec rails shakapacker:install")
|
256
|
+
return if success
|
257
|
+
|
258
|
+
handle_shakapacker_install_error
|
259
|
+
end
|
260
|
+
|
261
|
+
def finalize_shakapacker_setup
|
262
|
+
puts Rainbow("✅ Shakapacker installed successfully!").green
|
263
|
+
puts Rainbow("=" * 80).cyan
|
264
|
+
puts Rainbow("🚀 CONTINUING WITH REACT ON RAILS SETUP").cyan.bold
|
265
|
+
puts "#{Rainbow('=' * 80).cyan}\n"
|
266
|
+
|
267
|
+
# Create marker file so base generator can avoid copying shakapacker.yml
|
268
|
+
File.write(".shakapacker_just_installed", "")
|
269
|
+
end
|
270
|
+
|
271
|
+
def handle_shakapacker_gemfile_error
|
272
|
+
error = <<~MSG.strip
|
273
|
+
🚫 Failed to add Shakapacker to your Gemfile.
|
274
|
+
|
275
|
+
This could be due to:
|
276
|
+
• Bundle installation issues
|
277
|
+
• Network connectivity problems
|
278
|
+
• Gemfile permissions
|
279
|
+
|
280
|
+
Please try manually:
|
281
|
+
bundle add shakapacker --strict
|
282
|
+
|
283
|
+
Then re-run: rails generate react_on_rails:install
|
284
|
+
MSG
|
285
|
+
GeneratorMessages.add_error(error)
|
286
|
+
raise Thor::Error, error unless options.ignore_warnings?
|
98
287
|
end
|
99
288
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
289
|
+
def handle_shakapacker_install_error
|
290
|
+
error = <<~MSG.strip
|
291
|
+
🚫 Failed to install Shakapacker automatically.
|
292
|
+
|
293
|
+
This could be due to:
|
294
|
+
• Missing Node.js or npm/yarn
|
295
|
+
• Network connectivity issues
|
296
|
+
• Incomplete bundle installation
|
297
|
+
• Missing write permissions
|
298
|
+
|
299
|
+
Troubleshooting steps:
|
300
|
+
1. Ensure Node.js is installed: node --version
|
301
|
+
2. Run: bundle install
|
302
|
+
3. Try manually: bundle exec rails shakapacker:install
|
303
|
+
4. Check for error output above
|
304
|
+
5. Re-run: rails generate react_on_rails:install
|
305
|
+
|
306
|
+
Need help? Visit: https://github.com/shakacode/shakapacker/blob/main/docs/installation.md
|
307
|
+
MSG
|
308
|
+
GeneratorMessages.add_error(error)
|
309
|
+
raise Thor::Error, error unless options.ignore_warnings?
|
310
|
+
end
|
311
|
+
|
312
|
+
def missing_package_manager?
|
313
|
+
package_managers = %w[npm pnpm yarn bun]
|
314
|
+
missing = package_managers.none? { |pm| cli_exists?(pm) }
|
315
|
+
|
316
|
+
if missing
|
317
|
+
error = <<~MSG.strip
|
318
|
+
🚫 No JavaScript package manager found on your system.
|
319
|
+
|
320
|
+
React on Rails requires a JavaScript package manager to install dependencies.
|
321
|
+
Please install one of the following:
|
322
|
+
|
323
|
+
• npm: Usually comes with Node.js (https://nodejs.org/en/)
|
324
|
+
• yarn: npm install -g yarn (https://yarnpkg.com/)
|
325
|
+
• pnpm: npm install -g pnpm (https://pnpm.io/)
|
326
|
+
• bun: Install from https://bun.sh/
|
327
|
+
|
328
|
+
After installation, restart your terminal and try again.
|
329
|
+
MSG
|
330
|
+
GeneratorMessages.add_error(error)
|
331
|
+
return true
|
332
|
+
end
|
333
|
+
|
105
334
|
false
|
106
335
|
end
|
336
|
+
|
337
|
+
def install_typescript_dependencies
|
338
|
+
puts Rainbow("📝 Installing TypeScript dependencies...").yellow
|
339
|
+
|
340
|
+
# Install TypeScript and React type definitions
|
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)
|
362
|
+
end
|
363
|
+
|
364
|
+
def create_css_module_types
|
365
|
+
puts Rainbow("📝 Creating CSS module type definitions...").yellow
|
366
|
+
|
367
|
+
# Ensure the types directory exists
|
368
|
+
FileUtils.mkdir_p("app/javascript/types")
|
369
|
+
|
370
|
+
css_module_types_content = <<~TS.strip
|
371
|
+
// TypeScript definitions for CSS modules
|
372
|
+
declare module "*.module.css" {
|
373
|
+
const classes: { [key: string]: string };
|
374
|
+
export default classes;
|
375
|
+
}
|
376
|
+
|
377
|
+
declare module "*.module.scss" {
|
378
|
+
const classes: { [key: string]: string };
|
379
|
+
export default classes;
|
380
|
+
}
|
381
|
+
|
382
|
+
declare module "*.module.sass" {
|
383
|
+
const classes: { [key: string]: string };
|
384
|
+
export default classes;
|
385
|
+
}
|
386
|
+
TS
|
387
|
+
|
388
|
+
File.write("app/javascript/types/css-modules.d.ts", css_module_types_content)
|
389
|
+
puts Rainbow("✅ Created CSS module type definitions").green
|
390
|
+
end
|
391
|
+
|
392
|
+
def create_typescript_config
|
393
|
+
if File.exist?("tsconfig.json")
|
394
|
+
puts Rainbow("⚠️ tsconfig.json already exists, skipping creation").yellow
|
395
|
+
return
|
396
|
+
end
|
397
|
+
|
398
|
+
tsconfig_content = {
|
399
|
+
"compilerOptions" => {
|
400
|
+
"target" => "es2018",
|
401
|
+
"allowJs" => true,
|
402
|
+
"skipLibCheck" => true,
|
403
|
+
"strict" => true,
|
404
|
+
"noUncheckedIndexedAccess" => true,
|
405
|
+
"forceConsistentCasingInFileNames" => true,
|
406
|
+
"noFallthroughCasesInSwitch" => true,
|
407
|
+
"module" => "esnext",
|
408
|
+
"moduleResolution" => "bundler",
|
409
|
+
"resolveJsonModule" => true,
|
410
|
+
"isolatedModules" => true,
|
411
|
+
"noEmit" => true,
|
412
|
+
"jsx" => "react-jsx"
|
413
|
+
},
|
414
|
+
"include" => [
|
415
|
+
"app/javascript/**/*"
|
416
|
+
]
|
417
|
+
}
|
418
|
+
|
419
|
+
File.write("tsconfig.json", JSON.pretty_generate(tsconfig_content))
|
420
|
+
puts Rainbow("✅ Created tsconfig.json").green
|
421
|
+
end
|
422
|
+
|
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
|
+
# Removed: Shakapacker auto-installation logic (now explicit dependency)
|
552
|
+
|
553
|
+
# Removed: Shakapacker 8+ is now required as explicit dependency
|
554
|
+
# rubocop:enable Metrics/ClassLength
|
107
555
|
end
|
108
556
|
end
|
109
557
|
end
|
@@ -7,24 +7,37 @@ module ReactOnRails
|
|
7
7
|
module Generators
|
8
8
|
class ReactNoReduxGenerator < Rails::Generators::Base
|
9
9
|
include GeneratorHelper
|
10
|
+
|
10
11
|
Rails::Generators.hide_namespace(namespace)
|
11
12
|
source_root(File.expand_path("templates", __dir__))
|
12
13
|
|
14
|
+
class_option :typescript,
|
15
|
+
type: :boolean,
|
16
|
+
default: false,
|
17
|
+
desc: "Generate TypeScript files"
|
18
|
+
|
13
19
|
def copy_base_files
|
14
20
|
base_js_path = "base/base"
|
15
|
-
|
16
|
-
|
21
|
+
|
22
|
+
# Determine which component files to copy based on TypeScript option
|
23
|
+
component_files = [
|
24
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.client.#{component_extension(options)}",
|
25
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.server.#{component_extension(options)}",
|
26
|
+
"app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css"
|
27
|
+
]
|
28
|
+
|
29
|
+
component_files.each do |file|
|
30
|
+
copy_file("#{base_js_path}/#{file}", file)
|
31
|
+
end
|
17
32
|
end
|
18
33
|
|
19
34
|
def create_appropriate_templates
|
20
35
|
base_path = "base/base"
|
21
36
|
config = {
|
22
|
-
component_name: "HelloWorld"
|
23
|
-
app_relative_path: "../bundles/HelloWorld/components/HelloWorld"
|
37
|
+
component_name: "HelloWorld"
|
24
38
|
}
|
25
39
|
|
26
|
-
template
|
27
|
-
"app/javascript/packs/hello-world-bundle.js", config)
|
40
|
+
# Only create the view template - no manual bundle needed for auto-bundling
|
28
41
|
template("#{base_path}/app/views/hello_world/index.html.erb.tt",
|
29
42
|
"app/views/hello_world/index.html.erb", config)
|
30
43
|
end
|