Skip to content
Merged
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: 4 additions & 2 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
cache-dependency-path: '**/yarn.lock'
# TODO: Re-enable yarn caching once Node.js V8 cache crash is fixed
# Tracking: https://github.com/actions/setup-node/issues/1028
# cache: yarn
# cache-dependency-path: '**/yarn.lock'
- name: Print system information
run: |
echo "Linux release: "; cat /etc/issue
Expand Down
6 changes: 5 additions & 1 deletion lib/generators/react_on_rails/base_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@ def copy_base_files
app/views/layouts/hello_world.html.erb
Procfile.dev
Procfile.dev-static-assets
Procfile.dev-prod-assets]
Procfile.dev-prod-assets
bin/shakapacker-precompile-hook]
base_templates = %w[config/initializers/react_on_rails.rb]
base_files.each { |file| copy_file("#{base_path}#{file}", file) }
base_templates.each do |file|
template("#{base_path}/#{file}.tt", file)
end

# Make the hook script executable (copy_file guarantees it exists)
File.chmod(0o755, File.join(destination_root, "bin/shakapacker-precompile-hook"))
end

def copy_js_bundle_files
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# Shakapacker precompile hook for React on Rails
#
# This script runs before webpack compilation to generate pack files
# for auto-bundled components. It's called automatically by Shakapacker
# when configured in config/shakapacker.yml:
# precompile_hook: 'bin/shakapacker-precompile-hook'
#
# Emoji Scheme:
# 🔄 = Running/in-progress
# ✅ = Success
# ❌ = Error

# Skip validation during precompile hook execution
# The hook runs early in the build process, potentially before full Rails initialization,
# and doesn't need package version validation since it's part of the build itself
ENV["REACT_ON_RAILS_SKIP_VALIDATION"] = "true"

require_relative "../config/environment"

begin
puts Rainbow("🔄 Running React on Rails precompile hook...").cyan
ReactOnRails::PacksGenerator.instance.generate_packs_if_stale
rescue StandardError => e
warn Rainbow("❌ Error in precompile hook: #{e.message}").red
warn e.backtrace.first(5).join("\n")
exit 1
end
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ default: &default
# Raises an error if there is a mismatch in the shakapacker gem and npm package being used
ensure_consistent_versioning: true

# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
# SECURITY: Only reference trusted scripts within your project. Ensure the hook path
# points to a file within the project root that you control.
precompile_hook: 'bin/shakapacker-precompile-hook'

# Select whether the compiler will use SHA digest ('digest' option) or most recent modified timestamp ('mtime') to determine freshness
compiler_strategy: digest

Expand Down
45 changes: 28 additions & 17 deletions lib/react_on_rails/dev/server_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def help_mode_details
<<~MODES
#{Rainbow('🔥 HMR Development mode (default)').cyan.bold} - #{Rainbow('Procfile.dev').green}:
#{Rainbow('•').yellow} #{Rainbow('Hot Module Replacement (HMR) enabled').white}
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or bin/dev)').white}
#{Rainbow('•').yellow} #{Rainbow('Webpack dev server for fast recompilation').white}
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
#{Rainbow('•').yellow} #{Rainbow('May have Flash of Unstyled Content (FOUC)').white}
Expand All @@ -257,15 +257,15 @@ def help_mode_details

#{Rainbow('📦 Static development mode').cyan.bold} - #{Rainbow('Procfile.dev-static-assets').green}:
#{Rainbow('•').yellow} #{Rainbow('No HMR (static assets with auto-recompilation)').white}
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or bin/dev)').white}
#{Rainbow('•').yellow} #{Rainbow('Webpack watch mode for auto-recompilation').white}
#{Rainbow('•').yellow} #{Rainbow('CSS extracted to separate files (no FOUC)').white}
#{Rainbow('•').yellow} #{Rainbow('Development environment (faster builds than production)').white}
#{Rainbow('•').yellow} #{Rainbow('Source maps for debugging').white}
#{Rainbow('•').yellow} #{Rainbow('Access at:').white} #{Rainbow('http://localhost:3000/<route>').cyan.underline}

