private function getAllLibraries() { $modules = $this->moduleHandler->getModuleList(); $themes = $this->themeHandler->rebuildThemeData(); $extensions = array_merge($modules, $themes); $libraries = []; foreach ($extensions as $extensionName => $extension) { $libraryFile = $extension->getPath() . '/' . $extensionName . '.libraries.yml'; if (is_file($this->appRoot . '/' . $libraryFile)) { $libraries[$extensionName] = $this->libraryDiscovery->getLibrariesByExtension($extensionName); } } return array_keys($libraries); }
/** * Gets the given libraries with its dependencies. * * Helper method for ::getLibrariesWithDependencies(). * * @param string[] $libraries_with_unresolved_dependencies * A list of libraries, with unresolved dependencies, in the order they * should be loaded. * @param string[] $final_libraries * The final list of libraries (the return value) that is being built * recursively. * * @return string[] * A list of libraries, in the order they should be loaded, including their * dependencies. */ protected function doGetDependencies(array $libraries_with_unresolved_dependencies, array $final_libraries = []) { foreach ($libraries_with_unresolved_dependencies as $library) { if (!in_array($library, $final_libraries)) { list($extension, $name) = explode('/', $library, 2); $definition = $this->libraryDiscovery->getLibraryByName($extension, $name); if (!empty($definition['dependencies'])) { $final_libraries = $this->doGetDependencies($definition['dependencies'], $final_libraries); } $final_libraries[] = $library; } } return $final_libraries; }
/** * Asserts that the specified asset is not in the given library. * * @param string $asset * The asset file with the path for the file. * @param string $extension * The extension in which the $library_name is defined. * @param string $library_name * Name of the library. * @param mixed $sub_key * The library sub key where the given asset is defined. * @param string $message * (optional) A message to display with the assertion. * * @return bool * TRUE if the specified asset is not found in the library. */ protected function assertNoAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) { if (!isset($message)) { $message = sprintf('Asset %s not found in library "%s/%s"', $asset, $extension, $library_name); } $library = $this->libraryDiscovery->getLibraryByName($extension, $library_name); foreach ($library[$sub_key] as $definition) { if ($asset == $definition['data']) { return $this->fail($message); } } return $this->pass($message); }
/** * Gets all libraries for core and all installed modules. * * @return array[] * An associative array of libraries keyed by extension, then by library * name, and so on. */ protected function getAllLibraries() { $modules = \Drupal::moduleHandler()->getModuleList(); $module_list = array_keys($modules); sort($module_list); $this->assertEqual($this->allModules, $module_list, 'All core modules are installed.'); $libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core'); $root = \Drupal::root(); foreach ($modules as $module_name => $module) { $library_file = $module->getPath() . '/' . $module_name . '.libraries.yml'; if (is_file($root . '/' . $library_file)) { $libraries[$module_name] = $this->libraryDiscovery->getLibrariesByExtension($module_name); } } return $libraries; }
/** * {@inheritdoc} */ 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]; }
/** * Tests that an external asset library is registered as a core asset library. * * @see \Drupal\libraries\Extension\Extension * @see \Drupal\libraries\Extension\ExtensionHandler * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibrary * @see \Drupal\libraries\ExternalLibrary\Asset\AssetLibraryTrait * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryManager * @see \Drupal\libraries\ExternalLibrary\ExternalLibraryTrait * @see \Drupal\libraries\ExternalLibrary\Registry\ExternalLibraryRegistry */ public function testAssetLibrary() { $library = $this->libraryDiscovery->getLibraryByName('libraries', 'test_asset_library'); $this->assertNotEquals(FALSE, $library); $this->assertTrue(is_array($library)); }
/** * {@inheritdoc} */ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) { $javascript = []; $default_options = ['type' => 'file', 'group' => JS_DEFAULT, 'every_page' => FALSE, 'weight' => 0, 'cache' => TRUE, 'preprocess' => TRUE, 'attributes' => [], 'version' => NULL, 'browsers' => []]; $libraries_to_load = $this->getLibrariesToLoad($assets); // 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_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; } } } return [$js_assets_header, $js_assets_footer]; }
/** * {@inheritdoc} */ protected function setUp() { $this->libraryDiscovery = $this->getMockBuilder('Drupal\\Core\\Asset\\LibraryDiscovery')->disableOriginalConstructor()->setMethods(['getLibrariesByExtension'])->getMock(); $this->libraryDiscovery->expects($this->any())->method('getLibrariesByExtension')->with('test')->will($this->returnValue($this->libraryData)); $this->libraryDependencyResolver = new LibraryDependencyResolver($this->libraryDiscovery); }