Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 48 additions & 43 deletions lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,42 +95,6 @@ def add_base_gems_to_gemfile
run "bundle"
end

def add_js_dependencies
add_react_on_rails_package
add_react_dependencies
add_css_dependencies
add_dev_dependencies
end

def install_js_dependencies
# Detect which package manager to use
success = if File.exist?(File.join(destination_root, "yarn.lock"))
system("yarn", "install")
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
system("pnpm", "install")
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
File.exist?(File.join(destination_root, "package.json"))
# Use npm for package-lock.json or as default fallback
system("npm", "install")
else
true # No package manager detected, skip
end

unless success
GeneratorMessages.add_warning(<<~MSG.strip)
⚠️ JavaScript dependencies installation failed.

This could be due to network issues or missing package manager.
You can install dependencies manually later by running:
• npm install (if using npm)
• yarn install (if using yarn)
• pnpm install (if using pnpm)
MSG
end

success
end

def update_gitignore_for_auto_registration
gitignore_path = File.join(destination_root, ".gitignore")
return unless File.exist?(gitignore_path)
Expand All @@ -157,6 +121,28 @@ def append_to_spec_rails_helper
end
end

CONFIGURE_RSPEC_TO_COMPILE_ASSETS = <<-STR.strip_heredoc
RSpec.configure do |config|
# Ensure that if we are running js tests, we are using latest webpack assets
# This will use the defaults of :js and :server_rendering meta tags
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
end
STR

Comment on lines +124 to +131
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix RSpec injection: current gsub replaces only the opener with a full block (breaks rails_helper).

Replacing just "RSpec.configure do |config|" with an entire block will leave the original block body and closing end in place, yielding invalid Ruby and/or duplicated config. Inject inside the existing block instead.

Apply this diff to change the constant to a snippet (no open/close), then insert it inside the existing block (method change shown below):

-      CONFIGURE_RSPEC_TO_COMPILE_ASSETS = <<-STR.strip_heredoc
-        RSpec.configure do |config|
-          # Ensure that if we are running js tests, we are using latest webpack assets
-          # This will use the defaults of :js and :server_rendering meta tags
-          ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
-        end
-      STR
+      CONFIGURE_RSPEC_TO_COMPILE_ASSETS = <<~STR.strip
+        # Ensure that if we are running js tests, we are using latest webpack assets
+        # This will use the defaults of :js and :server_rendering meta tags
+        ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
+      STR

Update the injector method (outside this hunk) to insert rather than replace:

def add_configure_rspec_to_compile_assets(helper_file)
  snippet = "\n#{CONFIGURE_RSPEC_TO_COMPILE_ASSETS}\n"
  content = File.read(helper_file)
  return if content.include?("ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)")
  insert_into_file(helper_file, snippet, after: /RSpec\.configure do \|config\|\n/)
end
🤖 Prompt for AI Agents
lib/generators/react_on_rails/base_generator.rb lines 124-131: the constant
currently contains a full RSpec.configure open/close block which, when gsubbed
in, duplicates the existing block body and closing `end`; change the constant to
contain only the inner snippet (no opening `RSpec.configure` or `end`) and
update the injector to insert that snippet inside the existing RSpec.configure
block rather than replacing it: read the helper file, early-return if the
configure call is already present, then call insert_into_file with the snippet
after the existing `RSpec.configure do |config|` line so the helper keeps a
single open/close and the snippet is added into the block.

private

def setup_js_dependencies
add_js_dependencies
install_js_dependencies
end

def add_js_dependencies
add_react_on_rails_package
add_react_dependencies
add_css_dependencies
add_dev_dependencies
end

def add_react_on_rails_package
major_minor_patch_only = /\A\d+\.\d+\.\d+\z/

Expand Down Expand Up @@ -219,15 +205,34 @@ def add_dev_dependencies
handle_npm_failure("development dependencies", dev_deps, dev: true) unless success
end

CONFIGURE_RSPEC_TO_COMPILE_ASSETS = <<-STR.strip_heredoc
RSpec.configure do |config|
# Ensure that if we are running js tests, we are using latest webpack assets
# This will use the defaults of :js and :server_rendering meta tags
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
def install_js_dependencies
# Detect which package manager to use
success = if File.exist?(File.join(destination_root, "yarn.lock"))
system("yarn", "install")
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
system("pnpm", "install")
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
File.exist?(File.join(destination_root, "package.json"))
# Use npm for package-lock.json or as default fallback
system("npm", "install")
else
true # No package manager detected, skip
end

