Skip to content

Commit

Permalink
generate service worker for installable PWA
Browse files Browse the repository at this point in the history
(fixes #63, fixes #105)
  • Loading branch information
sheppard committed Sep 29, 2020
1 parent 58290a7 commit 87cdf31
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 4 deletions.
2 changes: 2 additions & 0 deletions build/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from .setversion import setversion
from .icons import icons
from .phonegap import phonegap
from .serviceworker import serviceworker

__all__ = (
"appcache",
Expand All @@ -18,4 +19,5 @@
"setversion",
"icons",
"phonegap",
"serviceworker",
)
4 changes: 2 additions & 2 deletions build/appcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def appcache(config, version):
build log.
Note that browser vendors are deprecating support for Application Cache
in favor of Service Workers. The `wq appcache` command will be removed in
wq.app 2.0.
in favor of Service Workers. The `wq appcache` command will be removed
in wq.app 2.0. Use the `wq serviceworker` command instead.
"""

click.echo("Warning: Application Cache is deprecated by browser vendors.")
Expand Down
5 changes: 3 additions & 2 deletions build/phonegap.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ def phonegap(ctx, config, version, **conf):
7. Save the returned app ID for future builds.
\b
Note that PhoneGap Build is no longer online. The `wq phonegap` command
will be removed in wq.app 2.0. @wq/react and @wq/material support using
React Native and/or Expo to deploy native apps.
will be removed in wq.app 2.0. Use `wq serviceworker` to generate an
installable PWA instead. Alternatively, you can deploy a native app using
React Native and/or Expo, together with @wq/react and @wq/material.
"""

click.echo(
Expand Down
115 changes: 115 additions & 0 deletions build/serviceworker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
from wq.core import wq
import click
import os
import json
from glob import glob


@wq.command()
@click.argument('version')
@click.option(
'--template',
type=click.Path(),
help="Override service worker template"
)
@click.option(
'--output',
type=click.Path(),
default="./htdocs/service-worker.js",
help="Destination file (default: ./htdocs/service-worker.js)"
)
@click.option(
'--cache',
type=click.Path(),
multiple=True,
help="File(s) or directories to cache"
)
@click.option(
'--timeout',
type=int,
default=400,
help="Timeout to use before falling back to cache."
)
def serviceworker(version, template, output, cache, timeout):
"""
Generate a service-worker.js. The default service worker will function as
follows:
\b
1. While installing, precache all of the listed paths.
2. While running, attempt to fetch (and re-cache) the latest version
of each file, but fall back to the cached version after the specified
timeout.
This ensures the app can work offline as needed, without requiring a double
refresh whenever a new version is available. Specify the --template option
to override the default.
The list of files to cache should be relative to the directory containing
service-worker.js. Wildcard paths will be resolved to existing filenames,
so this command should generally be run *after* ./manage.py collectstatic.
Note: If you are using wq with npm, a service worker will be generated by
create-react-app, so you do not need to use this command.
"""

if not template:
template = SW_TMPL

basedir = os.path.dirname(output)
paths = []
for path in cache:
if '*' not in path:
paths.append(path)
continue
if path.startswith('/'):
path = path[1:]
for filename in glob(os.path.join(basedir, path)):
paths.append(filename.replace(basedir, ''))

cache = ",".join(json.dumps(path) for path in paths)

with open(output, 'w') as f:
f.write(
template.replace("{{VERSION}}", version)
.replace("{{CACHE}}", cache)
.replace("{{TIMEOUT}}", str(timeout))
)


SW_TMPL = """const CACHE_NAME = 'wq-cache-v1';
// App Version {{VERSION}}
const cacheUrls = [{{CACHE}}];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(cacheUrls))
);
});
self.addEventListener('fetch', event => {
if (!cacheUrls.includes(new URL(event.request.url).pathname)) {
return;
}
event.respondWith(
new Promise((resolve, reject) => {
const timeout = setTimeout(reject, {{TIMEOUT}});
fetch(event.request).then(response => {
clearTimeout(timeout);
const cacheResponse = response.clone();
caches
.open(CACHE_NAME)
.then(cache => cache.put(event.request, cacheResponse));
resolve(response);
}, reject);
}).catch(() => {
return caches
.open(CACHE_NAME)
.then(cache => cache.match(event.request))
.then(response => response || Promise.reject('no-match'));
})
);
});
"""

0 comments on commit 87cdf31

Please sign in to comment.