diff --git a/README.md b/README.md index 9566e0c..8538a69 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,9 @@ Recipe types should follow the following rules: - 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: @@ -151,9 +154,31 @@ An example recipe: "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 +``` diff --git a/composer.json b/composer.json index e969e8e..358607e 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,10 @@ } }, "extra": { - "class": "SilverStripe\\RecipePlugin\\RecipePlugin" + "class": "SilverStripe\\RecipePlugin\\RecipePlugin", + "branch-alias": { + "dev-master": "1.x-dev" + } }, "require": { "composer-plugin-api": "^1.1" diff --git a/src/RecipeInstaller.php b/src/RecipeInstaller.php index e991767..d14b999 100644 --- a/src/RecipeInstaller.php +++ b/src/RecipeInstaller.php @@ -27,14 +27,16 @@ class RecipeInstaller extends LibraryInstaller { * @param string $sourceRoot Base of source files (no trailing slash) * @param string $destinationRoot Base of destination directory (no trailing slash) * @param array $filePatterns List of file patterns in wildcard format (e.g. `code/My*.php`) + * @param string $registrationKey Registration key for installed files + * @param string $name Name of project file type being installed */ - protected function installProjectFiles($recipe, $sourceRoot, $destinationRoot, $filePatterns) + protected function installProjectFiles($recipe, $sourceRoot, $destinationRoot, $filePatterns, $registrationKey, $name = 'project') { // load composer json data $composerFile = new JsonFile(Factory::getComposerFile(), null, $this->io); $composerData = $composerFile->read(); - $installedFiles = isset($composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED]) - ? $composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED] + $installedFiles = isset($composerData['extra'][$registrationKey]) + ? $composerData['extra'][$registrationKey] : []; // Load all project files @@ -46,7 +48,7 @@ class RecipeInstaller extends LibraryInstaller { // Write header if (!$any) { - $this->io->write("Installing project files for recipe {$recipe}:"); + $this->io->write("Installing {$name} files for recipe {$recipe}:"); $any = true; } @@ -85,7 +87,7 @@ class RecipeInstaller extends LibraryInstaller { if (!isset($composerData['extra'])) { $composerData['extra'] = []; } - $composerData['extra'][RecipePlugin::PROJECT_FILES_INSTALLED] = $installedFiles; + $composerData['extra'][$registrationKey] = $installedFiles; $composerFile->write($composerData); } } @@ -143,18 +145,41 @@ class RecipeInstaller extends LibraryInstaller { return; } + // Find recipe base dir + $recipePath = $this->getInstallPath($package); + // Find project path - $destinationPath = dirname(realpath(Factory::getComposerFile())); + $projectPath = dirname(realpath(Factory::getComposerFile())); + + // Find public path + $candidatePublicPath = $projectPath . DIRECTORY_SEPARATOR . RecipePlugin::PUBLIC_PATH; + $publicPath = is_dir($candidatePublicPath) ? $candidatePublicPath : $projectPath; // Copy project files to root $name = $package->getName(); $extra = $package->getExtra(); + + // Install project-files if (isset($extra[RecipePlugin::PROJECT_FILES])) { $this->installProjectFiles( $name, - $this->getInstallPath($package), - $destinationPath, - $extra[RecipePlugin::PROJECT_FILES] + $recipePath, + $projectPath, + $extra[RecipePlugin::PROJECT_FILES], + RecipePlugin::PROJECT_FILES_INSTALLED, + 'project' + ); + } + + // Install public-files + if (isset($extra[RecipePlugin::PUBLIC_FILES])) { + $this->installProjectFiles( + $name, + $recipePath . '/' . RecipePlugin::PUBLIC_PATH, + $publicPath, + $extra[RecipePlugin::PUBLIC_FILES], + RecipePlugin::PUBLIC_FILES_INSTALLED, + 'public' ); } } diff --git a/src/RecipePlugin.php b/src/RecipePlugin.php index 262b1c6..2e9e716 100644 --- a/src/RecipePlugin.php +++ b/src/RecipePlugin.php @@ -34,11 +34,26 @@ class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable */ const PROJECT_FILES = 'project-files'; + /** + * 'extra' key for public files + */ + const PUBLIC_FILES = 'public-files'; + + /** + * Hard-coded 'public' web-root folder + */ + const PUBLIC_PATH = 'public'; + /** * 'extra' key for list of project files installed */ const PROJECT_FILES_INSTALLED = 'project-files-installed'; + /** + * 'extra' key for list of public files installed + */ + const PUBLIC_FILES_INSTALLED = 'public-files-installed'; + /** * 'extra' key for project dependencies installed */ @@ -81,8 +96,11 @@ class RecipePlugin implements PluginInterface, EventSubscriberInterface, Capable $file = new JsonFile(Factory::getComposerFile()); $data = $file->read(); - // Remove project-files from project, and any empty extra - unset($data['extra']['project-files']); + // Remove project and public files from project + unset($data['extra'][self::PROJECT_FILES]); + unset($data['extra'][self::PUBLIC_FILES]); + + // Remove redundant empty extra if (empty($data['extra'])) { unset($data['extra']); }