From 784effb38b53ee15f601489e9bfc73997f4ac625 Mon Sep 17 00:00:00 2001
From: nod_ <nod_@598310.no-reply.drupal.org>
Date: Thu, 13 Mar 2025 14:38:03 +0100
Subject: [PATCH] Issue #734080 by catch, pwolanin, nod_, sun, owen barton,
 ksenzee: Set preprocess: false for jquery.min.js to reduce duplication
 between asset aggregates

---
 core/core.libraries.yml                       |  7 ++++-
 .../FunctionalJavascript/PerformanceTest.php  |  2 +-
 .../AssetAggregationAcrossPagesTest.php       | 27 ++++++++++++++++---
 ...nTelemetryAuthenticatedPerformanceTest.php |  2 +-
 .../Core/Asset/AttachedAssetsTest.php         |  5 ++--
 5 files changed, 35 insertions(+), 8 deletions(-)

diff --git a/core/core.libraries.yml b/core/core.libraries.yml
index 5a4d983ad593..ee5987c51685 100644
--- a/core/core.libraries.yml
+++ b/core/core.libraries.yml
@@ -756,7 +756,12 @@ jquery:
     url: https://raw.githubusercontent.com/jquery/jquery/4.0.0-beta.2/LICENSE.txt
     gpl-compatible: true
   js:
-    assets/vendor/jquery/jquery.min.js: { minified: true, weight: -20 }
+    # jquery.min.js is explicitly set to preprocess: false to prevent its
+    # inclusion in JavaScript aggregates. This prevents it being duplicated
+    # across different aggregates which can include different combinations of
+    # libraries that depend on jQuery, so that it is only downloaded once per
+    # browser instead of potentially multiple times from different pages.
+    assets/vendor/jquery/jquery.min.js: { preprocess: false, minified: true, weight: -20 }
 
 internal.jquery.form:
   # Internal library. Do not depend on it outside core nor add new core usage.
diff --git a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
index e1edd2f916eb..1acfd62fb1da 100644
--- a/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
+++ b/core/modules/navigation/tests/src/FunctionalJavascript/PerformanceTest.php
@@ -90,7 +90,7 @@ public function testLogin(): void {
       'CacheDeleteCount' => 0,
       'CacheTagInvalidationCount' => 0,
       'CacheTagLookupQueryCount' => 20,
-      'ScriptCount' => 2,
+      'ScriptCount' => 3,
       'ScriptBytes' => 215500,
       'StylesheetCount' => 1,
       'StylesheetBytes' => 46300,
diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
index 896e825d62f2..fa7539d89e61 100644
--- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
+++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/AssetAggregationAcrossPagesTest.php
@@ -36,7 +36,7 @@ public function testFrontAndRecipesPages(): void {
   }
 
   /**
-   * Checks the asset requests made when the front and recipe pages are visited.
+   * Checks the front and recipe page asset requests as an authenticated user.
    */
   public function testFrontAndRecipesPagesAuthenticated(): void {
     $user = $this->createUser();
@@ -47,14 +47,35 @@ public function testFrontAndRecipesPagesAuthenticated(): void {
     }, 'umamiFrontAndRecipePagesAuthenticated');
 
     $expected = [
-      'ScriptCount' => 2,
-      'ScriptBytes' => 249700,
+      'ScriptCount' => 3,
+      'ScriptBytes' => 170500,
       'StylesheetCount' => 6,
     ];
     $this->assertMetrics($expected, $performance_data);
 
   }
 
+  /**
+   * Checks the front and recipe page asset requests as an editor.
+   */
+  public function testFrontAndRecipesPagesEditor(): void {
+    $user = $this->createUser();
+    $user->addRole('editor');
+    $user->save();
+    $this->drupalLogin($user);
+    sleep(2);
+    $performance_data = $this->collectPerformanceData(function () {
+      $this->doRequests();
+    }, 'umamiFrontAndRecipePagesEditor');
+    $expected = [
+      'ScriptCount' => 5,
+      'ScriptBytes' => 338200,
+      'StylesheetCount' => 6,
+      'StylesheetBytes' => 308500,
+    ];
+    $this->assertMetrics($expected, $performance_data);
+  }
+
   /**
    * Performs a common set of requests so the above test methods stay in sync.
    */
diff --git a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php
index 4ff48563bbbb..fb54a85c23f4 100644
--- a/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php
+++ b/core/profiles/demo_umami/tests/src/FunctionalJavascript/OpenTelemetryAuthenticatedPerformanceTest.php
@@ -63,7 +63,7 @@ public function testFrontPageAuthenticatedWarmCache(): void {
       'CacheDeleteCount' => 0,
       'CacheTagInvalidationCount' => 0,
       'CacheTagLookupQueryCount' => 2,
-      'ScriptCount' => 1,
+      'ScriptCount' => 2,
       'ScriptBytes' => 123850,
       'StylesheetCount' => 2,
       'StylesheetBytes' => 42500,
diff --git a/core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php b/core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php
index 518ef1638ed0..6dd535e88f95 100644
--- a/core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Asset/AttachedAssetsTest.php
@@ -186,9 +186,10 @@ public function testAggregation(): void {
     [$header_js, $footer_js] = $this->assetResolver->getJsAssets($assets, TRUE, \Drupal::languageManager()->getCurrentLanguage());
     $this->assertEquals([], \Drupal::service('asset.js.collection_renderer')->render($header_js), 'There are 0 JavaScript assets in the header.');
     $rendered_footer_js = \Drupal::service('asset.js.collection_renderer')->render($footer_js);
-    $this->assertCount(2, $rendered_footer_js, 'There are 2 JavaScript assets in the footer.');
+    $this->assertCount(3, $rendered_footer_js, 'There are 3 JavaScript assets in the footer.');
     $this->assertEquals('drupal-settings-json', $rendered_footer_js[0]['#attributes']['data-drupal-selector'], 'The first of the two JavaScript assets in the footer has drupal settings.');
-    $this->assertStringStartsWith(base_path(), $rendered_footer_js[1]['#attributes']['src'], 'The second of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
+    $this->assertStringContainsString('jquery.min.js', $rendered_footer_js[1]['#attributes']['src'], 'The second of the two JavaScript assets in the footer is jquery.min.js.');
+    $this->assertStringStartsWith(base_path(), $rendered_footer_js[2]['#attributes']['src'], 'The third of the two JavaScript assets in the footer has the sole aggregated JavaScript asset.');
   }
 
   /**
-- 
GitLab