Skip to content

Instantly share code, notes, and snippets.

@meduzen
Created February 26, 2024 22:53
Show Gist options
  • Save meduzen/90c9af826af05158586ce0ec6ea1f9dd to your computer and use it in GitHub Desktop.
Save meduzen/90c9af826af05158586ce0ec6ea1f9dd to your computer and use it in GitHub Desktop.
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)
})
@meduzen
Copy link
Author

meduzen commented Feb 27, 2024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment