Skip to content

Commit

Permalink
feat: add pwa.cache.* option to precisely control caching (cotes202…
Browse files Browse the repository at this point in the history
  • Loading branch information
cotes2020 authored Jan 27, 2024
1 parent ea3a22e commit 1127c43
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 228 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ package-lock.json
.idea

# Misc
*.map
sw.min.js
assets/js/dist
17 changes: 10 additions & 7 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ google_analytics:
# light - Use the light color scheme
# dark - Use the dark color scheme
#
theme_mode: # [light|dark]
theme_mode: # [light | dark]

# The CDN endpoint for images.
# Notice that once it is assigned, the CDN url
Expand Down Expand Up @@ -108,10 +108,17 @@ assets:
enabled: # boolean, keep empty means false
# specify the Jekyll environment, empty means both
# only works if `assets.self_host.enabled` is 'true'
env: # [development|production]
env: # [development | production]

pwa:
enabled: true # the option for PWA feature
enabled: true # the option for PWA feature (installable)
cache:
enabled: true # the option for PWA offline cache
# Paths defined here will be excluded from the PWA cache.
# Usually its value is the `baseurl` of another website that
# shares the same domain name as the current website.
deny_paths:
# - "/example" # URLs match `<SITE_URL>/example/*` will not be cached by the PWA

paginate: 10

Expand Down Expand Up @@ -157,10 +164,6 @@ defaults:
values:
layout: page
permalink: /:title/
- scope:
path: assets/img/favicons
values:
swcache: true
- scope:
path: assets/js/dist
values:
Expand Down
2 changes: 2 additions & 0 deletions _data/origin/cors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ cdns:
- url: https://fonts.googleapis.com
# jsDelivr CDN
- url: https://cdn.jsdelivr.net
# polyfill.io for math
- url: https://polyfill.io

# fonts

Expand Down
4 changes: 3 additions & 1 deletion _includes/favicons.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
<link rel="apple-touch-icon" sizes="180x180" href="{{ favicon_path }}/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="{{ favicon_path }}/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="{{ favicon_path }}/favicon-16x16.png">
<link rel="manifest" href="{{ favicon_path }}/site.webmanifest">
{% if site.pwa.enabled %}
<link rel="manifest" href="{{ favicon_path }}/site.webmanifest">
{% endif %}
<link rel="shortcut icon" href="{{ favicon_path }}/favicon.ico">
<meta name="apple-mobile-web-app-title" content="{{ site.title }}">
<meta name="application-name" content="{{ site.title }}">
Expand Down
9 changes: 9 additions & 0 deletions _includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@

{{ seo_tags }}

<!-- PWA cache settings -->
<meta
name="pwa-cache"
content="{{ site.pwa.cache.enabled | default: 'false' }}"
{%- if site.baseurl and site.baseurl != empty -%}
data-baseurl="{{ site.baseurl }}"
{%- endif -%}
>

<title>
{%- unless page.layout == 'home' -%}
{{ page.title | append: ' | ' }}
Expand Down
8 changes: 4 additions & 4 deletions _includes/js-selector.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

<!-- layout specified -->

{% assign js_dist = '/assets/js/dist/' %}

{% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %}
{% assign urls = urls | append: ',' | append: site.data.origin[type]['lazy-polyfill'].js %}

Expand Down Expand Up @@ -65,7 +67,7 @@
{% assign js = 'commons' %}
{% endcase %}

{% capture script %}/assets/js/dist/{{ js }}.min.js{% endcapture %}
{% capture script %}{{ js_dist }}{{ js }}.min.js{% endcapture %}
<script defer src="{{ script | relative_url }}"></script>

{% if page.math %}
Expand Down Expand Up @@ -94,9 +96,7 @@
{% if jekyll.environment == 'production' %}
<!-- PWA -->
{% if site.pwa.enabled %}
<script defer src="{{ '/app.js' | relative_url }}"></script>
{% else %}
<script defer src="{{ '/unregister.js' | relative_url }}"></script>
<script defer src="{{ 'app.min.js' | prepend: js_dist | relative_url }}"></script>
{% endif %}

<!-- GA -->
Expand Down
58 changes: 58 additions & 0 deletions _javascript/pwa/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* PWA loader */

if ('serviceWorker' in navigator) {
const meta = document.querySelector('meta[name="pwa-cache"]');
const isEnabled = meta.content === 'true';

if (isEnabled) {
let swUrl = '/sw.min.js';
const baseUrl = meta.getAttribute('data-baseurl');

if (baseUrl !== null) {
swUrl = `${baseUrl}${swUrl}?baseurl=${encodeURIComponent(baseUrl)}`;
}

const $notification = $('#notification');
const $btnRefresh = $('#notification .toast-body>button');

navigator.serviceWorker.register(swUrl).then((registration) => {
// In case the user ignores the notification
if (registration.waiting) {
$notification.toast('show');
}

registration.addEventListener('updatefound', () => {
registration.installing.addEventListener('statechange', () => {
if (registration.waiting) {
if (navigator.serviceWorker.controller) {
$notification.toast('show');
}
}
});
});

$btnRefresh.on('click', () => {
if (registration.waiting) {
registration.waiting.postMessage('SKIP_WAITING');
}
$notification.toast('hide');
});
});

let refreshing = false;

// Detect controller change and refresh all the opened tabs
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (!refreshing) {
window.location.reload();
refreshing = true;
}
});
} else {
navigator.serviceWorker.getRegistrations().then(function (registrations) {
for (let registration of registrations) {
registration.unregister();
}
});
}
}
101 changes: 101 additions & 0 deletions _javascript/pwa/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/* PWA service worker */

