Skip to content
This repository has been archived by the owner on Jan 15, 2021. It is now read-only.

Commit

Permalink
Add the ability for any spec to have a service worker
Browse files Browse the repository at this point in the history
Part of whatwg/meta#3. This allows any standard using the shared deploy script to automatically get a service worker generated, which delegates all of its logic to a shared file on resources.whatwg.org. It allows room for future expansions of the deploy script to include extra resources.

This still requires registering the generated service worker in each spec's HTML, however.
  • Loading branch information
domenic committed Jun 22, 2017
1 parent fd7655c commit d8f7379
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 0 deletions.
11 changes: 11 additions & 0 deletions build/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ if [[ "$TRAVIS" == "true" ]]; then # For some reason the above does not work on
BRANCH=$TRAVIS_BRANCH
fi

SERVICE_WORKER_SHA=$(curl https://api.github.com/repos/whatwg/resources.whatwg.org/contents/standard-service-worker.js \
-H "Accept: application/vnd.github.v3+json" \
| grep -Po '(?<="sha": ")[^"]*') # Hacky JSON parsing but works for SHAs


BACK_TO_LS_LINK="<a href=\"/\" id=\"commit-snapshot-link\">Go to the living standard</a>"
SNAPSHOT_LINK="<a href=\"/commit-snapshots/$SHA/\" id=\"commit-snapshot-link\">Snapshot as of this commit</a>"

Expand Down Expand Up @@ -82,6 +87,12 @@ else
curl https://api.csswg.org/bikeshed/ -f -F file=@"$INPUT_FILE" \
-F md-Text-Macro="SNAPSHOT-LINK $SNAPSHOT_LINK" \
> "$WEB_ROOT/index.html"

echo "\"use strict\";
importScripts(\"https://resources.whatwg.org/standard-service-worker.js\");
// Version (for service worker freshness check): $SERVICE_WORKER_SHA" \
> "$WEB_ROOT/service-worker.js"

echo "Living standard output to $WEB_ROOT"
fi

Expand Down
121 changes: 121 additions & 0 deletions standard-service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"use strict";
/* USAGE:
self.extraResources = ["https://example.com/..."]; // optional
importScripts("https://resources.whatwg.org/standard-service-worker.js");
*/

const standardShortname = location.host.split(".")[0];

const cacheKey = "v3";
const toCache = [
location.origin + "/",
"https://resources.whatwg.org/standard.css",
"https://resources.whatwg.org/bikeshed.css",
"https://resources.whatwg.org/file-issue.js",
"https://resources.whatwg.org/commit-snapshot-shortcut-key.js",
"https://resources.whatwg.org/logo-" + standardShortname + ".svg"
].concat(self.extraResources || []);

self.oninstall = e => {
e.waitUntil(caches.open(cacheKey).then(cache => cache.addAll(toCache)));
};

self.onfetch = e => {
if (e.request.method !== "GET") {
return;
}

if (needsToBeFresh(e.request)) {
// Since this is a Living Standard, it is imperative that you see the freshest content, so we use a
// network-then-cache strategy for the main content.
e.respondWith(
fetch(e.request).then(res => {
e.waitUntil(refreshCacheFromNetworkResponse(e.request, res));
return res;
})
.catch(() => {
return caches.match(e.request);
})
);
} else {
// For auxiliary resources, we can use a cache-then-network strategy; it is OK to not get the freshest.
e.respondWith(
caches.match(e.request).then(cachedResponse => {
const networkFetchPromise = fetch(e.request);

// Ignore network fetch or caching errors; they just mean we won't be able to refresh the cache.
e.waitUntil(
networkFetchPromise
.then(res => refreshCacheFromNetworkResponse(e.request, res))
.catch(() => {})
);

return cachedResponse || networkFetchPromise;
})
);
}
};

self.onactivate = e => {
e.waitUntil(caches.keys().then(keys => {
return Promise.all(keys.filter(key => key !== cacheKey).map(key => caches.delete(key)));
}));
};

function refreshCacheFromNetworkResponse(req, res) {
if (!res.ok) {
throw new Error(`${res.url} is responding with ${res.status}`);
}

const resForCache = res.clone();

return caches.open(cacheKey).then(cache => cache.put(req, resForCache));
}

function needsToBeFresh(req) {
const requestURL = new URL(req.url);
return requestURL.origin === location.origin && requestURL.pathname === "/";
}

// From https://github.com/jakearchibald/async-waituntil-polyfill
// Apache 2 License: https://github.com/jakearchibald/async-waituntil-polyfill/blob/master/LICENSE
{
const waitUntil = ExtendableEvent.prototype.waitUntil;
const respondWith = FetchEvent.prototype.respondWith;
const promisesMap = new WeakMap();

ExtendableEvent.prototype.waitUntil = function(promise) {
const extendableEvent = this;
let promises = promisesMap.get(extendableEvent);

if (promises) {
promises.push(Promise.resolve(promise));
return;
}

promises = [Promise.resolve(promise)];
promisesMap.set(extendableEvent, promises);

// call original method
return waitUntil.call(extendableEvent, Promise.resolve().then(function processPromises() {
const len = promises.length;

// wait for all to settle
return Promise.all(promises.map(p => p.catch(()=>{}))).then(() => {
// have new items been added? If so, wait again
if (promises.length != len) return processPromises();
// we're done!
promisesMap.delete(extendableEvent);
// reject if one of the promises rejected
return Promise.all(promises);
});
}));
};

FetchEvent.prototype.respondWith = function(promise) {
this.waitUntil(promise);
return respondWith.call(this, promise);
};
}

0 comments on commit d8f7379

Please sign in to comment.