Skip to content

Commit bd46eff

Browse files
justin808claude
andcommitted
Skip generate_packs when shakapacker precompile hook configured
This change prevents react_on_rails from running generate_packs twice when shakapacker has a precompile hook configured that already runs it. Changes: - Add PackerUtils.shakapacker_precompile_hook_configured? to detect hook - Skip generate_packs in assets:precompile if hook is configured - Skip generate_packs in bin/dev if hook is configured - Add comprehensive test coverage for new behavior This ensures generate_packs runs only once during both: - Production asset precompilation (assets:precompile) - Development server startup (bin/dev) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2b5bab8 commit bd46eff

File tree

5 files changed

+131
-16
lines changed

5 files changed

+131
-16
lines changed

lib/react_on_rails/configuration.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@ def adjust_precompile_task
247247
raise(ReactOnRails::Error, compile_command_conflict_message) if ReactOnRails::PackerUtils.precompile?
248248

249249
precompile_tasks = lambda {
250-
Rake::Task["react_on_rails:generate_packs"].invoke
250+
# Skip generate_packs if shakapacker has a precompile hook configured
251+
unless ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
252+
Rake::Task["react_on_rails:generate_packs"].invoke
253+
end
254+
251255
Rake::Task["react_on_rails:assets:webpack"].invoke
252256

253257
# VERSIONS is per the shakacode/shakapacker clean method definition.

lib/react_on_rails/dev/pack_generator.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ module Dev
77
class PackGenerator
88
class << self
99
def generate(verbose: false)
10+
# Skip if shakapacker has a precompile hook configured
11+
if ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
12+
puts "⏭️ Skipping pack generation (handled by shakapacker precompile hook)" if verbose
13+
return
14+
end
15+
1016
if verbose
1117
puts "📦 Generating React on Rails packs..."
1218
success = system "bundle exec rake react_on_rails:generate_packs"

lib/react_on_rails/packer_utils.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,21 @@ def self.raise_shakapacker_version_incompatible_for_basic_pack_generation
162162

163163
raise ReactOnRails::Error, msg
164164
end
165+
166+
# Check if shakapacker.yml has a precompile hook configured
167+
# This prevents react_on_rails from running generate_packs twice
168+
def self.shakapacker_precompile_hook_configured?
169+
return false unless defined?(::Shakapacker)
170+
171+
config_data = ::Shakapacker.config.send(:data)
172+
hooks = config_data.dig("hooks", "precompile")
173+
174+
return false unless hooks
175+
176+
# Check if any hook contains the generate_packs rake task
177+
Array(hooks).any? { |hook| hook.to_s.include?("react_on_rails:generate_packs") }
178+
rescue StandardError
179+
false
180+
end
165181
end
166182
end

spec/react_on_rails/dev/pack_generator_spec.rb

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,50 @@
55

66
RSpec.describe ReactOnRails::Dev::PackGenerator do
77
describe ".generate" do
8-
it "runs pack generation successfully in verbose mode" do
9-
command = "bundle exec rake react_on_rails:generate_packs"
10-
allow(described_class).to receive(:system).with(command).and_return(true)
11-
12-
expect { described_class.generate(verbose: true) }
13-
.to output(/📦 Generating React on Rails packs.../).to_stdout_from_any_process
8+
before do
9+
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_precompile_hook_configured?).and_return(false)
1410
end
1511

16-
it "runs pack generation successfully in quiet mode" do
17-
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
18-
allow(described_class).to receive(:system).with(command).and_return(true)
12+
context "when shakapacker precompile hook is configured" do
13+
before do
14+
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_precompile_hook_configured?).and_return(true)
15+
end
16+
17+
it "skips pack generation in verbose mode" do
18+
expect { described_class.generate(verbose: true) }
19+
.to output(/⏭️ Skipping pack generation \(handled by shakapacker precompile hook\)/)
20+
.to_stdout_from_any_process
21+
end
1922