#{Rainbow('🏭 Production-assets mode').cyan.bold} - #{Rainbow('Procfile.dev-prod-assets').green}:
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation before Procfile start').white}
#{Rainbow('•').yellow} #{Rainbow('React on Rails pack generation (via precompile hook or assets:precompile)').white}
#{Rainbow('•').yellow} #{Rainbow('Asset precompilation with NODE_ENV=production (webpack optimizations)').white}
#{Rainbow('•').yellow} #{Rainbow('RAILS_ENV=development by default for assets:precompile (avoids credentials)').white}
#{Rainbow('•').yellow} #{Rainbow('Use --rails-env=production for assets:precompile only (not server processes)').white}
Expand All @@ -281,16 +281,20 @@ def help_mode_details
def run_production_like(_verbose: false, route: nil, rails_env: nil)
procfile = "Procfile.dev-prod-assets"

features = [
"Precompiling assets with production optimizations",
"Running Rails server on port 3001",
"No HMR (Hot Module Replacement)",
"CSS extracted to separate files (no FOUC)"
]

# NOTE: Pack generation happens automatically during assets:precompile
# either via precompile hook or via the configuration.rb adjust_precompile_task

print_procfile_info(procfile, route: route)
print_server_info(
"🏭 Starting production-like development server...",
[
"Generating React on Rails packs",
"Precompiling assets with production optimizations",
"Running Rails server on port 3001",
"No HMR (Hot Module Replacement)",
"CSS extracted to separate files (no FOUC)"
],
features,
3001,
route: route
)
Expand Down Expand Up @@ -409,15 +413,22 @@ def run_production_like(_verbose: false, route: nil, rails_env: nil)

def run_static_development(procfile, verbose: false, route: nil)
print_procfile_info(procfile, route: route)

features = [
"Using shakapacker --watch (no HMR)",
"CSS extracted to separate files (no FOUC)",
"Development environment (source maps, faster builds)",
"Auto-recompiles on file changes"
]

# Add pack generation info if not using precompile hook
unless ReactOnRails::PackerUtils.shakapacker_precompile_hook_configured?
features.unshift("Generating React on Rails packs")
end

print_server_info(
"⚡ Starting development server with static assets...",
[
"Generating React on Rails packs",
"Using shakapacker --watch (no HMR)",
"CSS extracted to separate files (no FOUC)",
"Development environment (source maps, faster builds)",
"Auto-recompiles on file changes"
],
features,
route: route
)

Expand Down
4 changes: 2 additions & 2 deletions lib/react_on_rails/packer_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ def self.extract_precompile_hook
config_data = ::Shakapacker.config.send(:data)

# Try symbol keys first (Shakapacker's internal format), then fall back to string keys
# Note: Currently only one hook value is supported, but this will change to support lists
config_data&.dig(:hooks, :precompile) || config_data&.dig("hooks", "precompile")
# The key is 'precompile_hook' at the top level of the config
config_data&.[](:precompile_hook) || config_data&.[]("precompile_hook")
end

def self.hook_contains_generate_packs?(hook_value)
Expand Down
9 changes: 5 additions & 4 deletions spec/dummy/config/shakapacker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ default: &default
# SWC has issues with PropTypes handling
javascript_transpiler: babel

# Hook to run before compilation (e.g., for ReScript builds, pack generation)
# See: https://github.com/shakacode/shakapacker/blob/main/docs/precompile_hook.md
precompile_hook: bin/shakapacker-precompile-hook

cache_path: tmp/cache/shakapacker
webpack_compile_output: false
ensure_consistent_versioning: true
Expand All @@ -25,6 +21,11 @@ default: &default
cache_manifest: false
nested_entries: true

# Hook to run before webpack compilation (e.g., for generating dynamic entry points)
# SECURITY: Only reference trusted scripts within your project. Ensure the hook path
# points to a file within the project root that you control.
precompile_hook: 'bin/shakapacker-precompile-hook'

development:
<<: *default
# Turn this to true if you want to use the rails/shakapacker check that the test
Expand Down
Loading
Loading