File Coverage

blib/lib/Mojolicious/Plugin/ServiceWorker.pm
Criterion Covered Total %
statement 32 32 100.0
branch 6 8 75.0
condition 2 3 66.6
subroutine 8 8 100.0
pod 1 1 100.0
total 49 52 94.2


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::ServiceWorker;
2 1     1   830 use Mojo::Base 'Mojolicious::Plugin';
  1         2  
  1         8  
3 1     1   228 use Mojo::JSON;
  1         2  
  1         591  
4              
5             our $VERSION = '0.02';
6              
7             my $SW_URL = 'serviceworker.js';
8             my @COPY_KEYS = qw(debug precache_urls network_only cache_only network_first);
9             my %DEFAULT_LISTENERS = (
10             install => [ <<'EOF' ],
11             event => {
12             console.log("Installing SW...");
13             event.waitUntil(caches.open(cachename).then(cache => {
14             console.log("Caching: ", config.precache_urls);
15             return cache.addAll(config.precache_urls);
16             }).then(() => console.log("The SW is now installed")));
17             }
18             EOF
19             fetch => [ <<'EOF' ],
20             event => {
21             var url = event.request.url;
22             if (maybeMatch(config, 'network_only', url)) {
23             if (config.debug) console.log('network_only', url);
24             return event.respondWith(fetch(event.request).catch(() => {}));
25             }
26             return caches.open(cachename).then(
27             cache => cache.match(event.request)
28             ).then(cacheResponse => {
29             if (cacheResponse && maybeMatch(config, 'cache_only', url)) {
30             if (config.debug) console.log('cache_only', url);
31             return cacheResponse;
32             }
33             if (maybeMatch(config, 'network_first', url)) {
34             if (config.debug) console.log('network_first', url);
35             return cachingFetchOrCached(event.request, cacheResponse);
36             }
37             if (config.debug) console.log('cache_first', url);
38             var cF = cachingFetch(event.request).catch(() => {});
39             return cacheResponse || cF;
40             });
41             }
42             EOF
43             );
44              
45             sub register {
46 2     2 1 2278 my ($self, $app, $conf) = @_;
47 2 50       6 my %config = %{ $conf || {} };
  2         13  
48 2   66     13 my $sw_route = $conf->{route_sw} || $SW_URL;
49 2         10 my $r = $app->routes;
50             $r->get($sw_route => sub {
51 2     2   98633 my ($c) = @_;
52 2         19 $c->render(
53             template => 'serviceworker',
54             format => 'js',
55             listeners => $c->serviceworker->event_listeners,
56             );
57 2         36 }, 'serviceworker.route');
58 2     2   946 $app->helper('serviceworker.route' => sub { $sw_route });
  2         1890  
59             $config{precache_urls} = [
60 2 100       919 @{ $config{precache_urls} || [] },
  2         16  
61             $sw_route,
62             ];
63 2 100       6 my %config_copy = map {$config{$_} ? ($_ => $config{$_}) : ()} @COPY_KEYS;
  10         32  
64 2     4   13 $app->helper('serviceworker.config' => sub { \%config_copy });
  4         17511  
65 2         671 push @{ $app->renderer->classes }, __PACKAGE__;
  2         7  
66 2         27 my %event_listeners = %DEFAULT_LISTENERS;
67 2     2   15 $app->helper('serviceworker.event_listeners' => sub { \%event_listeners });
  2         63  
68             $app->helper('serviceworker.add_event_listener' => sub {
69 1     1   1330 my ($c, $event, $expr) = @_;
70 1 50       4 $event_listeners{$event} = [ @{ $event_listeners{$event} || [] }, $expr ];
  1         10  
71 2         735 });
72 2         693 $self;
73             }
74              
75             1;
76              
77             =encoding utf8
78              
79             =head1 NAME
80              
81             Mojolicious::Plugin::ServiceWorker - plugin to add a Service Worker
82              
83             =head1 SYNOPSIS
84              
85             # Mojolicious::Lite
86             plugin 'ServiceWorker' => {
87             route_sw => '/sw2.js',
88             precache_urls => [
89             ],
90             };
91             app->serviceworker->add_event_listener(push => <<'EOF');
92             function(event) {
93             if (event.data) {
94             console.log('This push event has data: ', event.data.text());
95             } else {
96             console.log('This push event has no data.');
97             }
98             }
99             EOF
100              
101             =head1 DESCRIPTION
102              
103             L is a L plugin.
104              
105             =head1 METHODS
106              
107             L inherits all methods from
108             L and implements the following new ones.
109              
110             =head2 register
111              
112             my $p = $plugin->register(Mojolicious->new, \%conf);
113              
114             Register plugin in L application, returning the plugin
115             object. Takes a hash-ref as configuration, see L for keys.
116              
117             =head1 OPTIONS
118              
119             =head2 route_sw
120              
121             The service worker route. Defaults to C. Note that
122             you need this to be in your app's top level, since the service worker
123             can only affect URLs at or below its "scope".
124              
125             =head2 debug
126              
127             If a true value, C will be used to indicate various events
128             including SW caching choices.
129              
130             =head2 precache_urls
131              
132             An array-ref of URLs that are relative to the SW's scope to load into
133             the SW's cache on installation. The SW URL will always be added to this.
134              
135             =head2 network_only
136              
137             An array-ref of URLs. Any fetched URL in this list will never be cached,
138             and always fetched over the network.
139              
140             =head2 cache_only
141              
142             As above, except the matching URL will never be re-checked. Use only
143             where you cache-bust by including a hash in the filename.
144              
145             =head2 network_first
146              
147             As above, except the matching URL will be fetched from the network
148             every time and used if possible. The cached value will only be used if
149             that fails.
150              
151             B will be treated with a
152             "cache first" strategy, also known as "stale while revalidate": the cached
153             version will immediately by returned to the web client for performance,
154             but also fetched over the network and re-cached for freshness.
155              
156             =head1 HELPERS
157              
158             =head2 serviceworker.route
159              
160             my $route_name = $c->serviceworker->route;
161              
162             The configured L route.
163              
164             =head2 serviceworker.config
165              
166             my $config = $c->serviceworker->config;
167              
168             The SW configuration (a hash-ref). Keys: C, C,
169             C, C, C.
170              
171             =head2 serviceworker.add_event_listener
172              
173             my $config = $c->serviceworker->add_event_listener(push => <<'EOF');
174             function(event) {
175             if (event.data) {
176             console.log('This push event has data: ', event.data.text());
177             } else {
178             console.log('This push event has no data.');
179             }
180             }
181             EOF
182              
183             Add to the service worker an event listener. Arguments are the event
184             name, and a JavaScript function expression that takes the correct args
185             for that event.
186              
187             =head2 serviceworker.event_listeners
188              
189             my $listeners = $c->serviceworker->event_listeners;
190              
191             Returns a hash-ref mapping event name to array-ref of function
192             expressions as above. C and C are provided by default.
193              
194             =head1 TEMPLATES
195              
196             Various templates are available for including in the app's templates:
197              
198             =head2 serviceworker-install.html.ep
199              
200             A snippet of JavaScript that will install the supplied service
201             worker. Include it within a C
206              
207             =head1 SEE ALSO
208              
209             L, L, L.
210              
211             =cut
212              
213             __DATA__