You are here

public function AssetResolver::getJsAssets in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Asset/AssetResolver.php \Drupal\Core\Asset\AssetResolver::getJsAssets()

Returns the JavaScript assets for the current response's libraries.

References to JavaScript files are placed in a certain order: first, all 'core' files, then all 'module' and finally all 'theme' JavaScript files are added to the page. Then, all settings are output, followed by 'inline' JavaScript code. If running update.php, all preprocessing is disabled.

Note that hook_js_alter(&$javascript) is called during this function call to allow alterations of the JavaScript during its presentation. The correct way to add JavaScript during hook_js_alter() is to add another element to the $javascript array, deriving from drupal_js_defaults(). See locale_js_alter() for an example of this.

Parameters

\Drupal\Core\Asset\AttachedAssetsInterface $assets: The assets attached to the current response. Note that this object is modified to reflect the final JavaScript settings assets.

bool $optimize: Whether to apply the JavaScript asset collection optimizer, to return optimized JavaScript asset collections rather than an unoptimized ones.

Return value

array A nested array containing 2 values:

  • at index zero: the (possibly optimized) collection of JavaScript assets for the top of the page
  • at index one: the (possibly optimized) collection of JavaScript assets for the bottom of the page

Overrides AssetResolverInterface::getJsAssets

File

core/lib/Drupal/Core/Asset/AssetResolver.php, line 212

Class

AssetResolver
The default asset resolver.

Namespace

Drupal\Core\Asset

Code

public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
  $theme_info = $this->themeManager
    ->getActiveTheme();

  // Add the theme name to the cache key since themes may implement
  // hook_library_info_alter(). Additionally add the current language to
  // support translation of JavaScript files via hook_js_alter().
  $libraries_to_load = $this
    ->getLibrariesToLoad($assets);
  $cid = 'js:' . $theme_info
    ->getName() . ':' . $this->languageManager
    ->getCurrentLanguage()
    ->getId() . ':' . Crypt::hashBase64(serialize($libraries_to_load)) . (int) (count($assets
    ->getSettings()) > 0) . (int) $optimize;
  if ($cached = $this->cache
    ->get($cid)) {
    list($js_assets_header, $js_assets_footer, $settings, $settings_in_header) = $cached->data;
  }
  else {
    $javascript = [];
    $default_options = [
      'type' => 'file',
      'group' => JS_DEFAULT,
      'weight' => 0,
      'cache' => TRUE,
      'preprocess' => TRUE,
      'attributes' => [],
      'version' => NULL,
      'browsers' => [],
    ];

    // 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 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);
    foreach ($libraries_to_load as $library) {
      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 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) / 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;
        }
      }
    }

    // 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) {

    // Attached settings override both library definitions and
    // hook_js_settings_build().
    $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);

    // Update the $assets object accordingly, so that it reflects the final
    // settings.
    $assets
      ->setSettings($settings);
    $settings_as_inline_javascript = [
      'type' => 'setting',
      'group' => JS_SETTING,
      '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 [
    $js_assets_header,
    $js_assets_footer,
  ];
}