diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php
index 251b9036f3a5de4bb265531c5b461f5d9b28fbbf..2a2ce62e1a0e2e5a836b9ea6e27fd034bf2b2eb3 100644
--- a/core/lib/Drupal/Core/Asset/AssetResolver.php
+++ b/core/lib/Drupal/Core/Asset/AssetResolver.php
@@ -143,27 +143,30 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, Languag
       'preprocess' => TRUE,
     ];
 
-    foreach ($libraries_to_load as $library) {
+    foreach ($libraries_to_load as $key => $library) {
       [$extension, $name] = explode('/', $library, 2);
       $definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
-      if (isset($definition['css'])) {
-        foreach ($definition['css'] as $options) {
-          $options += $default_options;
-          // Copy the asset library license information to each file.
-          $options['license'] = $definition['license'];
-
-          // Files with a query string cannot be preprocessed.
-          if ($options['type'] === 'file' && $options['preprocess'] && str_contains($options['data'], '?')) {
-            $options['preprocess'] = FALSE;
-          }
+      if (empty($definition['css'])) {
+        unset($libraries_to_load[$key]);
+        continue;
+      }
 
-          // Always add a tiny value to the weight, to conserve the insertion
-          // order.
-          $options['weight'] += count($css) / 30000;
+      foreach ($definition['css'] as $options) {
+        $options += $default_options;
+        // Copy the asset library license information to each file.
+        $options['license'] = $definition['license'];
 
-          // CSS files are being keyed by the full path.
-          $css[$options['data']] = $options;
+        // Files with a query string cannot be preprocessed.
+        if ($options['type'] === 'file' && $options['preprocess'] && str_contains($options['data'], '?')) {
+          $options['preprocess'] = FALSE;
         }
+
+        // Always add a tiny value to the weight, to conserve the insertion
+        // order.
+        $options['weight'] += count($css) / 30000;
+
+        // CSS files are being keyed by the full path.
+        $css[$options['data']] = $options;
       }
     }
 
@@ -176,7 +179,7 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, Languag
       uasort($css, [static::class, 'sort']);
 
       if ($optimize) {
-        $css = \Drupal::service('asset.css.collection_optimizer')->optimize($css, $libraries_to_load, $language);
+        $css = \Drupal::service('asset.css.collection_optimizer')->optimize($css, array_values($libraries_to_load), $language);
       }
     }
     $this->cache->set($cid, $css, CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
@@ -240,14 +243,21 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
       ];
 
       // Collect all libraries that contain JS assets and are in the header.
+      // Also remove any libraries with no JavaScript from the libraries to
+      // load.
       $header_js_libraries = [];
-      foreach ($libraries_to_load as $library) {
+      foreach ($libraries_to_load as $key => $library) {
         [$extension, $name] = explode('/', $library, 2);
         $definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
-        if (isset($definition['js']) && !empty($definition['header'])) {
+        if (empty($definition['js'])) {
+          unset($libraries_to_load[$key]);
+          continue;
+        }
+        if (!empty($definition['header'])) {
           $header_js_libraries[] = $library;
         }
       }
+      $libraries_to_load = array_values($libraries_to_load);
       // The current list of header JS libraries are only those libraries that
       // are in the header, but their dependencies must also be loaded for them
       // to function correctly, so update the list with those.
@@ -256,28 +266,26 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, Language
       foreach ($libraries_to_load as $library) {
         [$extension, $name] = explode('/', $library, 2);
         $definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
-        if (isset($definition['js'])) {
-          foreach ($definition['js'] as $options) {
-            $options += $default_options;
-            // Copy the asset library license information to each file.
-            $options['license'] = $definition['license'];
-
-            // 'scope' is a calculated option, based on which libraries are
-            // marked to be loaded from the header (see above).
-            $options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer';
-
-            // Preprocess can only be set if caching is enabled and no
-            // attributes are set.
-            $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE;
-
-            // Always add a tiny value to the weight, to conserve the insertion
-            // order.
-            $options['weight'] += count($javascript) / 30000;
-
-            // Local and external files must keep their name as the associative
-            // key so the same JavaScript file is not added twice.
-            $javascript[$options['data']] = $options;
-          }
+        foreach ($definition['js'] as $options) {
+          $options += $default_options;
+          // Copy the asset library license information to each file.
+          $options['license'] = $definition['license'];
+
+          // 'scope' is a calculated option, based on which libraries are
+          // marked to be loaded from the header (see above).
+          $options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer';
+
+          // Preprocess can only be set if caching is enabled and no
+          // attributes are set.
+          $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE;
+
+          // Always add a tiny value to the weight, to conserve the insertion
+          // order.
+          $options['weight'] += count($javascript) / 30000;
+
+          // Local and external files must keep their name as the associative
+          // key so the same JavaScript file is not added twice.
+          $javascript[$options['data']] = $options;
         }
       }
 
diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
index 61b1b66543c25774f906ed20a77aca8597024883..49117a946d49f19c394e5ca802550f26c4cd2b93 100644
--- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
+++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
@@ -26,8 +26,8 @@ public function testFrontAndRecipesPages() {
     $performance_data = $this->doRequests();
     $this->assertSame(4, $performance_data->getStylesheetCount());
     $this->assertLessThan(82500, $performance_data->getStylesheetBytes());
-    $this->assertSame(2, $performance_data->getScriptCount());
-    $this->assertLessThan(14500, $performance_data->getScriptBytes());
+    $this->assertSame(1, $performance_data->getScriptCount());
+    $this->assertLessThan(7500, $performance_data->getScriptBytes());
   }
 
   /**
@@ -40,8 +40,8 @@ public function testFrontAndRecipesPagesAuthenticated() {
     $performance_data = $this->doRequests();
     $this->assertSame(4, $performance_data->getStylesheetCount());
     $this->assertLessThan(89500, $performance_data->getStylesheetBytes());
-    $this->assertSame(2, $performance_data->getScriptCount());
-    $this->assertLessThan(250000, $performance_data->getScriptBytes());
+    $this->assertSame(1, $performance_data->getScriptCount());
+    $this->assertLessThan(125500, $performance_data->getScriptBytes());
   }
 
   /**