* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Symfony\Component\AssetMapper\Command; use Symfony\Component\AssetMapper\AssetMapper; use Symfony\Component\AssetMapper\AssetMapperInterface; use Symfony\Component\AssetMapper\CompiledAssetMapperConfigReader; use Symfony\Component\AssetMapper\Event\PreAssetsCompileEvent; use Symfony\Component\AssetMapper\ImportMap\ImportMapGenerator; use Symfony\Component\AssetMapper\Path\PublicAssetsFilesystemInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; /** * Compiles the assets in the asset mapper to the final output directory. * * This command is intended to be used during deployment. * * @author Ryan Weaver */ #[AsCommand(name: 'asset-map:compile', description: 'Compile all mapped assets and writes them to the final public output directory')] final class AssetMapperCompileCommand extends Command { public function __construct( private readonly CompiledAssetMapperConfigReader $compiledConfigReader, private readonly AssetMapperInterface $assetMapper, private readonly ImportMapGenerator $importMapGenerator, private readonly PublicAssetsFilesystemInterface $assetsFilesystem, private readonly string $projectDir, private readonly bool $isDebug, private readonly ?EventDispatcherInterface $eventDispatcher = null, ) { parent::__construct(); } protected function configure(): void { $this ->setHelp(<<<'EOT' The %command.name% command compiles and dumps all the assets in the asset mapper into the final public directory (usually public/assets). This command is meant to be run during deployment. EOT ); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $this->eventDispatcher?->dispatch(new PreAssetsCompileEvent($io)); // remove existing config files $this->compiledConfigReader->removeConfig(AssetMapper::MANIFEST_FILE_NAME); $this->compiledConfigReader->removeConfig(ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME); $entrypointFiles = []; foreach ($this->importMapGenerator->getEntrypointNames() as $entrypointName) { $path = sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName); $this->compiledConfigReader->removeConfig($path); $entrypointFiles[$entrypointName] = $path; } $manifest = $this->createManifestAndWriteFiles($io); $manifestPath = $this->compiledConfigReader->saveConfig(AssetMapper::MANIFEST_FILE_NAME, $manifest); $io->comment(sprintf('Manifest written to %s', $this->shortenPath($manifestPath))); $importMapPath = $this->compiledConfigReader->saveConfig(ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME, $this->importMapGenerator->getRawImportMapData()); $io->comment(sprintf('Import map data written to %s.', $this->shortenPath($importMapPath))); foreach ($entrypointFiles as $entrypointName => $path) { $this->compiledConfigReader->saveConfig($path, $this->importMapGenerator->findEagerEntrypointImports($entrypointName)); } $styledEntrypointNames = array_map(fn (string $entrypointName) => sprintf('%s', $entrypointName), array_keys($entrypointFiles)); $io->comment(sprintf('Entrypoint metadata written for %d entrypoints (%s).', \count($entrypointFiles), implode(', ', $styledEntrypointNames))); if ($this->isDebug) { $io->warning(sprintf( 'Debug mode is enabled in your project: Symfony will not serve any changed assets until you delete the files in the "%s" directory again.', $this->shortenPath(\dirname($manifestPath)) )); } return 0; } private function shortenPath(string $path): string { return str_replace($this->projectDir.'/', '', $path); } private function createManifestAndWriteFiles(SymfonyStyle $io): array { $io->comment(sprintf('Compiling and writing asset files to %s', $this->shortenPath($this->assetsFilesystem->getDestinationPath()))); $manifest = []; foreach ($this->assetMapper->allAssets() as $asset) { if (null !== $asset->content) { // The original content has been modified by the AssetMapperCompiler $this->assetsFilesystem->write($asset->publicPath, $asset->content); } else { $this->assetsFilesystem->copy($asset->sourcePath, $asset->publicPath); } $manifest[$asset->logicalPath] = $asset->publicPath; } ksort($manifest); $io->comment(sprintf('Compiled %d assets', \count($manifest))); return $manifest; } }