feature(cloudcmd) add service worker

This commit is contained in:
coderaiser 2018-05-17 16:08:02 +03:00
parent 58c10e201a
commit 234f7bcac0
6 changed files with 164 additions and 2 deletions

View file

@ -1,10 +1,16 @@
{
"presets": [
"env"
["env", {
"exclude": [
"transform-regenerator"
]
}]
],
"plugins": [
"transform-object-assign",
"transform-object-rest-spread"
"transform-object-rest-spread",
"fast-async",
"babel-plugin-macros",
]
}

View file

@ -6,6 +6,12 @@ const {
join,
} = require('path');
const {
EnvironmentPlugin,
} = require('webpack');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');
const dir = './client';
const dirModules = './client/modules';
const modules = './modules';
@ -21,13 +27,33 @@ const devtool = isDev ? 'eval' : 'source-map';
const notEmpty = (a) => a;
const clean = (array) => array.filter(notEmpty);
const babelDev = {
babelrc: false,
plugins: [
'babel-plugin-macros',
]
};
const rules = clean([
!isDev && {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
isDev && {
test: /sw.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: babelDev
}]);
const plugins = [
new EnvironmentPlugin(['NODE_ENV']),
new ServiceWorkerWebpackPlugin({
entry: join(__dirname, '../client', 'sw.js'),
}),
];
const splitChunks = {
chunks: 'all',
name: 'cloudcmd.common',
@ -40,6 +66,7 @@ module.exports = {
},
entry: {
cloudcmd: `${dir}/cloudcmd.js`,
//[sw]: `${dir}/sw.js`,
[modules + '/edit']: `${dirModules}/edit.js`,
[modules + '/edit-file']: `${dirModules}/edit-file.js`,
[modules + '/edit-file-vim']: `${dirModules}/edit-file-vim.js`,
@ -70,6 +97,7 @@ module.exports = {
module: {
rules,
},
plugins,
};
function externals(context, request, fn) {

View file

@ -12,6 +12,8 @@ const join = require('join-io/www/join');
const jonny = require('jonny/legacy');
const currify = require('currify/legacy');
const runtime = require('serviceworker-webpack-plugin/lib/runtime');
const bind = (f, ...a) => () => f(...a);
const noop = () => {};
@ -39,6 +41,8 @@ function CloudCmdProto(Util, DOM) {
console.log(str);
};
serviceWorker();
Emitify.call(this);
const CloudCmd = this;
@ -607,5 +611,18 @@ function CloudCmdProto(Util, DOM) {
});
});
};
function serviceWorker() {
if (!navigator.serviceWorker)
return;
const isHTTPS = location.protocol === 'https:';
const isLocalhost = location.hostname === 'localhost';
if (!isHTTPS && !isLocalhost)
return;
runtime.register();
}
}

89
client/sw.js Normal file
View file

@ -0,0 +1,89 @@
'use strict';
const preval = require('preval.macro');
const tryToCatch = require('try-to-catch/legacy');
const currify = require('currify/legacy');
const wait = currify((f, e) => e.waitUntil(f()));
const respondWith = currify((f, e) => e.respondWith(f(e)));
const getPathName = (url) => new URL(url).pathname;
const date = preval`module.exports = Date()`;
const NAME = `cloudcmd: ${date}`;
const isGet = (a) => a.method === 'GET';
const isBasic = (a) => a.type === 'basic';
const createRequest = (a) => new Request(a, {
credentials: 'same-origin'
});
self.addEventListener('install', wait(onInstall));
self.addEventListener('fetch', respondWith(onFetch));
self.addEventListener('activate', wait(onActivate));
async function onActivate() {
console.info(`cloudcmd: sw: activate: ${NAME}`);
await self.clients.claim();
const keys = await caches.keys();
const deleteCache = caches.delete.bind(caches);
await Promise.all(keys.map(deleteCache));
}
async function onInstall() {
console.info(`cloudcmd: sw: install: ${NAME}`);
await self.skipWaiting();
const cache = await caches.open(NAME);
const urls = [
'/favicon.ico',
];
const requests = urls.map(createRequest);
return cache.addAll(requests);
}
async function onFetch(event) {
const {request} = event;
const url = request.url;
const pathname = getPathName(url);
const cache = await caches.open(NAME);
const response = await cache.match(request);
if (response)
return response;
const [e, resp] = await tryToCatch(fetch, request.clone());
if (e)
return console.error(e);
if (!isGet(request) || !resp.ok || !isBasic(resp))
return resp;
if (/^\/$/.test(pathname))
return resp;
if (/^\/api/.test(pathname))
return resp;
if (/^socket.io/.test(pathname))
return resp;
await addToCache(request, resp.clone());
return resp;
}
async function addToCache(request, response) {
const cache = await caches.open(NAME);
cache.put(request, response);
}

View file

@ -159,6 +159,7 @@
"@cloudcmd/clipboard": "^1.0.0",
"babel-cli": "^6.18.0",
"babel-loader": "^7.0.0",
"babel-plugin-macros": "^2.2.1",
"babel-plugin-transform-object-assign": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-env": "^1.6.0",
@ -171,6 +172,7 @@
"eslint": "^4.0.0",
"eslint-plugin-node": "^6.0.0",
"extract-text-webpack-plugin": "^4.0.0-alpha.0",
"fast-async": "^6.3.7",
"file-loader": "^1.1.4",
"gritty": "^2.2.0",
"gunzip-maybe": "^1.3.1",
@ -188,10 +190,12 @@
"nyc": "^11.0.2",
"philip": "^2.0.0",
"place": "^1.1.4",
"preval.macro": "^1.0.2",
"readjson": "^1.1.3",
"redrun": "^6.0.0",
"request": "^2.76.0",
"rimraf": "^2.5.4",
"serviceworker-webpack-plugin": "1.0.0-alpha02",
"shortdate": "^1.0.1",
"sinon": "^5.0.1",
"sinon-called-with-diff": "^2.0.0",

View file

@ -39,6 +39,7 @@ const defaultHtml = fs.readFileSync(getIndexPath(isDev), 'utf8');
const auth = currify(_auth);
const setUrl = currify(_setUrl);
const setSW = currify(_setSW);
const root = () => config('root');
@ -208,6 +209,7 @@ function cloudcmd(prefix, plugins, modules) {
}),
setUrl(prefix),
setSW(prefix),
logout,
authentication(),
config.middle,
@ -272,6 +274,22 @@ function _setUrl(pref, req, res, next) {
next();
}
function _setSW(pref, req, res, next) {
const prefix = getPrefix(pref);
const is = !req.url.indexOf(prefix);
if (!is)
return next();
const url = replacePrefix(req.url, prefix);
const isSW = /^\/sw\.js(\.map)?$/.test(url);
if (isSW)
req.url = replaceDist(`/dist${url}`);
next();
}
function checkPlugins(plugins) {
if (typeof plugins === 'undefined')
return;