From c6a6b4de5dc2b30cf95756419558266910d72926 Mon Sep 17 00:00:00 2001 From: Oliver Davies Date: Mon, 26 Feb 2024 07:17:04 +0000 Subject: [PATCH] Extract logic to determine which package manager ..is used Always Composer for PHP. npm, yarn or pnpm for node. --- src/Action/DeterminePackageManager.php | 37 ++++++++ src/Action/DetermineProjectLanguage.php | 5 +- .../DetermineProjectLanguageInterface.php | 11 +++ src/Console/Command/InstallCommand.php | 39 +++++--- src/Console/Command/PackageInstallCommand.php | 26 +++-- src/Enum/PackageManager.php | 11 +++ tests/DeterminePackageManagerTest.php | 94 +++++++++++++++++++ tests/ProjectLanguageTest.php | 2 + versa | 1 + 9 files changed, 202 insertions(+), 24 deletions(-) create mode 100644 src/Action/DeterminePackageManager.php create mode 100644 src/Action/DetermineProjectLanguageInterface.php create mode 100644 src/Enum/PackageManager.php create mode 100644 tests/DeterminePackageManagerTest.php diff --git a/src/Action/DeterminePackageManager.php b/src/Action/DeterminePackageManager.php new file mode 100644 index 0000000..40bfec7 --- /dev/null +++ b/src/Action/DeterminePackageManager.php @@ -0,0 +1,37 @@ +projectLanguage === ProjectLanguage::JavaScript->value) { + if ($this->filesystem->exists($this->workingDir.'/pnpm-lock.yaml')) { + return PackageManager::pnpm->value; + } + + if ($this->filesystem->exists($this->workingDir.'/yarn.lock')) { + return PackageManager::yarn->value; + } + + return PackageManager::npm->value; + } + + // TODO: throw an Exception if the language cannot be determined instead of returning a default. + return PackageManager::Composer->value; + } +} diff --git a/src/Action/DetermineProjectLanguage.php b/src/Action/DetermineProjectLanguage.php index cc59dd7..58e0565 100644 --- a/src/Action/DetermineProjectLanguage.php +++ b/src/Action/DetermineProjectLanguage.php @@ -5,7 +5,7 @@ namespace App\Action; use App\Enum\ProjectLanguage; use Symfony\Component\Filesystem\Filesystem; -final class DetermineProjectLanguage +final class DetermineProjectLanguage implements DetermineProjectLanguageInterface { public function __construct( private Filesystem $filesystem, @@ -13,9 +13,6 @@ final class DetermineProjectLanguage ) { } - /** - * @return non-empty-string - */ public function getLanguage(): string { if ($this->filesystem->exists($this->workingDir.'/composer.json')) { diff --git a/src/Action/DetermineProjectLanguageInterface.php b/src/Action/DetermineProjectLanguageInterface.php new file mode 100644 index 0000000..d5f7e94 --- /dev/null +++ b/src/Action/DetermineProjectLanguageInterface.php @@ -0,0 +1,11 @@ +getCommand( - filesystem: $filesystem, - language: $language, - workingDir: $workingDir, - ), + command: $this->getCommand(language: $language, workingDir: $workingDir), workingDir: $workingDir, ); @@ -44,23 +43,35 @@ final class InstallCommand extends AbstractCommand } /** - * @param Filesystem $filesystem * @param non-empty-string $language * @param non-empty-string $workingDir * @return non-empty-array + * @throws RuntimeException If the lanuage cannot be determined. */ - private function getCommand(Filesystem $filesystem, string $language, string $workingDir): array + private function getCommand(string $language, string $workingDir): array { if ($language === ProjectLanguage::JavaScript->value) { - if ($filesystem->exists($workingDir.'/yarn.lock')) { - return ['yarn']; - } elseif ($filesystem->exists($workingDir.'/pnpm-lock.yaml')) { - return ['pnpm', 'install']; - } else { - return ['npm', 'install']; + return ['composer', 'install']; + } elseif ($language === ProjectLanguage::JavaScript->value) { + $packageManager = new DeterminePackageManager( + filesystem: $this->filesystem, + projectLanguage: $language, + workingDir: $workingDir, + ); + + switch ($packageManager->getPackageManager()) { + case PackageManager::pnpm->value: + return ['pnpm', 'install']; + + case PackageManager::yarn->value: + return ['yarn']; + + default: + return ['npm', 'install']; } } - return ['composer', 'install']; + // TODO: add a test to ensure the exception is thrown? + throw new RuntimeException('Project language cannot be determined.'); } } diff --git a/src/Console/Command/PackageInstallCommand.php b/src/Console/Command/PackageInstallCommand.php index bc8c30a..ee0130b 100644 --- a/src/Console/Command/PackageInstallCommand.php +++ b/src/Console/Command/PackageInstallCommand.php @@ -2,7 +2,9 @@ namespace App\Console\Command; +use App\Action\DeterminePackageManager; use App\Action\DetermineProjectLanguage; +use App\Enum\PackageManager; use App\Enum\ProjectLanguage; use App\Process\Process; use Symfony\Component\Console\Command\Command; @@ -47,12 +49,24 @@ final class PackageInstallCommand extends AbstractCommand break; case ProjectLanguage::JavaScript->value: - if ($this->filesystem->exists($workingDir.'/yarn.lock')) { - $command = ['yarn', 'add']; - } elseif ($this->filesystem->exists($workingDir.'/pnpm-lock.yaml')) { - $command = ['pnpm', 'install']; - } else { - $command = ['npm', 'install']; + $packageManager = new DeterminePackageManager( + filesystem: $this->filesystem, + projectLanguage: $language, + workingDir: $workingDir, + ); + + switch ($packageManager->getPackageManager()) { + case PackageManager::pnpm->value: + $command = ['pnpm', 'install']; + break; + + case PackageManager::yarn->value: + $command = ['yarn', 'add']; + break; + + default: + $command = ['npm', 'install']; + break; } $process = Process::create( diff --git a/src/Enum/PackageManager.php b/src/Enum/PackageManager.php new file mode 100644 index 0000000..b935196 --- /dev/null +++ b/src/Enum/PackageManager.php @@ -0,0 +1,11 @@ +createMock(Filesystem::class); + $filesystem + ->method('exists') + ->with('./composer.json') + ->willReturn(true); + + $action = new DeterminePackageManager( + filesystem: $filesystem, + projectLanguage: ProjectLanguage::PHP->value, + ); + + self::assertSame( + actual: $action->getPackageManager(), + expected: PackageManager::Composer->value, + ); + } + + public function lockFileProvider(): array + { + return [ + 'npm' => [ + './package-lock.json', + PackageManager::npm->value, + [ + ['./package-lock.json', true], + ['./pnpm-lock.yaml', false], + ['./yarn.lock', false], + ], + ], + 'pnpm' => [ + './pnpm-lock.yaml', + PackageManager::pnpm->value, + [ + ['./package-lock.json', false], + ['./pnpm-lock.yaml', true], + ['./yarn.lock', false], + ], + ], + 'yarn' => [ + './yarn.lock', + PackageManager::yarn->value, + [ + ['./package-lock.json', false], + ['./pnpm-lock.yaml', false], + ['./yarn.lock', true], + ], + ], + ]; + } + + /** + * @dataProvider lockFileProvider + * @test + */ + public function it_finds_node( + string $lockFile, + string $expectedPackageManager, + array $valueMap, + ): void { + $filesystem = $this->createMock(Filesystem::class); + $filesystem + ->method('exists') + ->will(self::returnValueMap($valueMap)); + + $action = new DeterminePackageManager( + filesystem: $filesystem, + projectLanguage: ProjectLanguage::JavaScript->value, + ); + + self::assertSame( + actual: $action->getPackageManager(), + expected: $expectedPackageManager, + ); + } +} diff --git a/tests/ProjectLanguageTest.php b/tests/ProjectLanguageTest.php index e8accaa..ed2aebd 100644 --- a/tests/ProjectLanguageTest.php +++ b/tests/ProjectLanguageTest.php @@ -1,5 +1,7 @@