Created
February 26, 2024 22:53
-
-
Save meduzen/90c9af826af05158586ce0ec6ea1f9dd to your computer and use it in GitHub Desktop.
Potential answer to https://mastodon.social/@passle/111999742422826465
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const SW_VERSION = '1.2.3' // update it when the app changes: invalidates `resourcesToCache` | |
const resourcesCacheKey = `cache-v${SW_VERSION}` | |
const microfrontendCacheKey = `mfe` // not sure versioning is needed | |
const resourcesToCache = [ | |
'/', | |
'site.webmanifest', | |
'css/app.css', | |
'js/app.js', | |
// Standard icons | |
'android-chrome-192x192.png', | |
'android-chrome-512x512.png', | |
'android-chrome-maskable-192x192.png', | |
'android-chrome-maskable-512x512.png', | |
'favicon.svg', | |
// Apple icons | |
'apple-touch-icon.png', | |
'safari-pinned-tab.svg', | |
// Old stuff | |
'favicon-32x32.png', | |
'favicon-16x16.png', | |
// whatever else… | |
] | |
const createCaches = () => Promise.all([ | |
caches.open(resourcesCacheKey).then(cache => cache.addAll(resourcesToCache)), | |
]) | |
const flushOldCaches = () => caches.keys().then(keys => Promise.all( | |
keys | |
.filter(key => key != resourcesCacheKey) | |
.map(key => caches.delete(key).then(() => { | |
// App has been updated | |
if (key.startsWith('cache-v')) { | |
notifyClients({ appUpdate: true }) | |
} | |
})) | |
)) | |
const removeCachedMicrofrontend = name => caches.keys().then(keys => Promise.all( | |
keys | |
.filter(key => key !== microfrontendCacheKey) | |
.filter(key => key.url.includes(`/mfe/${name}/`)) | |
.map(key => caches.delete(key)) | |
)) | |
const putToCache = (cacheKey, request, response) => | |
caches.open(cacheKey).then(cache => cache.put(request, response)) | |
const respondWith = (e, url) => | |
e.respondWith(caches.match(url, { ignoreSearch: true }) | |
.then(response => response || fetch(e.request).then(response => response)) | |
) | |
const notifyClients = data => self.clients.matchAll().then(clients => | |
clients.forEach(client => client.postMessage(data)) | |
) | |
self.addEventListener('install', e => | |
e.waitUntil(createCaches().then(() => self.skipWaiting())) | |
) | |
self.addEventListener('activate', e => | |
e.waitUntil(flushOldCaches().then(() => self.clients.claim())) | |
) | |
self.addEventListener('fetch', e => { | |
// Assuming microfrontend hosted on /mfe/{mfeName}/1.0.0/index.js | |
let url = new URL(e.request.url) | |
if (url.pathname.startsWith('/mfe/')) { | |
caches.match(url).then(response => { | |
// Respond with exact same version already cached | |
if (response != undefined) { | |
return respondWith(e, e.request) | |
} | |
// Get microfrontend name and version from path | |
const [name, version] = url.pathname.split('/mfe/') | |
// Check for an already cached version of the wanted micro-frontend | |
caches.open(microfrontendCacheKey) | |
.then(cache => cache.keys().then(keys => { | |
const cachedMicrofrontend = keys.find(key => key.url.includes(`/${name}/`)) | |
// Other version not cached yet, so fetch it, cache it, returns it… technologit! | |
if (!cachedMicrofrontend) { | |
fetch(e.request).then(response => { | |
if (response.status == 200) { | |
// Cache microfrontend. | |
putToCache(microfrontendCacheKey, e.request.clone(), response.clone()) | |
// Send data date to app. | |
return response | |
} | |
}) | |
} | |
// Other version cached: returns the higher one | |
const cachedMicrofrontendUrl = new URL(cachedMicrofrontend.url) | |
const paths = [cachedMicrofrontendUrl.pathname, url.pathname] | |
paths.sort() | |
// Cached one is higher version, we return it | |
if (cachedMicrofrontend == paths[1]) { | |
return respondWith(e, cachedMicrofrontend) | |
} | |
// Requested one should be fetched, cached, then returned. But before, delete the current cached one. | |
removeCachedMicrofrontend(`${name}/${version}/index.js`) // delete currently cached version | |
// Now fetch and reply (code repetition: same as in ~30 lines before… in `if (!cachedMicrofrontend) {`…) | |
fetch(e.request).then(response => { | |
if (response.status == 200) { | |
// Cache microfrontend. | |
putToCache(microfrontendCacheKey, e.request.clone(), response.clone()) | |
// Send data date to app. | |
return response | |
} | |
}) | |
})) | |
}) | |
} | |
// Return from cache or fallback to network. | |
respondWith(e, e.request) | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Another approach could be https://github.com/jorenbroekema/cdn-dedupe-sw.