From 4b000375333f5ca730c7ca5f5f090cc95b619ca5 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Fri, 25 Sep 2026 20:09:37 -0500 Subject: [PATCH 1/4] add hints for missing commands Assisted-by: Claude Code --- src/Commands.php | 10 +++++++- src/Console/Registry.php | 49 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/Commands.php b/src/Commands.php index 82bea15..5271b2e 100644 --- a/src/Commands.php +++ b/src/Commands.php @@ -53,6 +53,13 @@ final class Commands */ public const PACKAGE = 'phalcon/crest'; + /** + * Command-name prefix => the package that provides those commands. When + * the package is not installed, the error for such a command names it. + * Strings only, so nothing breaks when a package changes its classes. + */ + private const PROVIDERS = ['migration:' => 'phalcon/migrations']; + /** * The registry the binary runs on: crest's own commands, plus anything * installed packages contribute through `extra.crest.commands`. @@ -77,6 +84,7 @@ public static function registry(): Registry ->add('serve', ServeCommand::class, 'server') ->add('stub:publish', StubPublishCommand::class) ->add('up', UpCommand::class) - ->withDiscovery(self::KEY); + ->withDiscovery(self::KEY) + ->withProviders(self::PROVIDERS); } } diff --git a/src/Console/Registry.php b/src/Console/Registry.php index b979ac3..4ab0482 100644 --- a/src/Console/Registry.php +++ b/src/Console/Registry.php @@ -17,11 +17,13 @@ use Crest\Console\Command\Command; use Crest\Console\Exceptions\Exception; +use function array_keys; use function class_exists; use function is_array; use function is_string; use function ksort; use function sprintf; +use function str_starts_with; /** * Lazy name-to-class map. Resolution instantiates nothing; the kernel @@ -49,6 +51,9 @@ final class Registry private ?string $discoveryKey = null; + /** @var array */ + private array $providers = []; + /** * @param array> $map */ @@ -121,7 +126,7 @@ public function get(string $name): string $resolved = $this->resolve($name); return $this->commands[$resolved] - ?? throw new Exception(sprintf("unknown command '%s'", $name)); + ?? throw new Exception(sprintf("unknown command '%s'%s", $name, $this->provider($name))); } public function has(string $name): bool @@ -144,6 +149,23 @@ public function withDiscovery(string $key): static return $this; } + /** + * Records which package provides the commands with a name prefix, for + * example `demo:` => `vendor/demo`. For an unknown name with that prefix, + * get() names the package in its message. + * + * Strings only: the owning tool names the packages, and nothing here + * loads their classes. + * + * @param array $providers Name prefix => package name. + */ + public function withProviders(array $providers): static + { + $this->providers = $providers; + + return $this; + } + /** * Registers whatever one package contributes under the discovery key. * @@ -210,6 +232,31 @@ private function discover(): void } } + /** + * The end of the message for an unknown name: `; provided by ` + * for the first prefix that the name starts with. Empty when no prefix + * matches, or when a registered command has the prefix: then the package + * is installed, and only the name is wrong. + */ + private function provider(string $name): string + { + foreach ($this->providers as $prefix => $package) { + if (false === str_starts_with($name, $prefix)) { + continue; + } + + foreach (array_keys($this->commands) as $command) { + if (true === str_starts_with($command, $prefix)) { + return ''; + } + } + + return sprintf('; provided by %s', $package); + } + + return ''; + } + /** * Canonical name for a name or alias. A miss triggers discovery once, then * re-resolves - a package may contribute the alias as well as the command. From ad71286c36b35d0527f42ee4b8b29579a736a742 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Fri, 25 Sep 2026 20:09:49 -0500 Subject: [PATCH 2/4] adding tests Assisted-by: Claude Code --- tests/Unit/CommandsTest.php | 11 ++++++ tests/Unit/Console/RegistryTest.php | 55 +++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/tests/Unit/CommandsTest.php b/tests/Unit/CommandsTest.php index e0e298a..e33bb36 100644 --- a/tests/Unit/CommandsTest.php +++ b/tests/Unit/CommandsTest.php @@ -15,6 +15,7 @@ use Crest\Command\ServeCommand; use Crest\Commands; +use Crest\Console\Exceptions\Exception; use PHPUnit\Framework\TestCase; use function array_keys; @@ -71,6 +72,16 @@ public function testRegistryIsSeeded(): void $this->assertNotSame([], Commands::registry()->all()); } + public function testRegistryNamesThePackageThatProvidesMigrationCommands(): void + { + // phalcon/migrations is not installed here. Crest cannot require it, + // not even for a test, because it needs PHP 8.2. + $this->expectException(Exception::class); + $this->expectExceptionMessage("unknown command 'migration:run'; provided by phalcon/migrations"); + + Commands::registry()->get('migration:run'); + } + public function testRegistryResolvesTheAboutAliases(): void { $registry = Commands::registry(); diff --git a/tests/Unit/Console/RegistryTest.php b/tests/Unit/Console/RegistryTest.php index 32c4273..d014dc9 100644 --- a/tests/Unit/Console/RegistryTest.php +++ b/tests/Unit/Console/RegistryTest.php @@ -162,6 +162,61 @@ public function testDiscoveryRunsOnlyOnce(): void $this->assertSame(['fake' => FakeCommand::class], $registry->all()); } + public function testGetGivesNoProviderForANameOutsideEveryPrefix(): void + { + // Anchored: the message with a provider starts the same way. + $registry = (new Registry())->withProviders(['demo:' => 'vendor/demo']); + + $this->expectException(Exception::class); + $this->expectExceptionMessageMatches("/^unknown command 'other'$/"); + + $registry->get('other'); + } + + public function testGetGivesNoProviderWhenACommandHasThePrefix(): void + { + // A command with the prefix exists, so the package is installed and + // only the name is wrong. + $registry = (new Registry()) + ->add('demo:run', FakeCommand::class) + ->withProviders(['demo:' => 'vendor/demo']); + + $this->expectException(Exception::class); + $this->expectExceptionMessageMatches("/^unknown command 'demo:rnu'$/"); + + $registry->get('demo:rnu'); + } + + public function testGetGivesNoProviderWhenADiscoveredCommandHasThePrefix(): void + { + // The same rule for a command that a package contributes: the miss + // runs discovery before the message is made. + $this->installExtra([self::KEY => ['commands' => ['demo:run' => FakeCommand::class]]]); + + $registry = (new Registry()) + ->withDiscovery(self::KEY) + ->withProviders(['demo:' => 'vendor/demo']); + + $this->expectException(Exception::class); + $this->expectExceptionMessageMatches("/^unknown command 'demo:rnu'$/"); + + $registry->get('demo:rnu'); + } + + public function testGetNamesTheProviderOfAnUnknownCommand(): void + { + // The first prefix does not match, so the search must go past it. + $registry = (new Registry())->withProviders([ + 'other:' => 'vendor/other', + 'demo:' => 'vendor/demo', + ]); + + $this->expectException(Exception::class); + $this->expectExceptionMessage("unknown command 'demo:run'; provided by vendor/demo"); + + $registry->get('demo:run'); + } + public function testGetStillThrowsAfterDiscoveryFindsNothing(): void { // A miss triggers the deferred scan; nothing in the test environment From 909ffc8c89786413a5e1452b5b82bcbec96232f2 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Fri, 25 Sep 2026 20:10:29 -0500 Subject: [PATCH 3/4] upcatig changelog Assisted-by: Claude Code --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 706c35b..c129169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ All notable changes are documented here. The format is based on [Keep a Changelo - Added `Crest\Generator\ClassName::namespace()`, validating a namespace with the same identifier rule as a class name. [#8](https://github.com/phalcon/crest/issues/8) - Added `Crest\Command\Make\NamedArtifactCommand`, the base of `make:command`, `make:middleware`, `make:provider` and `make:responder`. The four commands repeated the same `handle()` and `define()`; each now gives only its key, its suffix, its description, an example name and the instructions it prints after the file is written. The base declares the `name` argument and the `--force` option, because `handle()` reads both, so a new generator cannot leave them out. - Added `serve` (alias `server`), running PHP's built-in web server in the project root with the router script that `new` writes: `php -S 127.0.0.1:8080 -t public .htrouter.php`, with the same router and document root as the generated container. The port is `--port`, else `APP_PORT` from the environment or the project `.env`, else 8080, the order that docker compose uses. It finds the root from a subdirectory through the nearest `crest.php`, and it does not need the crest in `vendor/`. It stops before PHP starts when `.htrouter.php` or `vendor/autoload.php` is missing. `new` and the generated README now print `crest serve` for the host way. [#10](https://github.com/phalcon/crest/issues/10) +- Added a hint for an unknown command that a package provides. Without `phalcon/migrations`, `crest migration:run` now prints `unknown command 'migration:run'; provided by phalcon/migrations`. When a command with that prefix is registered, the package is installed, so a wrong name gets the plain message. `Crest\Console\Registry::withProviders()` takes the map of name prefix to package name; crest gives it in `Crest\Commands`. [#19](https://github.com/phalcon/crest/issues/19) ### Changed From 0621bd23ddfffd19f222f93d64911f17221f8606 Mon Sep 17 00:00:00 2001 From: Nikolaos Dimopoulos Date: Fri, 25 Sep 2026 20:13:56 -0500 Subject: [PATCH 4/4] updating changelog Assisted-by: Claude Code --- CHANGELOG.md | 98 ++++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c129169..e4e6431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,64 +6,64 @@ All notable changes are documented here. The format is based on [Keep a Changelo ### Added -- Added `route:list`, listing every route the application answers with its method and Action class. Reads the Action classes rather than a route table, since ADR has none. [#1](https://github.com/phalcon/crest/issues/1) -- Added `config:show`, showing the resolved project configuration and marking each value as declared or inferred. [#1](https://github.com/phalcon/crest/issues/1) -- Added `container:list`, listing the services registered in the project container with their class and whether they have been resolved. [#1](https://github.com/phalcon/crest/issues/1) -- Added `event:list`, listing the listeners attached to the project events manager. [#1](https://github.com/phalcon/crest/issues/1) -- Added `list` (aliases `commands`, `enumerate`), listing the available commands. [#1](https://github.com/phalcon/crest/issues/1) -- Added the `bootstrap` key to `crest.php`, naming the project front controller so commands that need a running application can boot one: `'bootstrap' => App\Front\AppFront::class`. Requires a `boot()` returning a container. [#1](https://github.com/phalcon/crest/issues/1) -- Added `Crest\Console\Input::argumentString()`, `optionString()` and `optionStringOrNull()`, narrowing the common string case so commands do not each repeat a type guard. -- Added `make:command`, generating a crest command for a package that contributes its own. Prints the `extra.crest.commands` block to declare it with, since that is the only way the registry finds a command. [#5](https://github.com/phalcon/crest/issues/5) -- Added `make:middleware`, generating an ADR middleware and printing the router middleware-map entry that activates it. [#5](https://github.com/phalcon/crest/issues/5) -- Added `make:provider`, generating a service provider for `Phalcon\Container` and printing the `registerProviders()` override that calls it. [#5](https://github.com/phalcon/crest/issues/5) -- Added `make:responder`, generating an ADR responder that implements the `Responder` contract directly. [#5](https://github.com/phalcon/crest/issues/5) -- Added `stub:publish`, copying packaged stubs into `resources/stubs//` so a project can edit them. The override chain already worked; nothing made it discoverable. [#5](https://github.com/phalcon/crest/issues/5) -- Added `--stub` to `make:action`, rendering any named stub instead of the `--responder` default. Passing both is rejected rather than silently resolved. [#5](https://github.com/phalcon/crest/issues/5) -- Added `command`, `middleware`, `provider` and `responder` to the default `paths` in `crest.php`, alongside `action`. Each is overridable per project as before. [#5](https://github.com/phalcon/crest/issues/5) -- Added `Crest\Command\ProjectCommand`, the base for commands that read the project being run against. Contributed commands can extend it for `--directory` and `--config` handling instead of resolving those options themselves. [#5](https://github.com/phalcon/crest/issues/5) -- Added `Crest\Generator\ClassName::suffixed()`, which appends an artifact suffix idempotently, so `make:middleware Cors` and `make:middleware CorsMiddleware` both produce `CorsMiddleware`. [#5](https://github.com/phalcon/crest/issues/5) -- Added `--template` to `make:action`, naming the template the view responder renders instead of accepting the derived `/index`. The derivation is crest's own convention, not the framework's - `withTemplate()` accepts any string - so it is now overridable rather than silent. [#5](https://github.com/phalcon/crest/issues/5) -- Added `Crest\Command\ProjectCommand::writer()`, assembling the stub writer once instead of repeating the same three-argument construction in five `make:*` commands. [#5](https://github.com/phalcon/crest/issues/5) -- Added `methodFor()` to `Crest\ADR\ActionResolver`, so the HTTP method an Action answers is asked of the framework rather than derived from the class name. [#1](https://github.com/phalcon/crest/issues/1) -- Added `Crest\Console\Registry::descriptions()` and `Crest\Console\Output::commandTable()`, so a bare `crest` and `crest list` render the command listing through one path instead of two copies kept in agreement by hand. -- Added `new`, creating an ADR project from stubs: front controller, web entry point, `crest.php`, an action for `GET /`, `composer.json` and docker files. It runs nothing - no composer, no docker, no network. `--namespace`, `--php` and `--phalcon` set the root namespace, the PHP version and the Phalcon variant. The project requires `phalcon/crest` as a dev dependency: the commands that work on the project need its autoloader and its Phalcon, so they run as `vendor/bin/crest`, not with the crest that created the project. The generated `crest.php` states `paths.action`, so a later crest default does not move the actions. `new` renders every file before it writes the first one, so a published stub that does not render leaves no files. [#8](https://github.com/phalcon/crest/issues/8) -- Added `up`, `down` and `install`, running `docker compose up -d`, `docker compose down` and `composer install` in the `app` container of a project that `new` created. `--directory` names the project. [#8](https://github.com/phalcon/crest/issues/8) -- Added `Crest\Process\Runner`, the seam through which commands run external programs, with `ShellRunner` as the default. A missing program or working directory is reported as a crest error. [#8](https://github.com/phalcon/crest/issues/8) -- Added `Crest\Generator\ClassName::namespace()`, validating a namespace with the same identifier rule as a class name. [#8](https://github.com/phalcon/crest/issues/8) -- Added `Crest\Command\Make\NamedArtifactCommand`, the base of `make:command`, `make:middleware`, `make:provider` and `make:responder`. The four commands repeated the same `handle()` and `define()`; each now gives only its key, its suffix, its description, an example name and the instructions it prints after the file is written. The base declares the `name` argument and the `--force` option, because `handle()` reads both, so a new generator cannot leave them out. -- Added `serve` (alias `server`), running PHP's built-in web server in the project root with the router script that `new` writes: `php -S 127.0.0.1:8080 -t public .htrouter.php`, with the same router and document root as the generated container. The port is `--port`, else `APP_PORT` from the environment or the project `.env`, else 8080, the order that docker compose uses. It finds the root from a subdirectory through the nearest `crest.php`, and it does not need the crest in `vendor/`. It stops before PHP starts when `.htrouter.php` or `vendor/autoload.php` is missing. `new` and the generated README now print `crest serve` for the host way. [#10](https://github.com/phalcon/crest/issues/10) -- Added a hint for an unknown command that a package provides. Without `phalcon/migrations`, `crest migration:run` now prints `unknown command 'migration:run'; provided by phalcon/migrations`. When a command with that prefix is registered, the package is installed, so a wrong name gets the plain message. `Crest\Console\Registry::withProviders()` takes the map of name prefix to package name; crest gives it in `Crest\Commands`. [#19](https://github.com/phalcon/crest/issues/19) +- Added `route:list`: lists routes with their method and Action class. [#1](https://github.com/phalcon/crest/issues/1) +- Added `config:show`: shows the resolved configuration, each value marked declared or inferred. [#1](https://github.com/phalcon/crest/issues/1) +- Added `container:list`: lists container services with their class and resolved state. [#1](https://github.com/phalcon/crest/issues/1) +- Added `event:list`: lists the events manager listeners. [#1](https://github.com/phalcon/crest/issues/1) +- Added `list` (aliases `commands`, `enumerate`). [#1](https://github.com/phalcon/crest/issues/1) +- Added the `bootstrap` key to `crest.php`: the front controller class to boot, e.g. `App\Front\AppFront::class`. Its `boot()` must return a container. [#1](https://github.com/phalcon/crest/issues/1) +- Added `Crest\Console\Input::argumentString()`, `optionString()` and `optionStringOrNull()`. +- Added `make:command`: generates a crest command and prints its `extra.crest.commands` entry. [#5](https://github.com/phalcon/crest/issues/5) +- Added `make:middleware`: generates an ADR middleware and prints its middleware-map entry. [#5](https://github.com/phalcon/crest/issues/5) +- Added `make:provider`: generates a `Phalcon\Container` service provider and prints the `registerProviders()` override. [#5](https://github.com/phalcon/crest/issues/5) +- Added `make:responder`: generates an ADR responder that implements `Responder`. [#5](https://github.com/phalcon/crest/issues/5) +- Added `stub:publish`: copies packaged stubs to `resources/stubs//`. [#5](https://github.com/phalcon/crest/issues/5) +- Added `make:action --stub`: renders a named stub instead of the `--responder` default. Using both is an error. [#5](https://github.com/phalcon/crest/issues/5) +- Added `command`, `middleware`, `provider` and `responder` to the default `paths`. [#5](https://github.com/phalcon/crest/issues/5) +- Added `Crest\Command\ProjectCommand`: base for commands that read a project; resolves `--directory` and `--config`. [#5](https://github.com/phalcon/crest/issues/5) +- Added `Crest\Generator\ClassName::suffixed()`: appends a suffix once (`Cors` and `CorsMiddleware` both give `CorsMiddleware`). [#5](https://github.com/phalcon/crest/issues/5) +- Added `make:action --template`: overrides the view template (default `/index`). [#5](https://github.com/phalcon/crest/issues/5) +- Added `Crest\Command\ProjectCommand::writer()`: builds the stub writer for the `make:*` commands. [#5](https://github.com/phalcon/crest/issues/5) +- Added `Crest\ADR\ActionResolver::methodFor()`: gets an Action's HTTP method from the router. [#1](https://github.com/phalcon/crest/issues/1) +- Added `Crest\Console\Registry::descriptions()` and `Crest\Console\Output::commandTable()`: one listing for `crest` and `crest list`. +- Added `new`: creates an ADR project from stubs (front controller, `public/index.php`, `crest.php`, a `GET /` action, `composer.json`, docker files). Runs no composer, docker or network. Options: `--namespace`, `--php`, `--phalcon`. The project requires `phalcon/crest` in `require-dev`; project commands run as `vendor/bin/crest`. Writes nothing if a stub fails to render. [#8](https://github.com/phalcon/crest/issues/8) +- Added `up`, `down` and `install`: `docker compose up -d`, `docker compose down` and `composer install` in the `app` container. `--directory` names the project. [#8](https://github.com/phalcon/crest/issues/8) +- Added `Crest\Process\Runner` and `ShellRunner`: run external programs. A missing program or directory is a crest error. [#8](https://github.com/phalcon/crest/issues/8) +- Added `Crest\Generator\ClassName::namespace()`: validates a namespace. [#8](https://github.com/phalcon/crest/issues/8) +- Added `Crest\Command\Make\NamedArtifactCommand`: base of `make:command`, `make:middleware`, `make:provider` and `make:responder`. +- Added `serve` (alias `server`): runs `php -S 127.0.0.1:8080 -t public .htrouter.php` in the project root. Port: `--port`, then `APP_PORT` (environment or `.env`), then 8080. Fails if `.htrouter.php` or `vendor/autoload.php` is missing. [#10](https://github.com/phalcon/crest/issues/10) +- Added a package hint for unknown commands: `unknown command 'migration:run'; provided by phalcon/migrations`. No hint when a command with that prefix is registered. The map is set with `Crest\Console\Registry::withProviders()`. [#19](https://github.com/phalcon/crest/issues/19) ### Changed -- `make:action` now writes a `params()` declaration for routes with attributes, so they arrive constrained and cast rather than as raw strings. -- `make:action` now rejects a static segment after a placeholder and suggests the supported spelling: `/album/{id}/edit` is reported as `/album/edit/{id}`. Arguments trail the static path, so the first form has no class name that describes it. -- Renamed `Crest\Adr` to `Crest\ADR`, and `Flavor::Adr`, `Flavor::Cli` and `Flavor::Mvc` to `Flavor::ADR`, `Flavor::CLI` and `Flavor::MVC`, matching `Phalcon\ADR`. Backed values are unchanged. -- Renamed `Crest\ADR\CandidateSource` to `ActionResolver` and `PhalconRouterCandidates` to `PhalconRouterResolver`. One path now names exactly one Action, so there are no candidates to choose between. -- Dependencies now resolve against the PHP 8.1 floor via `config.platform`, so the lock matches the declared minimum. -- Default `paths` are now per flavor rather than shared. Only `adr` is populated, so a `cli` or `mvc` project is no longer offered directories for artifacts it has no command to generate. [#5](https://github.com/phalcon/crest/issues/5) -- `crest`, `crest list` and `crest --version` now open with a chevron mark before the name and version. Only the color is dropped from piped output and when `NO_COLOR` is set; the glyph stays. [#5](https://github.com/phalcon/crest/issues/5) -- `make:action --responder=view` now prints the template the responder asks for. The action named one but nothing created it, and `Renderer::render()` takes a name rather than a path, so where it resolves belongs to the project's renderer. [#5](https://github.com/phalcon/crest/issues/5) -- `route:list` and `make:action` now accept an `ActionResolver`, defaulted so the kernel still constructs them with no arguments. This is what lets a test prove the routing answers come from the framework rather than from crest. [#5](https://github.com/phalcon/crest/issues/5) -- `event:list` now reads every listener in a single `getListenerMap()` call instead of one call per event type. [#1](https://github.com/phalcon/crest/issues/1) -- `phalcon/talon` moved from `^0.8` to `^0.9`. -- `stub:publish` with no name leaves out the `project-*` stubs that `new` renders. They have an effect only in the directory that `new` puts the project into; publish one by name. A `project-*` name goes where `new` reads it - the working directory, or `--directory` - with the `adr` flavor, and needs no `crest.php`. [#8](https://github.com/phalcon/crest/issues/8) -- Rendering a stub now fails when a placeholder has no value, and the error names the stub file. A published copy that kept a placeholder crest no longer sends put the raw `{{ name }}` into the generated file. [#8](https://github.com/phalcon/crest/issues/8) -- With no `crest.php`, the error for a `composer.json` whose psr-4 directories do not exist now names those directories: `no crest.php and no usable psr-4 autoload entry found; these psr-4 directories do not exist: 'src'`. Before, the message did not say that a missing directory was the cause. [#18](https://github.com/phalcon/crest/issues/18) +- `make:action` writes a `params()` declaration for routes with attributes. +- `make:action` rejects a static segment after a placeholder: `/album/{id}/edit` must be `/album/edit/{id}`. +- Renamed `Crest\Adr` to `Crest\ADR`, and the `Flavor` cases to `ADR`, `CLI` and `MVC`. Backed values are unchanged. +- Renamed `Crest\ADR\CandidateSource` to `ActionResolver`, and `PhalconRouterCandidates` to `PhalconRouterResolver`. +- Dependencies resolve against PHP 8.1 (`config.platform`). +- Default `paths` are per flavor. Only `adr` has defaults. [#5](https://github.com/phalcon/crest/issues/5) +- `crest`, `crest list` and `crest --version` show a chevron before the name. No color when piped or with `NO_COLOR`. [#5](https://github.com/phalcon/crest/issues/5) +- `make:action --responder=view` prints the template name. [#5](https://github.com/phalcon/crest/issues/5) +- `route:list` and `make:action` take an optional `ActionResolver`. [#5](https://github.com/phalcon/crest/issues/5) +- `event:list` reads all listeners with one `getListenerMap()` call. [#1](https://github.com/phalcon/crest/issues/1) +- `phalcon/talon` moved from `^0.8` to `^1.0.0`. +- `stub:publish` without a name skips the `project-*` stubs. Publish them by name; they go to the working directory or `--directory`. [#8](https://github.com/phalcon/crest/issues/8) +- Rendering a stub fails when a placeholder has no value. The error names the stub. [#8](https://github.com/phalcon/crest/issues/8) +- The config inference error names the missing psr-4 directories. [#18](https://github.com/phalcon/crest/issues/18) ### Fixed -- `make:middleware`, `make:provider` and `make:responder` no longer generate a class that cannot be parsed when the name given is already the suffix. `make:middleware Middleware` produced `final class Middleware implements Middleware` beside `use ...\Middleware;`. The contract is now imported under an alias. [#5](https://github.com/phalcon/crest/issues/5) -- Generators now fail instead of reporting a file they did not write. A target that could not be created produced two PHP warnings, `Created ` and exit 0; it now reports `could not create ` and exits 1. [#5](https://github.com/phalcon/crest/issues/5) -- `stub:publish` now rejects a name that is a path. `stub:publish ../../elsewhere/thing` resolved and copied a file from outside the package. [#5](https://github.com/phalcon/crest/issues/5) -- `ClassName::suffixed()` now accepts non-Latin class names, matching PHP's own rule for an identifier. [#5](https://github.com/phalcon/crest/issues/5) -- `container:list` and `event:list` no longer reach past Phalcon's published contracts. Both type against `Phalcon\Contracts\Container\Service\Collection` and the new `Enumerable` contracts instead of the concrete `Container` and `Manager`, and `event:list` no longer probes the container with `method_exists()`. The methods they relied on were absent from every published interface, so narrowing the concrete classes would have broken crest without breaking any contract. [#1](https://github.com/phalcon/crest/issues/1) -- `route:list` no longer derives the HTTP method from the class name. The verb's position in an Action name is part of the framework's naming rule, and reconstructing it here was a second copy of half the convention in a tool nobody would grep when the rule changed. [#1](https://github.com/phalcon/crest/issues/1) +- `make:middleware`, `make:provider` and `make:responder` generate valid code when the name is the suffix (`make:middleware Middleware`). [#5](https://github.com/phalcon/crest/issues/5) +- Generators exit 1 with `could not create ` when the target cannot be created. [#5](https://github.com/phalcon/crest/issues/5) +- `stub:publish` rejects a name that is a path. [#5](https://github.com/phalcon/crest/issues/5) +- `ClassName::suffixed()` accepts non-Latin class names. [#5](https://github.com/phalcon/crest/issues/5) +- `container:list` and `event:list` type against Phalcon's published contracts (`Phalcon\Contracts\Container\Service\Collection`, `Enumerable`), not `Container` and `Manager`. [#1](https://github.com/phalcon/crest/issues/1) +- `route:list` gets the HTTP method from the router, not from the class name. [#1](https://github.com/phalcon/crest/issues/1) ### Removed -- Removed the shadowed-action warning from `make:action`. One path names exactly one Action, so nothing can be shadowed. -- Removed the `phalcon/cli-options-parser` requirement. Crest never linked against it: the schema-aware definition layer stays in `Crest\Console\Parsing`, since `Cop\Parser` is schema-less by design. +- Removed the shadowed-action warning from `make:action`. +- Removed the `phalcon/cli-options-parser` requirement. [keep_a_changelog]: https://keepachangelog.com/en/1.0.0/ [semantic_versioning]: https://semver.org/spec/v2.0.0.html