Currently each library definition has to be defined on a theme-level in a themename.libraries.yml. This usually results in developers defining a "global" library for their theme, and they avoid using all the benefits of using the libraries system. If we want to move towards a component-based development process we need to make it possible to have libraries.yml files on a component-specific level. A component in this sense would be any folder inside the theme.
Conceptually: It should be possible to have multiple libraries.yml files inside a theme. In a component-based development environment it is conceptually considered an anti-pattern to define a whole set of libraries in one theme-specific file, instead of in multiple (component-specific) files. Each library that is defined inside a component-specific libraries.yml file should do the exact same thing as if it would have been defined in the theme-specific libraries.yml file.
Technically: Compony.io provides a theme that does this out of the box. The solution uses a `hook_library_info_alter` and inside there we do from inside each theme a discovery of libraries.yml files that exist inside the theme. A hook_library_info_alter will only be called after a cache clear, so it won't impact regular performance. The following example, has the assumption that all of the theme components are placed inside themename/components folder.
Let's assume this theme is called "compony" and in order to follow below example, let's assume we have a component called status-messages that exists under themes/custom/compony/components.
The contents of this component can be inspected here as a demo: https://gitlab.com/componies/flat-design/Core/status-messages
/**
* Implements hook_library_info_alter().
*/
function hook_library_info_alter(&$libraries, $extension) {
// Get the name of the theme where this function is being called
$theme_name = basename(__FILE__, '.theme'); // compony
// Get the path of the theme where this function is being called
$theme_path = drupal_get_path('theme', $theme_name); // themes/custom/compony
// Alter only the library definitions of the current theme.
if ($extension == $theme_name) {
$directory_iterator = new RecursiveDirectoryIterator($theme_path . '/components/');
// Iterate over all the files found in /themes/custom/compony/components/
foreach (new RecursiveIteratorIterator($directory_iterator) as $file) {
// Filter out all the files that have the exact name: "libraries.yml"
if ($file->getFilename() == 'libraries.yml') {
try {
// Let's assume we found a libraries.yml file on /themes/custom/compony/components/status-messages/libraries.yml
$componentPathFromRoot = substr($file->getPathName(), 0, -13); // /themes/custom/compony/components/status-messages/
$componentPathFromTheme = str_replace($theme_path . '/', '', $componentPathFromRoot); // /components/status-messages/
// Decode the libraries.yml
$new_libraries = Yaml::decode(file_get_contents($file->getRealPath()));
// Each libraries.yml could have multiple library-definitions
foreach ($new_libraries as $key => $new_library) {
// Check if the key of this library "status-messages" in our example isn't already defined somewhere else
if(isset($libraries[$key])) {
// If the library is defined somewhere else already,
// throw a warning that we have multiple definitions of
// the same library within the same theme.
\Drupal::messenger()
->addWarning(t('The library @key from the theme @themename has multiple definitions.', [
'@key' => $key,
'@themename' => $theme_name,
]));
} else {
// If the library key hasn't been defined yet, and the library contains CSS definitions
if (isset($new_library['css'])) {
// Go over each CSS group definition, multiple groups are possible, so we need to loop over them
foreach($new_library['css'] as $group_key => $css_grouped) {
// Inside each group definition, there can be multiple CSS-files, so again we need to loop over those
foreach($css_grouped as $file_key => $css_file) {
// If the path to the file is absolutely defined, for example:
// components/status-messages/dist/status-messages.css,
// then it will work out of the box.
if(substr($file_key, 0, 11) == 'components/') {
// no need to do anything
} else {
// If a type is defined
if (isset($css_file['type'])) {
// If the type of the css file is external
if ($css_file['type'] == 'external') {
// break out of the foreach loop of this CSS-file.
continue;
}
}
// We only arrive here if the path doesn't start with 'components/',
// which would indicate the path is relative.
// We prefix the path to the css file of the relative definition with $componentPathFromTheme,
// so internally it will be absolute positioned starting from the theme it is found in.
$new_library['css'][$group_key][$componentPathFromTheme . $file_key] = $css_file;
unset($new_library['css'][$group_key][$file_key]);
}
}
}
}
// Do the same for the JS as we did for CSS
if (isset($new_library['js'])) {
foreach($new_library['js'] as $file_key => $js_file) {
if(substr($file_key, 0, 11) == 'components/') {
} else {
if (isset($js_file['type'])) {
if ($js_file['type'] == 'external') {
continue;
}
}
// Path is relatively defined
$new_library['js'][$componentPathFromTheme . $file_key] = $js_file;
unset($new_library['js'][$file_key]);
}
}
}
// Set the libraries variable to now have the altered library.
$libraries[$key] = $new_library;
}
}
} catch (InvalidDataTypeException $e) {
// Throw a helpful exception to provide context.
throw new InvalidLibraryFileException(sprintf('Invalid library definition in %s: %s', $file->getRealPath(), $e->getMessage()), 0, $e);
}
}
}
};
The part that checks for the existence of the string 'components' is a shortcut, ideally it should always use the location of the libraries.yml as a relative starting point to where the assets are defined, because this is currently how it works for both modules and themes. (but that seems to be a coincidence looking at the code)
Find people that know where this would fit in core and then write a patch.
Impacts documentation on library definitions for theme developers.
Not Applicable
Closed: outdated
11.0 🔥
theme system
Not all content is available!
It's likely this issue predates Contrib.social: some issue and comment data are missing.