Cache Busting Drupal Libraries with Versions

Our team recently worked on a new feature for a JavaScript-powered component within a Drupal site. After reviewing the new feature and deploying it to production, our changes did not seem to take effect: Visitors to the production site were still getting served an outdated JavaScript file without our newest changes. Everyone's favorite Drupal debugging tool, drush cr, had already been run during the deployment, so clearly it was a deeper caching issue.

We began to inspect how the file in question was attached to the page, which quickly led us to the *.libraries.yml file. A *.libraries.yml file will often look something like this:

example:
version: VERSION
js:
js/example.js: {}
dependencies:
- core/once
- core/drupal.ajax
- core/drupalSettings

Everything in there made sense, except the version key, which has always been a bit of a mystery to me. Values such as 1.x or VERSION are often found in the *.libraries.yml files for some core/contrib modules and on others the version key is omitted altogether. All of this led me to think it wasn't very important. I couldn't have been more wrong.

Library Version & Caching

The version key is used for cache-busting static assets that in many cases may have a TTL of one year or more (depending on your TTL settings).

JavaScript assets in the browser inspector with the version as a URL query string.

If you want users to see the latest version of your static assets, the version key could not be more important.

tl;dr If a static asset in a library file has changed, increase the library version number to ensure that the latest version of the asset is served.

Understanding "VERSION"

So now we understand what the version key does, however, the mystery VERSION string value remained. With a quick search through Drupal core I found the answer in \Drupal\Core\Asset\LibraryDiscoveryParser.php.

if (isset($library['version'])) {
// @todo Retrieve version of a non-core extension.
if ($library['version'] === 'VERSION') {
$library['version'] = \Drupal::VERSION;
}
// Remove 'v' prefix from external library versions.
elseif (is_string($library['version']) && $library['version'][0] === 'v') {
$library['version'] = substr($library['version'], 1);
}
}

Using VERSION will give you the full version string for the current version of Drupal core as stored in \Drupal::VERSION (e.g. 9.4.1). This is great if you are developing a core module, as this will increment when a new version of core is released. However, if you are working on a custom module, falling back on the version of Drupal Core doesn't make much sense. Keep this in mind if you copy a core module to use as a basis for your custom module. Libraries in custom/contrib modules or themes should use a version string that increments in a way you control. In our case we went with 1.0.0 since this was the first version we were defining ourselves.

Note: There is a Drupal.org issue for making VERSION work in custom/contrib modules.

Hopefully this leaves you with a better understanding of why including version in your library definition is important, and perhaps the VERSION magic will soon be available to custom/contrib as well!