# Silverstripe recipe-plugin [![CI](https://github.com/silverstripe/recipe-plugin/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/recipe-plugin/actions/workflows/ci.yml) [![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/) ## Introduction This plugin enhances composer and allows for the installation of "silverstripe-recipe" packages. These recipes allow for the following features: - The ability to provide project resource files. These are copied to the appropriate project root location on install, and can be safely modified by the developer. On subsequent updates to a later recipe, composer will inform the user if a project file has been updated, and will ensure new files are copied as they are introduced to the recipe. - Recipes are composable, so resources or dependencies that are required by multiple recipes can include one another, rather than having to duplicate content. - Recipes also can be used as a base composer project. - A `require-recipe` command to inline a recipe into the root composer.json, allowing the developer to customise the recipe dependencies without mandating the inclusion of all requirements directly. - An `update-recipe` command to upgrade to a newer version of a recipe. ## Example output ![example-output](docs/_images/require-usage.png) ## Creating a new project Recipes can be introduced to any existing project (even if not created on a silverstripe base project) ```shell $ composer init $ composer require silverstripe/recipe-cms ^1.0@dev ```` Alternatively you can create a new project based on an existing recipe ```shell $ composer create-project silverstripe/recipe-cms ./myssproject ^1.0@dev ``` ## Inlining recipes You can "inline" either a previously installed recipe, or a new one that you would like to include dependencies for in your main project. By inlining a recipe, you promote its requirements, as well as its project files, up into your main project, and remove the recipe itself from your dependencies. This can be done with either `update-recipe`, which will update a recipe, or `require-recipe` which will install a new recipe. Note that if you wish to run this command you must first install either a recipe via normal composer commands, or install the recipe plugin: ```shell $ composer init $ composer require silverstripe/recipe-plugin ^0.1 $ composer require-recipe silverstripe/recipe-cms ^1.0@dev ``` or ```shell $ composer init $ composer require silverstripe/recipe-cms ^1.0@dev $ composer update-recipe silverstripe/recipe-cms ``` ## Removing recipe dependencies or files Any project file installed via a recipe, or any module installed by inlining a recipe, can be easily removed. Subsequent updates to this recipe will not re-install any of those files or dependencies. In order to ensure this, a record of all inlined modules, and all installed files are stored in composer.json as below. ```json { "extra": { "project-files-installed": [ "mysite/code/Page.php", "mysite/code/PageController.php" ], "project-dependencies-installed": { "silverstripe/admin": "1.0.x-dev", "silverstripe/asset-admin": "1.0.x-dev", "silverstripe/campaign-admin": "1.0.x-dev" } } } ``` To remove a file, simply delete it from the folder your project is installed in, but don't modify `project-files-installed` (as this is how composer knows what not to re-install). Likewise to remove a module, use `composer remove ` and it will be removed. As above, don't modify `project-dependencies-instaleld`, otherwise that module will be re-installed on subsequent `composer update-recipe`. ## Un-doing a deleted project file / dependency If you have deleted a module or file and want to re-install it you should remove the appropriate entry from either 'project-files-installed' or 'project-dependencies-installed' and then run `composer update-recipe ` again. The file or module will be re-installed. ## Removing recipes As installation of a recipe inlines all dependencies and passes ownership to the root project, there is no automatic removal process. To remove a recipe, you should manually remove any required module that is no longer desired via `composer remove `. The `provide` reference to the recipe can also be safely removed, although it has no practical result other than to disable future calls to `update-recipe` on this recipe. ## Installing or upgrading recipes without inlining them If desired, the optional inline behaviour of recipes can be omitted. Simply use the composer commands `require` and `update` in place of `require-recipe` and `update-recipe` respectively. This will not disable the project files feature, but will not inline the recipe directly, keeping your root composer.json from getting cluttered. If you have already inlined a recipe, it will be necessary to manually remove any undesired inlined requirements manually, and the recipe will need to be included with `require` subsequently. Note that using this method it's not necessary to include the `silverstripe/recipe-plugin` in the root project for this to work. ## Recipe composer.json schema Recipe types should follow the following rules: - No mandatory resources, other than project files. - Recipes must not rely on `autoload` as this are discarded on inline. Likewise any `*-dev` or other root-only options should not be used, as these are ignored outside of the root project. The exception to this is when these values are useful as a base project only. - The `type` must be `silverstripe-recipe` - The `require` must have `silverstripe/recipe-plugin` as a dependency. - `extra.project-files` must be declared as a list of wildcard patterns, matching the files in the recipe root as they should be copied to the root project. The relative paths of these resources are equivalent. - `extra.public-files` must be declared for any files which should be copied to the `public` web folder. If the project in question doesn't have any public folder, these will be copied to root instead. Note that all public files must be committed to the recipe `public` folder. An example recipe: ```json { "name": "silverstripe/example-recipe", "description": "Example silverstripe recipe", "type": "silverstripe-recipe", "require": { "silverstripe/recipe-plugin": "^0.1", "silverstripe/recipe-cms": "^1.0", "silverstripe/blog": "^3.0@dev", "silverstripe/lumberjack": "^2.1@dev", }, "extra": { "project-files": [ "mysite/_config/*.yml", "mysite/code/MyBlogPage.php" "client/src/*" ], "public-files": [ "client/dist/*" ] }, "prefer-stable": true, "minimum-stability": "dev" } ``` The files within this recipe would be organised in the structure: ``` client/ src/ blog.scss mysite/ _config/ settings.yml code/ MyBlogPage.php public/ client/ dist/ blog.css composer.json ```