workbox
workbox copied to clipboard
[feature] Add an option to InjectManifest as a function to be able to process assets-manifest.json and import it into the service worker
Library Affected: workbox-webpack-plugin
Browser & Platform: Any
Issue or Feature Request Description:
We need to smart clean runtime cache when a new version of service worker is activated.
Not having a complete list of assets from assets-manifest.json available - we can't do this: we need to either completely clear the runtime cache and reload and cache assets again, or leave all assets from previous versions there and wait until we reach quota limits
InjectManifest requires an option in the form of a function in which we could transform the original assets-manifest.json (not everything is needed there) and import it into a service worker where we could already compare what is not in this version and what, accordingly, can be painlessly removed from runtime cahe
To clarify, when you say assets-manifest.json, are you referring to the output of https://www.npmjs.com/package/webpack-assets-manifest? (I'm going to assume that's what you mean.)
workbox-webpack-plugin's InjectManifest mode is focused on creating a precache manifest, and injecting that into your swSrc file. Adding in functionality related to runtime caching to InjectManifest sounds like it's outside the scope of what the plugin is meant to focus on.
There are a few different options that you could consider, though.
First off, I have a writeup about what sounds to be a similar topic at https://jeffy.info/2021/10/10/smart-caching-hashes.html. That includes links to a Workbox plugin that attempts to clean up old assets when the same logical asset with a different hash is cached. As explained in the blog post, this approach requires an understanding of the naming format you're using for your hashed URLs.
As an alternative, if you have a JSON file that contains your asset mappings and you want to use it at runtime, the simplest way to do that would be to fetch('assets-manifest.json') and then parse and make use of the response. This is likely not going to be the most performance-friendly thing to do, though, as you'd have to re-fetch() each time your service worker started up, which could end up blocking your fetch handler from responding to requests. A similar, more performant technique would be to use webpack's JSON loader support to include assets-manifest.json inline in your final service worker file, and then make use of the data directly. I'm not 100% sure if that would work based on the timing of when workbox-webpack-pliugin runs its child compilation vs. when webpack-assets-manifest generates its output, though.
And finally, one more alternative assumes that all of your assets are potentially included in your precache manifest, but that you're currently filtering some of them out from being precached, and those are the ones that are instead cached at runtime. If that's the case, then something you could do is to include all of the assets in the precache manifest, but then manually post-process the manifest inside of your service worker's code, filtering out the URLs that are meant to be precached from the ones that are cached at runtime. Roughly speaking this would look like:
const fullManifest = self.__WB_MANIFEST;
const precacheManifest = [];
const runtimeCachedURLs = [];
for (const entry of fullManifest) {
// Super-basic filtering that assumes any URLs with /runtime/ in them should
// be cached at runtime, and everything else should be precached.
// Update this to match your exclude/include criteria used in the InjectManifest config.
if (entry.url.includes('/runtime/')) {
runtimeCachedURLs.push(entry.url);
} else {
precacheManifest.push(entry);
}
}
// Precache a subset of the original manifest.
precacheAndRoute(precacheManifest);
// Upon activation, delete all URLs that aren't in your current runtime list.
self.addEventListener('activate', (event) => {
const cleanup = async () => {
// Replace with your runtime cache name.
const runtimeCache = await caches.open('runtime');
const keys = await runtimeCache.keys();
const deletions = [];
for (const key of keys) {
const url = new URL(key.url);
// This assumes your runtimeCachedURLs only include paths.
// Change to url.href if it includes full URLs.
if (!runtimeCachedURLs.includes(url.pathname)) {
deletions.push(cache.delete(key));
}
}
await Promise.all(deletions);
};
event.waitUntil(cleanup());
});
To clarify, when you say assets-manifest.json, are you referring to the output of https://www.npmjs.com/package/webpack-assets-manifest? (I'm going to assume that's what you mean.)
Yes, I'm
You also have access to webpack-assets-manifest - you use it to filter include & exclude - so you can expose it in a hook
To fetch manifest - is not an option - there are a lot of troubles and cases with network.
We can not inline webpack-assets-manifest before the end of webpack building the project, but at the end of buildit it useless (- here is a closed circuit
I will read the the topic
I read the article and looked at the plugin - there is no other acceptable way to insert the webpack assets-manifest into the code of the service worker when using injectManifest plugin except in the code of the plugin itself. Moreover, it does not require much effort from you: the plugin uses webpack assets-manifest for its work.
I am convinced that you need to rethink the name and functions of the injectManifest webpack plugin - we use it to build a full-fledged service worker, and not just as injectPrecacheManifest.
It is not convenient for most developers to create a separate assembly process for the sake of creating a service worker - it needs to be created and maintained, and this takes both time and resources.
Therefore, injectManifest is the easiest and most effective way to develop a full-fledged service worker and not just a place to insert a precache manifest
I would rename this plugin to injectManifestS and add an option to include the full webpack assets-manifest (or the ability to describe the transformation function of the original manifest) in the plugin
if arrange a separate assembly for the service worker, redundancy appears (for example, the core-js module will be duplicated for both the application and the service worker, whereas with a single assembly, this module is used together and is present in one instance)