Skip to content

Add Precompile Hook Support with Environment-Based Skip Flag #849

@justin808

Description

@justin808

Add Precompile Hook Support with Environment-Based Skip Flag

Summary

Add support for precompile_hook configuration in shakapacker.yml that runs before webpack compilation, with an environment variable flag to skip the hook when needed (e.g., when called from bin/dev which runs the hook once before launching all processes).

Problem

Currently, there's no standardized way to run pre-build tasks (like ReScript compilation, locale generation, or other build prerequisites) before webpack starts. Projects resort to:

  1. Sleep delays in Procfiles - Fragile timing-based workarounds:

    wp-client: sleep 15 && bin/shakapacker-dev-server
    wp-server: sleep 15 && bundle exec rake react_on_rails:locale && bin/shakapacker --watch
  2. Manual task execution - Duplicating tasks in multiple places (Procfiles, rake tasks, CI scripts)

  3. No coordination - When bin/dev or development servers start, there's no single place to run prerequisites

Proposed Solution

1. Add precompile_hook to shakapacker.yml

default: &default
  # Command or script to run before webpack compilation
  # Can be a shell command or path to executable script
  precompile_hook: "yarn res:build && bundle exec rake react_on_rails:locale"

  # Or point to a script file
  precompile_hook: "./bin/precompile-hook"

2. Run hook automatically in bin/shakapacker and bin/shakapacker-dev-server

Unless SHAKAPACKER_SKIP_PRECOMPILE_HOOK=true is set:

# In Shakapacker::Runner.run or Shakapacker::DevServerRunner.run
unless ENV['SHAKAPACKER_SKIP_PRECOMPILE_HOOK'] == 'true'
  if (hook_command = Shakapacker.config.precompile_hook)
    puts "Running precompile hook: #{hook_command}"
    system(hook_command) or raise "Precompile hook failed"
  end
end

3. bin/dev integration pattern

The recommended pattern for bin/dev:

# bin/dev runs the hook ONCE before launching Procfile processes
if (hook_command = Shakapacker.config.precompile_hook)
  system(hook_command) or exit(1)
end

# Then launch Procfile with skip flag set
ENV['SHAKAPACKER_SKIP_PRECOMPILE_HOOK'] = 'true'
# ... launch foreman/overmind with Procfile.dev

This ensures:

  • Hook runs once when starting development
  • Individual webpack processes (client/server bundles) don't re-run it
  • Production deploys run it once during asset compilation
  • Can be skipped explicitly when needed

Benefits

  1. Eliminates sleep hacks - No more fragile timing delays
  2. Single source of truth - Precompile logic in one place (shakapacker.yml)
  3. Flexible control - Environment variable allows fine-grained control
  4. Framework integration - React on Rails, Vite Ruby, etc. can coordinate with this
  5. CI/CD friendly - Works consistently across development and production

Example Use Cases

ReScript + React on Rails

precompile_hook: "yarn res:build && bundle exec rake react_on_rails:locale"

TypeScript with custom codegen

precompile_hook: "./bin/generate-graphql-types"

Multi-step build

precompile_hook: "./bin/precompile-hook"

Where bin/precompile-hook:

#!/bin/bash
set -e
yarn res:build
bundle exec rake react_on_rails:locale
yarn run generate:icons

Implementation Checklist

  • Add precompile_hook config option to shakapacker.yml
  • Add Shakapacker::Configuration#precompile_hook method
  • Update Shakapacker::Runner to run hook before webpack (unless skip flag set)
  • Update Shakapacker::DevServerRunner to run hook before dev server (unless skip flag set)
  • Add SHAKAPACKER_SKIP_PRECOMPILE_HOOK environment variable check
  • Document in README with bin/dev integration pattern
  • Add examples for common frameworks (React on Rails, etc.)
  • Update generated bin/dev template to show recommended pattern

Related Issues

This coordinates with React on Rails issue about moving react_on_rails:locale from Procfiles into the precompile hook.

Version

Shakapacker: 9.3.3
Context: Working with React on Rails 16.2.0.beta.12, Rails 8.1.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions