Skip to content

Commit 9029119

Browse files
committed
Add site context to AssetResolver
1 parent ac6f1bd commit 9029119

3 files changed

Lines changed: 294 additions & 0 deletions

File tree

src/Asset/AssetResolver.php

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
<?php
2+
3+
namespace Drupal\pantheon_domain_masking\Asset;
4+
5+
use Drupal\Component\Utility\Crypt;
6+
use Drupal\Component\Utility\NestedArray;
7+
use Drupal\Component\Utility\UrlHelper;
8+
use Drupal\Core\Cache\CacheBackendInterface;
9+
use Drupal\Core\Language\LanguageInterface;
10+
use Drupal\Core\Asset\AssetResolver as BaseAssetResolver;
11+
use Drupal\Core\Asset\AttachedAssetsInterface;
12+
13+
/**
14+
* The default asset resolver.
15+
*/
16+
class AssetResolver extends BaseAssetResolver {
17+
18+
/**
19+
* {@inheritdoc}
20+
*/
21+
public function getCssAssets(AttachedAssetsInterface $assets, $optimize, LanguageInterface $language = NULL) {
22+
if (!isset($language)) {
23+
$language = $this->languageManager->getCurrentLanguage();
24+
}
25+
$theme_info = $this->themeManager->getActiveTheme();
26+
// Add the theme name to the cache key since themes may implement
27+
// hook_library_info_alter().
28+
$libraries_to_load = $this->getLibrariesToLoad($assets);
29+
30+
$site = '';
31+
$request = \Drupal::request();
32+
if ($request) {
33+
$site = $request->getSchemeAndHttpHost() . $request->getBaseUrl();
34+
}
35+
36+
$cid = 'css:' . $theme_info->getName() . ':' . $site . $language->getId() . Crypt::hashBase64(serialize($libraries_to_load)) . (int) $optimize;
37+
if ($cached = $this->cache->get($cid)) {
38+
return $cached->data;
39+
}
40+
41+
$css = [];
42+
$default_options = [
43+
'type' => 'file',
44+
'group' => CSS_AGGREGATE_DEFAULT,
45+
'weight' => 0,
46+
'media' => 'all',
47+
'preprocess' => TRUE,
48+
];
49+
50+
foreach ($libraries_to_load as $library) {
51+
[$extension, $name] = explode('/', $library, 2);
52+
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
53+
if (isset($definition['css'])) {
54+
foreach ($definition['css'] as $options) {
55+
$options += $default_options;
56+
// Copy the asset library license information to each file.
57+
$options['license'] = $definition['license'];
58+
59+
// Files with a query string cannot be preprocessed.
60+
if ($options['type'] === 'file' && $options['preprocess'] && str_contains($options['data'], '?')) {
61+
$options['preprocess'] = FALSE;
62+
}
63+
64+
// Always add a tiny value to the weight, to conserve the insertion
65+
// order.
66+
$options['weight'] += count($css) / 30000;
67+
68+
// CSS files are being keyed by the full path.
69+
$css[$options['data']] = $options;
70+
}
71+
}
72+
}
73+
74+
// Allow modules and themes to alter the CSS assets.
75+
$this->moduleHandler->alter('css', $css, $assets, $language);
76+
$this->themeManager->alter('css', $css, $assets, $language);
77+
78+
if (!empty($css)) {
79+
// Sort CSS items, so that they appear in the correct order.
80+
uasort($css, [static::class, 'sort']);
81+
82+
if ($optimize) {
83+
$css = \Drupal::service('asset.css.collection_optimizer')->optimize($css, $libraries_to_load, $language);
84+
}
85+
}
86+
$this->cache->set($cid, $css, CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
87+
88+
return $css;
89+
}
90+
91+
/**
92+
* {@inheritdoc}
93+
*/
94+
public function getJsAssets(AttachedAssetsInterface $assets, $optimize, LanguageInterface $language = NULL) {
95+
if (!isset($language)) {
96+
$language = $this->languageManager->getCurrentLanguage();
97+
}
98+
$theme_info = $this->themeManager->getActiveTheme();
99+
// Add the theme name to the cache key since themes may implement
100+
// hook_library_info_alter(). Additionally add the current language to
101+
// support translation of JavaScript files via hook_js_alter().
102+
$libraries_to_load = $this->getLibrariesToLoad($assets);
103+
104+
$site = '';
105+
$request = \Drupal::request();
106+
if ($request) {
107+
$site = $request->getSchemeAndHttpHost() . $request->getBaseUrl();
108+
}
109+
110+
$cid = 'js:' . $theme_info->getName() . ':' . $site . $language->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets->getSettings()) > 0) . (int) $optimize;
111+
112+
if ($cached = $this->cache->get($cid)) {
113+
[$js_assets_header, $js_assets_footer, $settings, $settings_in_header] = $cached->data;
114+
}
115+
else {
116+
$javascript = [];
117+
$default_options = [
118+
'type' => 'file',
119+
'group' => JS_DEFAULT,
120+
'weight' => 0,
121+
'cache' => TRUE,
122+
'preprocess' => TRUE,
123+
'attributes' => [],
124+
'version' => NULL,
125+
];
126+
127+
// Collect all libraries that contain JS assets and are in the header.
128+
$header_js_libraries = [];
129+
foreach ($libraries_to_load as $library) {
130+
[$extension, $name] = explode('/', $library, 2);
131+
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
132+
if (isset($definition['js']) && !empty($definition['header'])) {
133+
$header_js_libraries[] = $library;
134+
}
135+
}
136+
// The current list of header JS libraries are only those libraries that
137+
// are in the header, but their dependencies must also be loaded for them
138+
// to function correctly, so update the list with those.
139+
$header_js_libraries = $this->libraryDependencyResolver->getLibrariesWithDependencies($header_js_libraries);
140+
141+
foreach ($libraries_to_load as $library) {
142+
[$extension, $name] = explode('/', $library, 2);
143+
$definition = $this->libraryDiscovery->getLibraryByName($extension, $name);
144+
if (isset($definition['js'])) {
145+
foreach ($definition['js'] as $options) {
146+
$options += $default_options;
147+
// Copy the asset library license information to each file.
148+
$options['license'] = $definition['license'];
149+
150+
// 'scope' is a calculated option, based on which libraries are
151+
// marked to be loaded from the header (see above).
152+
$options['scope'] = in_array($library, $header_js_libraries) ? 'header' : 'footer';
153+
154+
// Preprocess can only be set if caching is enabled and no
155+
// attributes are set.
156+
$options['preprocess'] = $options['cache'] && empty($options['attributes']) ? $options['preprocess'] : FALSE;
157+
158+
// Always add a tiny value to the weight, to conserve the insertion
159+
// order.
160+
$options['weight'] += count($javascript) / 30000;
161+
162+
// Local and external files must keep their name as the associative
163+
// key so the same JavaScript file is not added twice.
164+
$javascript[$options['data']] = $options;
165+
}
166+
}
167+
}
168+
169+
// Allow modules and themes to alter the JavaScript assets.
170+
$this->moduleHandler->alter('js', $javascript, $assets, $language);
171+
$this->themeManager->alter('js', $javascript, $assets, $language);
172+
173+
// Sort JavaScript assets, so that they appear in the correct order.
174+
uasort($javascript, [static::class, 'sort']);
175+
176+
// Prepare the return value: filter JavaScript assets per scope.
177+
$js_assets_header = [];
178+
$js_assets_footer = [];
179+
foreach ($javascript as $key => $item) {
180+
if ($item['scope'] == 'header') {
181+
$js_assets_header[$key] = $item;
182+
}
183+
elseif ($item['scope'] == 'footer') {
184+
$js_assets_footer[$key] = $item;
185+
}
186+
}
187+
188+
if ($optimize) {
189+
$collection_optimizer = \Drupal::service('asset.js.collection_optimizer');
190+
$js_assets_header = $collection_optimizer->optimize($js_assets_header, $libraries_to_load);
191+
$js_assets_footer = $collection_optimizer->optimize($js_assets_footer, $libraries_to_load);
192+
}
193+
194+
// If the core/drupalSettings library is being loaded or is already
195+
// loaded, get the JavaScript settings assets, and convert them into a
196+
// single "regular" JavaScript asset.
197+
$libraries_to_load = $this->getLibrariesToLoad($assets);
198+
$settings_required = in_array('core/drupalSettings', $libraries_to_load) || in_array('core/drupalSettings', $this->libraryDependencyResolver->getLibrariesWithDependencies($assets->getAlreadyLoadedLibraries()));
199+
$settings_have_changed = count($libraries_to_load) > 0 || count($assets->getSettings()) > 0;
200+
201+
// Initialize settings to FALSE since they are not needed by default. This
202+
// distinguishes between an empty array which must still allow
203+
// hook_js_settings_alter() to be run.
204+
$settings = FALSE;
205+
if ($settings_required && $settings_have_changed) {
206+
$settings = $this->getJsSettingsAssets($assets);
207+
// Allow modules to add cached JavaScript settings.
208+
$this->moduleHandler->invokeAllWith('js_settings_build', function (callable $hook, string $module) use (&$settings, $assets) {
209+
$hook($settings, $assets);
210+
});
211+
}
212+
$settings_in_header = in_array('core/drupalSettings', $header_js_libraries);
213+
$this->cache->set($cid, [$js_assets_header, $js_assets_footer, $settings, $settings_in_header], CacheBackendInterface::CACHE_PERMANENT, ['library_info']);
214+
}
215+
216+
if ($settings !== FALSE) {
217+
// Attached settings override both library definitions and
218+
// hook_js_settings_build().
219+
$settings = NestedArray::mergeDeepArray([$settings, $assets->getSettings()], TRUE);
220+
// Allow modules and themes to alter the JavaScript settings.
221+
$this->moduleHandler->alter('js_settings', $settings, $assets);
222+
$this->themeManager->alter('js_settings', $settings, $assets);
223+
// Update the $assets object accordingly, so that it reflects the final
224+
// settings.
225+
$assets->setSettings($settings);
226+
// Convert ajaxPageState to a compressed string from an array, since it is
227+
// used by ajax.js to pass to AJAX requests as a query parameter.
228+
if (isset($settings['ajaxPageState']['libraries'])) {
229+
$settings['ajaxPageState']['libraries'] = UrlHelper::compressQueryParameter($settings['ajaxPageState']['libraries']);
230+
}
231+
$settings_as_inline_javascript = [
232+
'type' => 'setting',
233+
'group' => JS_SETTING,
234+
'weight' => 0,
235+
'data' => $settings,
236+
];
237+
$settings_js_asset = ['drupalSettings' => $settings_as_inline_javascript];
238+
// Prepend to the list of JS assets, to render it first. Preferably in
239+
// the footer, but in the header if necessary.
240+
if ($settings_in_header) {
241+
$js_assets_header = $settings_js_asset + $js_assets_header;
242+
}
243+
else {
244+
$js_assets_footer = $settings_js_asset + $js_assets_footer;
245+
}
246+
}
247+
return [
248+
$js_assets_header,
249+
$js_assets_footer,
250+
];
251+
}
252+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Drupal\pantheon_domain_masking\Compiler;
4+
5+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6+
use Symfony\Component\DependencyInjection\ContainerBuilder;
7+
8+
class AssetResolverExtensionPass implements CompilerPassInterface {
9+
10+
/**
11+
* {@inheritdoc}
12+
*/
13+
public function process(ContainerBuilder $container) {
14+
$definition = $container->getDefinition('asset.css.collection_optimizer');
15+
16+
// Apply override only when CssCollectionOptimizerLazy is used 10.1+.
17+
if ($definition->getClass() !== 'Drupal\\Core\\Asset\\CssCollectionOptimizerLazy') {
18+
return;
19+
}
20+
21+
$definition = $container->getDefinition('asset.resolver');
22+
$definition->setClass('Drupal\\pantheon_domain_masking\\Asset\\AssetResolver');
23+
}
24+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Drupal\pantheon_domain_masking;
4+
5+
use Drupal\Core\DependencyInjection\ContainerBuilder;
6+
use Drupal\Core\DependencyInjection\ServiceProviderBase;
7+
use Drupal\pantheon_domain_masking\Compiler\AssetResolverExtensionPass;
8+
9+
class PantheonDomainMaskingServiceProvider extends ServiceProviderBase {
10+
11+
/**
12+
* {@inheritdoc}
13+
*/
14+
public function register(ContainerBuilder $container) {
15+
$container->addCompilerPass(new AssetResolverExtensionPass());
16+
}
17+
18+
}

0 commit comments

Comments
 (0)