Skip to content

Commit

Permalink
Merge branch '8.x'
Browse files Browse the repository at this point in the history
Signed-off-by: Mior Muhammad Zaki <crynobone@gmail.com>
  • Loading branch information
crynobone committed Sep 13, 2023
2 parents b04ff13 + 54e75ae commit 579c7f7
Show file tree
Hide file tree
Showing 26 changed files with 652 additions and 48 deletions.
1 change: 0 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.phpunit.cache export-ignore
/phpstan-baseline.neon export-ignore
/phpstan.neon.dist export-ignore
/phpunit.xml export-ignore
Expand Down
12 changes: 10 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
},
"autoload-dev": {
"psr-4": {
"Orchestra\\Canvas\\Core\\Tests\\": "tests/"
"Orchestra\\Canvas\\Core\\Tests\\": "tests/",
"Workbench\\App\\": "workbench/app/",
"Workbench\\Database\\Factories\\": "workbench/database/factories/",
"Workbench\\Database\\Seeders\\": "workbench/database/seeders/"
}
},
"require": {
Expand Down Expand Up @@ -52,8 +55,13 @@
}
},
"scripts": {
"post-autoload-dump": "@prepare",
"post-autoload-dump": [
"@clear",
"@prepare"
],
"prepare": "@php vendor/bin/testbench package:discover --ansi",
"clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
"build": "@php vendor/bin/testbench workbench:build --ansi",
"lint": [
"@php vendor/bin/phpstan analyse",
"@php vendor/bin/pint"
Expand Down
5 changes: 5 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@ parameters:
message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Container\\\\Container\\:\\:basePath\\(\\)\\.$#"
count: 1
path: src/LaravelServiceProvider.php

-
message: "#^Method Orchestra\\\\Canvas\\\\Core\\\\Presets\\\\Preset\\:\\:config\\(\\) has no return type specified\\.$#"
count: 1
path: src/Presets/Preset.php
16 changes: 10 additions & 6 deletions src/CodeGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function setPreset(Presets\Preset $preset)
/**
* Generate code.
*
* @return mixed
* @return int
*/
public function generateCode(bool $force = false)
{
Expand All @@ -36,17 +36,21 @@ public function generateCode(bool $force = false)
/**
* Code already exists.
*/
public function codeAlreadyExists(string $className)
public function codeAlreadyExists(string $className): int
{
return false;
$this->components->error(sprintf('%s [%s] already exists!', $this->type, $className));

return static::FAILURE;
}

/**
* Code successfully generated.
*/
public function codeHasBeenGenerated(string $className)
public function codeHasBeenGenerated(string $className): int
{
return true;
$this->components->info(sprintf('%s [%s] created successfully.', $this->type, $className));

return static::SUCCESS;
}

/**
Expand All @@ -60,7 +64,7 @@ public function getDefaultNamespace(string $rootNamespace): string
/**
* Generator options.
*
* @return array{name: string}
* @return array<string, mixed>
*/
public function generatorOptions(): array
{
Expand Down
6 changes: 3 additions & 3 deletions src/Commands/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Console\Concerns\ConfiguresPrompts;
use Illuminate\Console\Concerns\HasParameters;
use Illuminate\Console\Concerns\InteractsWithIO;
use Illuminate\Console\Concerns\PromptsForMissingInput;
use Illuminate\Console\OutputStyle;
use Illuminate\Console\View\Components\Factory;
use Illuminate\Container\Container;
Expand All @@ -20,7 +21,8 @@ abstract class Command extends \Symfony\Component\Console\Command\Command
use CallsCommands,
ConfiguresPrompts,
HasParameters,
InteractsWithIO;
InteractsWithIO,
PromptsForMissingInput;

/**
* Canvas preset.
Expand Down Expand Up @@ -65,8 +67,6 @@ protected function initialize(InputInterface $input, OutputInterface $output)

/**
* Run the console command.
*
* @return int
*/
public function run(InputInterface $input, OutputInterface $output): int
{
Expand Down
217 changes: 193 additions & 24 deletions src/Commands/Generator.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@
namespace Orchestra\Canvas\Core\Commands;

use Illuminate\Console\Concerns\CreatesMatchingTest;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Orchestra\Canvas\Core\CodeGenerator;
use Orchestra\Canvas\Core\Contracts\GeneratesCodeListener;
use Orchestra\Canvas\Core\GeneratesCode;
use Orchestra\Canvas\Core\Presets\Preset;
use Orchestra\Canvas\Core\TestGenerator;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;

/**
* @property string|null $name
* @property string|null $description
*/
abstract class Generator extends Command implements GeneratesCodeListener
abstract class Generator extends Command implements GeneratesCodeListener, PromptsForMissingInput
{
use CodeGenerator;
use CodeGenerator, TestGenerator;

/**
* The console command name.
Expand Down Expand Up @@ -57,6 +60,96 @@ abstract class Generator extends Command implements GeneratesCodeListener
*/
protected string $processor = GeneratesCode::class;

/**
* Reserved names that cannot be used for generation.
*
* @var array<int, string>
*/
protected array $reservedNames = [
'__halt_compiler',
'abstract',
'and',
'array',
'as',
'break',
'callable',
'case',
'catch',
'class',
'clone',
'const',
'continue',
'declare',
'default',
'die',
'do',
'echo',
'else',
'elseif',
'empty',
'enddeclare',
'endfor',
'endforeach',
'endif',
'endswitch',
'endwhile',
'enum',
'eval',
'exit',
'extends',
'false',
'final',
'finally',
'fn',
'for',
'foreach',
'function',
'global',
'goto',
'if',
'implements',
'include',
'include_once',
'instanceof',
'insteadof',
'interface',
'isset',
'list',
'match',
'namespace',
'new',
'or',
'print',
'private',
'protected',
'public',
'readonly',
'require',
'require_once',
'return',
'self',
'static',
'switch',
'throw',
'trait',
'true',
'try',
'unset',
'use',
'var',
'while',
'xor',
'yield',
'__CLASS__',
'__DIR__',
'__FILE__',
'__FUNCTION__',
'__LINE__',
'__METHOD__',
'__NAMESPACE__',
'__TRAIT__',
];

/**
* Construct a new generator command.
*/
Expand Down Expand Up @@ -95,6 +188,15 @@ protected function configure()
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
// First we need to ensure that the given name is not a reserved word within the PHP
// language and that the class name will actually be valid. If it is not valid we
// can error now and prevent from polluting the filesystem using invalid files.
if ($this->isReservedName($name = $this->generatorName())) {
$this->components->error('The name "'.$name.'" is reserved by PHP.');

return Command::FAILURE;
}

$force = $this->hasOption('force') && $this->option('force') === true;

return $this->generateCode($force);
Expand All @@ -108,34 +210,13 @@ public function generatingCode(string $stub, string $className): string
return $stub;
}

/**
* Code already exists.
*/
public function codeAlreadyExists(string $className): int
{
$this->components->error(sprintf('%s [%s] already exists!', $this->type, $className));

return static::FAILURE;
}

/**
* Code successfully generated.
*/
public function codeHasBeenGenerated(string $className): int
{
$this->components->info(sprintf('%s [%s] created successfully.', $this->type, $className));

return static::SUCCESS;
}

/**
* Run after code successfully generated.
*/
public function afterCodeHasBeenGenerated(string $className, string $path): void
{
if (\in_array(CreatesMatchingTest::class, class_uses_recursive($this))) {
/** @phpstan-ignore-next-line */
$this->handleTestCreation($path);
$this->handleTestCreationUsingCanvas($path);
}
}

Expand All @@ -157,4 +238,92 @@ public function generatorName(): string
return trim($name);
});
}

/**
* Checks whether the given name is reserved.
*/
protected function isReservedName(string $name): bool
{
$name = strtolower($name);

return \in_array($name, $this->reservedNames);
}

/**
* Get a list of possible model names.
*
* @return array<int, string>
*/
protected function possibleModels()
{
$sourcePath = $this->preset->sourcePath();

$modelPath = is_dir("{$sourcePath}/Models") ? "{$sourcePath}/Models" : $sourcePath;

return collect((new Finder)->files()->depth(0)->in($modelPath))
->map(fn ($file) => $file->getBasename('.php'))
->sort()
->values()
->all();
}

/**
* Get a list of possible event names.
*
* @return array<int, string>
*/
protected function possibleEvents()
{
$eventPath = sprintf('%s/Events', $this->preset->sourcePath());

if (! is_dir($eventPath)) {
return [];
}

return collect((new Finder)->files()->depth(0)->in($eventPath))
->map(fn ($file) => $file->getBasename('.php'))
->sort()
->values()
->all();
}

/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'name' => [
'What should the '.strtolower($this->type).' be named?',
match ($this->type) {
'Cast' => 'E.g. Json',
'Channel' => 'E.g. OrderChannel',
'Console command' => 'E.g. SendEmails',
'Component' => 'E.g. Alert',
'Controller' => 'E.g. UserController',
'Event' => 'E.g. PodcastProcessed',
'Exception' => 'E.g. InvalidOrderException',
'Factory' => 'E.g. PostFactory',
'Job' => 'E.g. ProcessPodcast',
'Listener' => 'E.g. SendPodcastNotification',
'Mailable' => 'E.g. OrderShipped',
'Middleware' => 'E.g. EnsureTokenIsValid',
'Model' => 'E.g. Flight',
'Notification' => 'E.g. InvoicePaid',
'Observer' => 'E.g. UserObserver',
'Policy' => 'E.g. PostPolicy',
'Provider' => 'E.g. ElasticServiceProvider',
'Request' => 'E.g. StorePodcastRequest',
'Resource' => 'E.g. UserResource',
'Rule' => 'E.g. Uppercase',
'Scope' => 'E.g. TrendingScope',
'Seeder' => 'E.g. UserSeeder',
'Test' => 'E.g. UserTest',
default => '',
},
],
];
}
}
Loading

0 comments on commit 579c7f7

Please sign in to comment.