Skip to content

Commit 11050dc

Browse files
committed
[AssetMapper] Allowing for files to be written to some non-local location
1 parent b6418aa commit 11050dc

20 files changed

+349
-163
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,13 +1353,17 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde
13531353
->setArgument(0, $paths)
13541354
->setArgument(2, $excludedPathPatterns);
13551355

1356-
$publicDirName = $this->getPublicDirectoryName($container);
13571356
$container->getDefinition('asset_mapper.public_assets_path_resolver')
1358-
->setArgument(1, $config['public_prefix'])
1359-
->setArgument(2, $publicDirName);
1357+
->setArgument(0, $config['public_prefix']);
13601358

1361-
$container->getDefinition('asset_mapper.command.compile')
1362-
->setArgument(5, $publicDirName);
1359+
$publicDirectory = $this->getPublicDirectory($container);
1360+
$publicAssetsDirectory = rtrim($publicDirectory.'/'.ltrim($config['public_prefix'], '/'), '/');
1361+
$container->getDefinition('asset_mapper.local_public_assets_filesystem')
1362+
->setArgument(0, $publicDirectory)
1363+
;
1364+
1365+
$container->getDefinition('asset_mapper.compiled_asset_mapper_config_reader')
1366+
->setArgument(0, $publicAssetsDirectory);
13631367

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

3166-
private function getPublicDirectoryName(ContainerBuilder $container): string
3170+
private function getPublicDirectory(ContainerBuilder $container): string
31673171
{
3168-
$defaultPublicDir = 'public';
3172+
$projectDir = $container->getParameter('kernel.project_dir');
3173+
$defaultPublicDir = $projectDir.'/public';
31693174

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

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

3179-
return $composerConfig['extra']['public-dir'] ?? $defaultPublicDir;
3184+
return isset($composerConfig['extra']['public-dir']) ? $projectDir.'/'.$composerConfig['extra']['public-dir'] : $defaultPublicDir;
31803185
}
31813186
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\AssetMapper\Command\ImportMapRemoveCommand;
2525
use Symfony\Component\AssetMapper\Command\ImportMapRequireCommand;
2626
use Symfony\Component\AssetMapper\Command\ImportMapUpdateCommand;
27+
use Symfony\Component\AssetMapper\CompiledAssetMapperConfigReader;
2728
use Symfony\Component\AssetMapper\Compiler\CssAssetUrlCompiler;
2829
use Symfony\Component\AssetMapper\Compiler\JavaScriptImportPathCompiler;
2930
use Symfony\Component\AssetMapper\Compiler\SourceMappingUrlsCompiler;
@@ -40,6 +41,7 @@
4041
use Symfony\Component\AssetMapper\ImportMap\RemotePackageStorage;
4142
use Symfony\Component\AssetMapper\ImportMap\Resolver\JsDelivrEsmResolver;
4243
use Symfony\Component\AssetMapper\MapperAwareAssetPackage;
44+
use Symfony\Component\AssetMapper\Path\LocalPublicAssetsFilesystem;
4345
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolver;
4446

