From f3b0c8db916677f64e57d046a809634540e1c51a Mon Sep 17 00:00:00 2001
From: catch <6915-catch@users.noreply.drupalcode.org>
Date: Tue, 4 Mar 2025 11:32:22 +0000
Subject: [PATCH] Issue #3464388 by vidorado, mogtofu33, smustgrave, wim leers:
 SDC *.component.yml metadata is cached aggressively, gets in the way of
 component development

---
 core/core.services.yml                        |  1 +
 .../Core/Theme/ComponentPluginManager.php     | 17 +++++++
 .../Components/ComponentPluginManagerTest.php | 44 +++++++++++++++++++
 3 files changed, 62 insertions(+)

diff --git a/core/core.services.yml b/core/core.services.yml
index bb73e29d505b..f3477c8d8853 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1887,6 +1887,7 @@ services:
       - '@file_system'
       - '@Drupal\Core\Theme\Component\SchemaCompatibilityChecker'
       - '@Drupal\Core\Theme\Component\ComponentValidator'
+      - '@keyvalue'
       - '%app.root%'
   Drupal\Core\Theme\ComponentPluginManager: '@plugin.manager.sdc'
   Drupal\Core\Template\Loader\ComponentLoader:
diff --git a/core/lib/Drupal/Core/Theme/ComponentPluginManager.php b/core/lib/Drupal/Core/Theme/ComponentPluginManager.php
index a0c93317699f..3e1b501e2241 100644
--- a/core/lib/Drupal/Core/Theme/ComponentPluginManager.php
+++ b/core/lib/Drupal/Core/Theme/ComponentPluginManager.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Extension\ThemeHandlerInterface;
 use Drupal\Core\File\FileSystemInterface;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
 use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
@@ -62,6 +63,8 @@ class ComponentPluginManager extends DefaultPluginManager implements Categorizin
    *   The compatibility checker.
    * @param \Drupal\Core\Theme\Component\ComponentValidator $componentValidator
    *   The component validator.
+   * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $keyValueFactory
+   *   The key value factory.
    * @param string $appRoot
    *   The application root.
    */
@@ -75,6 +78,7 @@ public function __construct(
     protected FileSystemInterface $fileSystem,
     protected SchemaCompatibilityChecker $compatibilityChecker,
     protected ComponentValidator $componentValidator,
+    protected KeyValueFactoryInterface $keyValueFactory,
     protected string $appRoot,
   ) {
     // We are skipping the call to the parent constructor to avoid initializing
@@ -124,6 +128,19 @@ public function createInstance($plugin_id, array $configuration = []): Component
     }
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinitions(): array {
+    $development_settings = $this->keyValueFactory->get('development_settings');
+    $twig_debug = $development_settings->get('twig_debug', FALSE);
+    $twig_cache_disable = $development_settings->get('twig_cache_disable', FALSE);
+    if ($twig_debug || $twig_cache_disable) {
+      return $this->findDefinitions();
+    }
+    return parent::getDefinitions();
+  }
+
   /**
    * Gets a component for rendering.
    *
diff --git a/core/tests/Drupal/KernelTests/Components/ComponentPluginManagerTest.php b/core/tests/Drupal/KernelTests/Components/ComponentPluginManagerTest.php
index 1f110e36b4b5..59393564944d 100644
--- a/core/tests/Drupal/KernelTests/Components/ComponentPluginManagerTest.php
+++ b/core/tests/Drupal/KernelTests/Components/ComponentPluginManagerTest.php
@@ -5,6 +5,7 @@
 namespace Drupal\KernelTests\Components;
 
 use Drupal\Core\Render\Component\Exception\ComponentNotFoundException;
+use Drupal\Core\Cache\CacheBackendInterface;
 
 /**
  * Tests the component plugin manager.
@@ -49,4 +50,47 @@ public function testMismatchingFolderName(): void {
     $this->manager->find('sdc_theme_test:mismatching-folder-name');
   }
 
+  /**
+   * Test component definitions caching depending on twig debug/cache settings.
+   *
+   * @param bool $twigDebug
+   *   Whether twig debug is enabled.
+   * @param bool $cacheEnabled
+   *   Whether cache is enabled.
+   * @param bool $expectCacheGet
+   *   Whether we expect the cache to be called.
+   *
+   * @dataProvider providerTestComponentCachingDependingOnDevelopmentSettings
+   */
+  public function testComponentCachingDependingOnDevelopmentSettings(bool $twigDebug, bool $cacheEnabled, bool $expectCacheGet): void {
+    // Set the development settings.
+    $developmentSettings = $this->keyValue->get('development_settings');
+    $developmentSettings->set('twig_debug', $twigDebug);
+    $developmentSettings->set('twig_cache_disable', !$cacheEnabled);
+
+    // Set the cache backend as a spy mock.
+    $cacheBackend = $this->createMock(CacheBackendInterface::class);
+    $cacheBackend->expects($expectCacheGet ? $this->once() : $this->never())
+      ->method('get')
+      ->with('cache_key');
+    $this->manager->setCacheBackend($cacheBackend, 'cache_key');
+
+    // Make two calls to getDefinitions() to ensure the
+    // cache is/isn't called if it should/shouldn't be.
+    $this->manager->getDefinitions();
+    $this->manager->getDefinitions();
+  }
+
+  /**
+   * Data provider for testComponentCachingDependingOnDevelopmentSettings().
+   */
+  public static function providerTestComponentCachingDependingOnDevelopmentSettings(): array {
+    return [
+      'Debug enabled, cache enabled' => [TRUE, TRUE, FALSE],
+      'Debug enabled, cache disabled' => [TRUE, FALSE, FALSE],
+      'Debug disabled, cache enabled' => [FALSE, TRUE, TRUE],
+      'Debug disabled, cache disabled' => [FALSE, FALSE, FALSE],
+    ];
+  }
+
 }
-- 
GitLab