Skip to content

Commit 9c03f8c

Browse files
justin808claude
andcommitted
Add Shakapacker precompile hook support for auto pack generation
This change leverages Shakapacker's new precompile_hook feature to automatically generate packs before webpack compilation, replacing the need to manually modify assets:precompile tasks or bin/dev scripts. Changes: - Add bin/shakapacker-precompile-hook script to templates and spec/dummy - Update shakapacker.yml to configure precompile_hook - Add PackerUtils.shakapacker_precompile_hook_configured? method - Skip generate_packs in configuration and PackGenerator when hook configured - Update generator to copy hook script and make it executable The precompile hook runs before webpack compilation and is properly validated by Shakapacker to ensure it points to a file within the project root. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent a1c71ea commit 9c03f8c

File tree

8 files changed

+87
-19
lines changed

8 files changed

+87
-19
lines changed

lib/generators/react_on_rails/base_generator.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,16 @@ def copy_base_files
3737
app/views/layouts/hello_world.html.erb
3838
Procfile.dev
3939
Procfile.dev-static-assets
40-
Procfile.dev-prod-assets]
40+
Procfile.dev-prod-assets
41+
bin/shakapacker-precompile-hook]
4142
base_templates = %w[config/initializers/react_on_rails.rb]
4243
base_files.each { |file| copy_file("#{base_path}#{file}", file) }
4344
base_templates.each do |file|
4445
template("#{base_path}/#{file}.tt", file)
4546
end
47+
48+
# Make the hook script executable
49+
File.chmod(0o755, "bin/shakapacker-precompile-hook") if File.exist?("bin/shakapacker-precompile-hook")
4650
end
4751

4852
def copy_js_bundle_files
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
# Shakapacker precompile hook for React on Rails
5+
#
6+
# This script runs before webpack compilation to generate pack files
7+
# for auto-bundled components. It's called automatically by Shakapacker
8+
# when configured in config/shakapacker.yml:
9+
# precompile_hook: 'bin/shakapacker-precompile-hook'
10+
11+
require_relative "../config/environment"
12+
13+
begin
14+
puts Rainbow("🔄 Running React on Rails precompile hook...").cyan
15+
ReactOnRails::PacksGenerator.instance.generate_packs_if_stale
16+
rescue StandardError => e
17+
warn Rainbow("❌ Error in precompile hook: #{e.message}").red
18+
warn e.backtrace.first(5).join("\n")
19+
exit 1
20+
end

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ default: &default
4242
# Raises an error if there is a mismatch in the shakapacker gem and npm package being used
4343
ensure_consistent_versioning: true
4444

45+
# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
46+
# SECURITY: Only reference trusted scripts within your project. The hook command will be
47+
# validated to ensure it points to a file within the project root.
48+
precompile_hook: 'bin/shakapacker-precompile-hook'
49+
4550
# Select whether the compiler will use SHA digest ('digest' option) or most recent modified timestamp ('mtime') to determine freshness
4651
compiler_strategy: digest
4752

lib/react_on_rails/configuration.rb

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

240240
precompile_tasks = lambda {
241-
Rake::Task["react_on_rails:generate_packs"].invoke
241+
# Skip generate_packs if shakapacker has a precompile hook configured
242+
unless ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
243+
Rake::Task["react_on_rails:generate_packs"].invoke
244+
end
245+
242246
Rake::Task["react_on_rails:assets:webpack"].invoke
243247

244248
# 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
@@ -8,6 +8,12 @@ module Dev
88
class PackGenerator
99
class << self
1010
def generate(verbose: false)
11+
# Skip if shakapacker has a precompile hook configured
12+
if ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
13+
puts "⏭️ Skipping pack generation (handled by shakapacker precompile hook)" if verbose
14+
return
15+
end
16+
1117
if verbose
1218
puts "📦 Generating React on Rails packs..."
1319
success = run_pack_generation

lib/react_on_rails/dev/server_manager.rb

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def help_mode_details
248248
<<~MODES
249249
#{Rainbow('🔥 HMR Development mode (default)').cyan.bold} - #{Rainbow('Procfile.dev').green}:
250250
#{Rainbow('•').yellow} #{Rainbow('Hot Module Replacement (HMR) enabled').white}
251-
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
251+
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or bin/dev)').white}
252252
#{Rainbow('•').yellow} #{Rainbow('Webpack dev server for fast recompilation').white}
253253
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
254254
#{Rainbow('•').yellow} #{Rainbow('May have Flash of Unstyled Content (FOUC)').white}
@@ -257,15 +257,15 @@ def help_mode_details
257257
258258
#{Rainbow('📦 Static development mode').cyan.bold} - #{Rainbow('Procfile.dev-static-assets').green}:
259259
#{Rainbow('•').yellow} #{Rainbow('No HMR (static assets with auto-recompilation)').white}
260-
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
260+
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or bin/dev)').white}
261261
#{Rainbow('•').yellow} #{Rainbow('Webpack watch mode for auto-recompilation').white}
262262
#{Rainbow('•').yellow} #{Rainbow('CSS extracted to separate files (no FOUC)').white}
263263
#{Rainbow('•').yellow} #{Rainbow('Development environment (faster builds than production)').white}
264264
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
265265
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000/<route>').cyan.underline}
266266
267267
#{Rainbow('🏭 Production-assets mode').cyan.bold} - #{Rainbow('Procfile.dev-prod-assets').green}:
268-
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
268+
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or assets:precompile)').white}
269269
#{Rainbow('•').yellow} #{Rainbow('Asset precompilation with NODE_ENV=production (webpack optimizations)').white}
270270
#{Rainbow('•').yellow} #{Rainbow('RAILS_ENV=development by default for assets:precompile (avoids credentials)').white}
271271
#{Rainbow('•').yellow} #{Rainbow('Use --rails-env=production for assets:precompile only (not server processes)').white}
@@ -281,16 +281,20 @@ def help_mode_details
281281
def run_production_like(_verbose: false, route: nil, rails_env: nil)
282282
procfile = "Procfile.dev-prod-assets"
283283

