feat: add pwa.cache.*
option to precisely control caching (#1501)
This commit is contained in:
parent
ea3a22e13c
commit
1127c43823
18 changed files with 272 additions and 228 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -18,4 +18,6 @@ package-lock.json
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
|
*.map
|
||||||
|
sw.min.js
|
||||||
assets/js/dist
|
assets/js/dist
|
||||||
|
|
13
_config.yml
13
_config.yml
|
@ -111,7 +111,14 @@ assets:
|
||||||
env: # [development | production]
|
env: # [development | production]
|
||||||
|
|
||||||
pwa:
|
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
|
paginate: 10
|
||||||
|
|
||||||
|
@ -157,10 +164,6 @@ defaults:
|
||||||
values:
|
values:
|
||||||
layout: page
|
layout: page
|
||||||
permalink: /:title/
|
permalink: /:title/
|
||||||
- scope:
|
|
||||||
path: assets/img/favicons
|
|
||||||
values:
|
|
||||||
swcache: true
|
|
||||||
- scope:
|
- scope:
|
||||||
path: assets/js/dist
|
path: assets/js/dist
|
||||||
values:
|
values:
|
||||||
|
|
|
@ -8,6 +8,8 @@ cdns:
|
||||||
- url: https://fonts.googleapis.com
|
- url: https://fonts.googleapis.com
|
||||||
# jsDelivr CDN
|
# jsDelivr CDN
|
||||||
- url: https://cdn.jsdelivr.net
|
- url: https://cdn.jsdelivr.net
|
||||||
|
# polyfill.io for math
|
||||||
|
- url: https://polyfill.io
|
||||||
|
|
||||||
# fonts
|
# fonts
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ favicon_path }}/apple-touch-icon.png">
|
<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="32x32" href="{{ favicon_path }}/favicon-32x32.png">
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ favicon_path }}/favicon-16x16.png">
|
<link rel="icon" type="image/png" sizes="16x16" href="{{ favicon_path }}/favicon-16x16.png">
|
||||||
|
{% if site.pwa.enabled %}
|
||||||
<link rel="manifest" href="{{ favicon_path }}/site.webmanifest">
|
<link rel="manifest" href="{{ favicon_path }}/site.webmanifest">
|
||||||
|
{% endif %}
|
||||||
<link rel="shortcut icon" href="{{ favicon_path }}/favicon.ico">
|
<link rel="shortcut icon" href="{{ favicon_path }}/favicon.ico">
|
||||||
<meta name="apple-mobile-web-app-title" content="{{ site.title }}">
|
<meta name="apple-mobile-web-app-title" content="{{ site.title }}">
|
||||||
<meta name="application-name" content="{{ site.title }}">
|
<meta name="application-name" content="{{ site.title }}">
|
||||||
|
|
|
@ -45,6 +45,15 @@
|
||||||
|
|
||||||
{{ seo_tags }}
|
{{ 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>
|
<title>
|
||||||
{%- unless page.layout == 'home' -%}
|
{%- unless page.layout == 'home' -%}
|
||||||
{{ page.title | append: ' | ' }}
|
{{ page.title | append: ' | ' }}
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
<!-- layout specified -->
|
<!-- layout specified -->
|
||||||
|
|
||||||
|
{% assign js_dist = '/assets/js/dist/' %}
|
||||||
|
|
||||||
{% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %}
|
{% if page.layout == 'post' or page.layout == 'page' or page.layout == 'home' %}
|
||||||
{% assign urls = urls | append: ',' | append: site.data.origin[type]['lazy-polyfill'].js %}
|
{% assign urls = urls | append: ',' | append: site.data.origin[type]['lazy-polyfill'].js %}
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@
|
||||||
{% assign js = 'commons' %}
|
{% assign js = 'commons' %}
|
||||||
{% endcase %}
|
{% 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>
|
<script defer src="{{ script | relative_url }}"></script>
|
||||||
|
|
||||||
{% if page.math %}
|
{% if page.math %}
|
||||||
|
@ -94,9 +96,7 @@
|
||||||
{% if jekyll.environment == 'production' %}
|
{% if jekyll.environment == 'production' %}
|
||||||
<!-- PWA -->
|
<!-- PWA -->
|
||||||
{% if site.pwa.enabled %}
|
{% if site.pwa.enabled %}
|
||||||
<script defer src="{{ '/app.js' | relative_url }}"></script>
|
<script defer src="{{ 'app.min.js' | prepend: js_dist | relative_url }}"></script>
|
||||||
{% else %}
|
|
||||||
<script defer src="{{ '/unregister.js' | relative_url }}"></script>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<!-- GA -->
|
<!-- GA -->
|
||||||
|
|
58
_javascript/pwa/app.js
Normal file
58
_javascript/pwa/app.js
Normal file
|
@ -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
_javascript/pwa/sw.js
Normal file
101
_javascript/pwa/sw.js
Normal file
|
@ -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;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
|
@ -1,49 +0,0 @@
|
||||||
---
|
|
||||||
layout: compress
|
|
||||||
# The list to be cached by PWA
|
|
||||||
---
|
|
||||||
|
|
||||||
const resource = [
|
|
||||||
/* --- CSS --- */
|
|
||||||
'{{ "/assets/css/:THEME.css" | replace: ':THEME', site.theme | relative_url }}',
|
|
||||||
|
|
||||||
/* --- PWA --- */
|
|
||||||
'{{ "/app.js" | relative_url }}',
|
|
||||||
'{{ "/sw.js" | relative_url }}',
|
|
||||||
|
|
||||||
/* --- HTML --- */
|
|
||||||
'{{ "/index.html" | relative_url }}',
|
|
||||||
'{{ "/404.html" | relative_url }}',
|
|
||||||
|
|
||||||
{% for tab in site.tabs %}
|
|
||||||
'{{ tab.url | relative_url }}',
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
/* --- Favicons & compressed JS --- */
|
|
||||||
{% assign cache_list = site.static_files | where: 'swcache', true %}
|
|
||||||
{% for file in cache_list %}
|
|
||||||
'{{ file.path | relative_url }}'{%- unless forloop.last -%},{%- endunless -%}
|
|
||||||
{% endfor %}
|
|
||||||
];
|
|
||||||
|
|
||||||
/* The request url with below domain will be cached */
|
|
||||||
const allowedDomains = [
|
|
||||||
{% if site.google_analytics.id != empty and site.google_analytics.id %}
|
|
||||||
'www.googletagmanager.com',
|
|
||||||
'www.google-analytics.com',
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
'{{ site.url | split: "//" | last }}',
|
|
||||||
|
|
||||||
{% if site.img_cdn contains '//' and site.img_cdn %}
|
|
||||||
'{{ site.img_cdn | split: '//' | last | split: '/' | first }}',
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
'fonts.gstatic.com',
|
|
||||||
'fonts.googleapis.com',
|
|
||||||
'cdn.jsdelivr.net',
|
|
||||||
'polyfill.io'
|
|
||||||
];
|
|
||||||
|
|
||||||
/* Requests that include the following path will be banned */
|
|
||||||
const denyUrls = [];
|
|
51
assets/js/data/swconf.js
Normal file
51
assets/js/data/swconf.js
Normal file
|
@ -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 %}
|
||||||
|
};
|
|
@ -1,47 +0,0 @@
|
||||||
---
|
|
||||||
layout: compress
|
|
||||||
permalink: '/app.js'
|
|
||||||
---
|
|
||||||
|
|
||||||
const $notification = $('#notification');
|
|
||||||
const $btnRefresh = $('#notification .toast-body>button');
|
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
/* Registering Service Worker */
|
|
||||||
navigator.serviceWorker.register('{{ "/sw.js" | relative_url }}')
|
|
||||||
.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.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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,89 +0,0 @@
|
||||||
---
|
|
||||||
layout: compress
|
|
||||||
permalink: '/sw.js'
|
|
||||||
# PWA service worker
|
|
||||||
---
|
|
||||||
|
|
||||||
self.importScripts('{{ "/assets/js/data/swcache.js" | relative_url }}');
|
|
||||||
|
|
||||||
const cacheName = 'chirpy-{{ "now" | date: "%s" }}';
|
|
||||||
|
|
||||||
function verifyDomain(url) {
|
|
||||||
for (const domain of allowedDomains) {
|
|
||||||
const regex = RegExp(`^http(s)?:\/\/${domain}\/`);
|
|
||||||
if (regex.test(url)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isExcluded(url) {
|
|
||||||
for (const item of denyUrls) {
|
|
||||||
if (url === item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.addEventListener('install', (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.open(cacheName).then((cache) => {
|
|
||||||
return cache.addAll(resource);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
self.addEventListener('activate', (event) => {
|
|
||||||
event.waitUntil(
|
|
||||||
caches.keys().then((keyList) => {
|
|
||||||
return Promise.all(
|
|
||||||
keyList.map((key) => {
|
|
||||||
if (key !== 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 (
|
|
||||||
event.request.method !== 'GET' ||
|
|
||||||
!verifyDomain(url) ||
|
|
||||||
isExcluded(url)
|
|
||||||
) {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* see: <https://developers.google.com/web/fundamentals/primers/service-workers#cache_and_return_requests> */
|
|
||||||
let responseToCache = response.clone();
|
|
||||||
|
|
||||||
caches.open(cacheName).then((cache) => {
|
|
||||||
/* console.log('[sw] Caching new resource: ' + event.request.url); */
|
|
||||||
cache.put(event.request, responseToCache);
|
|
||||||
});
|
|
||||||
|
|
||||||
return response;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
layout: compress
|
|
||||||
permalink: '/unregister.js'
|
|
||||||
---
|
|
||||||
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.getRegistrations().then((registrations) => {
|
|
||||||
for (let reg of registrations) {
|
|
||||||
reg.unregister();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
|
||||||
spec.license = "MIT"
|
spec.license = "MIT"
|
||||||
|
|
||||||
spec.files = `git ls-files -z`.split("\x0").select { |f|
|
spec.files = `git ls-files -z`.split("\x0").select { |f|
|
||||||
f.match(%r!^((_(includes|layouts|sass|(data\/(locales|origin)))|assets)\/|README|LICENSE)!i)
|
f.match(%r!^((_(includes|layouts|sass|(data\/(locales|origin)))|assets)\/|sw|README|LICENSE)!i)
|
||||||
}
|
}
|
||||||
|
|
||||||
spec.metadata = {
|
spec.metadata = {
|
||||||
|
|
|
@ -13,9 +13,9 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
|
"homepage": "https://github.com/cotes2020/jekyll-theme-chirpy/",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "npx rimraf assets/js/dist",
|
"prebuild": "npx rimraf assets/js/dist sw.min.js*",
|
||||||
"build": "NODE_ENV=production npx rollup -c --bundleConfigAsCjs",
|
"build": "NODE_ENV=production npx rollup -c --bundleConfigAsCjs",
|
||||||
"prewatch": "npx rimraf assets/js/dist",
|
"prewatch": "npx rimraf assets/js/dist sw.min.js*",
|
||||||
"watch": "npx rollup -c --bundleConfigAsCjs -w",
|
"watch": "npx rollup -c --bundleConfigAsCjs -w",
|
||||||
"test": "npx stylelint _sass/**/*.scss",
|
"test": "npx stylelint _sass/**/*.scss",
|
||||||
"fixlint": "npm run test -- --fix"
|
"fixlint": "npm run test -- --fix"
|
||||||
|
|
|
@ -3,21 +3,29 @@ import terser from '@rollup/plugin-terser';
|
||||||
import license from 'rollup-plugin-license';
|
import license from 'rollup-plugin-license';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
const JS_SRC = '_javascript';
|
const SRC_DEFAULT = '_javascript';
|
||||||
const JS_DIST = 'assets/js/dist';
|
const DIST_DEFAULT = 'assets/js/dist';
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
function build(filename) {
|
function build(filename, opts) {
|
||||||
|
let src = SRC_DEFAULT;
|
||||||
|
let dist = DIST_DEFAULT;
|
||||||
|
|
||||||
|
if (typeof opts !== 'undefined') {
|
||||||
|
src = opts.src || src;
|
||||||
|
dist = opts.dist || dist;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
input: [`${JS_SRC}/${filename}.js`],
|
input: [`${src}/${filename}.js`],
|
||||||
output: {
|
output: {
|
||||||
file: `${JS_DIST}/${filename}.min.js`,
|
file: `${dist}/${filename}.min.js`,
|
||||||
format: 'iife',
|
format: 'iife',
|
||||||
name: 'Chirpy',
|
name: 'Chirpy',
|
||||||
sourcemap: !isProd
|
sourcemap: !isProd
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
include: `${JS_SRC}/**`
|
include: `${src}/**`
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
babel({
|
babel({
|
||||||
|
@ -28,7 +36,7 @@ function build(filename) {
|
||||||
license({
|
license({
|
||||||
banner: {
|
banner: {
|
||||||
commentStyle: 'ignored',
|
commentStyle: 'ignored',
|
||||||
content: { file: path.join(__dirname, JS_SRC, '_copyright') }
|
content: { file: path.join(__dirname, SRC_DEFAULT, '_copyright') }
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
isProd && terser()
|
isProd && terser()
|
||||||
|
@ -42,5 +50,7 @@ export default [
|
||||||
build('categories'),
|
build('categories'),
|
||||||
build('page'),
|
build('page'),
|
||||||
build('post'),
|
build('post'),
|
||||||
build('misc')
|
build('misc'),
|
||||||
|
build('app', { src: `${SRC_DEFAULT}/pwa` }),
|
||||||
|
build('sw', { src: `${SRC_DEFAULT}/pwa`, dist: '.' })
|
||||||
];
|
];
|
||||||
|
|
|
@ -103,7 +103,7 @@ init_files() {
|
||||||
npm i && npm run build
|
npm i && npm run build
|
||||||
|
|
||||||
# track the js output
|
# track the js output
|
||||||
_sedi "/^assets.*\/dist/d" .gitignore
|
_sedi "/^assets.*\/dist/d;/^sw.*\.js/d" .gitignore
|
||||||
}
|
}
|
||||||
|
|
||||||
commit() {
|
commit() {
|
||||||
|
|
|
@ -27,6 +27,7 @@ NODE_CONFIG="package.json"
|
||||||
CHANGE_LOG="docs/CHANGELOG.md"
|
CHANGE_LOG="docs/CHANGELOG.md"
|
||||||
|
|
||||||
JS_DIST="assets/js/dist"
|
JS_DIST="assets/js/dist"
|
||||||
|
PWA_SW="sw.min.js"
|
||||||
BACKUP_PATH="$(mktemp -d)"
|
BACKUP_PATH="$(mktemp -d)"
|
||||||
|
|
||||||
FILES=(
|
FILES=(
|
||||||
|
@ -70,12 +71,14 @@ _check_git() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
$opt_pre || (
|
||||||
if [[ $working_branch != "$DEFAULT_BRANCH" &&
|
if [[ $working_branch != "$DEFAULT_BRANCH" &&
|
||||||
$working_branch != hotfix/* &&
|
$working_branch != hotfix/* &&
|
||||||
$working_branch != "$PROD_BRANCH" ]]; then
|
$working_branch != "$PROD_BRANCH" ]]; then
|
||||||
echo "> Abort: Please run on the default, release or patch branch."
|
echo "> Abort: Please run on the default, release or patch branch."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_check_src() {
|
_check_src() {
|
||||||
|
@ -156,7 +159,7 @@ build_gem() {
|
||||||
rm -f ./*.gem
|
rm -f ./*.gem
|
||||||
|
|
||||||
npm run build
|
npm run build
|
||||||
git add "$JS_DIST" -f # add JS dist to gem
|
git add "$JS_DIST" "$PWA_SW" -f # add JS distribution files to gem
|
||||||
gem build "$GEM_SPEC"
|
gem build "$GEM_SPEC"
|
||||||
cp "$JS_DIST"/* "$BACKUP_PATH"
|
cp "$JS_DIST"/* "$BACKUP_PATH"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue