Add Fractal to versa build
and automatically
...select the language if there is a `package.json` file
This commit is contained in:
parent
c5248bb061
commit
18ce1e84c6
5 changed files with 117 additions and 42 deletions
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Add initial JavaScript/TypeScript/Fractal support to `versa install` and `versa run`.
|
||||||
* Add a Symfony project type.
|
* Add a Symfony project type.
|
||||||
* Automatically use PHPUnit or ParaTest based on `require-dev` dependencies.
|
* Automatically use PHPUnit or ParaTest based on `require-dev` dependencies.
|
||||||
* Automatically find the PHP project type (i.e. Drupal or Sculpin) based on its `composer.json` dependencies.
|
* Automatically find the PHP project type (i.e. Drupal or Sculpin) based on its `composer.json` dependencies.
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Console\Command;
|
namespace App\Console\Command;
|
||||||
|
|
||||||
|
use App\Enum\ProjectLanguage;
|
||||||
use App\Enum\ProjectType;
|
use App\Enum\ProjectType;
|
||||||
use App\Process\Process;
|
use App\Process\Process;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
|
@ -14,6 +15,7 @@ final class BuildCommand extends AbstractCommand
|
||||||
{
|
{
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
|
$projectLanguage = null;
|
||||||
$projectType = null;
|
$projectType = null;
|
||||||
|
|
||||||
$extraArgs = $input->getOption('extra-args');
|
$extraArgs = $input->getOption('extra-args');
|
||||||
|
@ -25,6 +27,8 @@ final class BuildCommand extends AbstractCommand
|
||||||
// based on its dependencies.
|
// based on its dependencies.
|
||||||
// TODO: move this logic to a service so it can be tested.
|
// TODO: move this logic to a service so it can be tested.
|
||||||
if ($filesystem->exists($workingDir.'/composer.json')) {
|
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||||
|
$projectLanguage = ProjectLanguage::PHP->value;
|
||||||
|
|
||||||
$json = json_decode(
|
$json = json_decode(
|
||||||
json: strval(file_get_contents($workingDir.'/composer.json')),
|
json: strval(file_get_contents($workingDir.'/composer.json')),
|
||||||
associative: true,
|
associative: true,
|
||||||
|
@ -39,14 +43,20 @@ final class BuildCommand extends AbstractCommand
|
||||||
} elseif (in_array(needle: 'symfony/framework-bundle', haystack: $dependencies, strict: true)) {
|
} elseif (in_array(needle: 'symfony/framework-bundle', haystack: $dependencies, strict: true)) {
|
||||||
$projectType = ProjectType::Symfony->value;
|
$projectType = ProjectType::Symfony->value;
|
||||||
}
|
}
|
||||||
|
} elseif ($filesystem->exists($workingDir.'/fractal.config.js')) {
|
||||||
|
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||||
|
$projectType = ProjectType::Fractal->value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if the project type is found automatically, still override it with
|
// Even if the project language or type is found automatically, still
|
||||||
// the option value if there is one.
|
// override it with the option value if there is one.
|
||||||
|
$projectLanguage = $input->getOption('language') ?? $projectLanguage;
|
||||||
$projectType = $input->getOption('type') ?? $projectType;
|
$projectType = $input->getOption('type') ?? $projectType;
|
||||||
|
|
||||||
$isDockerCompose = $filesystem->exists($workingDir . '/docker-compose.yaml');
|
$isDockerCompose = $filesystem->exists($workingDir . '/docker-compose.yaml');
|
||||||
|
|
||||||
|
switch ($projectLanguage) {
|
||||||
|
case ProjectLanguage::PHP->value:
|
||||||
switch ($projectType) {
|
switch ($projectType) {
|
||||||
case ProjectType::Drupal->value:
|
case ProjectType::Drupal->value:
|
||||||
if ($isDockerCompose) {
|
if ($isDockerCompose) {
|
||||||
|
@ -75,6 +85,21 @@ final class BuildCommand extends AbstractCommand
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ProjectLanguage::JavaScript->value:
|
||||||
|
switch ($projectType) {
|
||||||
|
case ProjectType::Fractal->value:
|
||||||
|
$process = Process::create(
|
||||||
|
command: ['npx', 'fractal', 'build'],
|
||||||
|
extraArgs: $extraArgs,
|
||||||
|
workingDir: $workingDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
$process->run();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,13 @@ final class InstallCommand extends AbstractCommand
|
||||||
|
|
||||||
// TODO: validate the language is an allowed value.
|
// TODO: validate the language is an allowed value.
|
||||||
|
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
|
||||||
// TODO: Composer in Docker Compose?
|
// TODO: Composer in Docker Compose?
|
||||||
$process = Process::create(
|
$process = Process::create(
|
||||||
command: $this->getCommand(
|
command: $this->getCommand(
|
||||||
language: $input->getOption('language'),
|
filesystem: $filesystem,
|
||||||
|
language: $this->getProjectLanguage($filesystem, $workingDir, $input),
|
||||||
workingDir: $workingDir,
|
workingDir: $workingDir,
|
||||||
),
|
),
|
||||||
extraArgs: $extraArgs,
|
extraArgs: $extraArgs,
|
||||||
|
@ -38,14 +41,13 @@ final class InstallCommand extends AbstractCommand
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param Filesystem $filesystem
|
||||||
* @param non-empty-string $language
|
* @param non-empty-string $language
|
||||||
* @param non-empty-string $workingDir
|
* @param non-empty-string $workingDir
|
||||||
* @return non-empty-array<int, non-empty-string>
|
* @return non-empty-array<int, non-empty-string>
|
||||||
*/
|
*/
|
||||||
private function getCommand(string $language, string $workingDir): array
|
private function getCommand(Filesystem $filesystem, string $language, string $workingDir): array
|
||||||
{
|
{
|
||||||
$filesystem = new Filesystem();
|
|
||||||
|
|
||||||
if ($language === ProjectLanguage::JavaScript->value) {
|
if ($language === ProjectLanguage::JavaScript->value) {
|
||||||
if ($filesystem->exists($workingDir.'/yarn.lock')) {
|
if ($filesystem->exists($workingDir.'/yarn.lock')) {
|
||||||
return ['yarn'];
|
return ['yarn'];
|
||||||
|
@ -59,4 +61,22 @@ final class InstallCommand extends AbstractCommand
|
||||||
return ['composer', 'install'];
|
return ['composer', 'install'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Filesystem $filesystem
|
||||||
|
* @param non-empty-string $workingDir
|
||||||
|
* @param InputInterface $input
|
||||||
|
* @return non-empty-string
|
||||||
|
*/
|
||||||
|
private function getProjectLanguage(Filesystem $filesystem, string $workingDir, InputInterface $input): string {
|
||||||
|
$projectLanguage = null;
|
||||||
|
|
||||||
|
// Determine the language based on the files.
|
||||||
|
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||||
|
$projectLanguage = ProjectLanguage::PHP->value;
|
||||||
|
} elseif ($filesystem->exists($workingDir.'/package.json')) {
|
||||||
|
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $input->getOption('language') ?? $projectLanguage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace App\Console\Command;
|
namespace App\Console\Command;
|
||||||
|
|
||||||
|
use App\Enum\ProjectLanguage;
|
||||||
use App\Enum\ProjectType;
|
use App\Enum\ProjectType;
|
||||||
use App\Process\Process;
|
use App\Process\Process;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
@ -13,14 +14,20 @@ final class RunCommand extends AbstractCommand
|
||||||
{
|
{
|
||||||
public function execute(InputInterface $input, OutputInterface $output): int
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
|
$projectLanguage = null;
|
||||||
$projectType = null;
|
$projectType = null;
|
||||||
|
|
||||||
$extraArgs = $input->getOption('extra-args');
|
$extraArgs = $input->getOption('extra-args');
|
||||||
$workingDir = $input->getOption('working-dir');
|
$workingDir = $input->getOption('working-dir');
|
||||||
|
|
||||||
|
$filesystem = new Filesystem();
|
||||||
|
|
||||||
// Attempt to prepopulate some of the options, such as the project type
|
// Attempt to prepopulate some of the options, such as the project type
|
||||||
// based on its dependencies.
|
// based on its dependencies.
|
||||||
// TODO: move this logic to a service so it can be tested.
|
// TODO: move this logic to a service so it can be tested.
|
||||||
|
if ($filesystem->exists($workingDir.'/composer.json')) {
|
||||||
|
$projectLanguage = ProjectLanguage::PHP->value;
|
||||||
|
|
||||||
$json = json_decode(
|
$json = json_decode(
|
||||||
json: strval(file_get_contents($workingDir.'/composer.json')),
|
json: strval(file_get_contents($workingDir.'/composer.json')),
|
||||||
associative: true,
|
associative: true,
|
||||||
|
@ -35,9 +42,17 @@ final class RunCommand extends AbstractCommand
|
||||||
} elseif (in_array(needle: 'symfony/framework-bundle', haystack: $dependencies, strict: true)) {
|
} elseif (in_array(needle: 'symfony/framework-bundle', haystack: $dependencies, strict: true)) {
|
||||||
$projectType = ProjectType::Symfony->value;
|
$projectType = ProjectType::Symfony->value;
|
||||||
}
|
}
|
||||||
|
} elseif ($filesystem->exists($workingDir.'/package.json')) {
|
||||||
|
$projectLanguage = ProjectLanguage::JavaScript->value;
|
||||||
|
|
||||||
|
if ($filesystem->exists($workingDir.'/fractal.config.js')) {
|
||||||
|
$projectType = ProjectType::Fractal->value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Even if the project type is found automatically, still override it with
|
// Even if the project type is found automatically, still override it with
|
||||||
// the option value if there is one.
|
// the option value if there is one.
|
||||||
|
$projectLanguage = $input->getOption('language') ?? $projectLanguage;
|
||||||
$projectType = $input->getOption('type') ?? $projectType;
|
$projectType = $input->getOption('type') ?? $projectType;
|
||||||
|
|
||||||
$filesystem = new Filesystem();
|
$filesystem = new Filesystem();
|
||||||
|
@ -49,19 +64,30 @@ final class RunCommand extends AbstractCommand
|
||||||
extraArgs: $extraArgs,
|
extraArgs: $extraArgs,
|
||||||
workingDir: $workingDir,
|
workingDir: $workingDir,
|
||||||
);
|
);
|
||||||
$process->setTimeout(null);
|
|
||||||
|
|
||||||
|
$process->setTimeout(null);
|
||||||
$process->run();
|
$process->run();
|
||||||
} else {
|
} else {
|
||||||
switch ($projectType) {
|
switch ($projectType) {
|
||||||
|
case ProjectType::Fractal->value:
|
||||||
|
$process = Process::create(
|
||||||
|
command: ['npx', 'fractal', 'start', '--sync'],
|
||||||
|
extraArgs: $extraArgs,
|
||||||
|
workingDir: $workingDir,
|
||||||
|
);
|
||||||
|
|
||||||
|
$process->setTimeout(null);
|
||||||
|
$process->run();
|
||||||
|
break;
|
||||||
|
|
||||||
case ProjectType::Sculpin->value:
|
case ProjectType::Sculpin->value:
|
||||||
$process = Process::create(
|
$process = Process::create(
|
||||||
command: ['./vendor/bin/sculpin', 'generate', '--server', '--watch'],
|
command: ['./vendor/bin/sculpin', 'generate', '--server', '--watch'],
|
||||||
extraArgs: $extraArgs,
|
extraArgs: $extraArgs,
|
||||||
workingDir: $workingDir,
|
workingDir: $workingDir,
|
||||||
);
|
);
|
||||||
$process->setTimeout(null);
|
|
||||||
|
|
||||||
|
$process->setTimeout(null);
|
||||||
$process->run();
|
$process->run();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ namespace App\Enum;
|
||||||
|
|
||||||
enum ProjectType: string
|
enum ProjectType: string
|
||||||
{
|
{
|
||||||
|
// JavaScript.
|
||||||
|
case Fractal = 'fractal';
|
||||||
|
|
||||||
// PHP.
|
// PHP.
|
||||||
case Drupal = 'drupal';
|
case Drupal = 'drupal';
|
||||||
case Sculpin = 'sculpin';
|
case Sculpin = 'sculpin';
|
||||||
|
|
Reference in a new issue