unless success
GeneratorMessages.add_warning(<<~MSG.strip)
⚠️ JavaScript dependencies installation failed.

This could be due to network issues or missing package manager.
You can install dependencies manually later by running:
• npm install (if using npm)
• yarn install (if using yarn)
• pnpm install (if using pnpm)
MSG
end
STR

private
success
end

def handle_npm_failure(dependency_type, packages, dev: false)
install_command = dev ? "npm install --save-dev" : "npm install"
Expand Down
140 changes: 139 additions & 1 deletion lib/generators/react_on_rails/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ def run_generators
if installation_prerequisites_met? || options.ignore_warnings?
invoke_generators
add_bin_scripts
add_post_install_message
# Only add the post install message if not using Redux
# Redux generator handles its own messages
add_post_install_message unless options.redux?
else
error = <<~MSG.strip
🚫 React on Rails generator prerequisites not met!
Expand Down Expand Up @@ -77,6 +79,14 @@ def invoke_generators
else
invoke "react_on_rails:react_no_redux", [], { typescript: options.typescript? }
end
setup_react_dependencies
end

def setup_react_dependencies
@added_dependencies_to_package_json ||= false
@ran_direct_installs ||= false
add_js_dependencies
install_js_dependencies if @added_dependencies_to_package_json && !@ran_direct_installs
end

# NOTE: other requirements for existing files such as .gitignore or application.
Expand Down Expand Up @@ -410,6 +420,134 @@ def create_typescript_config
puts Rainbow("✅ Created tsconfig.json").green
end

def add_js_dependencies
add_react_on_rails_package
add_react_dependencies
add_css_dependencies
add_dev_dependencies
end

def add_react_on_rails_package
major_minor_patch_only = /\A\d+\.\d+\.\d+\z/

# Try to use package_json gem first, fall back to direct npm commands
react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only)
["react-on-rails@#{ReactOnRails::VERSION}"]
else
puts "Adding the latest react-on-rails NPM module. " \
"Double check this is correct in package.json"
["react-on-rails"]
end

puts "Installing React on Rails package..."
if add_npm_dependencies(react_on_rails_pkg)
@added_dependencies_to_package_json = true
return
end

puts "Using direct npm commands as fallback"
success = system("npm", "install", *react_on_rails_pkg)
@ran_direct_installs = true if success
handle_npm_failure("react-on-rails package", react_on_rails_pkg) unless success
end

def add_react_dependencies
puts "Installing React dependencies..."
react_deps = %w[
react
react-dom
@babel/preset-react
prop-types
babel-plugin-transform-react-remove-prop-types
babel-plugin-macros
]
if add_npm_dependencies(react_deps)
@added_dependencies_to_package_json = true
return
end

success = system("npm", "install", *react_deps)
@ran_direct_installs = true if success
handle_npm_failure("React dependencies", react_deps) unless success
end

def add_css_dependencies
puts "Installing CSS handling dependencies..."
css_deps = %w[
css-loader
css-minimizer-webpack-plugin
mini-css-extract-plugin
style-loader
]
if add_npm_dependencies(css_deps)
@added_dependencies_to_package_json = true
return
end

success = system("npm", "install", *css_deps)
@ran_direct_installs = true if success
handle_npm_failure("CSS dependencies", css_deps) unless success
end

def add_dev_dependencies
puts "Installing development dependencies..."
dev_deps = %w[
@pmmmwh/react-refresh-webpack-plugin
react-refresh
]
if add_npm_dependencies(dev_deps, dev: true)
@added_dependencies_to_package_json = true
return
end

success = system("npm", "install", "--save-dev", *dev_deps)
@ran_direct_installs = true if success
handle_npm_failure("development dependencies", dev_deps, dev: true) unless success
end

def install_js_dependencies
# Detect which package manager to use
success = if File.exist?(File.join(destination_root, "yarn.lock"))
system("yarn", "install")
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
system("pnpm", "install")
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
File.exist?(File.join(destination_root, "package.json"))
# Use npm for package-lock.json or as default fallback
system("npm", "install")
else
true # No package manager detected, skip
end

