diff --git a/src/Symfony/Component/AssetMapper/CHANGELOG.md b/src/Symfony/Component/AssetMapper/CHANGELOG.md index 77f32884bdc02..ae70d59485362 100644 --- a/src/Symfony/Component/AssetMapper/CHANGELOG.md +++ b/src/Symfony/Component/AssetMapper/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Mark the component as non experimental * Add a `importmap:install` command to download all missing downloaded packages + * Allow specifying packages to update for the `importmap:update` command 6.3 --- diff --git a/src/Symfony/Component/AssetMapper/Command/ImportMapUpdateCommand.php b/src/Symfony/Component/AssetMapper/Command/ImportMapUpdateCommand.php index eed445e45056c..f4ad4f2bdba59 100644 --- a/src/Symfony/Component/AssetMapper/Command/ImportMapUpdateCommand.php +++ b/src/Symfony/Component/AssetMapper/Command/ImportMapUpdateCommand.php @@ -11,9 +11,11 @@ namespace Symfony\Component\AssetMapper\Command; +use Symfony\Component\AssetMapper\ImportMap\ImportMapEntry; use Symfony\Component\AssetMapper\ImportMap\ImportMapManager; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -21,7 +23,7 @@ /** * @author Kévin Dunglas */ -#[AsCommand(name: 'importmap:update', description: 'Updates all JavaScript packages to their latest versions')] +#[AsCommand(name: 'importmap:update', description: 'Updates JavaScript packages to their latest versions')] final class ImportMapUpdateCommand extends Command { public function __construct( @@ -33,21 +35,37 @@ public function __construct( protected function configure(): void { $this + ->addArgument('packages', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'List of packages\' names') ->setHelp(<<<'EOT' The %command.name% command will update all from the 3rd part packages in importmap.php to their latest version, including downloaded packages. php %command.full_name% + +Or specific packages only: + + php %command.full_name% EOT - ); + ) + ; } protected function execute(InputInterface $input, OutputInterface $output): int { + $packages = $input->getArgument('packages'); + $io = new SymfonyStyle($input, $output); - $this->importMapManager->update(); + $updatedPackages = $this->importMapManager->update($packages); - $io->success('Updated all packages in importmap.php.'); + if (0 < \count($packages)) { + $io->success(sprintf( + 'Updated %s package%s in importmap.php.', + implode(', ', array_map(static fn (ImportMapEntry $entry): string => $entry->importName, $updatedPackages)), + 1 < \count($updatedPackages) ? 's' : '', + )); + } else { + $io->success('Updated all packages in importmap.php.'); + } return Command::SUCCESS; } diff --git a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php index c6fde3e5718c5..79009fadd193d 100644 --- a/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php +++ b/src/Symfony/Component/AssetMapper/ImportMap/ImportMapManager.php @@ -88,7 +88,7 @@ public function getImportMapJson(): string */ public function require(array $packages): array { - return $this->updateImportMapConfig(false, $packages, []); + return $this->updateImportMapConfig(false, $packages, [], []); } /** @@ -98,15 +98,17 @@ public function require(array $packages): array */ public function remove(array $packages): void { - $this->updateImportMapConfig(false, [], $packages); + $this->updateImportMapConfig(false, [], $packages, []); } /** - * Updates all existing packages to the latest version. + * Updates either all existing packages or the specified ones to the latest version. + * + * @return ImportMapEntry[] */ - public function update(): array + public function update(array $packages = []): array { - return $this->updateImportMapConfig(true, [], []); + return $this->updateImportMapConfig(true, [], [], $packages); } /** @@ -190,7 +192,7 @@ private function buildImportMapJson(): void * * @return ImportMapEntry[] */ - private function updateImportMapConfig(bool $update, array $packagesToRequire, array $packagesToRemove): array + private function updateImportMapConfig(bool $update, array $packagesToRequire, array $packagesToRemove, array $packagesToUpdate): array { $currentEntries = $this->loadImportMapEntries(); @@ -205,7 +207,7 @@ private function updateImportMapConfig(bool $update, array $packagesToRequire, a if ($update) { foreach ($currentEntries as $importName => $entry) { - if (null === $entry->url) { + if (null === $entry->url || (0 !== \count($packagesToUpdate) && !\in_array($importName, $packagesToUpdate, true))) { continue; } diff --git a/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapManagerTest.php b/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapManagerTest.php index 86f3a96d4e1dc..e49bee4ab8ddc 100644 --- a/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapManagerTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/ImportMap/ImportMapManagerTest.php @@ -373,6 +373,64 @@ public function testUpdate() $this->assertSame('contents of cowsay.js', $actualContents); } + public function testUpdateWithSpecificPackages() + { + $rootDir = __DIR__.'/../fixtures/importmaps_for_writing'; + $manager = $this->createImportMapManager(['assets' => ''], $rootDir); + + $map = [ + 'lodash' => [ + 'url' => 'https://ga.jspm.io/npm:lodash@1.2.3/lodash.js', + ], + 'cowsay' => [ + 'url' => 'https://ga.jspm.io/npm:cowsay@4.5.6/cowsay.umd.js', + 'downloaded_to' => 'vendor/cowsay.js', + ], + 'bootstrap' => [ + 'url' => 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.esm.js', + 'preload' => true, + ], + 'app' => [ + 'path' => 'app.js', + ], + ]; + $mapString = var_export($map, true); + file_put_contents($rootDir.'/importmap.php', "filesystem->mkdir($rootDir.'/assets/vendor'); + file_put_contents($rootDir.'/assets/vendor/cowsay.js', 'cowsay.js original contents'); + file_put_contents($rootDir.'/assets/app.js', 'app.js contents'); + + $this->packageResolver->expects($this->once()) + ->method('resolvePackages') + ->willReturn([ + self::resolvedPackage('cowsay', 'https://ga.jspm.io/npm:cowsay@4.5.9/cowsay.umd.js', download: true, content: 'updated contents of cowsay.js'), + ]) + ; + + $manager->update(['cowsay']); + $actualImportMap = require $rootDir.'/importmap.php'; + $expectedImportMap = [ + 'lodash' => [ + 'url' => 'https://ga.jspm.io/npm:lodash@1.2.3/lodash.js', + ], + 'cowsay' => [ + 'url' => 'https://ga.jspm.io/npm:cowsay@4.5.9/cowsay.umd.js', + 'downloaded_to' => 'vendor/cowsay.js', + ], + 'bootstrap' => [ + 'url' => 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.esm.js', + 'preload' => true, + ], + 'app' => [ + 'path' => 'app.js', + ], + ]; + $this->assertEquals($expectedImportMap, $actualImportMap); + $this->assertFileExists($rootDir.'/assets/vendor/cowsay.js'); + $actualContents = file_get_contents($rootDir.'/assets/vendor/cowsay.js'); + $this->assertSame('updated contents of cowsay.js', $actualContents); + } + public function testDownloadMissingPackages() { $rootDir = __DIR__.'/../fixtures/download';