0
votes

I have a PWA, which has a manifest.json with a start_url.

I have a service worker with a fetch event that only caches certain requests. This is done in the service worker by overriding the response to proxy from the cache (TypeScript for clarity):

self.addEventListener('fetch', (event: FetchEvent) => {
    // This MUST be synchronous until respondWith is called

    const cache = isCachable(event.request);
    if (!isCachable)
        return; // Don't proxy, let the network request continue

    // Kick off a promise to check the cache, and clone/cache the response otherwise
    const proxy: Promise<Response> = cacheResponse(event);

    event.respondWith(proxy);
}

I want to cache the start_url, which means isCachable above needs to be able to tell that the value of start_url is the relative route being requested.

I can load the manifest.json in the SW but that feels really clunky. I can hard code the values in the SW, but then I need to update two places if config changes.

In the fetch handler event.request.url is absolute, but the start_url is relative to the manifest.json - so, for instance, I might have:

  • manifest.json: { "start_url": "appshell" ... }
  • Web site gets deployed to www.example.com/folder/ (but might be deployed to sub.example.com or www.example.com/a/b or whatever)
  • Online user visits site, either visits everything or install script caches direct.
  • Later, same user visits offline.
  • The fetch above fires with event.request.url === 'www.example.com/folder/appshell'
  • I need the isCachable function to be able to tell that resource should be cached synchronously. It needs to determine that www.example.com/folder/appshell is appshell (resolve the relative link) and that appshell is set as start_url (read the manifest).

Obviously, all this can be hard coded. However, every PWA needs respond from the cache for start_url, so this can't be a new problem. Before I reinvent the wheel is there a better way or something I'm missing?

So...

  • Given that service workers need the manifest, is there a good way to get the manifest's config in the worker?
  • Is there a way to resolve the relative link for comparison with the manifest?
1
You are talking about Web App manifest right? Not about the extension's one?Kaiido
And is your question only about resolving relative paths? If so, do you want to resolve it relatively to the SW or from the document that made the request?Kaiido
@Kaiido there's only one manifest.json that applies to service workers. Yes this is a progressive web app and not a browser extension. The start_url is relative to the manifest. The event.request.url passed to the fetch handler in the service worker is absolute, the cache in a service worker is relative to the service worker. I don't need to figure out relative links anywhere else, this question is just about the specific case of the start URL, which needs to be cached by the service worker.Keith
Then why do you keep using the tag that is meant for web extensions? And the documentation link you edited is out of date, since then a new extension .webmanifest has been officialised.Kaiido
@Kaiido because this is specifically a question about the manifest.json, and the service worker context is much more commonly understood than the extension one (guess which is the top hit on search?), and both are variants of the same thing.Keith

1 Answers

1
votes

I've figured out how to do this, but it's nasty and I'm sure there's a better way.

In particular I'm hard-coding in the manifest inline (I could fetch it, but that's nasty too).

const manifest = { start_url: 'appshell' };

Then I use self.location to get the path of the service worker. This includes the file name, so I trim that off the end:

// if file is 'sw.js' that's 5 chars off the end
const applicationRoot = new URL(self.location.href.slice(0, -5) + manifest.start_url); 

Then I can check against this:

self.addEventListener('fetch', (event: FetchEvent) => {
    // This MUST be synchronous until respondWith is called

    const url = new URL(event.request.url);
    if (url.pathname === applicationRoot.pathname) {
        // Request is for the start_url, always return something so the PWA starts offline
        event.respondWith(...):
    }

    // ... see question for rest
});

This is clunky, but at least it can always serve the cached start_url without caching everything else, so it is the accepted answer for now. I'd love to see a better way of doing this, ideally without hard coding/fetching the manifest.json so that config changes there don't require a new service worker.