From 1c5a1e02a41ebfb76583b8d8dc6b75ec9eaee8fb Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 10 May 2023 10:23:45 -0400 Subject: [PATCH] [AssetMapper] Adding "excluded_patterns" option --- .../DependencyInjection/Configuration.php | 6 ++++ .../FrameworkExtension.php | 9 ++++- .../Resources/config/asset_mapper.php | 1 + .../Resources/config/schema/symfony-1.0.xsd | 1 + .../DependencyInjection/ConfigurationTest.php | 2 ++ .../Fixtures/xml/asset_mapper.xml | 1 + .../AssetMapper/AssetMapperRepository.php | 27 +++++++++++++-- .../Tests/AssetMapperRepositoryTest.php | 33 +++++++++++++++++++ .../Component/AssetMapper/composer.json | 1 + 9 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php index 0dd13d245dce4..51f41c93cd957 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php @@ -821,6 +821,7 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->info('Asset Mapper configuration') ->{$enableIfStandalone('symfony/asset-mapper', AssetMapper::class)}() ->fixXmlConfig('path') + ->fixXmlConfig('excluded_pattern') ->fixXmlConfig('extension') ->fixXmlConfig('importmap_script_attribute') ->children() @@ -856,6 +857,11 @@ private function addAssetMapperSection(ArrayNodeDefinition $rootNode, callable $ ->end() ->prototype('scalar')->end() ->end() + ->arrayNode('excluded_patterns') + ->info('Array of glob patterns of asset file paths that should not be in the asset mapper') + ->prototype('scalar')->end() + ->example(['*/assets/build/*', '*/*_.scss']) + ->end() ->booleanNode('server') ->info('If true, a "dev server" will return the assets from the public directory (true in "debug" mode only by default)') ->defaultValue($this->debug) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 386cbb449f286..02c685980f755 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -72,6 +72,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\Glob; use Symfony\Component\Form\ChoiceList\Factory\CachingFactoryDecorator; use Symfony\Component\Form\Extension\HtmlSanitizer\Type\TextTypeHtmlSanitizerExtension; use Symfony\Component\Form\Form; @@ -1277,8 +1278,14 @@ private function registerAssetMapperConfiguration(array $config, ContainerBuilde $paths[$dir] = sprintf('bundles/%s', preg_replace('/bundle$/', '', strtolower($name))); } } + $excludedPathPatterns = []; + foreach ($config['excluded_patterns'] as $path) { + $excludedPathPatterns[] = Glob::toRegex($path, true, false); + } + $container->getDefinition('asset_mapper.repository') - ->setArgument(0, $paths); + ->setArgument(0, $paths) + ->setArgument(2, $excludedPathPatterns); $publicDirName = $this->getPublicDirectoryName($container); $container->getDefinition('asset_mapper.public_assets_path_resolver') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php index ca37d1e8ff43d..3b5b99217e4fe 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/asset_mapper.php @@ -45,6 +45,7 @@ ->args([ abstract_arg('array of asset mapper paths'), param('kernel.project_dir'), + abstract_arg('array of excluded path patterns'), ]) ->set('asset_mapper.public_assets_path_resolver', PublicAssetsPathResolver::class) diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd index 52b1f158d4391..37275385ed487 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd @@ -190,6 +190,7 @@ + diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php index e91d32a68e74e..81157ff6137ae 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/ConfigurationTest.php @@ -104,6 +104,7 @@ public function testAssetMapperCanBeEnabled() $defaultConfig = [ 'enabled' => true, 'paths' => [], + 'excluded_patterns' => [], 'server' => true, 'public_prefix' => '/assets/', 'strict_mode' => true, @@ -615,6 +616,7 @@ protected static function getBundleDefaultConfig() 'asset_mapper' => [ 'enabled' => !class_exists(FullStack::class), 'paths' => [], + 'excluded_patterns' => [], 'server' => true, 'public_prefix' => '/assets/', 'strict_mode' => true, diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/asset_mapper.xml b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/asset_mapper.xml index ebde46f585e2b..fcc8a4c8844eb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/asset_mapper.xml +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/xml/asset_mapper.xml @@ -10,6 +10,7 @@ assets/ assets2/ + */*.scss true /assets_path/ true diff --git a/src/Symfony/Component/AssetMapper/AssetMapperRepository.php b/src/Symfony/Component/AssetMapper/AssetMapperRepository.php index 70ef44b000060..d16305d23160c 100644 --- a/src/Symfony/Component/AssetMapper/AssetMapperRepository.php +++ b/src/Symfony/Component/AssetMapper/AssetMapperRepository.php @@ -33,7 +33,8 @@ class AssetMapperRepository */ public function __construct( private readonly array $paths, - private readonly string $projectRootDir + private readonly string $projectRootDir, + private readonly array $excludedPathPatterns = [], ) { } @@ -54,7 +55,7 @@ public function find(string $logicalPath): ?string } $file = rtrim($path, '/').'/'.$localLogicalPath; - if (is_file($file)) { + if (is_file($file) && !$this->isExcluded($file)) { return realpath($file); } } @@ -70,6 +71,10 @@ public function findLogicalPath(string $filesystemPath): ?string $filesystemPath = realpath($filesystemPath); + if ($this->isExcluded($filesystemPath)) { + return null; + } + foreach ($this->getDirectories() as $path => $namespace) { if (!str_starts_with($filesystemPath, $path)) { continue; @@ -104,6 +109,10 @@ public function all(): array continue; } + if ($this->isExcluded($file->getPathname())) { + continue; + } + /** @var RecursiveDirectoryIterator $innerIterator */ $innerIterator = $iterator->getInnerIterator(); $logicalPath = ($namespace ? rtrim($namespace, '/').'/' : '').$innerIterator->getSubPathName(); @@ -160,4 +169,18 @@ private function normalizeLogicalPath(string $logicalPath): string { return ltrim(str_replace('\\', '/', $logicalPath), '/\\'); } + + private function isExcluded(string $filesystemPath): bool + { + // normalize Windows slashes and remove trailing slashes + $filesystemPath = rtrim(str_replace('\\', '/', $filesystemPath), '/'); + + foreach ($this->excludedPathPatterns as $pattern) { + if (preg_match($pattern, $filesystemPath)) { + return true; + } + } + + return false; + } } diff --git a/src/Symfony/Component/AssetMapper/Tests/AssetMapperRepositoryTest.php b/src/Symfony/Component/AssetMapper/Tests/AssetMapperRepositoryTest.php index e92b419fddadb..bb02d0a72e660 100644 --- a/src/Symfony/Component/AssetMapper/Tests/AssetMapperRepositoryTest.php +++ b/src/Symfony/Component/AssetMapper/Tests/AssetMapperRepositoryTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\AssetMapper\AssetMapperRepository; +use Symfony\Component\Finder\Glob; class AssetMapperRepositoryTest extends TestCase { @@ -128,4 +129,36 @@ public function testAllWithNamespaces() $this->assertEquals($normalizedExpectedAllAssets, $normalizedActualAssets); } + + public function testExcludedPaths() + { + $excludedPatterns = [ + '*/subdir/*', + '*/*3.css', + '*/*.digested.*', + ]; + $excludedGlobs = array_map(function ($pattern) { + // globbed equally in FrameworkExtension + return Glob::toRegex($pattern, true, false); + }, $excludedPatterns); + $repository = new AssetMapperRepository([ + 'dir1' => '', + 'dir2' => '', + 'dir3' => '', + ], __DIR__.'/fixtures', $excludedGlobs); + + $expectedAssets = [ + 'file1.css', + 'file2.js', + 'file4.js', + 'test.gif.foo', + ]; + + $actualAssets = array_keys($repository->all()); + sort($actualAssets); + $this->assertEquals($expectedAssets, $actualAssets); + + $this->assertNull($repository->find('file3.css')); + $this->assertNull($repository->findLogicalPath(__DIR__.'/fixtures/dir2/file3.css')); + } } diff --git a/src/Symfony/Component/AssetMapper/composer.json b/src/Symfony/Component/AssetMapper/composer.json index 24fcdc65376df..6c0488731a54f 100644 --- a/src/Symfony/Component/AssetMapper/composer.json +++ b/src/Symfony/Component/AssetMapper/composer.json @@ -24,6 +24,7 @@ "symfony/asset": "^5.4|^6.0", "symfony/browser-kit": "^5.4|^6.0", "symfony/console": "^5.4|^6.0", + "symfony/finder": "^5.4|^6.0", "symfony/framework-bundle": "^6.3", "symfony/http-foundation": "^5.4|^6.0", "symfony/http-kernel": "^5.4|^6.0"