Issue #2506369 by catch, dawehner, Wim Leers: Cache CSS/JS asset resolving

8.0.x
Alex Pott 2015-06-20 12:16:07 -05:00
parent d305a8efaa
commit 5d3a761e61
8 changed files with 223 additions and 130 deletions

View File

@ -1318,7 +1318,7 @@ services:
arguments: ['@library.discovery'] arguments: ['@library.discovery']
asset.resolver: asset.resolver:
class: Drupal\Core\Asset\AssetResolver class: Drupal\Core\Asset\AssetResolver
arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager'] arguments: ['@library.discovery', '@library.dependency_resolver', '@module_handler', '@theme.manager', '@language_manager', '@cache.data']
info_parser: info_parser:
class: Drupal\Core\Extension\InfoParser class: Drupal\Core\Extension\InfoParser
twig: twig:

View File

@ -175,7 +175,7 @@ class AjaxResponse extends JsonResponse {
// Resolve the attached libraries into asset collections. // Resolve the attached libraries into asset collections.
$assets = new AttachedAssets(); $assets = new AttachedAssets();
$assets->setLibraries(isset($this->attachments['library']) ? $this->attachments['library'] : []) $assets->setLibraries(isset($this->attachments['library']) ? $this->attachments['library'] : [])
->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []) ->setAlreadyLoadedLibraries(isset($ajax_page_state['libraries']) ? explode(',', $ajax_page_state['libraries']) : [])
->setSettings(isset($this->attachments['drupalSettings']) ? $this->attachments['drupalSettings'] : []); ->setSettings(isset($this->attachments['drupalSettings']) ? $this->attachments['drupalSettings'] : []);
$asset_resolver = \Drupal::service('asset.resolver'); $asset_resolver = \Drupal::service('asset.resolver');
$css_assets = $asset_resolver->getCssAssets($assets, $optimize_css); $css_assets = $asset_resolver->getCssAssets($assets, $optimize_css);

View File