unless success
GeneratorMessages.add_warning(<<~MSG.strip)
⚠️ JavaScript dependencies installation failed.

This could be due to network issues or missing package manager.
You can install dependencies manually later by running:
• npm install (if using npm)
• yarn install (if using yarn)
• pnpm install (if using pnpm)
MSG
end

success
end
Comment on lines +508 to +535
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Support bun and avoid invoking missing CLIs; otherwise installs may fail silently.

You accept bun as a valid package manager earlier but don’t handle it here, and you might call yarn/pnpm when those CLIs aren’t present. Add bun detection and guard each branch by CLI availability.

Apply:

@@
-        success = if File.exist?(File.join(destination_root, "yarn.lock"))
-                    system("yarn", "install")
-                  elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
-                    system("pnpm", "install")
-                  elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
-                        File.exist?(File.join(destination_root, "package.json"))
-                    # Use npm for package-lock.json or as default fallback
-                    system("npm", "install")
-                  else
-                    true # No package manager detected, skip
-                  end
+        success = if File.exist?(File.join(destination_root, "yarn.lock")) && cli_exists?("yarn")
+                    system("yarn", "install")
+                  elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml")) && cli_exists?("pnpm")
+                    system("pnpm", "install")
+                  elsif File.exist?(File.join(destination_root, "bun.lockb")) && cli_exists?("bun")
+                    system("bun", "install")
+                  elsif (File.exist?(File.join(destination_root, "package-lock.json")) ||
+                         File.exist?(File.join(destination_root, "package.json")))
+                    # Use npm for package-lock.json or as default fallback
+                    system("npm", "install")
+                  else
+                    true # No supported package manager detected, skip
+                  end
@@
-            • pnpm install (if using pnpm)
+            • pnpm install (if using pnpm)
+            • bun install (if using bun)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
lib/generators/react_on_rails/install_generator.rb around lines 508 to 535: the
current install_js_dependencies block omits bun detection and may call package
manager CLIs that are not installed; update the logic to first detect bun (e.g.,
check for bun lockfile like bun.lockb or presence of the bun binary) and include
a branch that runs bun install, and for each package manager branch (bun, yarn,
pnpm, npm) verify the CLI is available before invoking it (use a portable check
such as `system("command -v <cli> > /dev/null 2>&1")` or equivalent on Windows)
and if the CLI is missing either skip to the next available manager or set
success = false and add a clear GeneratorMessages.add_warning explaining the
missing CLI and how to install or run installs manually; ensure the method still
returns a boolean success value and does not silently attempt to run a missing
binary.


def handle_npm_failure(dependency_type, packages, dev: false)
install_command = dev ? "npm install --save-dev" : "npm install"
GeneratorMessages.add_warning(<<~MSG.strip)
⚠️ Failed to install #{dependency_type}.

The following packages could not be installed automatically:
#{packages.map { |pkg| " • #{pkg}" }.join("\n")}

This could be due to network issues or missing package manager.
You can install them manually later by running:
#{install_command} #{packages.join(' ')}
MSG
end

# Removed: Shakapacker auto-installation logic (now explicit dependency)

# Removed: Shakapacker 8+ is now required as explicit dependency
Expand Down
15 changes: 7 additions & 8 deletions lib/generators/react_on_rails/react_with_redux_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ def add_redux_npm_dependencies
install_packages_with_fallback(regular_packages, dev: false, package_manager: package_manager)
end

def add_redux_specific_messages
# Append Redux-specific post-install instructions
GeneratorMessages.add_info(
GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp", route: "hello_world")
)
end

private

def install_packages_with_fallback(packages, dev:, package_manager:)
Expand Down Expand Up @@ -132,14 +139,6 @@ def dev_flag_for(package_manager)
when "yarn", "bun" then "--dev"
end
end

def add_redux_specific_messages
# Override the generic messages with Redux-specific instructions
GeneratorMessages.output.clear
GeneratorMessages.add_info(
GeneratorMessages.helpful_message_after_installation(component_name: "HelloWorldApp", route: "hello_world")
)
end
end
end
end
Loading