diff --git a/src/Flex.php b/src/Flex.php index de4ee857a..ad7c9b484 100644 --- a/src/Flex.php +++ b/src/Flex.php @@ -587,7 +587,7 @@ private function synchronizePackageJson(string $rootDir) $vendorDir = trim((new Filesystem())->makePathRelative($this->config->get('vendor-dir'), $rootDir), '/'); $executor = new ScriptExecutor($this->composer, $this->io, $this->options); - $synchronizer = new PackageJsonSynchronizer($rootDir, $vendorDir, $executor); + $synchronizer = new PackageJsonSynchronizer($rootDir, $vendorDir, $executor, $this->io); if ($synchronizer->shouldSynchronize()) { $lockData = $this->composer->getLocker()->getLockData(); diff --git a/src/PackageJsonSynchronizer.php b/src/PackageJsonSynchronizer.php index 3b7fdcb75..ed60bbe27 100644 --- a/src/PackageJsonSynchronizer.php +++ b/src/PackageJsonSynchronizer.php @@ -11,8 +11,10 @@ namespace Symfony\Flex; +use Composer\IO\IOInterface; use Composer\Json\JsonFile; use Composer\Json\JsonManipulator; +use Composer\Semver\Semver; use Composer\Semver\VersionParser; use Seld\JsonLint\ParsingException; @@ -25,13 +27,15 @@ class PackageJsonSynchronizer private $rootDir; private $vendorDir; private $scriptExecutor; + private $io; private $versionParser; - public function __construct(string $rootDir, string $vendorDir, ScriptExecutor $scriptExecutor) + public function __construct(string $rootDir, string $vendorDir, ScriptExecutor $scriptExecutor, IOInterface $io) { $this->rootDir = $rootDir; $this->vendorDir = $vendorDir; $this->scriptExecutor = $scriptExecutor; + $this->io = $io; $this->versionParser = new VersionParser(); } @@ -246,8 +250,23 @@ private function updateImportMap(array $importMapEntries): void $importMapData = include $this->rootDir.'/importmap.php'; foreach ($importMapEntries as $name => $importMapEntry) { + if (isset($importMapData[$name])) { - continue; + if (!isset($importMapData[$name]['version'])) { + // AssetMapper 6.3 + continue; + } + + + $version = $importMapData[$name]['version']; + $versionConstraint = $importMapEntry['version'] ?? null; + + // if the version constraint is satisfied, skip - else, update the package + if (Semver::satisfies($version, $versionConstraint)) { + continue; + } + + $this->io->writeError(sprintf('Updating package %s from %s to %s.', $name, $version, $versionConstraint)); } if (isset($importMapEntry['path'])) { diff --git a/tests/PackageJsonSynchronizerTest.php b/tests/PackageJsonSynchronizerTest.php index 588cc98c7..7e6b27c0d 100644 --- a/tests/PackageJsonSynchronizerTest.php +++ b/tests/PackageJsonSynchronizerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Flex\Tests; +use Composer\IO\IOInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\Filesystem\Filesystem; use Symfony\Flex\PackageJsonSynchronizer; @@ -31,7 +32,8 @@ protected function setUp(): void $this->synchronizer = new PackageJsonSynchronizer( $this->tempDir, 'vendor', - $this->scriptExecutor + $this->scriptExecutor, + $this->createMock(IOInterface::class) ); } @@ -385,4 +387,55 @@ public function testSynchronizeAssetMapperNewPackage() json_decode(file_get_contents($this->tempDir.'/assets/controllers.json'), true) ); } + + public function testSynchronizeAssetMapperUpgradesPackageIfNeeded() + { + $importMap = [ + '@hotcake/foo' => [ + // constraint in package.json is ^1.9.0 + 'version' => '1.8.0', + ], + ]; + file_put_contents($this->tempDir.'/importmap.php', sprintf('tempDir.'/vendor/symfony/new-package/assets/dist/loader.js'; + $this->scriptExecutor->expects($this->exactly(2)) + ->method('execute') + ->withConsecutive( + ['symfony-cmd', 'importmap:require', ['@hotcake/foo@^1.9.0']], + ['symfony-cmd', 'importmap:require', ['@symfony/new-package', '--path='.$fileModulePath]] + ); + + $this->synchronizer->synchronize([ + [ + 'name' => 'symfony/new-package', + 'keywords' => ['symfony-ux'], + ], + ]); + } + + public function testSynchronizeAssetMapperSkipsUpgradeIfAlreadySatisfied() + { + $importMap = [ + '@hotcake/foo' => [ + // constraint in package.json is ^1.9.0 + 'version' => '1.9.1', + ], + ]; + file_put_contents($this->tempDir.'/importmap.php', sprintf('tempDir.'/vendor/symfony/new-package/assets/dist/loader.js'; + $this->scriptExecutor->expects($this->once()) + ->method('execute') + ->withConsecutive( + ['symfony-cmd', 'importmap:require', ['@symfony/new-package', '--path='.$fileModulePath]] + ); + + $this->synchronizer->synchronize([ + [ + 'name' => 'symfony/new-package', + 'keywords' => ['symfony-ux'], + ], + ]); + } }