20-
expect { described_class.generate(verbose: false) }
21-
.to output(/📦 Generating packs\.\.\. ✅/).to_stdout_from_any_process
23+
it "skips pack generation in quiet mode" do
24+
expect { described_class.generate(verbose: false) }
25+
.not_to output.to_stdout_from_any_process
26+
end
2227
end
2328

24-
it "exits with error when pack generation fails" do
25-
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
26-
allow(described_class).to receive(:system).with(command).and_return(false)
29+
context "when shakapacker precompile hook is not configured" do
30+
it "runs pack generation successfully in verbose mode" do
31+
command = "bundle exec rake react_on_rails:generate_packs"
32+
allow(described_class).to receive(:system).with(command).and_return(true)
33+
34+
expect { described_class.generate(verbose: true) }
35+
.to output(/📦 Generating React on Rails packs.../).to_stdout_from_any_process
36+
end
37+
38+
it "runs pack generation successfully in quiet mode" do
39+
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
40+
allow(described_class).to receive(:system).with(command).and_return(true)
41+
42+
expect { described_class.generate(verbose: false) }
43+
.to output(/📦 Generating packs\.\.\. ✅/).to_stdout_from_any_process
44+
end
45+
46+
it "exits with error when pack generation fails" do
47+
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
48+
allow(described_class).to receive(:system).with(command).and_return(false)
2749

28-
expect { described_class.generate(verbose: false) }.to raise_error(SystemExit)
50+
expect { described_class.generate(verbose: false) }.to raise_error(SystemExit)
51+
end
2952
end
3053
end
3154
end

spec/react_on_rails/packer_utils_spec.rb

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,72 @@ module ReactOnRails
139139
expect(described_class.supports_autobundling?).to be(false)
140140
end
141141
end
142+
143+
describe ".shakapacker_precompile_hook_configured?" do
144+
let(:mock_config) { instance_double("::Shakapacker::Config") } # rubocop:disable RSpec/VerifiedDoubleReference
145+
146+
before do
147+
allow(::Shakapacker).to receive(:config).and_return(mock_config)
148+
end
149+
150+
context "when shakapacker is not defined" do
151+
before do
152+
hide_const("::Shakapacker")
153+
end
154+
155+
it "returns false" do
156+
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
157+
end
158+
end
159+
160+
context "when precompile hook contains react_on_rails:generate_packs" do
161+
it "returns true for single hook" do
162+
allow(mock_config).to receive(:send).with(:data).and_return(
163+
{ "hooks" => { "precompile" => "bundle exec rake react_on_rails:generate_packs" } }
164+
)
165+
166+
expect(described_class.shakapacker_precompile_hook_configured?).to be(true)
167+
end
168+
169+
it "returns true for hook in array" do
170+
allow(mock_config).to receive(:send).with(:data).and_return(
171+
{ "hooks" => { "precompile" => ["bundle exec rake react_on_rails:generate_packs", "echo done"] } }
172+
)
173+
174+
expect(described_class.shakapacker_precompile_hook_configured?).to be(true)
175+
end
176+
end
177+
178+
context "when precompile hook does not contain react_on_rails:generate_packs" do
179+
it "returns false for different hook" do
180+
allow(mock_config).to receive(:send).with(:data).and_return(
181+
{ "hooks" => { "precompile" => "bundle exec rake some_other_task" } }
182+
)
183+
184+
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
185+
end
186+
187+
it "returns false when hooks is nil" do
188+
allow(mock_config).to receive(:send).with(:data).and_return({})
189+
190+
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
191+
end
192+
193+
it "returns false when precompile hook is nil" do
194+
allow(mock_config).to receive(:send).with(:data).and_return({ "hooks" => {} })
195+
196+
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
197+
end
198+
end
199+
200+
context "when an error occurs" do
201+
it "returns false" do
202+
allow(mock_config).to receive(:send).with(:data).and_raise(StandardError.new("test error"))
203+
204+
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
205+
end
206+
end
207+
end
142208
end
143209

144210
describe "version constants validation" do

0 commit comments

Comments
 (0)