Skip to content
Open
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
6 changes: 5 additions & 1 deletion lib/react_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,11 @@ def adjust_precompile_task
raise(ReactOnRails::Error, compile_command_conflict_message) if ReactOnRails::PackerUtils.precompile?

precompile_tasks = lambda {
Rake::Task["react_on_rails:generate_packs"].invoke
# Skip generate_packs if shakapacker has a precompile hook configured
unless ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
Rake::Task["react_on_rails:generate_packs"].invoke
end

Rake::Task["react_on_rails:assets:webpack"].invoke

# VERSIONS is per the shakacode/shakapacker clean method definition.
Expand Down
6 changes: 6 additions & 0 deletions lib/react_on_rails/dev/pack_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ module Dev
class PackGenerator
class << self
def generate(verbose: false)
# Skip if shakapacker has a precompile hook configured
if ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
puts "⏭️ Skipping pack generation (handled by shakapacker precompile hook)" if verbose
return
end

if verbose
puts "📦 Generating React on Rails packs..."
success = system "bundle exec rake react_on_rails:generate_packs"
Expand Down
16 changes: 16 additions & 0 deletions lib/react_on_rails/packer_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,21 @@ def self.raise_shakapacker_version_incompatible_for_basic_pack_generation

raise ReactOnRails::Error, msg
end

# Check if shakapacker.yml has a precompile hook configured
# This prevents react_on_rails from running generate_packs twice
def self.shakapacker_precompile_hook_configured?
return false unless defined?(::Shakapacker)

config_data = ::Shakapacker.config.send(:data)
hooks = config_data.dig("hooks", "precompile")

return false unless hooks

# Check if any hook contains the generate_packs rake task
Array(hooks).any? { |hook| hook.to_s.include?("react_on_rails:generate_packs") }
rescue StandardError
false
end
end
end
53 changes: 38 additions & 15 deletions spec/react_on_rails/dev/pack_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,50 @@

RSpec.describe ReactOnRails::Dev::PackGenerator do
describe ".generate" do
it "runs pack generation successfully in verbose mode" do
command = "bundle exec rake react_on_rails:generate_packs"
allow(described_class).to receive(:system).with(command).and_return(true)

expect { described_class.generate(verbose: true) }
.to output(/📦 Generating React on Rails packs.../).to_stdout_from_any_process
before do
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_precompile_hook_configured?).and_return(false)
end

it "runs pack generation successfully in quiet mode" do
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
allow(described_class).to receive(:system).with(command).and_return(true)
context "when shakapacker precompile hook is configured" do
before do
allow(ReactOnRails::PackerUtils).to receive(:shakapacker_precompile_hook_configured?).and_return(true)
end

it "skips pack generation in verbose mode" do
expect { described_class.generate(verbose: true) }
.to output(/⏭️ Skipping pack generation \(handled by shakapacker precompile hook\)/)
.to_stdout_from_any_process
end

expect { described_class.generate(verbose: false) }
.to output(/📦 Generating packs\.\.\. ✅/).to_stdout_from_any_process
it "skips pack generation in quiet mode" do
expect { described_class.generate(verbose: false) }
.not_to output.to_stdout_from_any_process
end
end

it "exits with error when pack generation fails" do
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
allow(described_class).to receive(:system).with(command).and_return(false)
context "when shakapacker precompile hook is not configured" do
it "runs pack generation successfully in verbose mode" do
command = "bundle exec rake react_on_rails:generate_packs"
allow(described_class).to receive(:system).with(command).and_return(true)

expect { described_class.generate(verbose: true) }
.to output(/📦 Generating React on Rails packs.../).to_stdout_from_any_process
end

it "runs pack generation successfully in quiet mode" do
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
allow(described_class).to receive(:system).with(command).and_return(true)

expect { described_class.generate(verbose: false) }
.to output(/📦 Generating packs\.\.\. ✅/).to_stdout_from_any_process
end

it "exits with error when pack generation fails" do
command = "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1"
allow(described_class).to receive(:system).with(command).and_return(false)

expect { described_class.generate(verbose: false) }.to raise_error(SystemExit)
expect { described_class.generate(verbose: false) }.to raise_error(SystemExit)
end
end
end
end
66 changes: 66 additions & 0 deletions spec/react_on_rails/packer_utils_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,72 @@ module ReactOnRails
expect(described_class.supports_autobundling?).to be(false)
end
end

describe ".shakapacker_precompile_hook_configured?" do
let(:mock_config) { instance_double("::Shakapacker::Config") } # rubocop:disable RSpec/VerifiedDoubleReference

before do
allow(::Shakapacker).to receive(:config).and_return(mock_config)
end

context "when shakapacker is not defined" do
before do
hide_const("::Shakapacker")
end

it "returns false" do
expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
end
end

context "when precompile hook contains react_on_rails:generate_packs" do
it "returns true for single hook" do
allow(mock_config).to receive(:send).with(:data).and_return(
{ "hooks" => { "precompile" => "bundle exec rake react_on_rails:generate_packs" } }
)

expect(described_class.shakapacker_precompile_hook_configured?).to be(true)
end

it "returns true for hook in array" do
allow(mock_config).to receive(:send).with(:data).and_return(
{ "hooks" => { "precompile" => ["bundle exec rake react_on_rails:generate_packs", "echo done"] } }
)

expect(described_class.shakapacker_precompile_hook_configured?).to be(true)
end
end

context "when precompile hook does not contain react_on_rails:generate_packs" do
it "returns false for different hook" do
allow(mock_config).to receive(:send).with(:data).and_return(
{ "hooks" => { "precompile" => "bundle exec rake some_other_task" } }
)

expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
end

it "returns false when hooks is nil" do
allow(mock_config).to receive(:send).with(:data).and_return({})

expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
end

it "returns false when precompile hook is nil" do
allow(mock_config).to receive(:send).with(:data).and_return({ "hooks" => {} })

expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
end
end

context "when an error occurs" do
it "returns false" do
allow(mock_config).to receive(:send).with(:data).and_raise(StandardError.new("test error"))

expect(described_class.shakapacker_precompile_hook_configured?).to be(false)
end
end
end
end

describe "version constants validation" do
Expand Down
Loading