recipe_code_installer
Recipe Code Installer enables Drupal Recipes to bundle custom modules that get installed directly into your project's codebase. This module bridges the gap between Drupal's configuration-focused Recipe system and the need for custom functionality.
Overview
Recipe Code Installer solves a fundamental limitation of Drupal Recipes - their inability to contain custom code. While Recipes excel at installing modules and applying configuration, many real-world solutions require custom code that doesn't warrant a separate module on Drupal.org.
This module allows Recipe creators to include a custom module within their Recipe that gets extracted and installed as a proper Drupal module when the Recipe is applied.
Requirements
- Drupal 10.3 or higher
- Recipe API (included in Drupal core 10.3+)
Installation
composer require drupal/recipe_code_installer && drush en recipe_code_installerHow it works
Recipe Code Installer runs as the last stage of Recipe installation. The process works as follows:
- When a Recipe is applied, Recipe Code Installer checks for the presence of a
codesubdirectory within the Recipe - If custom module code is detected, it extracts this code and moves it to your project's custom module directory
- It then installs the module by calling Drupal's module install function
The module listens for the \Drupal\Core\Recipe\RecipeAppliedEvent event, which is fired when a Recipe is applied (not when it's installed via Composer). This makes Recipe Code Installer fully compatible with the Recipe Unpack Composer plugin, as it operates at a different stage in the Recipe lifecycle.
Why the module name matches the recipe name
Recipe Code Installer intentionally enforces that the custom module generated from a recipe's code/ folder uses the same machine name as the recipe. This means a recipe named foo_bar is expected to ship its code as a foo_bar module, rather than allowing arbitrary module names inside the recipe bundle.
This is a recommendation encoded as a guardrail, not a statement about code quality.
This constraint is opinionated on purpose. The project's intent is to support bundling small, recipe-adjacent code that does not warrant being maintained and distributed as a standalone contributed module, and to keep that bundled code minimal and specific to the consuming project. Since this code becomes custom code in every consumer project and has no automated update path, the tool optimizes for clarity and traceability over flexibility.
Benefits of this approach:
- Clear provenance: It is easy to trace that the
XYcustom module was added by theXYrecipe, which helps during code review, debugging, and later maintenance. - Encourages healthy usage: The constraint reinforces the intended workflow where only non-reusable, non-generic, overly specific code should be bundled in a recipe, while reusable code should be packaged and reused through Composer patterns.
- Avoids "recipe as module distributor": Supporting arbitrary (or multiple) modules inside one recipe can shift the tool toward being a general custom-module installation mechanism, which is explicitly not the project's primary scope.
Recipe structure example
enhanced_event_recipe/ ├── code/ # Custom module code to be installed │ ├── enhanced_event_recipe.info.yml │ ├── enhanced_event_recipe.module │ ├── enhanced_event_recipe.services.yml │ └── src/ │ ├── PricingCalculator.php │ └── Plugin/ │ └── Validation/ │ └── EventCapacityConstraint.php │ └── ... ├── config/ # Standard Recipe configuration │ ├── node.type.event.yml │ └── ... └── recipe.yml # Standard Recipe definition
Development Mode
Recipe Code Installer includes a development mode that's particularly helpful when developing Recipes with bundled code. When enabled, instead of copying the bundled code to the custom modules folder, the code gets symlinked. This creates a direct connection between the Recipe's code directory and the installed module, allowing changes to the bundled module to be immediately reflected without needing to reapply the Recipe.
You can enable development mode modifying the services.local.yml or a custom services file that is loaded at container build:
parameters:
recipe_code_installer.dev_mode: trueClear container cache (e.g., by executing drush cr) after this change.
This feature is particularly useful for:
- Iterative development of Recipe-bundled modules
- Testing Recipe code changes without repeatedly applying the Recipe
- Debugging bundled module install hooks in a live environment
Note that development mode should only be used during Recipe development and not in production environments.
Learn more about Recipe Code Installer
Want to understand the benefits of Recipe Code Installer for both Recipe creators and adopters? Curious about how it can streamline your Drupal development workflow?
Visit our blog to read our comprehensive article: "Recipe Code Installer: Bridging the gap between configuration and functionality" where we explore the problems it solves, practical use cases, and best practices for implementation.