├── skeleton ├── .styleci.yml ├── .gitignore.tpl ├── .editorconfig ├── .gitattributes.tpl ├── phpunit.xml ├── src │ └── ServiceProvider.php.tpl ├── LICENSE.tpl ├── composer.json.tpl ├── .github │ └── workflows │ │ └── default.yml └── README.md.tpl ├── stubs ├── config.php ├── MainClass.php.tpl ├── Facade.php.tpl └── MainClassTest.php.tpl ├── config └── package-generator.php ├── src ├── ServiceProvider.php ├── Exceptions │ └── RuntimeException.php └── Commands │ ├── Traits │ ├── InteractsWithGit.php │ ├── ManipulatesPackageFolder.php │ ├── InteractsWithComposer.php │ ├── InteractsWithUser.php │ ├── ChangesComposerJson.php │ └── CopiesSkeleton.php │ ├── PackageRemove.php │ └── PackageNew.php ├── composer.json ├── LICENSE └── README.md /skeleton/.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel 2 | -------------------------------------------------------------------------------- /stubs/config.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace \; 4 | 5 | class 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /skeleton/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [Makefile] 12 | indent_style = tab 13 | 14 | [*.php] 15 | indent_size = 4 16 | -------------------------------------------------------------------------------- /stubs/Facade.php.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace \\Facades; 4 | 5 | use Illuminate\Support\Facades\Facade; 6 | 7 | class extends Facade 8 | { 9 | protected static function getFacadeAccessor() 10 | { 11 | return ''; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /skeleton/.gitattributes.tpl: -------------------------------------------------------------------------------- 1 | * text=auto 2 | /.github export-ignore 3 | /tests export-ignore 4 | /.editorconfig export-ignore 5 | /.gitattributes export-ignore 6 | /.gitignore export-ignore 7 | /.travis.yml export-ignore 8 | /.styleci.yml export-ignore 9 | /.sensiolabs.yml export-ignore 10 | /.coveralls.yml export-ignore 11 | /phpunit.xml export-ignore 12 | /CHANGELOG-* export-ignore 13 | /CONTRIBUTING.md export-ignore 14 | /CODE_OF_CONDUCT.md export-ignore 15 | -------------------------------------------------------------------------------- /config/package-generator.php: -------------------------------------------------------------------------------- 1 | null, 16 | 17 | ]; 18 | -------------------------------------------------------------------------------- /skeleton/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | tests 14 | 15 | 16 | 17 | 18 | src 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /stubs/MainClassTest.php.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace \\Tests; 4 | 5 | use \\Facades\; 6 | use \\ServiceProvider; 7 | use Orchestra\Testbench\TestCase; 8 | 9 | class Test extends TestCase 10 | { 11 | protected function getPackageProviders($app) 12 | { 13 | return [ServiceProvider::class]; 14 | } 15 | 16 | protected function getPackageAliases($app) 17 | { 18 | return [ 19 | '' => ::class, 20 | ]; 21 | } 22 | 23 | public function testExample() 24 | { 25 | $this->assertEquals(1, 1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /skeleton/src/ServiceProvider.php.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace \; 4 | 5 | class ServiceProvider extends \Illuminate\Support\ServiceProvider 6 | { 7 | const CONFIG_PATH = __DIR__ . '/../config/.php'; 8 | 9 | public function boot() 10 | { 11 | $this->publishes([ 12 | self::CONFIG_PATH => config_path('.php'), 13 | ], 'config'); 14 | } 15 | 16 | public function register() 17 | { 18 | $this->mergeConfigFrom( 19 | self::CONFIG_PATH, 20 | '' 21 | ); 22 | 23 | $this->app->bind('', function () { 24 | return new (); 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 15 | self::CONFIG_PATH => config_path('package-generator.php'), 16 | ], 'config'); 17 | 18 | if ($this->app->runningInConsole()) { 19 | $this->commands([ 20 | PackageNew::class, 21 | PackageRemove::class, 22 | ]); 23 | } 24 | } 25 | 26 | public function register() 27 | { 28 | $this->mergeConfigFrom( 29 | self::CONFIG_PATH, 30 | 'package-generator' 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "melihovv/laravel-package-generator", 3 | "description": "A laravel package generator", 4 | "license": "MIT", 5 | "keywords": [ 6 | "laravel", 7 | "package", 8 | "generator" 9 | ], 10 | "type": "library", 11 | "authors": [ 12 | { 13 | "name": "Alexander Melihov", 14 | "email": "amelihovv@ya.ru" 15 | } 16 | ], 17 | "require": { 18 | "php": ">=7.2", 19 | "illuminate/container": "^6.0|^7.0|^8.0", 20 | "illuminate/console": "^6.0|^7.0|^8.0", 21 | "illuminate/filesystem": "^6.0|^7.0|^8.0", 22 | "illuminate/support": "^6.0|^7.0|^8.0", 23 | "illuminate/view": "^6.0|^7.0|^8.0" 24 | }, 25 | "autoload": { 26 | "psr-4": { 27 | "Melihovv\\LaravelPackageGenerator\\": "src" 28 | } 29 | }, 30 | "extra": { 31 | "laravel": { 32 | "providers": [ 33 | "Melihovv\\LaravelPackageGenerator\\ServiceProvider" 34 | ] 35 | } 36 | }, 37 | "config": { 38 | "preferred-install": "dist", 39 | "sort-packages": true, 40 | "optimize-autoloader": true 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Exceptions/RuntimeException.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /skeleton/LICENSE.tpl: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) <> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /skeleton/composer.json.tpl: -------------------------------------------------------------------------------- 1 | { 2 | "name": "", 3 | "description": "", 4 | "license": "", 5 | "keywords": [ 6 | 7 | ], 8 | "type": "library", 9 | "authors": [ 10 | { 11 | "name": "", 12 | "email": "" 13 | } 14 | ], 15 | "require": { 16 | "php": "", 17 | "illuminate/support": "^6.0|^7.0|^8.0" 18 | }, 19 | "require-dev": { 20 | "orchestra/testbench": "^4.0|^5.0|^6.0", 21 | "phpunit/phpunit": "^8.4|^9.0" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "\\\\": "src" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "\\\\Tests\\": "tests" 31 | } 32 | }, 33 | "scripts": { 34 | "phpunit": "phpunit" 35 | }, 36 | "extra": { 37 | "laravel": { 38 | "providers": [ 39 | "\\\\ServiceProvider" 40 | ] 41 | } 42 | }, 43 | "config": { 44 | "preferred-install": "dist", 45 | "sort-packages": true, 46 | "optimize-autoloader": true 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /skeleton/.github/workflows/default.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '0 0 * * *' 7 | 8 | jobs: 9 | php-tests: 10 | runs-on: ${{ matrix.os }} 11 | 12 | strategy: 13 | matrix: 14 | php: [7.4, 7.3, 7.2] 15 | laravel: [7.*, 6.*] 16 | dependency-version: [prefer-lowest, prefer-stable] 17 | os: [ubuntu-latest, windows-latest] 18 | include: 19 | - laravel: 8.* 20 | testbench: 6.* 21 | - laravel: 7.* 22 | testbench: 5.* 23 | - laravel: 6.* 24 | testbench: 4.* 25 | exclude: 26 | - laravel: 8.* 27 | php: 7.2 28 | 29 | name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }} 30 | 31 | steps: 32 | - name: Checkout code 33 | uses: actions/checkout@v1 34 | 35 | - name: Setup PHP 36 | uses: shivammathur/setup-php@v1 37 | with: 38 | php-version: ${{ matrix.php }} 39 | extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick 40 | coverage: none 41 | 42 | - name: Install dependencies 43 | run: | 44 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update 45 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest 46 | 47 | - name: Execute tests 48 | run: ./vendor/bin/phpunit 49 | -------------------------------------------------------------------------------- /src/Commands/Traits/InteractsWithGit.php: -------------------------------------------------------------------------------- 1 | info("Run \"$command\"."); 23 | 24 | File::makeDirectory($dest, 0755, true); 25 | 26 | $output = []; 27 | exec($command, $output, $returnStatusCode); 28 | 29 | if ($returnStatusCode !== 0) { 30 | throw RuntimeException::commandExecutionFailed( 31 | $command, $returnStatusCode 32 | ); 33 | } 34 | 35 | $this->info("\"$command\" was successfully ran."); 36 | } 37 | 38 | /** 39 | * Init git repo. 40 | * @param string $repoPath 41 | */ 42 | protected function initRepo($repoPath) 43 | { 44 | $command = "git init $repoPath"; 45 | $this->info("Run \"$command\"."); 46 | 47 | $output = []; 48 | exec($command, $output, $returnStatusCode); 49 | 50 | if ($returnStatusCode !== 0) { 51 | throw RuntimeException::commandExecutionFailed( 52 | $command, $returnStatusCode 53 | ); 54 | } 55 | 56 | $this->info("\"$command\" was successfully ran."); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Commands/Traits/ManipulatesPackageFolder.php: -------------------------------------------------------------------------------- 1 | info('Create package folder.'); 20 | 21 | if (File::exists($packagePath)) { 22 | $this->info('Package folder already exists. Skipping.'); 23 | 24 | return; 25 | } 26 | 27 | if (! File::makeDirectory($packagePath, 0755, true)) { 28 | throw new RuntimeException('Cannot create package folder'); 29 | } 30 | 31 | $this->info('Package folder was successfully created.'); 32 | } 33 | 34 | /** 35 | * Remove package folder. 36 | * 37 | * @param $packagePath 38 | * 39 | * @throws RuntimeException 40 | */ 41 | protected function removePackageFolder($packagePath) 42 | { 43 | $this->info('Remove package folder.'); 44 | 45 | if (File::exists($packagePath)) { 46 | if (! File::deleteDirectory($packagePath)) { 47 | throw new RuntimeException('Cannot remove package folder'); 48 | } 49 | 50 | $this->info('Package folder was successfully removed.'); 51 | } else { 52 | $this->info('Package folder does not exists. Skipping.'); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Commands/Traits/InteractsWithComposer.php: -------------------------------------------------------------------------------- 1 | composerRunCommand('composer dump-autoload'); 15 | } 16 | 17 | /** 18 | * Run "composer update $vendor/$package". 19 | * 20 | * @param string $vendor 21 | * @param string $package 22 | */ 23 | protected function composerUpdatePackage($vendor, $package) 24 | { 25 | $this->composerRunCommand("composer update --ignore-platform-reqs $vendor/$package"); 26 | } 27 | 28 | /** 29 | * Run "composer remove $vendor/$package". 30 | * 31 | * @param string $vendor 32 | * @param string $package 33 | */ 34 | protected function composerRemovePackage($vendor, $package) 35 | { 36 | $this->composerRunCommand("composer remove --ignore-platform-reqs $vendor/$package"); 37 | } 38 | 39 | /** 40 | * Run arbitrary composer command. 41 | * 42 | * @param $command 43 | */ 44 | protected function composerRunCommand($command) 45 | { 46 | $this->info("Run \"$command\"."); 47 | 48 | $output = []; 49 | exec($command, $output, $returnStatusCode); 50 | 51 | if ($returnStatusCode !== 0) { 52 | throw RuntimeException::commandExecutionFailed($command, $returnStatusCode); 53 | } 54 | 55 | $this->info("\"$command\" was successfully ran."); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /skeleton/README.md.tpl: -------------------------------------------------------------------------------- 1 | # 2 | 3 | [![GitHub Workflow Status](https://github.com///workflows/Run%20tests/badge.svg)](https://github.com///actions) 4 | [![styleci](https://styleci.io/repos/CHANGEME/shield)](https://styleci.io/repos/CHANGEME) 5 | 6 | [![Packagist](https://img.shields.io/packagist/v//.svg)](https://packagist.org/packages//) 7 | [![Packagist](https://poser.pugx.org///d/total.svg)](https://packagist.org/packages//) 8 | [![Packagist](https://img.shields.io/packagist/l//.svg)](https://packagist.org/packages//) 9 | 10 | Package description: CHANGE ME 11 | 12 | ## Installation 13 | 14 | Install via composer 15 | ```bash 16 | composer require / 17 | ``` 18 | 19 | ### Publish package assets 20 | 21 | ```bash 22 | php artisan vendor:publish --provider="\\ServiceProvider" 23 | ``` 24 | 25 | ## Usage 26 | 27 | CHANGE ME 28 | 29 | ## Security 30 | 31 | If you discover any security related issues, please email 32 | instead of using the issue tracker. 33 | 34 | ## Credits 35 | 36 | - []() 37 | - [All contributors](/graphs/contributors) 38 | 39 | This package is bootstrapped with the help of 40 | [melihovv/laravel-package-generator](https://github.com/melihovv/laravel-package-generator). 41 | -------------------------------------------------------------------------------- /src/Commands/Traits/InteractsWithUser.php: -------------------------------------------------------------------------------- 1 | argument('vendor') ?: $default; 19 | 20 | return $this->askUser('The vendor name?', $vendor); 21 | } 22 | 23 | /** 24 | * Get the name of package for the namespace. 25 | * 26 | * @param string $default 27 | * 28 | * @return string 29 | */ 30 | protected function getPackage($default = '') 31 | { 32 | $package = $this->argument('package') ?: $default; 33 | 34 | return $this->askUser('The package name?', $package); 35 | } 36 | 37 | /** 38 | * Get vendor folder name. 39 | * 40 | * @param string $vendor 41 | * 42 | * @return string 43 | */ 44 | protected function getVendorFolderName($vendor) 45 | { 46 | $vendorFolderName = Str::kebab($vendor); 47 | 48 | return $this->askUser('The vendor folder name?', $vendorFolderName); 49 | } 50 | 51 | /** 52 | * Get package folder name. 53 | * 54 | * @param string $package 55 | * 56 | * @return string 57 | */ 58 | protected function getPackageFolderName($package) 59 | { 60 | $packageFolderName = Str::kebab($package); 61 | 62 | return $this->askUser('The package folder name?', $packageFolderName); 63 | } 64 | 65 | /** 66 | * Ask user. 67 | * 68 | * @param $question 69 | * @param $defaultValue 70 | * 71 | * @return string 72 | */ 73 | protected function askUser($question, $defaultValue = '') 74 | { 75 | if ($this->option('interactive')) { 76 | return $this->ask($question, $defaultValue); 77 | } 78 | 79 | return $defaultValue; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Commands/PackageRemove.php: -------------------------------------------------------------------------------- 1 | getVendor(); 44 | $package = $this->getPackage(); 45 | 46 | $vendorFolderName = $this->getVendorFolderName($vendor); 47 | $packageFolderName = $this->getPackageFolderName($package); 48 | 49 | $relPackagePath = "packages/$vendorFolderName/$packageFolderName"; 50 | $packagePath = base_path($relPackagePath); 51 | 52 | try { 53 | $this->composerRemovePackage($vendorFolderName, $packageFolderName); 54 | $this->removePackageFolder($packagePath); 55 | $this->unregisterPackage($vendor, $package, "packages/$vendorFolderName/$packageFolderName"); 56 | $this->composerDumpAutoload(); 57 | 58 | return 0; 59 | } catch (Exception $e) { 60 | $this->error($e->getMessage()); 61 | 62 | return -1; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Commands/PackageNew.php: -------------------------------------------------------------------------------- 1 | getVendor(); 46 | $package = $this->getPackage(); 47 | 48 | $vendorFolderName = $this->getVendorFolderName($vendor); 49 | $packageFolderName = $this->getPackageFolderName($package); 50 | 51 | $relPackagePath = "packages/$vendorFolderName/$packageFolderName"; 52 | $packagePath = base_path($relPackagePath); 53 | 54 | try { 55 | $this->createPackageFolder($packagePath); 56 | $this->registerPackage($vendorFolderName, $packageFolderName, $relPackagePath); 57 | $this->copySkeleton($packagePath, $vendor, $package, $vendorFolderName, $packageFolderName); 58 | $this->initRepo($packagePath); 59 | $this->composerUpdatePackage($vendorFolderName, $packageFolderName); 60 | $this->composerDumpAutoload(); 61 | 62 | $this->info('Finished. Are you ready to write awesome package?'); 63 | 64 | return 0; 65 | } catch (Exception $e) { 66 | $this->error($e->getMessage()); 67 | 68 | return -1; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Laravel package generator 2 | ========================= 3 | 4 | [![GitHub Workflow Status](https://github.com/melihovv/laravel-package-generator/workflows/Run%20tests/badge.svg)](https://github.com/melihovv/laravel-package-generator/actions) 5 | [![styleci](https://styleci.io/repos/96041272/shield)](https://styleci.io/repos/96041272) 6 | 7 | [![Packagist](https://img.shields.io/packagist/v/melihovv/laravel-package-generator.svg)](https://packagist.org/packages/melihovv/laravel-package-generator) 8 | [![Packagist](https://poser.pugx.org/melihovv/laravel-package-generator/d/total.svg)](https://packagist.org/packages/melihovv/laravel-package-generator) 9 | [![Packagist](https://img.shields.io/packagist/l/melihovv/laravel-package-generator.svg)](https://packagist.org/packages/melihovv/laravel-package-generator) 10 | 11 | Simple package to quickly generate basic structure for other laravel packages. 12 | 13 | ## Install 14 | 15 | Install via composer 16 | ```bash 17 | composer require --dev melihovv/laravel-package-generator 18 | ``` 19 | 20 | Publish package config if you want customize default values 21 | ```bash 22 | php artisan vendor:publish --provider="Melihovv\LaravelPackageGenerator\ServiceProvider" --tag="config" 23 | ``` 24 | 25 | ## Available commands 26 | 27 | ### php artisan package:new -i {vendor} {package} 28 | 29 | Create new package. 30 | 31 | Example: `php artisan package:new Melihovv SomeAwesomePackage` 32 | 33 | This command will: 34 | 35 | * Create `packages/melihovv/some-awesome-package` folder 36 | * Register package in app composer.json 37 | * Copy package skeleton from skeleton folder to created folder (you can provide 38 | your custom skeleton path in config) 39 | * Run `git init packages/melihovv/some-awesome-package` 40 | * Run `composer update melihovv/some-awesome-package` 41 | * Run `composer dump-autoload` 42 | 43 | With interactive `-i` flag you will be prompted for every needed value from you. 44 | 45 | ### php artisan package:remove {vendor} {package} 46 | 47 | Remove the existing package. 48 | 49 | Example: `php artisan package:remove Melihovv SomeAwesomePackage` 50 | 51 | This command will: 52 | 53 | * Run `composer remove melihovv/some-awesome-package` 54 | * Remove `packages/melihovv/some-awesome-package` folder 55 | * Unregister package in app composer.json 56 | * Run `composer dump-autoload` 57 | 58 | Interactive mode also possible. 59 | 60 | ## Custom skeleton 61 | 62 | This package will copy all folders and files from specified skeleton path to 63 | package folder. You can use templates in your skeleton. All files with `tpl` 64 | extension will be provided with some variables available to use in them. `tpl` 65 | extension will be stripped. 66 | 67 | Available variables to use in templates: 68 | 69 | * vendor (e.g. Melihovv) 70 | * package (e.g. SomeAwesomePackage) 71 | * vendorFolderName (e.g. melihovv) 72 | * packageFolderName (e.g. some-awesome-package) 73 | * packageHumanName (e.g. Some awesome package) 74 | * composerName (e.g. melihovv/some-awesome-package) 75 | * composerDesc (e.g. A some awesome package) 76 | * composerKeywords (e.g. some,awesome,package) 77 | * licence (e.g. MIT) 78 | * phpVersion (e.g. >=7.0) 79 | * aliasName (e.g. some-awesome-package) 80 | * configFileName (e.g. some-awesome-package) 81 | * year (e.g. 2017) 82 | * name (e.g. Alexander Melihov) 83 | * email (e.g. amelihovv@ya.ru) 84 | * githubPackageUrl (e.g. https://github.com/melihov/some-awesome-package) 85 | 86 | ## Things you need to do manually: 87 | 88 | * In README.md: 89 | * StyleCI repository identifier 90 | * Package description 91 | * Usage section 92 | 93 | ## Security 94 | 95 | If you discover any security related issues, please email amelihovv@ya.ru instead of using the issue tracker. 96 | 97 | ## Credits 98 | 99 | - [Alexander Melihov](https://github.com/melihovv) 100 | - [All contributors](https://github.com/melihovv/laravel-package-generator/graphs/contributors) 101 | -------------------------------------------------------------------------------- /src/Commands/Traits/ChangesComposerJson.php: -------------------------------------------------------------------------------- 1 | info('Register package in composer.json.'); 24 | 25 | $composerJson = $this->loadComposerJson(); 26 | 27 | if (! isset($composerJson['repositories'])) { 28 | Arr::set($composerJson, 'repositories', []); 29 | } 30 | 31 | $filtered = array_filter($composerJson['repositories'], function ($repository) use ($relPackagePath) { 32 | return $repository['type'] === 'path' 33 | && $repository['url'] === $relPackagePath; 34 | }); 35 | 36 | if (count($filtered) === 0) { 37 | $this->info('Register composer repository for package.'); 38 | 39 | $composerJson['repositories'][] = (object) [ 40 | 'type' => 'path', 41 | 'url' => $relPackagePath, 42 | ]; 43 | } else { 44 | $this->info('Composer repository for package is already registered.'); 45 | } 46 | 47 | Arr::set($composerJson, "require.$vendor/$package", 'dev-master'); 48 | 49 | $this->saveComposerJson($composerJson); 50 | 51 | $this->info('Package was successfully registered in composer.json.'); 52 | } 53 | 54 | /** 55 | * Unregister package from composer.json. 56 | * 57 | * @param $vendor 58 | * @param $package 59 | * 60 | * @throws FileNotFoundException 61 | * @throws RuntimeException 62 | */ 63 | protected function unregisterPackage($vendor, $package, $relPackagePath) 64 | { 65 | $this->info('Unregister package from composer.json.'); 66 | 67 | $composerJson = $this->loadComposerJson(); 68 | 69 | unset($composerJson['require']["$vendor\\$package\\"]); 70 | 71 | $repositories = array_filter($composerJson['repositories'], function ($repository) use ($relPackagePath) { 72 | return $repository['type'] !== 'path' 73 | || $repository['url'] !== $relPackagePath; 74 | }); 75 | 76 | $composerJson['repositories'] = $repositories; 77 | 78 | if (count($composerJson['repositories']) === 0) { 79 | unset($composerJson['repositories']); 80 | } 81 | 82 | $this->saveComposerJson($composerJson); 83 | 84 | $this->info('Package was successfully unregistered from composer.json.'); 85 | } 86 | 87 | /** 88 | * Load and parse content of composer.json. 89 | * 90 | * @return array 91 | * 92 | * @throws FileNotFoundException 93 | * @throws RuntimeException 94 | */ 95 | protected function loadComposerJson() 96 | { 97 | $composerJsonPath = $this->getComposerJsonPath(); 98 | 99 | if (! File::exists($composerJsonPath)) { 100 | throw new FileNotFoundException('composer.json does not exist'); 101 | } 102 | 103 | $composerJsonContent = File::get($composerJsonPath); 104 | $composerJson = json_decode($composerJsonContent, true); 105 | 106 | if (! is_array($composerJson)) { 107 | throw new RuntimeException("Invalid composer.json file [$composerJsonPath]"); 108 | } 109 | 110 | return $composerJson; 111 | } 112 | 113 | /** 114 | * @param array $composerJson 115 | * 116 | * @throws RuntimeException 117 | */ 118 | protected function saveComposerJson($composerJson) 119 | { 120 | $newComposerJson = json_encode( 121 | $composerJson, 122 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES 123 | ); 124 | 125 | $composerJsonPath = $this->getComposerJsonPath(); 126 | if (File::put($composerJsonPath, $newComposerJson) === false) { 127 | throw new RuntimeException("Cannot write to composer.json [$composerJsonPath]"); 128 | } 129 | } 130 | 131 | /** 132 | * Get composer.json path. 133 | * 134 | * @return string 135 | */ 136 | protected function getComposerJsonPath() 137 | { 138 | return base_path('composer.json'); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Commands/Traits/CopiesSkeleton.php: -------------------------------------------------------------------------------- 1 | info('Copy skeleton.'); 36 | 37 | $skeletonDirPath = $this->getPathFromConfig( 38 | 'skeleton_dir_path', $this->packageBaseDir.'/skeleton' 39 | ); 40 | 41 | foreach (File::allFiles($skeletonDirPath, true) as $filePath) { 42 | $filePath = realpath($filePath); 43 | 44 | $destFilePath = Str::replaceFirst( 45 | $skeletonDirPath, $packagePath, $filePath 46 | ); 47 | 48 | $this->copyFileWithDirsCreating($filePath, $destFilePath); 49 | } 50 | 51 | $this->copyStubs($packagePath, $package, $packageFolderName); 52 | 53 | $variables = $this->getVariables( 54 | $vendor, $package, $vendorFolderName, $packageFolderName 55 | ); 56 | $this->replaceTemplates($packagePath, $variables); 57 | 58 | $this->info('Skeleton was successfully copied.'); 59 | } 60 | 61 | /** 62 | * Copy stubs. 63 | * 64 | * @param $packagePath 65 | * @param $package 66 | * @param $packageFolderName 67 | */ 68 | protected function copyStubs($packagePath, $package, $packageFolderName) 69 | { 70 | $facadeFilePath = $this->packageBaseDir.'/stubs/Facade.php.tpl'; 71 | $mainClassFilePath = $this->packageBaseDir.'/stubs/MainClass.php.tpl'; 72 | $mainClassTestFilePath = $this->packageBaseDir.'/stubs/MainClassTest.php.tpl'; 73 | $configFilePath = $this->packageBaseDir.'/stubs/config.php'; 74 | 75 | $filePaths = [ 76 | $facadeFilePath => "$packagePath/src/Facades/$package.php.tpl", 77 | $mainClassFilePath => "$packagePath/src/$package.php.tpl", 78 | $mainClassTestFilePath => "$packagePath/tests/{$package}Test.php.tpl", 79 | $configFilePath => "$packagePath/config/$packageFolderName.php", 80 | ]; 81 | 82 | foreach ($filePaths as $filePath => $destFilePath) { 83 | $this->copyFileWithDirsCreating($filePath, $destFilePath); 84 | } 85 | } 86 | 87 | /** 88 | * Substitute all variables in *.tpl files and remove tpl extension. 89 | * 90 | * @param string $packagePath 91 | * @param array $variables 92 | */ 93 | protected function replaceTemplates($packagePath, $variables) 94 | { 95 | $phpEngine = app()->make(PhpEngine::class); 96 | 97 | foreach (File::allFiles($packagePath, true) as $filePath) { 98 | $filePath = realpath($filePath); 99 | 100 | if (! Str::endsWith($filePath, '.tpl')) { 101 | continue; 102 | } 103 | 104 | try { 105 | $newFileContent = $phpEngine->get($filePath, $variables); 106 | } catch (Exception $e) { 107 | $this->error("Template [$filePath] contains syntax errors"); 108 | $this->error($e->getMessage()); 109 | continue; 110 | } 111 | 112 | $filePathWithoutTplExt = Str::replaceLast( 113 | '.tpl', '', $filePath 114 | ); 115 | 116 | File::put($filePathWithoutTplExt, $newFileContent); 117 | File::delete($filePath); 118 | } 119 | } 120 | 121 | /** 122 | * Copy source file to destination with needed directories creating. 123 | * 124 | * @param string $src 125 | * @param string $dest 126 | */ 127 | protected function copyFileWithDirsCreating($src, $dest) 128 | { 129 | $dirPathOfDestFile = dirname($dest); 130 | 131 | if (! File::exists($dirPathOfDestFile)) { 132 | File::makeDirectory($dirPathOfDestFile, 0755, true); 133 | } 134 | 135 | if (! File::exists($dest)) { 136 | File::copy($src, $dest); 137 | } 138 | } 139 | 140 | /** 141 | * Get variables for substitution in templates. 142 | * 143 | * @param string $vendor 144 | * @param string $package 145 | * @param string $vendorFolderName 146 | * @param string $packageFolderName 147 | * 148 | * @return array 149 | */ 150 | protected function getVariables( 151 | $vendor, 152 | $package, 153 | $vendorFolderName, 154 | $packageFolderName 155 | ) { 156 | $packageWords = str_replace('-', ' ', Str::snake($packageFolderName)); 157 | 158 | $composerDescription = $this->askUser( 159 | 'The composer description?', "A $packageWords" 160 | ); 161 | $composerKeywords = $this->getComposerKeywords($packageWords); 162 | 163 | $packageHumanName = $this->askUser( 164 | 'The package human name?', Str::title($packageWords) 165 | ); 166 | 167 | return [ 168 | 'vendor' => $vendor, 169 | 'package' => $package, 170 | 'vendorFolderName' => $vendorFolderName, 171 | 'packageFolderName' => $packageFolderName, 172 | 'packageHumanName' => $packageHumanName, 173 | 174 | 'composerName' => "$vendorFolderName/$packageFolderName", 175 | 'composerDesc' => $composerDescription, 176 | 'composerKeywords' => $composerKeywords, 177 | 'license' => $this->askUser('The package licence?', 'MIT'), 178 | 'phpVersion' => $this->askUser('Php version constraint?', '>=7.2'), 179 | 180 | 'aliasName' => $packageFolderName, 181 | 'configFileName' => $packageFolderName, 182 | 183 | 'year' => date('Y'), 184 | 185 | 'name' => $this->askUser('Your name?'), 186 | 'email' => $this->askUser('Your email?'), 187 | 'githubPackageUrl' => "https://github.com/$vendorFolderName/$packageFolderName", 188 | ]; 189 | } 190 | 191 | /** 192 | * Get path from config. 193 | * 194 | * @param string $configName 195 | * @param string $default 196 | * 197 | * @return string 198 | * 199 | * @throws RuntimeException 200 | */ 201 | protected function getPathFromConfig($configName, $default) 202 | { 203 | $path = config("package-generator.$configName"); 204 | 205 | if (empty($path)) { 206 | $path = $default; 207 | } else { 208 | $path = base_path($path); 209 | } 210 | 211 | $realPath = realpath($path); 212 | 213 | if ($realPath === false) { 214 | throw RuntimeException::noAccessTo($path); 215 | } 216 | 217 | return $realPath; 218 | } 219 | 220 | /** 221 | * Get composer keywords. 222 | * 223 | * @param $packageWords 224 | * 225 | * @return string 226 | */ 227 | protected function getComposerKeywords($packageWords) 228 | { 229 | $keywords = $this->askUser( 230 | 'The composer keywords? (comma delimited)', str_replace(' ', ',', $packageWords) 231 | ); 232 | $keywords = explode(',', $keywords); 233 | $keywords = array_map(function ($keyword) { 234 | return "\"$keyword\""; 235 | }, $keywords); 236 | 237 | return implode(",\n".str_repeat(' ', 4), $keywords); 238 | } 239 | } 240 | --------------------------------------------------------------------------------