const swconfPath = '/assets/js/data/swconf.js';
const params = new URL(location).searchParams;
const swconfUrl = params.has('baseurl')
? `${params.get('baseurl')}${swconfPath}`
: swconfPath;

importScripts(swconfUrl);
const purge = swconf.purge;

function verifyHost(url) {
for (const host of swconf.allowHosts) {
const regex = RegExp(`^http(s)?://${host}/`);
if (regex.test(url)) {
return true;
}
}
return false;
}

function verifyUrl(url) {
if (!verifyHost(url)) {
return false;
}

const requestPath = new URL(url).pathname;

for (const path of swconf.denyPaths) {
if (requestPath.startsWith(path)) {
return false;
}
}
return true;
}

if (!purge) {
swconf.allowHosts.push(location.host);
}

self.addEventListener('install', (event) => {
if (purge) {
return;
}

event.waitUntil(
caches.open(swconf.cacheName).then((cache) => {
return cache.addAll(swconf.resources);
})
);
});

self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((keyList) => {
return Promise.all(
keyList.map((key) => {
if (purge) {
return caches.delete(key);
} else {
if (key !== swconf.cacheName) {
return caches.delete(key);
}
}
})
);
})
);
});

self.addEventListener('message', (event) => {
if (event.data === 'SKIP_WAITING') {
self.skipWaiting();
}
});

self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
if (response) {
return response;
}

return fetch(event.request).then((response) => {
const url = event.request.url;

if (purge || event.request.method !== 'GET' || !verifyUrl(url)) {
return response;
}

// See : <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests>
let responseToCache = response.clone();

caches.open(swconf.cacheName).then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
49 changes: 0 additions & 49 deletions assets/js/data/swcache.js

This file was deleted.

51 changes: 51 additions & 0 deletions assets/js/data/swconf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
layout: compress
permalink: '/:path/swconf.js'
# Note that this file will be fetched by the ServiceWorker, so it will not be cached.
---

const swconf = {
{% if site.pwa.cache.enabled %}
cacheName: 'chirpy-{{ "now" | date: "%s" }}',

{%- comment -%} Resources added to the cache during PWA installation. {%- endcomment -%}
resources: [
'{{ "/assets/css/:THEME.css" | replace: ':THEME', site.theme | relative_url }}',
'{{ "/" | relative_url }}',
{% for tab in site.tabs %}
'{{- tab.url | relative_url -}}',
{% endfor %}

{% assign cache_list = site.static_files | where: 'swcache', true %}
{% for file in cache_list %}
'{{ file.path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
],

{%- comment -%} The request url with below domain will be cached. {%- endcomment -%}
allowHosts: [
{% if site.img_cdn and site.img_cdn contains '//' %}
'{{ site.img_cdn | split: '//' | last | split: '/' | first }}',
{% endif %}

{%- unless site.assets.self_host.enabled -%}
{% for cdn in site.data.origin["cors"].cdns %}
'{{ cdn.url | split: "//" | last }}'
{%- unless forloop.last -%},{%- endunless -%}
{% endfor %}
{% endunless %}
],

{%- comment -%} The request url with below path will not be cached. {%- endcomment -%}
denyPaths: [
{% for path in site.pwa.cache.deny_paths %}
{% unless path == empty %}
'{{ path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%}
{% endunless %}
{% endfor %}
],
purge: false
{% else %}
purge: true
{% endif %}
};
Loading

0 comments on commit 1127c43

Please sign in to comment.