Skip to content

Commit 3549506

Browse files
justin808claude
andcommitted
Implement seamless Shakapacker integration for React on Rails v15+
Major architectural changes: - Make Shakapacker explicit dependency in gemspec (~> 8.0) - Remove webpacker compatibility and auto-installation complexity - Add automatic Shakapacker setup with clear visual separation - Move CSS modules to be siblings of React components Key improvements: - Automatic `bundle add shakapacker` when not in Gemfile - Auto-run `./bin/rails shakapacker:install` when binstubs missing - Enhanced post-install messaging with Shakapacker status - Updated shakapacker.yml template to match current version - Simplified test suite (Shakapacker-only, removed webpacker tests) - Improved file organization (CSS modules as component siblings) User experience: - One-command setup: `rails generate react_on_rails:install` - Clear visual feedback during Shakapacker installation - Informative post-install summary with setup status - No manual intervention required for standard setups 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent cb886c5 commit 3549506

File tree

11 files changed

+104
-205
lines changed

11 files changed

+104
-205
lines changed

Gemfile.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ PATH
77
execjs (~> 2.5)
88
rails (>= 5.2)
99
rainbow (~> 3.0)
10+
shakapacker (~> 8.0)
1011

1112
GEM
1213
remote: https://rubygems.org/

lib/generators/USAGE

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ Description:
22

33
The react_on_rails:install generator integrates a React frontend, including SSR, with Rails.
44

5-
If shakapacker is not installed, it will be installed.
6-
75
* Redux (Optional)
86

97
Passing the --redux generator option causes the generated Hello World example

lib/generators/react_on_rails/base_generator.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def copy_js_bundle_files
5151

5252
# Only copy HelloWorld.module.css for non-Redux components
5353
# Redux components handle their own CSS files
54-
base_files << "app/javascript/src/HelloWorld/HelloWorld.module.css" unless options.redux?
54+
base_files << "app/javascript/src/HelloWorld/ror_components/HelloWorld.module.css" unless options.redux?
5555

5656
base_files.each { |file| copy_file("#{base_path}#{file}", file) }
5757
end
@@ -77,6 +77,14 @@ def copy_webpack_config
7777
end
7878

7979
def copy_packer_config
80+
# Skip copying if Shakapacker was just installed (to avoid conflicts)
81+
# Check for a temporary marker file that indicates fresh Shakapacker install
82+
if File.exist?(".shakapacker_just_installed")
83+
puts "Skipping Shakapacker config copy (already installed by Shakapacker installer)"
84+
File.delete(".shakapacker_just_installed") # Clean up marker
85+
return
86+
end
87+
8088
puts "Adding Shakapacker #{ReactOnRails::PackerUtils.shakapacker_version} config"
8189
base_path = "base/base/"
8290
config = "config/shakapacker.yml"

lib/generators/react_on_rails/generator_messages.rb

Lines changed: 23 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ def clear
4040

4141
def helpful_message_after_installation(component_name: "HelloWorld")
4242
process_manager_section = build_process_manager_section
43-
shakapacker_section = build_shakapacker_section
44-
webpacker_warning = build_webpacker_warning
4543
testing_section = build_testing_section
4644
package_manager = detect_package_manager
45+
shakapacker_status = build_shakapacker_status_section
4746

4847
<<~MSG
4948
@@ -61,10 +60,10 @@ def helpful_message_after_installation(component_name: "HelloWorld")
6160
./bin/dev static # Static bundles (no HMR, faster initial load)
6261
./bin/dev prod # Production-like mode for testing
6362
./bin/dev help # See all available options
64-
#{process_manager_section}#{shakapacker_section}
63+
#{process_manager_section}
6564
6665
3. Visit: http://localhost:3000/hello_world
67-
66+
#{shakapacker_status}
6867
✨ KEY FEATURES:
6968
─────────────────────────────────────────────────────────────────────────
7069
• Auto-registration enabled - Your layout only needs:
@@ -79,7 +78,7 @@ def helpful_message_after_installation(component_name: "HelloWorld")
7978
• Documentation: https://www.shakacode.com/react-on-rails/docs/
8079
• Webpack customization: https://github.com/shakacode/shakapacker#webpack-configuration
8180
82-
💡 TIP: Run 'bin/dev help' for development server options#{testing_section}#{webpacker_warning}
81+
💡 TIP: Run 'bin/dev help' for development server options#{testing_section}
8382
MSG
8483
end
8584

@@ -104,32 +103,6 @@ def build_process_manager_section
104103
end
105104
end
106105

