8000 [AssetMapper] Allowing for files to be written to some non-local location by weaverryan · Pull Request #51847 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[AssetMapper] Allowing for files to be written t 8000 o some non-local location #51847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1353,13 +1353,17 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde
->setArgument(0, $paths)
->setArgument(2, $excludedPathPatterns);

$publicDirName = $this->getPublicDirectoryName($container);
$container->getDefinition('asset_mapper.public_assets_path_resolver')
->setArgument(1, $config['public_prefix'])
->setArgument(2, $publicDirName);
->setArgument(0, $config['public_prefix']);

$container->getDefinition('asset_mapper.command.compile')
->setArgument(5, $publicDirName);
$publicDirectory = $this->getPublicDirectory($container);
$publicAssetsDirectory = rtrim($publicDirectory.'/'.ltrim($config['public_prefix'], '/'), '/');
$container->getDefinition('asset_mapper.local_public_assets_filesystem')
->setArgument(0, $publicDirectory)
;

$container->getDefinition('asset_mapper.compiled_asset_mapper_config_reader')
->setArgument(0, $publicAssetsDirectory);

if (!$config['server']) {
$container->removeDefinition('asset_mapper.dev_server_subscriber');
Expand Down Expand Up @@ -3163,11 +3167,12 @@ private function writeConfigEnabled(string $path, bool $value, array &$config):
$config['enabled'] = $value;
}

