diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c9c1473 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml,json}] +indent_size = 2 diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 7e21426..ae11553 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Validate composer.json and composer.lock run: composer validate @@ -20,8 +20,8 @@ jobs: - name: Install dependencies run: composer install --prefer-dist --no-progress - - name: Run test suite - run: composer test + - name: Check code style + run: composer test:lint - - name: Run php-cs-fixer - run: composer csfix . + - name: Run test suite + run: composer test:unit diff --git a/Command/Create/CommandController.php b/Command/Create/CommandController.php new file mode 100644 index 0000000..c857f1c --- /dev/null +++ b/Command/Create/CommandController.php @@ -0,0 +1,81 @@ +info('Command: '); + $command = $input->read(); + + $this->createCommandFile($this->buildCommandPath($command), $command); + } + + private function buildCommandPath(string $command): array + { + $commandsPath = realpath($this->config->app_path[0]); + $commandArray = explode(' ', $command); + $commandPartsCount = count($commandArray); + if ($commandPartsCount > 2) { + $this->error('Command name must be one or two words.'); + + return []; + } + + $commandPath = []; + + do { + $commandPart = array_shift($commandArray); + $commandPart = ucfirst(strtolower($commandPart)); + $commandPath[] = $commandPart; + + if (count($commandArray) === 0 && $commandPartsCount > 1) { + break; + } + + $dir = "{$commandsPath}/" . implode('/', $commandPath); + if (! is_dir($dir)) { + mkdir($dir); + } + } while (count($commandArray) > 0); + + return array_map(fn ($item) => ucfirst(strtolower($item)), $commandPath); + } + + private function createCommandFile(array $commandPath, string $command): void + { + if ($commandPath === []) { + return; + } + $commandsPath = realpath($this->config->app_path[0]); + $commandName = count($commandPath) > 1 ? array_pop($commandPath) : 'Default'; + $commandClass = "{$commandName}Controller"; + $commandFilePath = realpath("{$commandsPath}/" . implode('/', $commandPath)) . "/{$commandClass}.php"; + + if (file_exists($commandFilePath)) { + $this->error("Command file already exists at {$commandFilePath}"); + + return; + } + + $commandNamespace = 'namespace App\Command' . '\\' . implode('\\', $commandPath); + $commandFileContent = file_get_contents(__DIR__ . '/../../stubs/command.stub'); + $commandFileContent = str_replace( + ['{{command_namespace}}', '{{command_class}}', '{{command_name}}'], + [$commandNamespace, $commandClass, $command], + $commandFileContent + ); + + file_put_contents($commandFilePath, $commandFileContent); + + $this->success("{$command} command created!"); + } +} diff --git a/Command/Create/ContentController.php b/Command/Create/ContentController.php index 06263a3..b3a8d82 100644 --- a/Command/Create/ContentController.php +++ b/Command/Create/ContentController.php @@ -1,11 +1,13 @@ getApp()->config->has('stencil_dir')) { - $this->error("You must define a stencil_dir config option."); + if (! $this->getApp()->config->has('stencil_dir')) { + $this->error('You must define a stencil_dir config option.'); + return; } - if (!$this->getApp()->config->has('stencil_locations')) { - $this->error("You must define a stencil_locations array config option."); + if (! $this->getApp()->config->has('stencil_locations')) { + $this->error('You must define a stencil_locations array config option.'); + return; } $args = $this->getArgs(); $template_name = $args[3] ?? null; - if (!$template_name) { + if (! $template_name) { $template_name = 'post'; } @@ -34,21 +38,22 @@ public function handle(): void $input = new Input(' '); - $this->info("Content Title: "); + $this->info('Content Title: '); $title = $input->read(); - $this->info("Content Description: "); + $this->info('Content Description: '); $description = $input->read(); $content = $stencil->applyTemplate($template_name, [ 'title' => $title, - 'description' => $description + 'description' => $description, ]); $save_locations = $this->getApp()->config->stencil_locations; - if (!array_key_exists($template_name, $save_locations)) { - $this->error("Save location not found for template $template_name"); + if (! array_key_exists($template_name, $save_locations)) { + $this->error("Save location not found for template {$template_name}"); + return; } @@ -57,7 +62,7 @@ public function handle(): void $file = fopen($path . '/' . $save_name, 'a+'); fwrite($file, $content); - $this->info("Content generated at " . $path . '/' . $save_name); + $this->info('Content generated at ' . $path . '/' . $save_name); } public function slugify($title) diff --git a/Command/Create/DefaultController.php b/Command/Create/DefaultController.php index 904ee4b..985db88 100644 --- a/Command/Create/DefaultController.php +++ b/Command/Create/DefaultController.php @@ -1,15 +1,16 @@ info("./librarian create [subcommand]", true); - $this->info("Run \"./librarian create content\" to create a content file based on a template."); + $this->info('./librarian create [subcommand]', true); + $this->info('Run "./librarian create content" to create a content file based on a template.'); } } diff --git a/README.md b/README.md index 106537f..9768a42 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,18 @@ -# command-create +
+

Command Create

+

Librarian's built-in create command.

+
-Librarian's built-in create command. +## Commands Available + +### Create Content ```shell ./librarian create content [template] ``` + +### Create Command + +```shell +./librarian create command +``` diff --git a/composer.json b/composer.json index e93e1f6..39b2b8b 100644 --- a/composer.json +++ b/composer.json @@ -1,30 +1,41 @@ { - "name": "librarianphp/command-create", - "type": "library", - "description": "Librarian's built-in command to create new content", - "license": "MIT", - "homepage": "https://github.com/librarianphp/command-demo", - "keywords": ["cli","command-line", "markdown"], - "autoload": { - "psr-4": { - "librarianphp\\": "Command/" - } - }, - "require": { - "minicli/minicli": "^4.0", - "minicli/stencil": "^0.1.1" - }, - "require-dev": { - "pestphp/pest": "^2.4", - "friendsofphp/php-cs-fixer": "^3.16" - }, - "scripts": { - "test" : ["pest"], - "csfix": ["php-cs-fixer fix"] - }, - "config": { - "allow-plugins": { - "pestphp/pest-plugin": true - } + "name": "librarianphp/command-create", + "type": "library", + "description": "Librarian's built-in command to create new content", + "license": "MIT", + "homepage": "https://github.com/librarianphp/command-demo", + "keywords": [ + "cli", + "command-line", + "markdown" + ], + "autoload": { + "psr-4": { + "librarianphp\\": "Command/" } + }, + "require": { + "php": ">=8.1", + "minicli/minicli": "^4.0", + "minicli/stencil": "^0.1.1" + }, + "require-dev": { + "pestphp/pest": "^2.6", + "friendsofphp/php-cs-fixer": "^3.17", + "laravel/pint": "^1.10" + }, + "scripts": { + "lint": ["pint"], + "test:lint": ["pint --test"], + "test:unit": ["pest"], + "test": [ + "@test:lint", + "@test:unit" + ] + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + } } diff --git a/composer.lock b/composer.lock index d22a97f..4e88f17 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "34d9c79907e425762d9dd7110e76bf13", + "content-hash": "96c508cb13bbadb79ad683bcecc4154b", "packages": [ { "name": "minicli/minicli", - "version": "4.0.3", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/minicli/minicli.git", - "reference": "7ccff45d4311e31a2fba896dfe70d4d2c6671aab" + "reference": "1f71a5d8ec76d5b07280816ffcc9b36499830f43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minicli/minicli/zipball/7ccff45d4311e31a2fba896dfe70d4d2c6671aab", - "reference": "7ccff45d4311e31a2fba896dfe70d4d2c6671aab", + "url": "https://api.github.com/repos/minicli/minicli/zipball/1f71a5d8ec76d5b07280816ffcc9b36499830f43", + "reference": "1f71a5d8ec76d5b07280816ffcc9b36499830f43", "shasum": "" }, "require": { @@ -32,6 +32,9 @@ }, "type": "library", "autoload": { + "files": [ + "src/helpers.php" + ], "psr-4": { "Minicli\\": "src/" } @@ -48,7 +51,7 @@ ], "support": { "issues": "https://github.com/minicli/minicli/issues", - "source": "https://github.com/minicli/minicli/tree/4.0.3" + "source": "https://github.com/minicli/minicli/tree/4.0.5" }, "funding": [ { @@ -56,7 +59,7 @@ "type": "github" } ], - "time": "2023-05-20T22:04:58+00:00" + "time": "2023-06-07T18:49:30+00:00" }, { "name": "minicli/stencil", @@ -495,25 +498,29 @@ }, { "name": "doctrine/deprecations", - "version": "v1.1.0", + "version": "v1.1.1", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "8cffffb2218e01f3b370bf763e00e81697725259" + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/8cffffb2218e01f3b370bf763e00e81697725259", - "reference": "8cffffb2218e01f3b370bf763e00e81697725259", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", + "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3", "shasum": "" }, "require": { - "php": "^7.1|^8.0" + "php": "^7.1 || ^8.0" }, "require-dev": { "doctrine/coding-standard": "^9", - "phpunit/phpunit": "^7.5|^8.5|^9.5", - "psr/log": "^1|^2|^3" + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -532,9 +539,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/v1.1.0" + "source": "https://github.com/doctrine/deprecations/tree/v1.1.1" }, - "time": "2023-05-29T18:55:17+00:00" + "time": "2023-06-03T09:27:29+00:00" }, { "name": "doctrine/lexer", @@ -900,6 +907,72 @@ }, "time": "2021-10-08T21:21:46+00:00" }, + { + "name": "laravel/pint", + "version": "v1.10.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/pint.git", + "reference": "d69f914aa347a448628b672ba90adf0b4ea0ce4a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/pint/zipball/d69f914aa347a448628b672ba90adf0b4ea0ce4a", + "reference": "d69f914aa347a448628b672ba90adf0b4ea0ce4a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-mbstring": "*", + "ext-tokenizer": "*", + "ext-xml": "*", + "php": "^8.1.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.17.0", + "illuminate/view": "^10.5.1", + "laravel-zero/framework": "^10.0.2", + "mockery/mockery": "^1.5.1", + "nunomaduro/larastan": "^2.5.1", + "nunomaduro/termwind": "^1.15.1", + "pestphp/pest": "^2.4.0" + }, + "bin": [ + "builds/pint" + ], + "type": "project", + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Seeders\\": "database/seeders/", + "Database\\Factories\\": "database/factories/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "An opinionated code formatter for PHP.", + "homepage": "https://laravel.com", + "keywords": [ + "format", + "formatter", + "lint", + "linter", + "php" + ], + "support": { + "issues": "https://github.com/laravel/pint/issues", + "source": "https://github.com/laravel/pint" + }, + "time": "2023-06-03T15:01:17+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.1", @@ -1199,16 +1272,16 @@ }, { "name": "pestphp/pest", - "version": "v2.6.1", + "version": "v2.6.3", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "faafedd55ca4479b0634f85cc1a68bf5af44764e" + "reference": "3c20e8114e5d2f5e39cf013f0f9b8ebc0ac1a6fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/faafedd55ca4479b0634f85cc1a68bf5af44764e", - "reference": "faafedd55ca4479b0634f85cc1a68bf5af44764e", + "url": "https://api.github.com/repos/pestphp/pest/zipball/3c20e8114e5d2f5e39cf013f0f9b8ebc0ac1a6fa", + "reference": "3c20e8114e5d2f5e39cf013f0f9b8ebc0ac1a6fa", "shasum": "" }, "require": { @@ -1216,17 +1289,17 @@ "nunomaduro/collision": "^7.5.2", "nunomaduro/termwind": "^1.15.1", "pestphp/pest-plugin": "^2.0.1", - "pestphp/pest-plugin-arch": "^2.1.2", + "pestphp/pest-plugin-arch": "^2.2.0", "php": "^8.1.0", - "phpunit/phpunit": "^10.1.3" + "phpunit/phpunit": "^10.2.1" }, "conflict": { - "phpunit/phpunit": ">10.1.3", + "phpunit/phpunit": ">10.2.1", "webmozart/assert": "<1.11.0" }, "require-dev": { - "pestphp/pest-dev-tools": "^2.9.0", - "symfony/process": "^6.2.10" + "pestphp/pest-dev-tools": "^2.10.0", + "symfony/process": "^6.3.0" }, "bin": [ "bin/pest" @@ -1282,7 +1355,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.6.1" + "source": "https://github.com/pestphp/pest/tree/v2.6.3" }, "funding": [ { @@ -1294,7 +1367,7 @@ "type": "github" } ], - "time": "2023-05-12T08:22:02+00:00" + "time": "2023-06-07T19:19:04+00:00" }, { "name": "pestphp/pest-plugin", @@ -1367,27 +1440,27 @@ }, { "name": "pestphp/pest-plugin-arch", - "version": "v2.1.2", + "version": "v2.2.0", "source": { "type": "git", "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "485cbfbe2e194e9cfd8284625bd8922c9d27ac6f" + "reference": "88725fd0d6ae4025df39c27bd91e98d14b8f1916" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/485cbfbe2e194e9cfd8284625bd8922c9d27ac6f", - "reference": "485cbfbe2e194e9cfd8284625bd8922c9d27ac6f", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/88725fd0d6ae4025df39c27bd91e98d14b8f1916", + "reference": "88725fd0d6ae4025df39c27bd91e98d14b8f1916", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.5.0", + "nunomaduro/collision": "^7.5.2", "pestphp/pest-plugin": "^2.0.1", "php": "^8.1", "ta-tikoma/phpunit-architecture-test": "^0.7.3" }, "require-dev": { - "pestphp/pest": "^2.5.1", - "pestphp/pest-dev-tools": "^2.6.0" + "pestphp/pest": "dev-develop as 2.6.2", + "pestphp/pest-dev-tools": "^2.10.0" }, "type": "library", "autoload": { @@ -1415,7 +1488,7 @@ "unit" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.1.2" + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.2.0" }, "funding": [ { @@ -1427,7 +1500,7 @@ "type": "github" } ], - "time": "2023-04-19T08:48:22+00:00" + "time": "2023-06-02T23:15:55+00:00" }, { "name": "phar-io/manifest", @@ -2077,16 +2150,16 @@ }, { "name": "phpunit/phpunit", - "version": "10.1.3", + "version": "10.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2379ebafc1737e71cdc84f402acb6b7f04198b9d" + "reference": "599b33294350e8f51163119d5670512f98b0490d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2379ebafc1737e71cdc84f402acb6b7f04198b9d", - "reference": "2379ebafc1737e71cdc84f402acb6b7f04198b9d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/599b33294350e8f51163119d5670512f98b0490d", + "reference": "599b33294350e8f51163119d5670512f98b0490d", "shasum": "" }, "require": { @@ -2126,7 +2199,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "10.2-dev" } }, "autoload": { @@ -2158,7 +2231,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.3" + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.1" }, "funding": [ { @@ -2174,7 +2247,7 @@ "type": "tidelift" } ], - "time": "2023-05-11T05:16:22+00:00" + "time": "2023-06-05T05:15:51+00:00" }, { "name": "psr/cache", @@ -4751,7 +4824,9 @@ "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, - "platform": [], + "platform": { + "php": ">=8.1" + }, "platform-dev": [], "plugin-api-version": "2.3.0" } diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..ba1ad05 --- /dev/null +++ b/pint.json @@ -0,0 +1,56 @@ +{ + "preset": "laravel", + "rules": { + "array_push": true, + "assign_null_coalescing_to_coalesce_equal": true, + "combine_consecutive_issets": true, + "combine_consecutive_unsets": true, + "concat_space": { + "spacing": "one" + }, + "declare_strict_types": true, + "explicit_indirect_variable": true, + "explicit_string_variable": true, + "global_namespace_import": true, + "method_argument_space": { + "on_multiline": "ensure_fully_multiline" + }, + "modernize_strpos": true, + "modernize_types_casting": true, + "new_with_braces": true, + "no_superfluous_elseif": true, + "no_useless_else": true, + "nullable_type_declaration_for_default_null_value": true, + "ordered_imports": { + "sort_algorithm": "alpha" + }, + "ordered_class_elements": { + "order": [ + "use_trait", + "case", + "constant", + "constant_public", + "constant_protected", + "constant_private", + "property_public", + "property_protected", + "property_private", + "construct", + "destruct", + "magic", + "phpunit", + "method_abstract", + "method_public_static", + "method_public", + "method_protected_static", + "method_protected", + "method_private_static", + "method_private" + ], + "sort_algorithm": "none" + }, + "strict_comparison": true, + "ternary_to_null_coalescing": true, + "use_arrow_functions": true + } +} diff --git a/stubs/command.stub b/stubs/command.stub new file mode 100644 index 0000000..4b0d976 --- /dev/null +++ b/stubs/command.stub @@ -0,0 +1,15 @@ +display('Hello from {{command_name}}!'); + } +} diff --git a/tests/Feature/CommandTest.php b/tests/Feature/CommandTest.php index 03a05bf..2cb3ccd 100644 --- a/tests/Feature/CommandTest.php +++ b/tests/Feature/CommandTest.php @@ -1,11 +1,13 @@ runCommand(['minicli', 'create']); -})->expectOutputRegex("/librarian create/"); +})->expectOutputRegex('/librarian create/'); test('command "create content" throws error if stencil_dir is not defined', function () { $app = getMinicli(); $app->runCommand(['minicli', 'create', 'content']); -})->expectOutputRegex("/You must define a stencil_dir config option/"); +})->expectOutputRegex('/You must define a stencil_dir config option/'); diff --git a/tests/Pest.php b/tests/Pest.php index 8f64f63..926538d 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,7 @@ extend('toBeOne', function () { - return $this->toBe(1); -}); +expect()->extend('toBeOne', fn () => $this->toBe(1)); /* |-------------------------------------------------------------------------- @@ -45,8 +45,8 @@ function getMinicli() { return new App([ 'app_path' => [ - __DIR__ . '/../Command' + __DIR__ . '/../Command', ], - 'debug' => true + 'debug' => true, ]); } diff --git a/tests/TestCase.php b/tests/TestCase.php index cfb05b6..d42f312 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,5 +1,7 @@