107-
def build_shakapacker_section
108-
if shakapacker_installed?
109-
"\n 📦 Shakapacker integration: #{Rainbow('Ready ✓').green}"
110-
else
111-
"\n 📦 Shakapacker will be installed automatically when needed"
112-
end
113-
end
114-
115-
def build_webpacker_warning
116-
return "" unless webpacker_installed?
117-
118-
<<~WARNING
119-
120-
121-
#{Rainbow('⚠️ WEBPACKER DETECTED', :red, :bold)}
122-
─────────────────────────────────────────────────────────────────────────
123-
Webpacker is deprecated. This generated code is designed for Shakapacker.
124-
125-
#{Rainbow('Recommended action:').yellow} Install Shakapacker:
126-
#{Rainbow('bundle add shakapacker && bundle exec rails shakapacker:install').cyan}
127-
128-
#{Rainbow('Need help upgrading?').yellow} Contact: #{Rainbow('[email protected]').cyan}
129-
(Maintainers of React on Rails)
130-
WARNING
131-
end
132-
133106
def build_testing_section
134107
# Check if we have any spec files to determine if testing setup is needed
135108
has_spec_files = File.exist?("spec/rails_helper.rb") || File.exist?("spec/spec_helper.rb")
@@ -139,12 +112,12 @@ def build_testing_section
139112
<<~TESTING
140113
141114
142-
🧪 TESTING SETUP (Optional):
143-
─────────────────────────────────────────────────────────────────────────
144-
For JavaScript testing with asset compilation, add this to your RSpec config:
115+
🧪 TESTING SETUP (Optional):
116+
─────────────────────────────────────────────────────────────────────────
117+
For JavaScript testing with asset compilation, add this to your RSpec config:
145118
146-
# In spec/rails_helper.rb or spec/spec_helper.rb:
147-
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
119+
# In spec/rails_helper.rb or spec/spec_helper.rb:
120+
ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)
148121
TESTING
149122
end
150123

@@ -156,25 +129,21 @@ def detect_process_manager
156129
end
157130
end
158131