private function getPublicDirectoryName(ContainerBuilder $container): string
private function getPublicDirectory(ContainerBuilder $container): string
{
$defaultPublicDir = 'public';
$projectDir = $container->getParameter('kernel.project_dir');
$defaultPublicDir = $projectDir.'/public';

$composerFilePath = $container->getParameter('kernel.project_dir').'/composer.json';
$composerFilePath = $projectDir.'/composer.json';

if (!file_exists($composerFilePath)) {
return $defaultPublicDir;
Expand All @@ -3176,6 +3181,6 @@ private function getPublicDirectoryName(ContainerBuilder $container): string
$container->addResource(new FileResource($composerFilePath));
$composerConfig = json_decode(file_get_contents($composerFilePath), true);

return $composerConfig['extra']['public-dir'] ?? $defaultPublicDir;
return isset($composerConfig['extra']['public-dir']) ? $projectDir.'/'.$composerConfig['extra']['public-dir'] : $defaultPublicDir;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Symfony\Component\AssetMapper\Command\ImportMapRemoveCommand;
use Symfony\Component\AssetMapper\Command\ImportMapRequireCommand;
use Symfony\Component\AssetMapper\Command\ImportMapUpdateCommand;
use Symfony\Component\AssetMapper\CompiledAssetMapperConfigReader;
use Symfony\Component\AssetMapper\Compiler\CssAssetUrlCompiler;
use Symfony\Component\AssetMapper\Compiler\JavaScriptImportPathCompiler;
use Symfony\Component\AssetMapper\Compiler\SourceMappingUrlsCompiler;
Expand All @@ -40,6 +41,7 @@
use Symfony\Component\AssetMapper\ImportMap\RemotePackageStorage;
use Symfony\Component\AssetMapper\ImportMap\Resolver\JsDelivrEsmResolver;
use Symfony\Component\AssetMapper\MapperAwareAssetPackage;
use Symfony\Component\AssetMapper\Path\LocalPublicAssetsFilesystem;
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolver;

return static function (ContainerConfigurator $container) {
Expand All @@ -48,7 +50,7 @@
->args([
service('asset_mapper.repository'),
service('asset_mapper.mapped_asset_factory'),
service('asset_mapper.public_assets_path_resolver'),
service('asset_mapper.compiled_asset_mapper_config_reader'),
])
->alias(AssetMapperInterface::class, 'asset_mapper')

Expand Down Expand Up @@ -76,9 +78,17 @@

->set('asset_mapper.public_assets_path_resolver', PublicAssetsPathResolver::class)
->args([
param('kernel.project_dir'),
abstract_arg('asset public prefix'),
abstract_arg('public directory name'),
])

->set('asset_mapper.local_public_assets_filesystem', LocalPublicAssetsFilesystem::class)
->args([
abstract_arg('public directory'),
])

->set('asset_mapper.compiled_asset_mapper_config_reader', CompiledAssetMapperConfigReader::class)
->args([
abstract_arg('public assets directory'),
])

->set('asset_mapper.asset_package', MapperAwareAssetPackage::class)
Expand All @@ -100,12 +110,11 @@

->set('asset_mapper.command.compile', AssetMapperCompileCommand::class)
->args([
service('asset_mapper.public_assets_path_resolver'),
service('asset_mapper.compiled_asset_mapper_config_reader'),
service('asset_mapper'),
service('asset_mapper.importmap.generator'),
service('filesystem'),
service('asset_mapper.local_public_assets_filesystem'),
param('kernel.project_dir'),
abstract_arg('public directory name'),
param('kernel.debug'),
service('event_dispatcher')->nullOnInvalid(),
])
Expand Down Expand Up @@ -163,7 +172,7 @@
->set('asset_mapper.importmap.generator', ImportMapGenerator::class)
->args([
service('asset_mapper'),
service('asset_mapper.public_assets_path_resolver'),
service('asset_mapper.compiled_asset_mapper_config_reader'),
service('asset_mapper.importmap.config_reader'),
])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public function testAssetMapper()
$container = $this->createContainerFromFile('asset_mapper');

$definition = $container->getDefinition('asset_mapper.public_assets_path_resolver');
$this->assertSame('/assets_path/', $definition->getArgument(1));
$this->assertSame('/assets_path/', $definition->getArgument(0));

$definition = $container->getDefinition('asset_mapper.dev_server_subscriber');
$this->assertSame(['zip' => 'application/zip'], $definition->getArgument(2));
Expand Down
9 changes: 3 additions & 6 deletions src/Symfony/Component/AssetMapper/AssetMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
namespace Symfony\Component\AssetMapper;

use Symfony\Component\AssetMapper\Factory\MappedAssetFactoryInterface;
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface;

/**
* Finds and returns assets in the pipeline.
Expand All @@ -28,7 +27,7 @@ class AssetMapper implements AssetMapperInterface
public function __construct(
private readonly AssetMapperRepository $mapperRepository,
private readonly MappedAssetFactoryInterface $mappedAssetFactory,
private readonly PublicAssetsPathResolverInterface $assetsPathResolver,
private readonly CompiledAssetMapperConfigReader $compiledConfigReader,
) {
}

Expand Down Expand Up @@ -78,12 +77,10 @@ public function getPublicPath(string $logicalPath): ?string
private function loadManifest(): array
{
if (null === $this->manifestData) {
$path = $this->assetsPathResolver->getPublicFilesystemPath().'/'.self::MANIFEST_FILE_NAME;

if (!is_file($path)) {
if (!$this->compiledConfigReader->configExists(self::MANIFEST_FILE_NAME)) {
$this->manifestData = [];
} else {
$this->manifestData = json_decode(file_get_contents($path), true);
$this->manifestData = $this->compiledConfigReader->loadConfig(self::MANIFEST_FILE_NAME);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,15 @@

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\PublicAssetsPathResolverInterface;
use Symfony\Component\AssetMapper\Path\PublicAssetsFilesystemInterface;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\InvalidArgumentException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;

/**
Expand All @@ -36,12 +35,11 @@
final class AssetMapperCompileCommand extends Command
{
public function __construct(
private readonly PublicAssetsPathResolverInterface $publicAssetsPathResolver,
private readonly CompiledAssetMapperConfigReader $compiledConfigReader,
private readonly AssetMapperInterface $assetMapper,
private readonly ImportMapGenerator $importMapGenerator,
private readonly Filesystem $filesystem,
private readonly PublicAssetsFilesystemInterface $assetsFilesystem,
private readonly string $projectDir,
private readonly string $publicDirName,
private readonly bool $isDebug,
private readonly ?EventDispatcherInterface $eventDispatcher = null,
) {
Expand All @@ -51,7 +49,6 @@ public function __construct(
protected function configure(): void
{
$this
->addOption('clean', null, null, 'Whether to clean the public directory before compiling assets')
->setHelp(<<<'EOT'
The <info>%command.name%</info> command compiles and dumps all the assets in
the asset mapper into the final public directory (usually <comment>public/assets</comment>).
Expand All @@ -64,61 +61,36 @@ protected function configure(): void
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$publicDir = $this->projectDir.'/'.$this->publicDirName;
if (!is_dir($publicDir)) {
throw new InvalidArgumentException(sprintf('The public directory "%s" does not exist.', $publicDir));
}

$outputDir = $this->publicAssetsPathResolver->getPublicFilesystemPath();
if ($input->getOption('clean')) {
$io->comment(sprintf('Cleaning <info>%s</info>', $outputDir));
$this->filesystem->remove($outputDir);
$this->filesystem->mkdir($outputDir);
}

// set up the file paths
$files = [];
$manifestPath = $outputDir.'/'.AssetMapper::MANIFEST_FILE_NAME;
$files[] = $manifestPath;

$importMapPath = $outputDir.'/'.ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME;
$files[] = $importMapPath;
$this->eventDispatcher?->dispatch(new PreAssetsCompileEvent($output));

$entrypointFilePaths = [];
// 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) {
$dumpedEntrypointPath = $outputDir.'/'.sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName);
$files[] = $dumpedEntrypointPath;
$entrypointFilePaths[$entrypointName] = $dumpedEntrypointPath;
$path = sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName);
$this->compiledConfigReader->removeConfig($path);
$entrypointFiles[$entrypointName] = $path;
}

// remove existing files
foreach ($files as $file) {
if (is_file($file)) {
$this->filesystem->remove($file);
}
}

$this->eventDispatcher?->dispatch(new PreAssetsCompileEvent($outputDir, $output));

// dump new files
$manifest = $this->createManifestAndWriteFiles($io, $publicDir);
$this->filesystem->dumpFile($manifestPath, json_encode($manifest, \JSON_PRETTY_PRINT));
$manifest = $this->createManifestAndWriteFiles($io);
$manifestPath = $this->compiledConfigReader->saveConfig(AssetMapper::MANIFEST_FILE_NAME, $manifest);
$io->comment(sprintf('Manifest written to <info>%s</info>', $this->shortenPath($manifestPath)));

$this->filesystem->dumpFile($importMapPath, json_encode($this->importMapGenerator->getRawImportMapData(), \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_HEX_TAG));
$importMapPath = $this->compiledConfigReader->saveConfig(ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME, $this->importMapGenerator->getRawImportMapData());
$io->comment(sprintf('Import map data written to <info>%s</info>.', $this->shortenPath($importMapPath)));

$entrypointNames = $this->importMapGenerator->getEntrypointNames();
foreach ($entrypointFilePaths as $entrypointName => $path) {
$this->filesystem->dumpFile($path, json_encode($this->importMapGenerator->findEagerEntrypointImports($entrypointName), \JSON_THROW_ON_ERROR | \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_HEX_TAG));
foreach ($entrypointFiles as $entrypointName => $path) {
$this->compiledConfigReader->saveConfig($path, $this->importMapGenerator->findEagerEntrypointImports($entrypointName));
}
$styledEntrypointNames = array_map(fn (string $entrypointName) => sprintf('<info>%s</>', $entrypointName), $entrypointNames);
$io->comment(sprintf('Entrypoint metadata written for <comment>%d</> entrypoints (%s).', \count($entrypointNames), implode(', ', $styledEntrypointNames)));
$styledEntrypointNames = array_map(fn (string $entrypointName) => sprintf('<info>%s</>', $entrypointName), array_keys($entrypointFiles));
$io->comment(sprintf('Entrypoint metadata written for <comment>%d</> entrypoints (%s).', \count($entrypointFiles), implode(', ', $styledEntrypointNames)));

if ($this->isDebug) {
$io->warning(sprintf(
'You are compiling assets in development. Symfony will not serve any changed assets until you delete the "%s" directory.',
$this->shortenPath($outputDir)
'You are compiling assets in development. Symfony will not serve any changed assets until you delete the files in the "%s" directory.',
$this->shortenPath(\dirname($manifestPath))
));
}

Expand All @@ -130,20 +102,18 @@ private function shortenPath(string $path): string
return str_replace($this->projectDir.'/', '', $path);
}

private function createManifestAndWriteFiles(SymfonyStyle $io, string $publicDir): array
private function createManifestAndWriteFiles(SymfonyStyle $io): array
{
$allAssets = $this->assetMapper->allAssets();

$io->comment(sprintf('Compiling assets to <info>%s%s</info>', $publicDir, $this->publicAssetsPathResolver->resolvePublicPath('')));
$io->comment(sprintf('Compiling and writing asset files to <info>%s</info>', $this->shortenPath($this->assetsFilesystem->getDestinationPath())));
$manifest = [];
foreach ($allAssets as $asset) {
// $asset->getPublicPath() will start with a "/"
$targetPath = $publicDir.$asset->publicPath;
if (null !== $asset->content) {
// The original content has been modified by the AssetMapperCompiler
$this->filesystem->dumpFile($targetPath, $asset->content);
$this->assetsFilesystem->write($asset->publicPath, $asset->content);
} else {
$this->filesystem->copy($asset->sourcePath, $targetPath, true);
$this->assetsFilesystem->copy($asset->sourcePath, $asset->publicPath);
}

$manifest[$asset->logicalPath] = $asset->publicPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\AssetMapper;

use Symfony\Component\Filesystem\Path;

/**
* Reads and writes compiled configuration files for asset mapper.
*/
class CompiledAssetMapperConfigReader
{
public function __construct(private readonly string $directory)
{
}

public function configExists(string $filename): bool
{
return is_file(Path::join($this->directory, $filename));
}

public function loadConfig(string $filename): array
{
return json_decode(file_get_contents(Path::join($this->directory, $filename)), true, 512, \JSON_THROW_ON_ERROR);
}

public function saveConfig(string $filename, array $data): string
{
$path = Path::join($this->directory, $filename);
@mkdir(\dirname($path), 0777, true);
file_put_contents($path, json_encode($data, \JSON_PRETTY_PRINT | \JSON_THROW_ON_ERROR));

return $path;
}

public function removeConfig(string $filename): void
{
$path = Path::join($this->directory, $filename);

if (is_file($path)) {
unlink($path);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,13 @@
*/
class PreAssetsCompileEvent extends Event
{
private string $outputDir;
private OutputInterface $output;

public function __construct(string $outputDir, OutputInterface $output)
public function __construct(OutputInterface $output)
{
$this->outputDir = $outputDir;
$this->output = $output;
}

public function getOutputDir(): string
{
return $this->outputDir;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created this event - and just included $outputDir because I had it available. Now that it's not readily available, I don't think it's a big deal to not include it. My use-case for this event is to run some code that pre-compiles some internal files (not into the output directory) so that when the true compilation process happens, those files are ready and available.


public function getOutput(): OutputInterface
{
return $this->output;
Expand Down
Loading
0