Skip to content
This repository has been archived by the owner on Nov 27, 2018. It is now read-only.

[WIP] Offliner integration #96

Closed
wants to merge 9 commits into from
5 changes: 4 additions & 1 deletion lib/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var rename = require('gulp-rename');
var promptly = require('promisified-promptly');
var gitconfiglocal = require('gitconfiglocal');
var through2 = require('through2');
var gutil = require('gulp-util');

function gitUrl(dir) {
return new Promise(function (resolve, reject) {
Expand Down Expand Up @@ -124,7 +125,9 @@ module.exports = function(config) {
})
.then(function(templateConfig) {
return new Promise(function(resolve, reject) {
var stream = gulp.src([__dirname + '/../templates/**'])
var contents = __dirname + '/../templates/**';
var workerTemplate = __dirname + '/../templates/app/offline-worker.js';
var stream = gulp.src([contents, '!' + workerTemplate])
.pipe(rename(function (path) {
// NPM can't include a .gitignore file so we have to rename it.
if (path.basename === 'gitignore') {
Expand Down
72 changes: 47 additions & 25 deletions lib/offline.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
var promisify = require('promisify-node');

var fs = require('fs');
var conflict = require('gulp-conflict');
var path = require('path');
var swPrecache = require('sw-precache');
var gutil = require('gulp-util');
var ghslug = promisify(require('github-slug'));
var glob = require('glob');
var template = require('gulp-template');
var crypto = require('crypto');
var gulp = require('gulp');
var ghslug = promisify(require('github-slug'));

module.exports = promisify(function(config, callback) {
var rootDir = config.rootDir || './';
Expand All @@ -40,7 +42,7 @@ module.exports = promisify(function(config, callback) {

var fileGlobs = config.fileGlobs || ['**/*'];

// Remove the existing service worker, if any, so sw-precache doesn't include
// Remove the existing service worker, if any, so offliner doesn't include
// it in the list of files to cache.
try {
fs.unlinkSync(path.join(rootDir, 'offline-worker.js'));
Expand All @@ -59,29 +61,49 @@ module.exports = promisify(function(config, callback) {
return '';
}
}).then(function(cacheId) {
var staticFileGlobs = fileGlobs.map(function(v) {
return path.join(rootDir, v)
});
var absoluteGlobs =
fileGlobs.map(function (v) { return path.join(rootDir, v); });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that there's no line width restriction for this codebase. Statements like this are welcome to live on one line and exceed 80 columns:

    var absoluteGlobs = fileGlobs.map(function (v) { return path.join(rootDir, v); });

(That said, it's also ok to wrap lines; do whatever feels most readable!)

var files = flatGlobs(absoluteGlobs);
var filesAndHashes = getFilesAndHashes(files, rootDir);
var replacements = { resources: filesAndHashes, name: cacheId };

var stream = gulp.src([__dirname + '/../templates/app/offline-worker.js'])
.pipe(template(replacements))
.pipe(conflict(rootDir))
.pipe(gulp.dest(rootDir));

staticFileGlobs.forEach(function(globPattern) {
glob.sync(globPattern.replace(path.sep, '/')).forEach(function(file) {
var stat = fs.statSync(file);
stream.on('finish', function () { callback(); });
stream.on('error', function (e) { callback(e); });
});

if (stat.isFile() && stat.size > 2 * 1024 * 1024 && staticFileGlobs.indexOf(file) === -1) {
gutil.log(gutil.colors.red.bold(file + ' is bigger than 2 MiB. Are you sure you want to cache it? To suppress this warning, explicitly include the file in the fileGlobs list.'));
}
function flatGlobs(fileGlobs) {
return Object.keys(fileGlobs.reduce(function (matchings, fileGlob) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: matchings -> matches

fileGlob = fileGlob.replace(path.sep, '/');
glob.sync(fileGlob, { nodir: true }).forEach(function (m) {
matchings[m] = m;
});
});
return matchings;
}, {}));
}

function getFilesAndHashes(files, stripPrefix) {
return files
.map(function (filepath) {
var data = fs.readFileSync(filepath);
var hash = getHash(data);
return {
filepath: filepath.replace(stripPrefix, function (match, offset) {
return offset === 0 ? '' : match;
}),
hash: hash
};
});
}

function getHash(data) {
var sha1 = crypto.createHash('sha1');
sha1.update(data);
return sha1.digest('hex');
}

swPrecache.write(path.join(rootDir, 'offline-worker.js'), {
staticFileGlobs: staticFileGlobs,
stripPrefix: rootDir,
verbose: true,
logger: gutil.log,
importScripts: config.importScripts || [],
cacheId: cacheId,
ignoreUrlParametersMatching: config.ignoreUrlParametersMatching || [/./],
maximumFileSizeToCacheInBytes: Infinity,
}, callback);
});
});
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"promisify-node": "^0.2.1",
"read-yaml": "^1.0.0",
"rimraf": "^2.4.3",
"sw-precache": "^2.0.0",
"temp": "^0.8.3",
"through2": "^2.0.0",
"travis-ci": "^2.0.3",
Expand All @@ -60,7 +59,6 @@
},
"devDependencies": {
"chai": "^3.3.0",
"glob": "^5.0.15",
"gulp-istanbul": "^0.10.1",
"gulp-mocha": "^2.1.3",
"mocha": "^2.3.3",
Expand Down
2 changes: 0 additions & 2 deletions templates/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
<meta name="msapplication-square310x310logo" content="images/mstile-310x310.png" />

<link rel="stylesheet" type="text/css" media="screen" href="styles/stylesheet.css">

<script src="scripts/main.js"></script>
<script src="scripts/offline-manager.js"></script>
</head>

Expand Down
19 changes: 19 additions & 0 deletions templates/app/offline-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
importScripts('./scripts/offliner/offliner.js');
importScripts('./scripts/offliner/middlewares.js');

var offliner = new off.Offliner("<%= name %>");
offliner.prefetch.use(off.fetchers.urls).resources([
<% resources.forEach(function (resource) {
%> '<%= resource.filepath %>', /* <%= resource.hash %> */
<% }); %>
]);

offliner.fetch
.use(off.sources.cache)
.use(off.sources.network)
.orFail();

offliner.update
.use(off.updaters.reinstall);

offliner.standalone();
57 changes: 30 additions & 27 deletions templates/app/scripts/offline-manager.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,32 @@
function updateFound() {
var installingWorker = this.installing;
(function (global) {
'use strict';

// Wait for the new service worker to be installed before prompting to update.
installingWorker.addEventListener('statechange', function() {
switch (installingWorker.state) {
case 'installed':
// Only show the prompt if there is currently a controller so it is not
// shown on first load.
if (navigator.serviceWorker.controller &&
window.confirm('An updated version of this page is available, would you like to update?')) {
window.location.reload();
return;
}
break;

case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
});
}
if ('serviceWorker' in navigator) {
var script = document.createElement('SCRIPT');
script.src = 'scripts/offliner/offliner-client.js';
script.dataset.worker = 'offline-worker.js';
script.onload = function () {
var off = global.off.restore();
var isActivationDelayed = false;

if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('offline-worker.js').then(function(registration) {
console.log('offline worker registered');
registration.addEventListener('updatefound', updateFound);
});
}
off.on('activationPending', function () {
if (confirm('An updated version of this page is available, would you like to update?')) {
off.activate().then(function () { window.location.reload(); });
}
else if (!isActivationDelayed) {
global.addEventListener('beforeunload', function () {
off.activate();
});
isActivationDelayed = true;
}
});
off.install().then(function () {
console.log('offline worker registered');
});
};
document.addEventListener('DOMContentLoaded', function onBody() {
document.removeEventListener('DOMContentLoaded', onBody);
document.body.appendChild(script);
});
}
}(this));
46 changes: 46 additions & 0 deletions templates/app/scripts/offliner/middlewares.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

self.off.sources.cache = function (request, activeCache) {
return activeCache.match(request).then(function (response) {
return response ? Promise.resolve(response) : Promise.reject();
});
};
self.off.sources.network = function (request) {
return fetch(request);
};

self.off.fetchers.urls = {

type: 'url',

normalize: function (resource) {
return { type: this.type, url: resource };
},

prefetch: function (resources, cache) {
return Promise.all(resources.map(function (resource) {
var bustedUrl = resource.url + '?__b=' + Date.now();
var request = new Request(bustedUrl, { mode: 'no-cors' });
return fetch(request).then(function (response) {
var url = new URL(request.url, location);
if (url.pathname.match(/\/index\.html?$/)) {
cache.put('/', response.clone());
}
cache.put(resource.url, response);
});
}));
}
};

self.off.updaters.reinstall = {
check: function () {
return Promise.resolve('v' + Date().toString());
},

isNewVersion: function () {
return this.flags.isCalledFromInstall;
},

evolve: function (previousCache, newCache, reinstall) {
return reinstall();
}
};
Loading