4547
return static function (ContainerConfigurator $container) {
@@ -48,7 +50,7 @@
4850
->args([
4951
service('asset_mapper.repository'),
5052
service('asset_mapper.mapped_asset_factory'),
51-
service('asset_mapper.public_assets_path_resolver'),
53+
service('asset_mapper.compiled_asset_mapper_config_reader'),
5254
])
5355
->alias(AssetMapperInterface::class, 'asset_mapper')
5456

@@ -76,9 +78,17 @@
7678

7779
->set('asset_mapper.public_assets_path_resolver', PublicAssetsPathResolver::class)
7880
->args([
79-
param('kernel.project_dir'),
8081
abstract_arg('asset public prefix'),
81-
abstract_arg('public directory name'),
82+
])
83+
84+
->set('asset_mapper.local_public_assets_filesystem', LocalPublicAssetsFilesystem::class)
85+
->args([
86+
abstract_arg('public directory'),
87+
])
88+
89+
->set('asset_mapper.compiled_asset_mapper_config_reader', CompiledAssetMapperConfigReader::class)
90+
->args([
91+
abstract_arg('public assets directory'),
8292
])
8393

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

101111
->set('asset_mapper.command.compile', AssetMapperCompileCommand::class)
102112
->args([
103-
service('asset_mapper.public_assets_path_resolver'),
113+
service('asset_mapper.compiled_asset_mapper_config_reader'),
104114
service('asset_mapper'),
105115
service('asset_mapper.importmap.generator'),
106-
service('filesystem'),
116+
service('asset_mapper.local_public_assets_filesystem'),
107117
param('kernel.project_dir'),
108-
abstract_arg('public directory name'),
109118
param('kernel.debug'),
110119
service('event_dispatcher')->nullOnInvalid(),
111120
])
@@ -163,7 +172,7 @@
163172
->set('asset_mapper.importmap.generator', ImportMapGenerator::class)
164173
->args([
165174
service('asset_mapper'),
166-
service('asset_mapper.public_assets_path_resolver'),
175+
service('asset_mapper.compiled_asset_mapper_config_reader'),
167176
service('asset_mapper.importmap.config_reader'),
168177
])
169178

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/XmlFrameworkExtensionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public function testAssetMapper()
7979
$container = $this->createContainerFromFile('asset_mapper');
8080

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

8484
$definition = $container->getDefinition('asset_mapper.dev_server_subscriber');
8585
$this->assertSame(['zip' => 'application/zip'], $definition->getArgument(2));

src/Symfony/Component/AssetMapper/AssetMapper.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
namespace Symfony\Component\AssetMapper;
1313

1414
use Symfony\Component\AssetMapper\Factory\MappedAssetFactoryInterface;
15-
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface;
1615

1716
/**
1817
* Finds and returns assets in the pipeline.
@@ -28,7 +27,7 @@ class AssetMapper implements AssetMapperInterface
2827
public function __construct(
2928
private readonly AssetMapperRepository $mapperRepository,
3029
private readonly MappedAssetFactoryInterface $mappedAssetFactory,
31-
private readonly PublicAssetsPathResolverInterface $assetsPathResolver,
30+
private readonly CompiledAssetMapperConfigReader $compiledConfigReader,
3231
) {
3332
}
3433

@@ -78,12 +77,10 @@ public function getPublicPath(string $logicalPath): ?string
7877
private function loadManifest(): array
7978
{
8079
if (null === $this->manifestData) {
81-
$path = $this->assetsPathResolver->getPublicFilesystemPath().'/'.self::MANIFEST_FILE_NAME;
82-
83-
if (!is_file($path)) {
80+
if (!$this->compiledConfigReader->configExists(self::MANIFEST_FILE_NAME)) {
8481
$this->manifestData = [];
8582
} else {
86-
$this->manifestData = json_decode(file_get_contents($path), true);
83+
$this->manifestData = $this->compiledConfigReader->loadConfig(self::MANIFEST_FILE_NAME);
8784
}
8885
}
8986

src/Symfony/Component/AssetMapper/Command/AssetMapperCompileCommand.php

Lines changed: 26 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,17 @@
1313

1414
use Symfony\Component\AssetMapper\AssetMapper;
1515
use Symfony\Component\AssetMapper\AssetMapperInterface;
16+
use Symfony\Component\AssetMapper\CompiledAssetMapperConfigReader;
1617
use Symfony\Component\AssetMapper\Event\PreAssetsCompileEvent;
1718
use Symfony\Component\AssetMapper\ImportMap\ImportMapGenerator;
1819
use Symfony\Component\AssetMapper\Path\PublicAssetsPathResolverInterface;
20+
use Symfony\Component\AssetMapper\ImportMap\ImportMapManager;
21+
use Symfony\Component\AssetMapper\Path\PublicAssetsFilesystemInterface;
1922
use Symfony\Component\Console\Attribute\AsCommand;
2023
use Symfony\Component\Console\Command\Command;
21-
use Symfony\Component\Console\Exception\InvalidArgumentException;
2224
use Symfony\Component\Console\Input\InputInterface;
2325
use Symfony\Component\Console\Output\OutputInterface;
2426
use Symfony\Component\Console\Style\SymfonyStyle;
25-
use Symfony\Component\Filesystem\Filesystem;
2627
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
2728

2829
/**
@@ -36,12 +37,11 @@
3637
final class AssetMapperCompileCommand extends Command
3738
{
3839
public function __construct(
39-
private readonly PublicAssetsPathResolverInterface $publicAssetsPathResolver,
40+
private readonly CompiledAssetMapperConfigReader $compiledConfigReader,
4041
private readonly AssetMapperInterface $assetMapper,
4142
private readonly ImportMapGenerator $importMapGenerator,
42-
private readonly Filesystem $filesystem,
43+
private readonly PublicAssetsFilesystemInterface $assetsFilesystem,
4344
private readonly string $projectDir,
44-
private readonly string $publicDirName,
4545
private readonly bool $isDebug,
4646
private readonly ?EventDispatcherInterface $eventDispatcher = null,
4747
) {
@@ -51,7 +51,6 @@ public function __construct(
5151
protected function configure(): void
5252
{
5353
$this
54-
->addOption('clean', null, null, 'Whether to clean the public directory before compiling assets')
5554
->setHelp(<<<'EOT'
5655
The <info>%command.name%</info> command compiles and dumps all the assets in
5756
the asset mapper into the final public directory (usually <comment>public/assets</comment>).
@@ -64,61 +63,36 @@ protected function configure(): void
6463
protected function execute(InputInterface $input, OutputInterface $output): int
6564
{
6665
$io = new SymfonyStyle($input, $output);
67-
$publicDir = $this->projectDir.'/'.$this->publicDirName;
68-
if (!is_dir($publicDir)) {
69-
throw new InvalidArgumentException(sprintf('The public directory "%s" does not exist.', $publicDir));
70-
}
71-
72-
$outputDir = $this->publicAssetsPathResolver->getPublicFilesystemPath();
73-
if ($input->getOption('clean')) {
74-
$io->comment(sprintf('Cleaning <info>%s</info>', $outputDir));
75-
$this->filesystem->remove($outputDir);
76-
$this->filesystem->mkdir($outputDir);
77-
}
78-
79-
// set up the file paths
80-
$files = [];
81-
$manifestPath = $outputDir.'/'.AssetMapper::MANIFEST_FILE_NAME;
82-
$files[] = $manifestPath;
8366

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

87-
$entrypointFilePaths = [];
69+
// remove existing config files
70+
$this->compiledConfigReader->removeConfig(AssetMapper::MANIFEST_FILE_NAME);
71+
$this->compiledConfigReader->removeConfig(ImportMapGenerator::IMPORT_MAP_CACHE_FILENAME);
72+
$entrypointFiles = [];
8873
foreach ($this->importMapGenerator->getEntrypointNames() as $entrypointName) {
89-
$dumpedEntrypointPath = $outputDir.'/'.sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName);
90-
$files[] = $dumpedEntrypointPath;
91-
$entrypointFilePaths[$entrypointName] = $dumpedEntrypointPath;
74+
$path = sprintf(ImportMapGenerator::ENTRYPOINT_CACHE_FILENAME_PATTERN, $entrypointName);
75+
$this->compiledConfigReader->removeConfig($path);
76+
$entrypointFiles[$entrypointName] = $path;
9277
}
9378

94-
// remove existing files
95-
foreach ($files as $file) {
96-
if (is_file($file)) {
97-
$this->filesystem->remove($file);
98-
}
99-
}
100-
101-
$this->eventDispatcher?->dispatch(new PreAssetsCompileEvent($outputDir, $output));
102-
103-
// dump new files
104-
$manifest = $this->createManifestAndWriteFiles($io, $publicDir);
105-
$this->filesystem->dumpFile($manifestPath, json_encode($manifest, \JSON_PRETTY_PRINT));
79+
$manifest = $this->createManifestAndWriteFiles($io);
80+
$manifestPath = $this->compiledConfigReader->saveConfig(AssetMapper::MANIFEST_FILE_NAME, $manifest);
10681
$io->comment(sprintf('Manifest written to <info>%s</info>', $this->shortenPath($manifestPath)));
10782

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

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

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

@@ -130,20 +104,18 @@ private function shortenPath(string $path): string
130104
return str_replace($this->projectDir.'/', '', $path);
131105
}
132106

133-
private function createManifestAndWriteFiles(SymfonyStyle $io, string $publicDir): array
107+
private function createManifestAndWriteFiles(SymfonyStyle $io): array
134108
{
135109
$allAssets = $this->assetMapper->allAssets();
136110

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

149121
$manifest[$asset->logicalPath] = $asset->publicPath;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\AssetMapper;
13+
14+
use Symfony\Component\Filesystem\Path;
15+
16+
/**
17+
* Reads and writes compiled configuration files for asset mapper.
18+
*/
19+
class CompiledAssetMapperConfigReader
20+
{
21+
public function __construct(private readonly string $directory)
22+
{
23+
}
24+
25+
public function configExists(string $filename): bool
26+
{
27+
return is_file(Path::join($this->directory, $filename));
28+
}
29+
30+
public function loadConfig(string $filename): array
31+
{
32+
return json_decode(file_get_contents(Path::join($this->directory, $filename)), true, 512, \JSON_THROW_ON_ERROR);
33+
}
34+
35+
public function saveConfig(string $filename, array $data): string
36+
{
37+
$path = Path::join($this->directory, $filename);
38+
@mkdir(\dirname($path), 0777, true);
39+
file_put_contents($path, json_encode($data, \JSON_PRETTY_PRINT | \JSON_THROW_ON_ERROR));
40+
41+
return $path;
42+
}
43+
44+
public function removeConfig(string $filename): void
45+
{
46+
$path = Path::join($this->directory, $filename);
47+
48+
if (is_file($path)) {
49+
unlink($path);
50+
}
51+
}
52+
}

src/Symfony/Component/AssetMapper/Event/PreAssetsCompileEvent.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,13 @@
2121
*/
2222
class PreAssetsCompileEvent extends Event
2323
{
24-
private string $outputDir;
2524
private OutputInterface $output;
2625

27-
public function __construct(string $outputDir, OutputInterface $output)
26+
public function __construct(OutputInterface $output)
2827
{
29-
$this->outputDir = $outputDir;
3028
$this->output = $output;
3129
}
3230

33-
public function getOutputDir(): string
34-
{
35-
return $this->outputDir;
36-
}
37-
3831
public function getOutput(): OutputInterface
3932
{
4033
return $this->output;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy