Cache buster for extension files

Hi!

I’ve hacked my own build step that will inject the current version in the URL of any files in the `umbraco-package.json` file

{
  "$schema": "../../../../Our.Umbraco.TheDashboard.Testsite/umbraco-package-schema.json",
  "name": "TheDashboard",
  "version": "16.0.0",
  "extensions": [
    {
      "type": "backofficeEntryPoint",
      "alias": "TheDashboard.EntryPoint",
      "name": "The Dashboard Entry Point",
      "js": "/App_Plugins/Our.Umbraco.TheDashboard/dist/the-dashboard-umbraco.js?v1600",
      "weight": 13000
    }
  ]
}

Notice `?v1600` in the end of the file url.

I made this back in the v14 days and I’m wondering if there is any better way to do this as of now?

So far I havent had the need for cache busting in 16, is that really an issue?

I guess it depends on how the extension is built,

In my configuration for Vite, the output file would be named something like “the-dashboard-umbraco.js”, if the content of that file changes, I guess we would need a cache buster to ensure that the users browser does not use an old version after an update.

Personally, I almost always use dev tools so I don’t have this issu,e but “regular” backoffice editors probably do not.

Are you saying that you’ve shipped updates of packages without having to use a cache buster and that consuming users could update without having issues with cached client files?

You can move the umbraco.package.json back into c# if you want by adding a IManifestReader

Then you can stamp the version of the assembly onto the script file and you don’t need to change it each release as the version will automatically update.

this is how it’s done in uSync (a bit complicated because we also add some import stuff)

essentially, you can use

assembly.GetName()?.Version?.ToString(3) ?? "16.0.0";

to get the version and apply that to the script.

1 Like

Kevin’s solution works. I just wanted to add that there is really no reason why we couldn’t add a ?v parameter to scripts loaded from the core, like the old backoffice did, but I guess we dont know the extent of the damage it could cause.

The Backoffice’s own files have a %CACHE_BUSTER% token in their filename that gets replaced in the importmap. I don’t know if something generic could be made of that.

Thanks @KevinJump! This approach is a lot easier compared to my buildstep.

@jacob Yeah, I know I raised an issue about back in 2024 Make `BackOfficeCacheBustHash` usable for packages · Issue #16893 · umbraco/Umbraco-CMS · GitHub

Maybe we can continue the discussion in there :slight_smile:

Given that umbraco-package.json has both a version number and a straightforward JS entry point for each extension, I don’t think it’s unreasonable to expect that URL to be automatically cachebusted based on the version number supplied.

For those of us using the bundle approach with a single entry point, I guess cache busting only becomes a problem if we modify the manifest bundle itself - rather than any individual extension’s entry points.

@JasonElkin Yeah, that might work, but it would still require “something” (manually or automatic) to remember to change that version number.

I think that it would be cool if there was something in the core that could create a hash placeholder based on the version of the package.

The code that “finds” the Umbraco-package.json file could maybe note the version from the assembly where it was found or something like that.

I used IFileVersionProvider to add a cachebuster. It’s the same interface the asp-append-version TagHelper uses.

public class BackofficeManifest(IFileVersionProvider fileVersionProvider) : IPackageManifestReader
{
    public Task<IEnumerable<PackageManifest>> ReadPackageManifestsAsync()
    {
        var filePath = "/build/backoffice/backoffice.js";
        var filePathVersioned = fileVersionProvider.AddFileVersionToPath(new PathString("/"), filePath);
        var manifest = (IEnumerable<PackageManifest>)new List<PackageManifest>() {
            new PackageManifest()
            {
                AllowTelemetry = true,
                Version = filePathVersioned.TrimStart(filePath).TrimStart("?v="),
                Name = "My.Backoffice",
                Extensions = [
                    new {
                        type = "bundle",
                        alias = "My.Backoffice.Bundle",
                        name = "My Backoffice Bundle",
                        js = filePathVersioned
                    },
                ]
            }
        };

        return Task.FromResult(manifest);
    }
}

This way, both the version and the cachebusting variable is automatically updated whenever the bundle changes.

2 Likes