Verified Commit d9cc5091 authored by godotislate's avatar godotislate
Browse files

task: #3565258 Support library-specific aggregates

By: catch
By: berdir
By: nicxvan
By: grimreaper
By: pdureau
By: alexpott
By: neclimdul
By: godotislate
parent b05412fc
Loading
Loading
Loading
Loading
Loading
+19 −7
Changes for core/core.libraries.yml: 19 added lines, 7 removed lines.
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ internal.backbone:
    url: https://raw.githubusercontent.com/jashkenas/backbone/1.6.1/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true }
    assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true, preprocess: false }
  dependencies:
    - core/internal.underscore

@@ -352,6 +352,8 @@ ckeditor5.fullscreen:
    - core/ckeditor5

drupal:
  aggregate_target:
    js: 'drupal'
  version: VERSION
  js:
    misc/drupal.js: { weight: -18 }
@@ -362,6 +364,8 @@ drupal:
    suppressDeprecationErrors: true

drupalSettings:
  aggregate_target:
    js: 'drupal'
  version: VERSION
  js:
    # Need to specify a negative weight like drupal.js until
@@ -420,6 +424,8 @@ drupal.announce:
    - core/drupal.debounce

loadjs:
  aggregate_target:
    js: 'htmx'
  remote: https://github.com/muicss/loadjs
  version: "4.3.0"
  license:
@@ -627,6 +633,8 @@ drupal.form:
    - core/once

drupal.htmx:
  aggregate_target:
    js: 'htmx'
  version: VERSION
  js:
    misc/htmx/htmx-utils.js: {}
@@ -843,6 +851,8 @@ drupal.vertical-tabs:
    - core/drupalSettings
    - core/drupal.form
htmx:
  aggregate_target:
    js: 'htmx'
  remote: https://github.com/bigskysoftware/htmx
  version: "2.0.4"
  license:
@@ -850,9 +860,11 @@ htmx:
    url:  https://raw.githubusercontent.com/bigskysoftware/htmx/refs/tags/v2.0.4/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/htmx/htmx.min.js: { minified: true, preprocess: false }
    assets/vendor/htmx/htmx.min.js: { minified: true }

internal.floating-ui:
  aggregate_target:
    js: true
  remote: https://github.com/floating-ui/floating-ui
  version: "1.7.6"
  license:
@@ -919,7 +931,7 @@ once:
    url: https://git.drupalcode.org/project/once/-/raw/v1.0.1/LICENSE.md
    gpl-compatible: true
  js:
    assets/vendor/once/once.min.js: { weight: -19, minified: true }
    assets/vendor/once/once.min.js: { weight: -19, minified: true, preprocess: false }

sortable:
  remote: https://github.com/SortableJS/Sortable
@@ -929,7 +941,7 @@ sortable:
    url: https://raw.githubusercontent.com/SortableJS/Sortable/1.15.6/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/sortable/Sortable.min.js: { minified: true }
    assets/vendor/sortable/Sortable.min.js: { minified: true, preprocess: false }

tabbable:
  remote: https://github.com/focus-trap/tabbable
@@ -939,7 +951,7 @@ tabbable:
    url: https://raw.githubusercontent.com/focus-trap/tabbable/v6.3.0/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/tabbable/index.umd.min.js: { weight: -1, minified: true }
    assets/vendor/tabbable/index.umd.min.js: { weight: -1, minified: true, preprocess: false }

transliteration:
  remote: https://github.com/dzcpy/transliteration
@@ -949,7 +961,7 @@ transliteration:
    url: https://raw.githubusercontent.com/dzcpy/transliteration/1.6.5/LICENSE.txt
    gpl-compatible: true
  js:
    assets/vendor/transliteration/bundle.umd.min.js: { minified: true }
    assets/vendor/transliteration/bundle.umd.min.js: { minified: true, preprocess: false }

internal.underscore:
  # Internal library. Do not depend on it outside core nor add new core usage.
@@ -965,7 +977,7 @@ internal.underscore:
    url: https://raw.githubusercontent.com/jashkenas/underscore/1.13.8/LICENSE
    gpl-compatible: true
  js:
    assets/vendor/underscore/underscore-min.js: { weight: -20, minified: true }
    assets/vendor/underscore/underscore-min.js: { weight: -20, minified: true, preprocess: false }