159-
def shakapacker_installed?
160-
# Check if shakapacker is in the Gemfile (more reliable for just-installed gems)
161-
if File.exist?("Gemfile")
162-
gemfile_content = File.read("Gemfile")
163-
return true if gemfile_content.match?(/gem\s+['"]shakapacker['"]/)
164-
end
132+
def build_shakapacker_status_section
133+
if File.exist?(".shakapacker_just_installed")
134+
<<~SHAKAPACKER
165135
166-
# Fallback to gem specification check
167-
Gem::Specification.find_by_name("shakapacker")
168-
true
169-
rescue Gem::LoadError
170-
false
171-
end
172-
173-
def webpacker_installed?
174-
Gem::Specification.find_by_name("webpacker")
175-
true
176-
rescue Gem::LoadError
177-
false
136+
📦 SHAKAPACKER SETUP:
137+
─────────────────────────────────────────────────────────────────────────
138+
#{Rainbow('✓ Added to Gemfile automatically').green}
139+
#{Rainbow('✓ Installer ran successfully').green}
140+
#{Rainbow('✓ Webpack integration configured').green}
141+
SHAKAPACKER
142+
elsif File.exist?("bin/shakapacker") && File.exist?("bin/shakapacker-dev-server")
143+
"\n 📦 #{Rainbow('Shakapacker already configured ✓').green}"
144+
else
145+
"\n 📦 #{Rainbow('Shakapacker setup may be incomplete').yellow}"
146+
end
178147
end
179148

180149
def detect_package_manager

lib/generators/react_on_rails/install_generator.rb

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,10 @@ class InstallGenerator < Rails::Generators::Base
2525
default: false,
2626
desc: "Skip warnings. Default: false"
2727

28-
# --skip-shakapacker-install
29-
class_option :skip_shakapacker_install,
30-
type: :boolean,
31-
default: false,
32-
desc: "Skip automatic Shakapacker installation. Default: false"
28+
# Removed: --skip-shakapacker-install (Shakapacker is now a required dependency)
3329

3430
def run_generators
3531
if installation_prerequisites_met? || options.ignore_warnings?
36-
return if !options.skip_shakapacker_install? && !ensure_shakapacker_installed
37-
3832
invoke_generators
3933
add_bin_scripts
4034
add_post_install_message
@@ -55,14 +49,13 @@ def print_generator_messages
5549
end
5650

5751
def invoke_generators
52+
ensure_shakapacker_installed
5853
invoke "react_on_rails:base"
5954
if options.redux?
6055
invoke "react_on_rails:react_with_redux"
6156
else
6257
invoke "react_on_rails:react_no_redux"
6358
end
64-
65-
invoke "react_on_rails:adapt_for_older_shakapacker" unless using_shakapacker_7_or_above?
6659
end
6760

6861
# NOTE: other requirements for existing files such as .gitignore or application.
@@ -80,6 +73,58 @@ def missing_node?
8073
true
8174
end
8275

76+
def ensure_shakapacker_installed
77+
return if File.exist?("bin/shakapacker") && File.exist?("bin/shakapacker-dev-server")
78+
79+
puts Rainbow("\n" + "=" * 80).cyan
80+
puts Rainbow("🔧 SHAKAPACKER SETUP").cyan.bold
81+
puts Rainbow("=" * 80).cyan
82+
83+
# Add Shakapacker to Gemfile if not present
84+
unless shakapacker_in_gemfile?
85+
puts Rainbow("📝 Adding Shakapacker to Gemfile...").yellow
86+
success = system("bundle add shakapacker --strict")
87+
unless success
88+
GeneratorMessages.add_error("Failed to add Shakapacker to Gemfile. Please run 'bundle add shakapacker' manually.")
89+
exit(1)
90+
end
91+
puts Rainbow("✅ Shakapacker added to Gemfile successfully!").green
92+
end
93+
94+
# Install Shakapacker
95+
puts Rainbow("⚙️ Installing Shakapacker (required for webpack integration)...").yellow
96+
success = system("./bin/rails shakapacker:install")
97+
98+
unless success
99+
error = <<~MSG.strip
100+
** Failed to install Shakapacker automatically.
101+
102+
Please run this command manually:
103+
104+
./bin/rails shakapacker:install
105+
106+
Then re-run: rails generate react_on_rails:install
107+
MSG
108+
GeneratorMessages.add_error(error)
109+
exit(1)
110+
end
111+
112+
puts Rainbow("✅ Shakapacker installed successfully!").green
113+
puts Rainbow("=" * 80).cyan
114+
puts Rainbow("🚀 CONTINUING WITH REACT ON RAILS SETUP").cyan.bold
115+
puts Rainbow("=" * 80).cyan + "\n"
116+
117+
# Create marker file so base generator can avoid copying shakapacker.yml
118+
File.write(".shakapacker_just_installed", "")
119+
end
120+
121+
def shakapacker_in_gemfile?
122+
return false unless File.exist?("Gemfile")
123+
124+
gemfile_content = File.read("Gemfile")
125+
gemfile_content.match?(/gem\s+['"]shakapacker['"]/)
126+
end
127+
83128
def add_bin_scripts
84129
directory "#{__dir__}/bin", "bin"
85130

@@ -97,47 +142,9 @@ def add_post_install_message
97142
GeneratorMessages.add_info(GeneratorMessages.helpful_message_after_installation)
98143
end
99144

100-
def ensure_shakapacker_installed
101-
return true if shakapacker_installed?
102-
103-
puts "Installing Shakapacker (required for React on Rails)..."
104-
105-
added = system("bundle", "add", "shakapacker")
106-
unless added
107-
GeneratorMessages.add_error(<<~MSG.strip)
108-
Failed to add Shakapacker to your Gemfile.
109-
Please run 'bundle add shakapacker' manually and re-run the generator.
110-
MSG
111-
return false
112-
end
113-
114-
installed = system("bundle", "exec", "rails", "shakapacker:install")
115-
unless installed
116-
GeneratorMessages.add_error(<<~MSG.strip)
117-
Failed to install Shakapacker automatically.
118-
Please run 'bundle exec rails shakapacker:install' manually.
119-
MSG
120-
return false
121-
end
122-
123-
puts "Shakapacker installed successfully!"
124-
true
125-
end
126-
127-
def shakapacker_installed?
128-
Gem::Specification.find_by_name("shakapacker")
129-
true
130-
rescue Gem::LoadError
131-
false
132-
end
145+
# Removed: Shakapacker auto-installation logic (now explicit dependency)
133146

134-
def using_shakapacker_7_or_above?
135-
shakapacker_gem = Gem::Specification.find_by_name("shakapacker")
136-
shakapacker_gem.version.segments.first >= 7
137-
rescue Gem::MissingSpecError
138-
# In case using Webpacker
139-
false
140-
end
147+
# Removed: Shakapacker 8+ is now required as explicit dependency
141148
end
142149
end
143150
end

lib/generators/react_on_rails/templates/base/base/app/javascript/src/HelloWorld/ror_components/HelloWorld.client.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import PropTypes from 'prop-types';
22
import React, { useState } from 'react';
3-
import * as style from '../HelloWorld.module.css';
3+
import * as style from './HelloWorld.module.css';
44

55
const HelloWorld = (props) => {
66
const [name, setName] = useState(props.name);

lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ default: &default
5555
# SHAKAPACKER_ASSET_HOST will override both configurations.
5656
# asset_host: custom-path
5757

58+
# Utilizing webpack-subresource-integrity plugin, will generate integrity hashes for all entries in manifest.json
59+
# https://github.com/waysact/webpack-subresource-integrity/tree/main/webpack-subresource-integrity
60+
integrity:
61+
enabled: false
62+
# Which cryptographic function(s) to use, for generating the integrity hash(es). Default sha-384. Other possible values sha256, sha512
63+
hash_functions: ["sha384"]
64+
# Default "anonymous". Other possible value "use-credentials"
65+
# https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity#cross-origin_resource_sharing_and_subresource_integrity
66+
cross_origin: "anonymous"
67+
5868
development:
5969
<<: *default
6070
compile: true

react_on_rails.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Gem::Specification.new do |s|
3131
s.add_dependency "execjs", "~> 2.5"
3232
s.add_dependency "rails", ">= 5.2"
3333
s.add_dependency "rainbow", "~> 3.0"
34+
s.add_dependency "shakapacker", "~> 8.0"
3435

3536
s.add_development_dependency "gem-release"
3637
s.post_install_message = '

0 commit comments

Comments
 (0)