284+
features = [
285+
"Precompiling assets with production optimizations",
286+
"Running Rails server on port 3001",
287+
"No HMR (Hot Module Replacement)",
288+
"CSS extracted to separate files (no FOUC)"
289+
]
290+
291+
# NOTE: Pack generation happens automatically during assets:precompile
292+
# either via precompile hook or via the configuration.rb adjust_precompile_task
293+
284294
print_procfile_info(procfile, route: route)
285295
print_server_info(
286296
"🏭 Starting production-like development server...",
287-
[
288-
"Generating React on Rails packs",
289-
"Precompiling assets with production optimizations",
290-
"Running Rails server on port 3001",
291-
"No HMR (Hot Module Replacement)",
292-
"CSS extracted to separate files (no FOUC)"
293-
],
297+
features,
294298
3001,
295299
route: route
296300
)
@@ -409,15 +413,22 @@ def run_production_like(_verbose: false, route: nil, rails_env: nil)
409413

410414
def run_static_development(procfile, verbose: false, route: nil)
411415
print_procfile_info(procfile, route: route)
416+
417+
features = [
418+
"Using shakapacker --watch (no HMR)",
419+
"CSS extracted to separate files (no FOUC)",
420+
"Development environment (source maps, faster builds)",
421+
"Auto-recompiles on file changes"
422+
]
423+
424+
# Add pack generation info if not using precompile hook
425+
unless ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
426+
features.unshift("Generating React on Rails packs")
427+
end
428+
412429
print_server_info(
413430
"⚡ Starting development server with static assets...",
414-
[
415-
"Generating React on Rails packs",
416-
"Using shakapacker --watch (no HMR)",
417-
"CSS extracted to separate files (no FOUC)",
418-
"Development environment (source maps, faster builds)",
419-
"Auto-recompiles on file changes"
420-
],
431+
features,
421432
route: route
422433
)
423434

lib/react_on_rails/packer_utils.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,18 @@ def self.raise_shakapacker_version_incompatible_for_basic_pack_generation
166166

167167
raise ReactOnRails::Error, msg
168168
end
169+
170+
# Check if shakapacker.yml has a precompile_hook configured
171+
# This prevents react_on_rails from running generate_packs redundantly
172+
def self.shakapacker_precompile_hook_configured?
173+
return false unless defined?(::Shakapacker)
174+
175+
config_data = ::Shakapacker.config.send(:data)
176+
hook = config_data[:precompile_hook]
177+
178+
hook.present?
179+
rescue StandardError
180+
false
181+
end
169182
end
170183
end

spec/dummy/config/shakapacker.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ default: &default
2525
cache_manifest: false
2626
nested_entries: true
2727

28+
# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
29+
# SECURITY: Only reference trusted scripts within your project. The hook command will be
30+
# validated to ensure it points to a file within the project root.
31+
precompile_hook: 'bin/shakapacker-precompile-hook'
32+
2833
development:
2934
<<: *default
3035
# Turn this to true if you want to use the rails/shakapacker check that the test

0 commit comments

Comments
 (0)