drupal.dialog.off_canvas:
+18 −3
Changes for core/lib/Drupal/Core/Asset/AssetResolver.php: 18 added lines, 3 removed lines.
Original line number Diff line number Diff line
@@ -219,12 +219,17 @@ public function getFontAssets(AttachedAssetsInterface $assets, ?LanguageInterfac
  /**
   * {@inheritdoc}
   */
  public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL) {
  public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL, bool $with_dependencies = TRUE) {
    if (!$assets->getLibraries()) {
      return [];
    }
    if ($with_dependencies) {
      // Get the complete list of libraries to load including dependencies.
      $libraries_to_load = $this->getLibrariesToLoad($assets, 'css');
    }
    else {
      $libraries_to_load = $assets->getLibraries();
    }

    if (!$libraries_to_load) {
      return [];
@@ -270,6 +275,9 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?Langua
        // order.
        $options['weight'] += count($css) / 30000;

        $options['library'] = $library;
        $options['aggregate_target'] = $definition['aggregate_target'] ?? ['js' => FALSE, 'css' => FALSE];

        // CSS files are being keyed by the full path.
        $css[$options['data']] = $options;
      }
@@ -321,7 +329,7 @@ protected function getJsSettingsAssets(AttachedAssetsInterface $assets) {
  /**
   * {@inheritdoc}
   */
  public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL) {
  public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL, bool $with_dependencies = TRUE) {
    $asset_settings = $assets->getSettings();
    if (!$assets->getLibraries() && !$asset_settings) {
      return [[], []];
@@ -331,8 +339,13 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?Languag
    }
    $theme_info = $this->themeManager->getActiveTheme();

    if ($with_dependencies) {
      // Get the complete list of libraries to load including dependencies.
      $libraries_to_load = $this->getLibrariesToLoad($assets, 'js');
    }
    else {
      $libraries_to_load = $assets->getLibraries();
    }

    // Collect all libraries that contain JS assets and are in the header.
    $header_js_libraries = [];
@@ -395,6 +408,9 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?Languag
          // order.
          $options['weight'] += count($javascript) / 30000;

          $options['library'] = $library;
          $options['aggregate_target'] = $definition['aggregate_target'] ?? ['js' => FALSE, 'css' => FALSE];

          // 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;
@@ -511,7 +527,6 @@ public static function sort(array $a, array $b) {
    elseif ($a['group'] > $b['group']) {
      return 1;
    }
    // Finally, order by weight.
    elseif ($a['weight'] < $b['weight']) {
      return -1;
    }
+6 −2
Changes for core/lib/Drupal/Core/Asset/AssetResolverInterface.php: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -47,13 +47,15 @@ interface AssetResolverInterface {
   *   optimized CSS asset collection rather than an unoptimized one.
   * @param \Drupal\Core\Language\LanguageInterface $language
   *   (optional) The interface language the assets will be rendered with.
   * @param bool $with_dependencies
   *   Whether to get assets including library dependencies, defaults to TRUE.
   *
   * @return array
   *   A (possibly optimized) collection of CSS assets.
   *
   * @see https://www.drupal.org/node/1887918#separate-concerns
   */
  public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL);
  public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL, bool $with_dependencies = TRUE);

  /**
   * Returns the JavaScript assets for the current response's libraries.
@@ -77,6 +79,8 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?Langua
   *   optimized JavaScript asset collections rather than an unoptimized ones.
   * @param \Drupal\Core\Language\LanguageInterface $language
   *   (optional) The interface language for the assets will be rendered with.
   * @param bool $with_dependencies
   *   Whether to get assets including library dependencies, defaults to TRUE.
   *
   * @return array
   *   A nested array containing 2 values:
@@ -85,7 +89,7 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize, ?Langua
   *   - at index one: the (possibly optimized) collection of JavaScript assets
   *     for the bottom of the page
   */
  public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL);
  public function getJsAssets(AttachedAssetsInterface $assets, $optimize, ?LanguageInterface $language = NULL, bool $with_dependencies = TRUE);

  /**
   * Returns the fonts for the current response's libraries.
+49 −14
Changes for core/lib/Drupal/Core/Asset/CssCollectionGrouper.php: 49 added lines, 14 removed lines.
Original line number Diff line number Diff line
@@ -32,27 +32,37 @@ public function group(array $css_assets) {
    // When creating a new group, we pre-increment $i, so by initializing it to
    // -1, the first group will have index 0.
    $i = -1;
    foreach ($css_assets as $item) {

    // If a group fully encapsulates a library and its dependencies, in that all
    // files are contained in the group, and no files from other libraries are
    // included, then when specified, the aggregate can be served with a
    // simplified URL to improve front end cache hit rates. In order to do this,
    // we need to keep track of whether libraries span multiple groups or not.
    $first_seen_libraries = [];
    $record_libraries = TRUE;

    foreach ($css_assets as $item) {
      // If the item can be grouped with other items, set $group_keys to an
      // array of information that must be the same for all items in its group.
      // If the item can't be grouped with other items, set $group_keys to
      // FALSE. We put items into a group that can be aggregated together:
      // whether they will be aggregated is up to the _drupal_css_aggregate()
      // function or an
      // override of that function specified in hook_css_alter(), but regardless
      // of the details of that function, a group represents items that can be
      // aggregated. Since a group may be rendered with a single HTML tag, all
      // FALSE. Since a group may be rendered with a single HTML tag, all
      // items in the group must share the same information that would need to
      // be part of that HTML tag.
      switch ($item['type']) {
        case 'file':
          // Group file items if their 'preprocess' flag is TRUE.
          // Help ensure maximum reuse of aggregate files by only grouping
          // together items that share the same 'group' value. The CSS optimizer
          // adds inline 'media' statements for everything except 'print', so
          // only vary groups based on that.
          $group_keys = $item['preprocess'] ? [$item['type'], $item['group'], $item['media'] === 'print'] : FALSE;
          // Group file items if their 'preprocess' flag is TRUE. The CSS
          // optimizer adds inline 'media' statements for everything except
          // 'print', so only vary groups based on that.
          $group_keys = $item['preprocess'] ? [$item['type'], $item['media'] === 'print'] : FALSE;
          if ($item['aggregate_target']['css']) {
            if ($item['aggregate_target']['css'] === TRUE) {
              $group_keys[] = $item['library'];
            }
            else {
              $group_keys[] = $item['aggregate_target']['css'];
            }
            $group_keys[] = $item['category'];
          }
          break;

        case 'external':
@@ -64,6 +74,7 @@ public function group(array $css_assets) {
      // If the group keys don't match the most recent group we're working with,
      // then a new group must be made.
      if ($group_keys !== $current_group_keys) {
        $record_libraries = TRUE;
        $i++;
        // Initialize the new group with the same properties as the first item
        // being placed into it. The item's 'data', 'weight' and 'basename'
@@ -73,10 +84,34 @@ public function group(array $css_assets) {
        if ($item['media'] !== 'print') {
          $groups[$i]['media'] = 'all';
        }
        unset($groups[$i]['data'], $groups[$i]['weight'], $groups[$i]['basename']);
        unset($groups[$i]['data'], $groups[$i]['weight'], $groups[$i]['basename'], $groups[$i]['library'], $groups[$i]['group']);
        $groups[$i]['items'] = [];
        $current_group_keys = $group_keys ?: NULL;
      }
      // Alter hooks can add items without a library.
      if ($item['aggregate_target']['css']) {
        if ($record_libraries) {
          $groups[$i]['libraries'][] = $item['library'];
        }
        // The first time a library is seen, add it to the list of libraries for
        // the group and record the index.
        $seen_index = $first_seen_libraries[$item['library']][$item['category']] ?? NULL;
        if ($seen_index === NULL) {
          $first_seen_libraries[$item['library']][$item['category']] = $i;
          $seen_index = $i;
        }
        // If a library has been seen in a previous index, this means that index
        // does not contain all of the assets for that library, so unset the
        // libraries key from that group.
        if ($seen_index !== $i) {
          unset($groups[$seen_index]['libraries']);
          unset($groups[$i]['libraries']);
          unset($groups[$seen_index]['category']);
          unset($groups[$i]['category']);
          // Stop recording libraries until the next group.
          $record_libraries = FALSE;
        }
      }

      // Add the item to the current group.
      $groups[$i]['items'][] = $item;
+11 −0
Changes for core/lib/Drupal/Core/Asset/CssCollectionOptimizerLazy.php: 11 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -90,6 +90,9 @@ public function optimize(array $css_assets, array $libraries) {
          // groups, whether or not files in a group are from a particular
          // library or not.
          $css_assets[$order]['preprocessed'] = TRUE;
          if (!empty($css_group['libraries'])) {
            $css_assets[$order]['libraries'] = $css_group['libraries'];
          }
        }
      }
      if ($css_group['type'] === 'external') {
@@ -112,12 +115,20 @@ public function optimize(array $css_assets, array $libraries) {
    if ($already_loaded) {
      $query_args['exclude'] = UrlHelper::compressQueryParameter(implode(',', $this->dependencyResolver->getMinimalRepresentativeSubset($already_loaded)));
    }
    // When an asset corresponds 1-1 with a single library, we don't need
    // either the include or exclude query arguments. Replace this with just
    // the library needed to produce the aggregate.

    // Generate a URL for each group of assets, but do not process them inline,
    // this is done using optimizeGroup() when the asset path is requested.
    foreach ($css_assets as $order => $css_asset) {
      if (!empty($css_asset['preprocessed'])) {
        $query = ['delta' => "$order"] + $query_args;
        if (isset($css_asset['libraries'])) {
          unset($query['include'], $query['exclude'], $query['delta']);
          $query['libraries'] = UrlHelper::compressQueryParameter(implode(',', $css_asset['libraries']));
          $query['category'] = $css_asset['category'];
        }
        $filename = 'css_' . $this->generateHash($css_asset) . '.css';
        $uri = 'assets://css/' . $filename;
        $css_assets[$order]['data'] = $this->fileUrlGenerator->generateString($uri) . '?' . UrlHelper::buildQuery($query);
Loading