-
-
Notifications
You must be signed in to change notification settings - Fork 638
Add Shakapacker precompile hook with ReScript support to Pro dummy app #1977
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+155
−94
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
850c7e9
Fix Pro dummy app Playwright test timeouts by aligning with defer loa…
justin808 c0e8e4f
Fix RuboCop violations and add comment about defer loading strategy
justin808 6bbd3eb
Revert Pro app to async and fix ReScript build in precompile hook
justin808 0f91a20
Replace Ruby precompile hooks with shared bash script
justin808 641132c
Refactor shakapacker-precompile-hook to use shared implementation
justin808 64d14e8
Make generator template precompile hook use shared implementation whe…
justin808 fc024b6
Move shared precompile hook to spec/support and simplify generator te…
justin808 ab317e2
Simplify generator template precompile hook to minimal bash script
justin808 278c932
Improve precompile hook error handling and config detection
justin808 d584749
Remove unnecessary REACT_ON_RAILS_SKIP_VALIDATION from generator temp…
justin808 5682b3d
Simplify generator template precompile hook to minimal bash script
justin808 6da1351
Restore Rainbow colors and SKIP_VALIDATION to generator template
justin808 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
19 changes: 19 additions & 0 deletions
19
react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| # Shakapacker precompile hook for React on Rails Pro test dummy app | ||
| # | ||
| # This script loads the shared test helper implementation. | ||
| # For production apps, use the generator template which includes a standalone implementation. | ||
|
|
||
| # Find the gem root directory (four levels up from react_on_rails_pro/spec/dummy/bin) | ||
| gem_root = File.expand_path("../../../..", __dir__) | ||
| shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") | ||
|
|
||
| unless File.exist?(shared_hook) | ||
| warn "❌ Error: Shared precompile hook not found at #{shared_hook}" | ||
| exit 1 | ||
| end | ||
|
|
||
| # Load and execute the shared hook | ||
| load shared_hook |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,101 +1,19 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| # Shakapacker precompile hook | ||
| # This script runs before Shakapacker compilation in both development and production. | ||
| # See: https://github.com/shakacode/shakapacker/blob/main/docs/precompile_hook.md | ||
| # Shakapacker precompile hook for React on Rails test dummy app | ||
| # | ||
| # This script loads the shared test helper implementation. | ||
| # For production apps, use the generator template which includes a standalone implementation. | ||
|
|
||
| require "fileutils" | ||
| # Find the gem root directory (three levels up from spec/dummy/bin) | ||
| gem_root = File.expand_path("../../..", __dir__) | ||
| shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") | ||
|
|
||
| # Find Rails root by walking upward looking for config/environment.rb | ||
| def find_rails_root | ||
| dir = Dir.pwd | ||
| loop do | ||
| return dir if File.exist?(File.join(dir, "config", "environment.rb")) | ||
|
|
||
| parent = File.dirname(dir) | ||
| return nil if parent == dir # Reached filesystem root | ||
|
|
||
| dir = parent | ||
| end | ||
| end | ||
|
|
||
| # Build ReScript if needed | ||
| def build_rescript_if_needed | ||
| # Check for both old (bsconfig.json) and new (rescript.json) config files | ||
| return unless File.exist?("bsconfig.json") || File.exist?("rescript.json") | ||
|
|
||
| puts "🔧 Building ReScript..." | ||
|
|
||
| # Cross-platform package manager detection | ||
| yarn_available = system("yarn", "--version", out: File::NULL, err: File::NULL) | ||
| npm_available = system("npm", "--version", out: File::NULL, err: File::NULL) | ||
|
|
||
| success = if yarn_available | ||
| system("yarn", "build:rescript") | ||
| elsif npm_available | ||
| system("npm", "run", "build:rescript") | ||
| else | ||
| warn "⚠️ Warning: Neither yarn nor npm found. Skipping ReScript build." | ||
| return | ||
| end | ||
|
|
||
| if success | ||
| puts "✅ ReScript build completed successfully" | ||
| else | ||
| warn "❌ ReScript build failed" | ||
| exit 1 | ||
| end | ||
| end | ||
|
|
||
| # Generate React on Rails packs if needed | ||
| # rubocop:disable Metrics/CyclomaticComplexity | ||
| def generate_packs_if_needed | ||
| # Find Rails root directory | ||
| rails_root = find_rails_root | ||
| return unless rails_root | ||
|
|
||
| # Check if React on Rails initializer exists | ||
| initializer_path = File.join(rails_root, "config", "initializers", "react_on_rails.rb") | ||
| return unless File.exist?(initializer_path) | ||
|
|
||
| # Check if auto-pack generation is configured (match actual config assignments, not comments) | ||
| config_file = File.read(initializer_path) | ||
| # Match uncommented configuration lines only (lines not starting with #) | ||
| has_auto_load = config_file =~ /^\s*(?!#).*config\.auto_load_bundle\s*=/ | ||
| has_components_subdir = config_file =~ /^\s*(?!#).*config\.components_subdirectory\s*=/ | ||
| return unless has_auto_load || has_components_subdir | ||
|
|
||
| puts "📦 Generating React on Rails packs..." | ||
|
|
||
| # Cross-platform bundle availability check | ||
| bundle_available = system("bundle", "--version", out: File::NULL, err: File::NULL) | ||
| return unless bundle_available | ||
|
|
||
| # Check if rake task exists (use array form for security) | ||
| task_list = IO.popen(["bundle", "exec", "rails", "-T"], err: [:child, :out], &:read) | ||
| return unless task_list.include?("react_on_rails:generate_packs") | ||
|
|
||
| # Use array form for better cross-platform support | ||
| success = system("bundle", "exec", "rails", "react_on_rails:generate_packs") | ||
|
|
||
| if success | ||
| puts "✅ Pack generation completed successfully" | ||
| else | ||
| warn "❌ Pack generation failed" | ||
| exit 1 | ||
| end | ||
| end | ||
| # rubocop:enable Metrics/CyclomaticComplexity | ||
|
|
||
| # Main execution | ||
| begin | ||
| build_rescript_if_needed | ||
| generate_packs_if_needed | ||
|
|
||
| exit 0 | ||
| rescue StandardError => e | ||
| warn "❌ Precompile hook failed: #{e.message}" | ||
| warn e.backtrace.join("\n") | ||
| unless File.exist?(shared_hook) | ||
| warn "❌ Error: Shared precompile hook not found at #{shared_hook}" | ||
| exit 1 | ||
| end | ||
|
|
||
| # Load and execute the shared hook | ||
| load shared_hook | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,119 @@ | ||
| #!/usr/bin/env ruby | ||
| # frozen_string_literal: true | ||
|
|
||
| # Shakapacker precompile hook for React on Rails - Shared Implementation | ||
| # | ||
| # This is the shared implementation used by both test dummy apps: | ||
| # - spec/dummy/bin/shakapacker-precompile-hook | ||
| # - react_on_rails_pro/spec/dummy/bin/shakapacker-precompile-hook | ||
| # | ||
| # This script runs before webpack compilation to: | ||
| # 1. Build ReScript files (if configured) | ||
| # 2. Generate pack files for auto-bundled components | ||
| # | ||
| # See: https://github.com/shakacode/shakapacker/blob/main/docs/precompile_hook.md | ||
|
|
||
| require "fileutils" | ||
| require "json" | ||
|
|
||
| # Find Rails root by walking upward looking for config/environment.rb | ||
| def find_rails_root | ||
| dir = Dir.pwd | ||
| while dir != "/" | ||
| return dir if File.exist?(File.join(dir, "config", "environment.rb")) | ||
|
|
||
| dir = File.dirname(dir) | ||
| end | ||
| nil | ||
| end | ||
|
|
||
| # Build ReScript if needed | ||
| # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity | ||
| def build_rescript_if_needed | ||
| rails_root = find_rails_root | ||
| unless rails_root | ||
| warn "⚠️ Warning: Could not find Rails root. Skipping ReScript build." | ||
| return | ||
| end | ||
|
|
||
| # Check for both old (bsconfig.json) and new (rescript.json) config files | ||
| return unless File.exist?(File.join(rails_root, "bsconfig.json")) || | ||
| File.exist?(File.join(rails_root, "rescript.json")) | ||
|
|
||
| puts "🔧 Building ReScript..." | ||
|
|
||
| # Validate that build:rescript script exists in package.json | ||
| package_json_path = File.join(rails_root, "package.json") | ||
| unless File.exist?(package_json_path) | ||
| warn "❌ Error: ReScript config found but package.json not found" | ||
| warn " ReScript requires a package.json with a build:rescript script" | ||
| exit 1 | ||
| end | ||
|
|
||
| package_json = JSON.parse(File.read(package_json_path)) | ||
| unless package_json.dig("scripts", "build:rescript") | ||
| warn "❌ Error: ReScript config found but no build:rescript script in package.json" | ||
| warn " Add this to your package.json scripts section:" | ||
| warn ' "build:rescript": "rescript build"' | ||
| exit 1 | ||
| end | ||
|
|
||
| Dir.chdir(rails_root) do | ||
| # Cross-platform package manager detection | ||
| if system("which yarn > /dev/null 2>&1") | ||
| system("yarn", "build:rescript", exception: true) | ||
| elsif system("which npm > /dev/null 2>&1") | ||
| system("npm", "run", "build:rescript", exception: true) | ||
| else | ||
| warn "❌ Error: Neither yarn nor npm found but ReScript build required" | ||
| warn " Install yarn or npm to build ReScript files" | ||
| exit 1 | ||
| end | ||
|
|
||
| puts "✅ ReScript build completed successfully" | ||
| end | ||
| rescue JSON::ParserError => e | ||
| warn "❌ Error: Invalid package.json: #{e.message}" | ||
| exit 1 | ||
| rescue StandardError => e | ||
| warn "❌ ReScript build failed: #{e.message}" | ||
| exit 1 | ||
| end | ||
| # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity | ||
|
|
||
| # Generate React on Rails packs if needed | ||
| def generate_packs_if_needed | ||
| rails_root = find_rails_root | ||
| return unless rails_root | ||
|
|
||
| initializer_path = File.join(rails_root, "config", "initializers", "react_on_rails.rb") | ||
| return unless File.exist?(initializer_path) | ||
|
|
||
| # Check if auto-pack generation is configured | ||
| # Match config lines that aren't commented out and allow flexible spacing | ||
| initializer_content = File.read(initializer_path) | ||
| return unless initializer_content.match?(/^\s*(?!#).*config\.auto_load_bundle\s*=/) || | ||
| initializer_content.match?(/^\s*(?!#).*config\.components_subdirectory\s*=/) | ||
|
|
||
| puts "📦 Generating React on Rails packs..." | ||
|
|
||
| Dir.chdir(rails_root) do | ||
| # Skip validation during precompile hook execution | ||
| ENV["REACT_ON_RAILS_SKIP_VALIDATION"] = "true" | ||
|
|
||
| # Run pack generation | ||
| system("bundle", "exec", "rails", "react_on_rails:generate_packs", exception: true) | ||
| puts "✅ Pack generation completed successfully" | ||
| end | ||
| rescue Errno::ENOENT => e | ||
| warn "⚠️ Warning: #{e.message}" | ||
| rescue StandardError => e | ||
| warn "❌ Pack generation failed: #{e.message}" | ||
| exit 1 | ||
| end | ||
|
|
||
| # Main execution (only if run directly, not when required) | ||
| if __FILE__ == $PROGRAM_NAME | ||
| build_rescript_if_needed | ||
| generate_packs_if_needed | ||
| end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Shared precompile hook is never executed (bin script only loads it).
This script currently just
loadsspec/support/shakapacker_precompile_hook_shared.rband exits. Because the shared file only runs its main block when__FILE__ == $PROGRAM_NAME, loading it from this bin script does not triggerbuild_rescript_if_neededorgenerate_packs_if_needed, so the Shakapacker precompile hook becomes a no-op.Explicitly invoke the shared methods after loading the file so ReScript builds and pack generation actually run.
Suggested fix:
gem_root = File.expand_path("../../..", __dir__) shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") unless File.exist?(shared_hook) warn "❌ Error: Shared precompile hook not found at #{shared_hook}" exit 1 end # Load and execute the shared hook -load shared_hook +load shared_hook + +# Run shared precompile steps +build_rescript_if_needed +generate_packs_if_neededI'll verify the review comment by inspecting the relevant files in the codebase.
🏁 Script executed:
Length of output: 4345
Shared precompile hook is never executed (bin script only loads it).
This script currently just
loadsspec/support/shakapacker_precompile_hook_shared.rband exits. Because the shared file only runs its main block when__FILE__ == $PROGRAM_NAME, loading it from this bin script does not triggerbuild_rescript_if_neededorgenerate_packs_if_needed, so the Shakapacker precompile hook becomes a no-op.Explicitly invoke the shared methods after loading the file so ReScript builds and pack generation actually run.
Suggested fix:
gem_root = File.expand_path("../../..", __dir__) shared_hook = File.join(gem_root, "spec", "support", "shakapacker_precompile_hook_shared.rb") unless File.exist?(shared_hook) warn "❌ Error: Shared precompile hook not found at #{shared_hook}" exit 1 end # Load and execute the shared hook -load shared_hook +load shared_hook + +# Run shared precompile steps +build_rescript_if_needed +generate_packs_if_needed📝 Committable suggestion
🤖 Prompt for AI Agents