diff --git a/lib/react_on_rails/dev/pack_generator.rb b/lib/react_on_rails/dev/pack_generator.rb index 9e74bf0631..907f3595bc 100644 --- a/lib/react_on_rails/dev/pack_generator.rb +++ b/lib/react_on_rails/dev/pack_generator.rb @@ -9,10 +9,10 @@ class << self def generate(verbose: false) if verbose puts "📦 Generating React on Rails packs..." - success = system "bundle exec rake react_on_rails:generate_packs" + success = run_pack_generation else print "📦 Generating packs... " - success = system "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1" + success = run_pack_generation(silent: true) puts success ? "✅" : "❌" end @@ -21,6 +21,100 @@ def generate(verbose: false) puts "❌ Pack generation failed" exit 1 end + + private + + def run_pack_generation(silent: false) + # If we're already inside a Bundler context AND Rails is available (e.g., called from bin/dev), + # we can directly require and run the task. Otherwise, use bundle exec. + if should_run_directly? + run_rake_task_directly(silent: silent) + else + run_via_bundle_exec(silent: silent) + end + end + + def should_run_directly? + # Check if we're in a meaningful Bundler context with BUNDLE_GEMFILE + return false unless defined?(Bundler) + return false unless ENV["BUNDLE_GEMFILE"] + return false unless rails_available? + + true + end + + def rails_available? + return false unless defined?(Rails) + return false unless Rails.respond_to?(:application) + return false if Rails.application.nil? + + # Verify Rails app can actually load tasks + begin + Rails.application.respond_to?(:load_tasks) + rescue StandardError + false + end + end + + def run_rake_task_directly(silent: false) + require "rake" + + load_rake_tasks + task = prepare_rake_task + + capture_output(silent) do + task.invoke + true + end + rescue StandardError => e + handle_rake_error(e, silent) + false + end + + def load_rake_tasks + return if Rake::Task.task_defined?("react_on_rails:generate_packs") + + Rails.application.load_tasks + end + + def prepare_rake_task + task = Rake::Task["react_on_rails:generate_packs"] + task.reenable # Allow re-execution if called multiple times + task + end + + def capture_output(silent) + return yield unless silent + + original_stdout = $stdout + original_stderr = $stderr + output_buffer = StringIO.new + $stdout = output_buffer + $stderr = output_buffer + + begin + yield + ensure + $stdout = original_stdout + $stderr = original_stderr + end + end + + def handle_rake_error(error, _silent) + error_msg = "Error generating packs: #{error.message}" + error_msg += "\n#{error.backtrace.join("\n")}" if ENV["DEBUG"] + + # Always write to stderr, even in silent mode + warn error_msg + end + + def run_via_bundle_exec(silent: false) + if silent + system "bundle exec rake react_on_rails:generate_packs > /dev/null 2>&1" + else + system "bundle exec rake react_on_rails:generate_packs" + end + end end end end