Skip to content

Commit be43617

Browse files
refactor: Simplify JS dependency manager by removing redundant package_json checks
Since react_on_rails requires shakapacker (>= 6.0) and shakapacker includes package_json as a dependency, the package_json gem is always available. Changes: - Remove package_json_available? method and all fallback logic - Simplify version detection to always use exact gem version - Remove detect_and_run_package_manager_install (package_json handles this) - Remove @ran_direct_installs tracking (no longer needed) - Update tests to remove fallback scenarios - Update documentation to reflect guaranteed package_json availability This eliminates ~60 lines of redundant code and makes the module more maintainable and less error-prone. Co-authored-by: Justin Gordon <justin808@users.noreply.github.com>
1 parent 067a8fe commit be43617

File tree

2 files changed

+25
-124
lines changed

2 files changed

+25
-124
lines changed

lib/generators/react_on_rails/js_dependency_manager.rb

Lines changed: 23 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@ module Generators
88
# This module provides common functionality for adding and installing
99
# JS dependencies to avoid code duplication between generators.
1010
#
11+
# Since react_on_rails requires shakapacker, and shakapacker includes
12+
# package_json as a dependency, the package_json gem is always available.
13+
#
1114
# == Required Instance Variables
1215
# Including classes must support these instance variables:
1316
# - @added_dependencies_to_package_json: Boolean tracking if package_json gem was used
14-
# - @ran_direct_installs: Boolean tracking if direct npm/yarn commands were run
1517
#
1618
# == Required Methods
1719
# Including classes must include GeneratorHelper module which provides:
1820
# - add_npm_dependencies(packages, dev: false): Add packages via package_json gem
19-
# - package_json: Access to PackageJson instance
21+
# - package_json: Access to PackageJson instance (always available via shakapacker)
2022
# - destination_root: Generator destination directory
21-
# - system(*args): Execute system commands
2223
#
2324
# == Usage
2425
# Include this module in generator classes and call setup_js_dependencies
25-
# to handle all JS dependency installation with automatic fallbacks.
26+
# to handle all JS dependency installation via package_json gem.
2627
module JsDependencyManager
2728
# Core React dependencies required for React on Rails
2829
REACT_DEPENDENCIES = %w[
@@ -52,7 +53,6 @@ module JsDependencyManager
5253

5354
def setup_js_dependencies
5455
@added_dependencies_to_package_json ||= false
55-
@ran_direct_installs ||= false
5656
add_js_dependencies
5757
install_js_dependencies
5858
end
@@ -65,25 +65,15 @@ def add_js_dependencies
6565
end
6666

6767
def add_react_on_rails_package
68-
major_minor_patch_only = /\A\d+\.\d+\.\d+\z/
69-
70-
react_on_rails_pkg = if ReactOnRails::VERSION.match?(major_minor_patch_only)
71-
"react-on-rails@#{ReactOnRails::VERSION}"
72-
else
73-
puts "Adding the latest react-on-rails NPM module. " \
74-
"Double check this is correct in package.json"
75-
"react-on-rails"
76-
end
68+
# Always use exact version match between gem and npm package
69+
react_on_rails_pkg = "react-on-rails@#{ReactOnRails::VERSION}"
7770

7871
puts "Installing React on Rails package..."
7972
if add_js_dependency(react_on_rails_pkg)
8073
@added_dependencies_to_package_json = true
8174
else
82-
# Fallback to direct npm install
83-
puts "Using direct npm commands as fallback"
84-
success = system("npm", "install", react_on_rails_pkg)
85-
@ran_direct_installs = true if success
86-
handle_npm_failure("react-on-rails package", [react_on_rails_pkg]) unless success
75+
# This should not happen since package_json is always available via shakapacker
76+
raise "Failed to add react-on-rails package via package_json gem"
8777
end
8878
end
8979

@@ -93,10 +83,8 @@ def add_react_dependencies
9383
if add_js_dependencies_batch(REACT_DEPENDENCIES)
9484
@added_dependencies_to_package_json = true
9585
else
96-
# Fallback to direct npm install
97-
success = system("npm", "install", *REACT_DEPENDENCIES)
98-
@ran_direct_installs = true if success
99-
handle_npm_failure("React dependencies", REACT_DEPENDENCIES) unless success
86+
# This should not happen since package_json is always available via shakapacker
87+
raise "Failed to add React dependencies via package_json gem"
10088
end
10189
end
10290

@@ -106,10 +94,8 @@ def add_css_dependencies
10694
if add_js_dependencies_batch(CSS_DEPENDENCIES)
10795
@added_dependencies_to_package_json = true
10896
else
109-
# Fallback to direct npm install
110-
success = system("npm", "install", *CSS_DEPENDENCIES)
111-
@ran_direct_installs = true if success
112-
handle_npm_failure("CSS dependencies", CSS_DEPENDENCIES) unless success
97+
# This should not happen since package_json is always available via shakapacker
98+
raise "Failed to add CSS dependencies via package_json gem"
11399
end
114100
end
115101

@@ -119,17 +105,13 @@ def add_dev_dependencies
119105
if add_js_dependencies_batch(DEV_DEPENDENCIES, dev: true)
120106
@added_dependencies_to_package_json = true
121107
else
122-
# Fallback to direct npm install
123-
success = system("npm", "install", "--save-dev", *DEV_DEPENDENCIES)
124-
@ran_direct_installs = true if success
125-
handle_npm_failure("development dependencies", DEV_DEPENDENCIES, dev: true) unless success
108+
# This should not happen since package_json is always available via shakapacker
109+
raise "Failed to add development dependencies via package_json gem"
126110
end
127111
end
128112

129113
# Add a single dependency using package_json gem
130114
def add_js_dependency(package, dev: false)
131-
return false unless package_json_available?
132-
133115
pj = package_json
134116
return false unless pj
135117

@@ -150,83 +132,30 @@ def add_js_dependency(package, dev: false)
150132

151133
# Add multiple dependencies at once using package_json gem
152134
def add_js_dependencies_batch(packages, dev: false)
153-
return false unless package_json_available?
154-
155135
# Use the add_npm_dependencies helper from GeneratorHelper
156136
add_npm_dependencies(packages, dev: dev)
157137
end
158138

159-
# Check if package_json gem is available and loaded
160-
def package_json_available?
161-
# Check if Shakapacker or package_json gem is available
162-
return true if defined?(PackageJson)
163-
139+
def install_js_dependencies
140+
# Use package_json gem's install method (always available via shakapacker)
164141
begin
165-
require "package_json"
142+
package_json.manager.install
166143
true
167-
rescue LoadError
168-
false
169-
end
170-
end
171-
172-
def install_js_dependencies
173-
# First try to use package_json gem's install method if available
174-
if package_json_available? && package_json
175-
begin
176-
package_json.manager.install
177-
return true
178-
rescue StandardError => e
179-
puts "Warning: package_json gem install failed: #{e.message}"
180-
# Fall through to manual detection
181-
end
182-
end
183-
184-
# Fallback to detecting package manager and running install
185-
success = detect_and_run_package_manager_install
186-
187-
unless success
144+
rescue StandardError => e
188145
GeneratorMessages.add_warning(<<~MSG.strip)
189-
⚠️ JavaScript dependencies installation failed.
146+
⚠️ JavaScript dependencies installation failed: #{e.message}
190147
191-
This could be due to network issues or missing package manager.
148+
This could be due to network issues or package manager problems.
192149
You can install dependencies manually later by running:
193150
• npm install (if using npm)
194151
• yarn install (if using yarn)
195152
• pnpm install (if using pnpm)
196153
MSG
197-
end
198-
199-
success
200-
end
201-
202-
def detect_and_run_package_manager_install
203-
# Detect which package manager to use based on lock files
204-
if File.exist?(File.join(destination_root, "yarn.lock"))
205-
system("yarn", "install")
206-
elsif File.exist?(File.join(destination_root, "pnpm-lock.yaml"))
207-
system("pnpm", "install")
208-
elsif File.exist?(File.join(destination_root, "package-lock.json")) ||
209-
File.exist?(File.join(destination_root, "package.json"))
210-
# Use npm for package-lock.json or as default fallback
211-
system("npm", "install")
212-
else
213-
true # No package manager detected, skip
154+
false
214155
end
215156
end
216157

217-
def handle_npm_failure(dependency_type, packages, dev: false)
218-
install_command = dev ? "npm install --save-dev" : "npm install"
219-
GeneratorMessages.add_warning(<<~MSG.strip)
220-
⚠️ Failed to install #{dependency_type}.
221-
222-
The following packages could not be installed automatically:
223-
#{packages.map { |pkg| " • #{pkg}" }.join("\n")}
224-
225-
This could be due to network issues or missing package manager.
226-
You can install them manually later by running:
227-
#{install_command} #{packages.join(' ')}
228-
MSG
229-
end
158+
# No longer needed since package_json gem handles package manager detection
230159
end
231160
end
232161
end

spec/react_on_rails/generators/message_deduplication_spec.rb

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,11 @@
7979

8080
# Initialize instance variables
8181
install_generator.instance_variable_set(:@added_dependencies_to_package_json, false)
82-
install_generator.instance_variable_set(:@ran_direct_installs, false)
8382
end
8483

85-
context "when using package_json gem" do
84+
context "when using package_json gem (always available via shakapacker)" do
8685
before do
87-
# Simply mock that the individual package_json gem methods succeed
86+
# Mock that the package_json gem methods succeed
8887
allow(install_generator).to receive_messages(add_js_dependency: true, add_js_dependencies_batch: true,
8988
install_js_dependencies: true)
9089
end
@@ -98,33 +97,6 @@
9897

9998
# Verify state was set correctly to indicate package_json was used
10099
expect(install_generator.instance_variable_get(:@added_dependencies_to_package_json)).to be true
101-
expect(install_generator.instance_variable_get(:@ran_direct_installs)).to be false
102-
end
103-
end
104-
105-
context "when falling back to direct npm commands" do
106-
before do
107-
allow(install_generator).to receive_messages(add_npm_dependencies: false, package_json_available?: false,
108-
package_json: nil)
109-
# Mock File.exist? to not detect any lock files, forcing npm as default
110-
allow(File).to receive(:exist?).and_call_original
111-
allow(File).to receive(:exist?).with(File.join(install_generator.destination_root,
112-
"yarn.lock")).and_return(false)
113-
allow(File).to receive(:exist?).with(File.join(install_generator.destination_root,
114-
"pnpm-lock.yaml")).and_return(false)
115-
allow(File).to receive(:exist?).with(File.join(install_generator.destination_root,
116-
"package-lock.json")).and_return(false)
117-
allow(File).to receive(:exist?).with(File.join(install_generator.destination_root,
118-
"package.json")).and_return(true)
119-
end
120-
121-
it "runs individual installs plus final install" do
122-
# Expect individual package installs plus one final bulk install
123-
expect(install_generator).to receive(:system).with("npm", "install", anything).at_least(:once).and_return(true)
124-
expect(install_generator).to receive(:system).with("npm", "install").once.and_return(true)
125-
126-
# Run the dependency setup
127-
install_generator.send(:setup_js_dependencies)
128100
end
129101
end
130102
end

0 commit comments

Comments
 (0)