@ -6,8 +6,11 @@
namespace Drupal\Core\Asset; namespace Drupal\Core\Asset;
use Drupal\Component\Utility\Crypt;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface; use Drupal\Core\Theme\ThemeManagerInterface;
/** /**
@ -43,6 +46,20 @@ class AssetResolver implements AssetResolverInterface {
*/ */
protected $themeManager; protected $themeManager;
/**
* The language manager.
*
* @var \Drupal\Core\Language\LanguageManagerInterface $language_manager
*/
protected $languageManager;
/**
* The cache backend.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cache;
/** /**
* Constructs a new AssetResolver instance. * Constructs a new AssetResolver instance.
* *
@ -54,12 +71,18 @@ class AssetResolver implements AssetResolverInterface {
* The module handler. * The module handler.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager. * The theme manager.
* @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache
* The cache backend.
*/ */
public function __construct(LibraryDiscoveryInterface $library_discovery, LibraryDependencyResolverInterface $library_dependency_resolver, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager) { public function __construct(LibraryDiscoveryInterface $library_discovery, LibraryDependencyResolverInterface $library_dependency_resolver, ModuleHandlerInterface $module_handler, ThemeManagerInterface $theme_manager, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) {
$this->libraryDiscovery = $library_discovery; $this->libraryDiscovery = $library_discovery;
$this->libraryDependencyResolver = $library_dependency_resolver; $this->libraryDependencyResolver = $library_dependency_resolver;
$this->moduleHandler = $module_handler; $this->moduleHandler = $module_handler;
$this->themeManager = $theme_manager; $this->themeManager = $theme_manager;
$this->languageManager = $language_manager;
$this->cache = $cache;
} }
/** /**
@ -92,6 +115,12 @@ class AssetResolver implements AssetResolverInterface {
*/ */
public function getCssAssets(AttachedAssetsInterface $assets, $optimize) { public function getCssAssets(AttachedAssetsInterface $assets, $optimize) {
$theme_info = $this->themeManager->getActiveTheme(); $theme_info = $this->themeManager->getActiveTheme();
// Add the theme name to the cache key since themes may implement
// hook_css_alter().
$cid = 'css:' . $theme_info->getName() . ':' . Crypt::hashBase64(serialize($assets)) . (int) $optimize;
if ($cached = $this->cache->get($cid)) {
return $cached->data;
}
$css = []; $css = [];
$default_options = [ $default_options = [
@ -149,6 +178,7 @@ class AssetResolver implements AssetResolverInterface {
if ($optimize) { if ($optimize) {
$css = \Drupal::service('asset.css.collection_optimizer')->optimize($css); $css = \Drupal::service('asset.css.collection_optimizer')->optimize($css);
} }
$this->cache->set($cid, $css, CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
return $css; return $css;
} }
@ -180,10 +210,6 @@ class AssetResolver implements AssetResolverInterface {
// Attached settings win over settings in libraries. // Attached settings win over settings in libraries.
$settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE); $settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE);
// Allow modules and themes to alter the JavaScript settings.
$this->moduleHandler->alter('js_settings', $settings, $assets);
$this->themeManager->alter('js_settings', $settings, $assets);
return $settings; return $settings;
} }
@ -191,115 +217,142 @@ class AssetResolver implements AssetResolverInterface {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
$javascript = []; $theme_info = $this->themeManager->getActiveTheme();
$default_options = [ // Add the theme name to the cache key since themes may implement
'type' => 'file', // hook_js_alter(). Additionally add the current language to support
'group' => JS_DEFAULT, // translation of JavaScript files.
'every_page' => FALSE, $cid = 'js:' . $theme_info->getName() . ':' . $this->languageManager->getCurrentLanguage()->getId() . ':' . Crypt::hashBase64(serialize($assets));
'weight' => 0,
'cache' => TRUE,
'preprocess' => TRUE,
'attributes' => [],
'version' => NULL,
'browsers' => [],
];
$libraries_to_load = $this->getLibrariesToLoad($assets); if ($cached = $this->cache->get($cid)) {
list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data;
// Collect all libraries that contain JS assets and are in the header.
$header_js_libraries = [];
foreach ($libraries_to_load as $library) {
list($extension, $name) = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (isset($definition['js']) && !empty($definition['header'])) {
$header_js_libraries[] = $library;
}
} }
// The current list of header JS libraries are only those libraries that are else {
// in the header, but their dependencies must also be loaded for them to $javascript = [];
// function correctly, so update the list with those. $default_options = [
$header_js_libraries = $this->libraryDependencyResolver->getLibrariesWithDependencies($header_js_libraries); 'type' => 'file',
'group' => JS_DEFAULT,
'every_page' => FALSE,
'weight' => 0,
'cache' => TRUE,
'preprocess' => TRUE,
'attributes' => [],
'version' => NULL,
'browsers' => [],
];
foreach ($libraries_to_load as $library) { $libraries_to_load = $this->getLibrariesToLoad($assets);
list($extension, $name) = explode('/', $library, 2);
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (isset($definition['js'])) {
foreach ($definition['js'] as $options) {
$options += $default_options;
// 'scope' is a calculated option, based on which libraries are marked // Collect all libraries that contain JS assets and are in the header.
// to be loaded from the header (see above). $header_js_libraries = [];
$options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer'; foreach ($libraries_to_load as $library) {
list($extension, $name) = explode('/', $library, 2);
// Preprocess can only be set if caching is enabled and no attributes $definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
// are set. if (isset($definition['js']) && !empty($definition['header'])) {
$options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE; $header_js_libraries[] = $library;
// Always add a tiny value to the weight, to conserve the insertion
// order.
$options['weight'] += count($javascript) / 1000;
// 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;
} }
} }
} // 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.
$header_js_libraries = $this->libraryDependencyResolver->getLibrariesWithDependencies($header_js_libraries);
// Allow modules and themes to alter the JavaScript assets. foreach ($libraries_to_load as $library) {
$this->moduleHandler->alter('js', $javascript, $assets); list($extension, $name) = explode('/', $library, 2);
$this->themeManager->alter('js', $javascript, $assets); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
if (isset($definition['js'])) {
foreach ($definition['js'] as $options) {
$options += $default_options;
// Sort JavaScript assets, so that they appear in the correct order. // 'scope' is a calculated option, based on which libraries are
uasort($javascript, 'static::sort'); // marked to be loaded from the header (see above).
$options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer';
// Prepare the return value: filter JavaScript assets per scope. // Preprocess can only be set if caching is enabled and no
$js_assets_header = []; // attributes are set.
$js_assets_footer = []; $options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE;
foreach ($javascript as $key => $item) {
if ($item['scope'] == 'header') {
$js_assets_header[$key] = $item;
}
elseif ($item['scope'] == 'footer') {
$js_assets_footer[$key] = $item;
}
}
if ($optimize) { // Always add a tiny value to the weight, to conserve the insertion
$collection_optimizer = \Drupal::service('asset.js.collection_optimizer'); // order.
$js_assets_header = $collection_optimizer->optimize($js_assets_header); $options['weight'] += count($javascript) / 1000;
$js_assets_footer = $collection_optimizer->optimize($js_assets_footer);
}
// If the core/drupalSettings library is being loaded or is already loaded, // Local and external files must keep their name as the associative
// get the JavaScript settings assets, and convert them into a single // key so the same JavaScript file is not added twice.
// "regular" JavaScript asset. $javascript[$options['data']] = $options;
$libraries_to_load = $this->getLibrariesToLoad($assets); }
$settings_needed = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()));
$settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0;
if ($settings_needed && $settings_have_changed) {
$settings = $this->getJsSettingsAssets($assets);
if (!empty($settings)) {
$settings_as_inline_javascript = [
'type' => 'setting',
'group' => JS_SETTING,
'every_page' => TRUE,
'weight' => 0,
'browsers' => [],
'data' => $settings,
];
$settings_js_asset = ['drupalSettings' => $settings_as_inline_javascript];
// Prepend to the list of JS assets, to render it first. Preferably in
// the footer, but in the header if necessary.
if (in_array('core/drupalSettings', $header_js_libraries)) {
$js_assets_header = $settings_js_asset + $js_assets_header;
}
else {
$js_assets_footer = $settings_js_asset + $js_assets_footer;
} }
} }
// Allow modules and themes to alter the JavaScript assets.
$this->moduleHandler->alter('js', $javascript, $assets);
$this->themeManager->alter('js', $javascript, $assets);
// Sort JavaScript assets, so that they appear in the correct order.
uasort($javascript, 'static::sort');
// Prepare the return value: filter JavaScript assets per scope.
$js_assets_header = [];
$js_assets_footer = [];
foreach ($javascript as $key => $item) {
if ($item['scope'] == 'header') {
$js_assets_header[$key] = $item;
}
elseif ($item['scope'] == 'footer') {
$js_assets_footer[$key] = $item;
}
}
if ($optimize) {
$collection_optimizer = \Drupal::service('asset.js.collection_optimizer');
$js_assets_header = $collection_optimizer->optimize($js_assets_header);
$js_assets_footer = $collection_optimizer->optimize($js_assets_footer);
}
// If the core/drupalSettings library is being loaded or is already
// loaded, get the JavaScript settings assets, and convert them into a
// single "regular" JavaScript asset.
$libraries_to_load = $this->getLibrariesToLoad($assets);
$settings_required = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()));
$settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0;
// Initialize settings to FALSE since they are not needed by default. This
// distinguishes between an empty array which must still allow
// hook_js_settings_alter() to be run.
$settings = FALSE;
if ($settings_required && $settings_have_changed) {
$settings = $this->getJsSettingsAssets($assets);
// Allow modules to add cached JavaScript settings.
foreach ($this->moduleHandler->getImplementations('js_settings_build') as $module) {
$function = $module . '_' . 'js_settings_build';
$function($settings, $assets);
}
}
$settings_in_header = in_array('core/drupalSettings', $header_js_libraries);
$this->cache->set($cid, [$js_assets_header, $js_assets_footer, $settings, $settings_in_header], CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
} }
if ($settings !== FALSE) {
// Allow modules and themes to alter the JavaScript settings.
$this->moduleHandler->alter('js_settings', $settings, $assets);
$this->themeManager->alter('js_settings', $settings, $assets);
$settings_as_inline_javascript = [
'type' => 'setting',
'group' => JS_SETTING,
'every_page' => TRUE,
'weight' => 0,
'browsers' => [],
'data' => $settings,
];
$settings_js_asset = ['drupalSettings' => $settings_as_inline_javascript];
// Prepend to the list of JS assets, to render it first. Preferably in
// the footer, but in the header if necessary.
if ($settings_in_header) {
$js_assets_header = $settings_js_asset + $js_assets_header;
}
else {
$js_assets_footer = $settings_js_asset + $js_assets_footer;
}
}
return [ return [
$js_assets_header, $js_assets_header,
$js_assets_footer, $js_assets_footer,

View File

@ -825,6 +825,27 @@ function hook_library_info_build() {
return $libraries; return $libraries;
} }
/**
* Modify the JavaScript settings (drupalSettings).
*
* @param array &$settings
* An array of all JavaScript settings (drupalSettings) being presented on the
* page.
* @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
* The assets attached to the current response.
*
* @see \Drupal\Core\Asset\AssetResolver
*
* The results of this hook are cached, however modules may use
* hook_js_settings_alter() to dynamically alter settings.
*/
function hook_js_settings_build(array &$settings, \Drupal\Core\Asset\AttachedAssetsInterface $assets) {
// Manipulate settings.
if (isset($settings['dialog'])) {
$settings['dialog']['autoResize'] = FALSE;
}
}
/** /**
* Perform necessary alterations to the JavaScript settings (drupalSettings). * Perform necessary alterations to the JavaScript settings (drupalSettings).
* *

View File

@ -158,7 +158,7 @@ class CKEditorLoadingTest extends WebTestBase {
// Editor.module's JS present. Note: ckeditor/drupal.ckeditor depends on // Editor.module's JS present. Note: ckeditor/drupal.ckeditor depends on
// editor/drupal.editor, hence presence of the former implies presence of // editor/drupal.editor, hence presence of the former implies presence of
// the latter. // the latter.
isset($settings['ajaxPageState']) && in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])), isset($settings['ajaxPageState']['libraries']) && in_array('ckeditor/drupal.ckeditor', explode(',', $settings['ajaxPageState']['libraries'])),
// Body field. // Body field.
$this->xpath('//textarea[@id="edit-body-0-value"]'), $this->xpath('//textarea[@id="edit-body-0-value"]'),
// Format selector. // Format selector.

View File

@ -130,7 +130,7 @@ class CommentCSSTest extends CommentTestBase {
if ($case['comment_status'] == CommentInterface::PUBLISHED || $case['user'] == 'admin') { if ($case['comment_status'] == CommentInterface::PUBLISHED || $case['user'] == 'admin') {
$this->assertIdentical(1, count($this->xpath('//*[contains(@class, "comment")]/*[@data-comment-timestamp="' . $comment->getChangedTime() . '"]')), 'data-comment-timestamp attribute is set on comment'); $this->assertIdentical(1, count($this->xpath('//*[contains(@class, "comment")]/*[@data-comment-timestamp="' . $comment->getChangedTime() . '"]')), 'data-comment-timestamp attribute is set on comment');
$expectedJS = ($case['user'] !== 'anonymous'); $expectedJS = ($case['user'] !== 'anonymous');
$this->assertIdentical($expectedJS, isset($settings['ajaxPageState']) && in_array('comment/drupal.comment-new-indicator', explode(',', $settings['ajaxPageState']['libraries'])), 'drupal.comment-new-indicator library is present.'); $this->assertIdentical($expectedJS, isset($settings['ajaxPageState']['libraries']) && in_array('comment/drupal.comment-new-indicator', explode(',', $settings['ajaxPageState']['libraries'])), 'drupal.comment-new-indicator library is present.');
} }
} }
} }

View File

@ -2053,9 +2053,13 @@ abstract class WebTestBase extends TestBase {
protected function getAjaxPageStatePostData() { protected function getAjaxPageStatePostData() {
$post = array(); $post = array();
$drupal_settings = $this->drupalSettings; $drupal_settings = $this->drupalSettings;
if (isset($drupal_settings['ajaxPageState'])) { if (isset($drupal_settings['ajaxPageState']['theme'])) {
$post['ajax_page_state[theme]'] = $drupal_settings['ajaxPageState']['theme']; $post['ajax_page_state[theme]'] = $drupal_settings['ajaxPageState']['theme'];
}
if (isset($drupal_settings['ajaxPageState']['theme_token'])) {
$post['ajax_page_state[theme_token]'] = $drupal_settings['ajaxPageState']['theme_token']; $post['ajax_page_state[theme_token]'] = $drupal_settings['ajaxPageState']['theme_token'];
}
if (isset($drupal_settings['ajaxPageState']['libraries'])) {
$post['ajax_page_state[libraries]'] = $drupal_settings['ajaxPageState']['libraries']; $post['ajax_page_state[libraries]'] = $drupal_settings['ajaxPageState']['libraries'];
} }
return $post; return $post;

View File

@ -606,10 +606,46 @@ function system_page_attachments(array &$page) {
} }
} }
/**
* Implements hook_js_settings_build().
*
* Sets values for the core/drupal.ajax library, which just depends on the
* active theme but no other request-dependent values.
*/
function system_js_settings_build(&$settings, AttachedAssetsInterface $assets) {
// Generate the values for the core/drupal.ajax library.
// We need to send ajaxPageState settings for core/drupal.ajax if:
// - ajaxPageState is being loaded in this Response, in which case it will
// already exist at $settings['ajaxPageState'] (because the core/drupal.ajax
// library definition specifies a placeholder 'ajaxPageState' setting).
// - core/drupal.ajax already has been loaded and hence this is an AJAX
// Response in which we must send the list of extra asset libraries that are
// being added in this AJAX Response.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver');
if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
// Provide the page with information about the theme that's used, so that
// a later AJAX request can be rendered using the same theme.
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
$settings['ajaxPageState']['theme'] = $theme_key;
// Provide the page with information about the individual asset libraries
// used, information not otherwise available when aggregation is enabled.
$minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
$assets->getLibraries(),
$assets->getAlreadyLoadedLibraries()
));
sort($minimal_libraries);
$settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
}
}
/** /**
* Implements hook_js_settings_alter(). * Implements hook_js_settings_alter().
* *
* Sets values for the core/drupalSettings and core/drupal.ajax libraries. * Sets values which depend on the current request, like core/drupalSettings
* as well as theme_token ajax state.
*/ */
function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) { function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
// url() generates the script and prefix using hook_url_outbound_alter(). // url() generates the script and prefix using hook_url_outbound_alter().
@ -647,36 +683,15 @@ function system_js_settings_alter(&$settings, AttachedAssetsInterface $assets) {
if (!isset($settings['pluralDelimiter'])) { if (!isset($settings['pluralDelimiter'])) {
$settings['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER; $settings['pluralDelimiter'] = LOCALE_PLURAL_DELIMITER;
} }
// Add the theme token to ajaxPageState, ensuring the database is available
// Now generate the values for the core/drupal.ajax library. // before doing so.
// We need to send ajaxPageState settings for core/drupal.ajax if:
// - ajaxPageState is being loaded in this Response, in which case it will
// already exist at $settings['ajaxPageState'] (because the core/drupal.ajax
// library definition specifies a placeholder 'ajaxPageState' setting).
// - core/drupal.ajax already has been loaded and hence this is an AJAX
// Response in which we must send the list of extra asset libraries that are
// being added in this AJAX Response.
/** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */ /** @var \Drupal\Core\Asset\LibraryDependencyResolver $library_dependency_resolver */
$library_dependency_resolver = \Drupal::service('library.dependency_resolver'); $library_dependency_resolver = \Drupal::service('library.dependency_resolver');
if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) { if (isset($settings['ajaxPageState']) || in_array('core/drupal.ajax', $library_dependency_resolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()))) {
// Provide the page with information about the theme that's used, so that
// a later AJAX request can be rendered using the same theme.
// @see \Drupal\Core\Theme\AjaxBasePageNegotiator
$theme_key = \Drupal::theme()->getActiveTheme()->getName();
$settings['ajaxPageState']['theme'] = $theme_key;
// Checks that the DB is available before filling theme_token.
if (!defined('MAINTENANCE_MODE')) { if (!defined('MAINTENANCE_MODE')) {
$settings['ajaxPageState']['theme_token'] = \Drupal::csrfToken()->get($theme_key); $settings['ajaxPageState']['theme_token'] = \Drupal::csrfToken()
->get(\Drupal::theme()->getActiveTheme()->getName());
} }
// Provide the page with information about the individual asset libraries
// used, information not otherwise available when aggregation is enabled.
$minimal_libraries = $library_dependency_resolver->getMinimalRepresentativeSubset(array_merge(
$assets->getLibraries(),
$assets->getAlreadyLoadedLibraries()
));
sort($minimal_libraries);
$settings['ajaxPageState']['libraries'] = implode(',', $minimal_libraries);
} }
} }