diff --git a/Asset/AbstractAssetManager.php b/Asset/AbstractAssetManager.php index 50c3dab..dcbdcb2 100644 --- a/Asset/AbstractAssetManager.php +++ b/Asset/AbstractAssetManager.php @@ -13,7 +13,6 @@ use Composer\IO\IOInterface; use Composer\Package\RootPackageInterface; -use Composer\Semver\Constraint\Constraint; use Composer\Semver\VersionParser; use Composer\Util\Filesystem; use Composer\Util\Platform; @@ -62,6 +61,11 @@ abstract class AbstractAssetManager implements AssetManagerInterface */ protected $updatable = true; + /** + * @var null|string + */ + private $version = ''; + /** * Constructor. * @@ -90,9 +94,7 @@ public function __construct( */ public function isAvailable() { - $this->executor->execute($this->getVersionCommand(), $version); - - return '' !== trim($version); + return null !== $this->getVersion(); } /** @@ -160,11 +162,10 @@ public function isValidForUpdate() */ public function validate() { - $this->executor->execute($this->getVersionCommand(), $version); - $version = trim($version); + $version = $this->getVersion(); $constraintVersion = $this->config->get('manager-version'); - if ('' === $version) { + if (null === $version) { throw new RuntimeException(sprintf('The binary of "%s" must be installed', $this->getName())); } @@ -172,7 +173,7 @@ public function validate() $parser = new VersionParser(); $constraint = $parser->parseConstraints($constraintVersion); - if (!$constraint->matches(new Constraint('=', $version))) { + if (!$constraint->matches($parser->parseConstraints($version))) { throw new RuntimeException(sprintf('The installed %s version "%s" doesn\'t match with the constraint version "%s"', $this->getName(), $version, $constraintVersion)); } } @@ -232,9 +233,9 @@ protected function actionWhenComposerDependenciesAreAlreadyInstalled($names) /** * Build the command with binary and command options. * - * @param string $defaultBin The default binary of command if option isn't defined - * @param string $action The command action to retrieve the options in config - * @param string $command The command + * @param string $defaultBin The default binary of command if option isn't defined + * @param string $action The command action to retrieve the options in config + * @param string|string[] $command The command * * @return string */ @@ -245,11 +246,24 @@ protected function buildCommand($defaultBin, $action, $command) $gOptions = trim($this->config->get('manager-options', '')); $options = trim($this->config->get('manager-'.$action.'-options', '')); - return $bin.' '.$command + return $bin.' '.implode(' ', (array) $command) .(empty($gOptions) ? '' : ' '.$gOptions) .(empty($options) ? '' : ' '.$options); } + /** + * @return null|string + */ + protected function getVersion() + { + if ('' === $this->version) { + $this->executor->execute($this->getVersionCommand(), $version); + $this->version = '' !== trim($version) ? trim($version) : null; + } + + return $this->version; + } + /** * Get the command to retrieve the version. * diff --git a/Asset/YarnManager.php b/Asset/YarnManager.php index 74492ae..6777653 100644 --- a/Asset/YarnManager.php +++ b/Asset/YarnManager.php @@ -11,6 +11,8 @@ namespace Foxy\Asset; +use Composer\Semver\VersionParser; + /** * Yarn Manager. * @@ -47,7 +49,11 @@ public function isInstalled() */ public function isValidForUpdate() { - $cmd = $this->buildCommand('yarn', 'check', 'check --non-interactive'); + if ($this->isYarnNext()) { + return true; + } + + $cmd = $this->buildCommand('yarn', 'check', $this->mergeInteractiveCommand(array('check'))); return 0 === $this->executor->execute($cmd); } @@ -65,7 +71,7 @@ protected function getVersionCommand() */ protected function getInstallCommand() { - return $this->buildCommand('yarn', 'install', 'install --non-interactive'); + return $this->buildCommand('yarn', 'install', $this->mergeInteractiveCommand(array('install'))); } /** @@ -73,6 +79,29 @@ protected function getInstallCommand() */ protected function getUpdateCommand() { - return $this->buildCommand('yarn', 'update', 'upgrade --non-interactive'); + $commandName = $this->isYarnNext() ? 'up' : 'upgrade'; + + return $this->buildCommand('yarn', 'update', $this->mergeInteractiveCommand(array($commandName))); + } + + /** + * @return bool + */ + private function isYarnNext() + { + $version = $this->getVersion(); + $parser = new VersionParser(); + $constraint = $parser->parseConstraints('>=2.0.0'); + + return $constraint->matches($parser->parseConstraints($version)); + } + + private function mergeInteractiveCommand(array $command) + { + if (!$this->isYarnNext()) { + $command[] = '--non-interactive'; + } + + return $command; } } diff --git a/Tests/Asset/YarnAssetManagerTest.php b/Tests/Asset/YarnAssetManagerTest.php index f03359f..bc86fb0 100644 --- a/Tests/Asset/YarnAssetManagerTest.php +++ b/Tests/Asset/YarnAssetManagerTest.php @@ -27,8 +27,11 @@ final class YarnAssetManagerTest extends AbstractAssetManagerTest */ public function actionForTestRunForInstallCommand($action) { + $this->executor->addExpectedValues(0, '1.0.0'); + if ('update' === $action) { - $this->executor->addExpectedValues(0, 'CHECK OUTPUT'); + $this->executor->addExpectedValues(0, '1.0.0'); + $this->executor->addExpectedValues(0, '1.0.0'); $this->executor->addExpectedValues(0, 'CHECK OUTPUT'); } } @@ -86,6 +89,7 @@ protected function getValidUpdateCommand() */ protected function actionForTestAddDependenciesForUpdateCommand() { + $this->executor->addExpectedValues(0, '1.0.0'); $this->executor->addExpectedValues(0, 'CHECK OUTPUT'); } } diff --git a/Tests/Asset/YarnNextAssetManagerTest.php b/Tests/Asset/YarnNextAssetManagerTest.php new file mode 100644 index 0000000..5953db5 --- /dev/null +++ b/Tests/Asset/YarnNextAssetManagerTest.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Foxy\Tests\Asset; + +use Foxy\Asset\YarnManager; + +/** + * Yarn Next asset manager tests. + * + * @author François Pluchino + * + * @internal + */ +final class YarnNextAssetManagerTest extends AbstractAssetManagerTest +{ + /** + * {@inheritdoc} + */ + public function actionForTestRunForInstallCommand($action) + { + $this->executor->addExpectedValues(0, '2.0.0'); + + if ('update' === $action) { + $this->executor->addExpectedValues(0, '2.0.0'); + } + } + + /** + * {@inheritdoc} + */ + protected function getManager() + { + return new YarnManager($this->io, $this->config, $this->executor, $this->fs, $this->fallback); + } + + /** + * {@inheritdoc} + */ + protected function getValidName() + { + return 'yarn'; + } + + /** + * {@inheritdoc} + */ + protected function getValidLockPackageName() + { + return 'yarn.lock'; + } + + /** + * {@inheritdoc} + */ + protected function getValidVersionCommand() + { + return 'yarn --version'; + } + + /** + * {@inheritdoc} + */ + protected function getValidInstallCommand() + { + return 'yarn install'; + } + + /** + * {@inheritdoc} + */ + protected function getValidUpdateCommand() + { + return 'yarn up'; + } + + /** + * {@inheritdoc} + */ + protected function actionForTestAddDependenciesForUpdateCommand() + { + $this->executor->addExpectedValues(0, '2.0.0'); + $this->executor->addExpectedValues(0, 'CHECK OUTPUT'); + } +} diff --git a/composer.lock b/composer.lock index 1dac3f1..35ae723 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b505710172dc7eb7fb0793d870e73b69", + "content-hash": "17d61bf2437290faf7b19842b886271e", "packages": [], "packages-dev": [ {