Skip to content

Commit

Permalink
LiveReload always sends all assets
Browse files Browse the repository at this point in the history
A problem with this plugin is that regardless of what has changed, the
same output assets are always emitted. I've taken a stable at filtering
down, but to be honest, I made it a total hack job. For some reason the
webpack plugin API doesn't not make it easy (even after reading docs) to
figure out what files changed and what assets they belong to.

That aside, the goal of this PR is to allow CSS to LiveReload without
doing a full page refresh. It does work. However, it relies on adjusting
the `startTime`
(https://github.com/webpack/docs/wiki/how-to-write-a-plugin#monitoring-the-watch-graph)
by offseting `process.uptime()` at least for the first call, otherwise
the modified timestamps aren't accurate. It also relies on enabling
sourceMaps for css-loader to get access to the css asset children.

I'd love a review and tips on how to improve this, but for now this
should greatly assist with reloading ExtractTextPlugin-based webpack
configs, without requiring the webpack-dev-server.
  • Loading branch information
tbranyen committed Feb 20, 2018
1 parent 1ecdcdb commit 5c2efa9
Showing 1 changed file with 43 additions and 4 deletions.
47 changes: 43 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* jshint node:true */
var statSync = require('fs').statSync;
var lr = require('tiny-lr');
var servers = {};

Expand All @@ -17,6 +18,10 @@ function LiveReloadPlugin(options) {
this.protocol = this.options.protocol ? this.options.protocol + ':' : '';
this.hostname = this.options.hostname || '" + location.hostname + "';
this.server = null;

this.startTime = new Date();
this.startTime.setSeconds(-process.uptime());
this.prevTimestamps = {};
}

function arraysEqual(a1, a2){
Expand Down Expand Up @@ -53,18 +58,52 @@ LiveReloadPlugin.prototype.start = function start(watching, cb) {
};

LiveReloadPlugin.prototype.done = function done(stats) {
var timestamps = stats.compilation ? stats.compilation.fileTimestamps : {};
var assets = stats.compilation.assets;
var include = [];

// If no timestamp information is available, use all assets.
if (!timestamps) {
include.push.apply(include, Object.keys(assets));
}
else {
this.changedFiles = Object.keys(timestamps).filter(function(watchfile) {
return this.startTime < Math.ceil(statSync(watchfile).mtime);
}.bind(this)).forEach(function(changedFile) {
Object.keys(assets).forEach(function(assetName) {
const asset = Object.assign({}, assets[assetName]);
const sources = [];

if (asset.emitted && asset.existsAt.split('.').slice(-1)[0] !== 'css') {
include.push(assetName);
}

(asset.children || []).forEach(function(child) {
if (child && child._sourceMap && child._sourceMap.sources) {
sources.push.apply(sources, child._sourceMap.sources);
}
});

if (sources.includes(changedFile)) {
include.push(assetName);
}
}, this);
}, this);
}

this.startTime = Date.now();

var hash = stats.compilation.hash;
var childHashes = (stats.compilation.children || []).map(child => child.hash);
var files = Object.keys(stats.compilation.assets);
var include = files.filter(function(file) {
var updated = include.filter(function(file) {
return !file.match(this.ignore);
}, this);

if (this.isRunning && (hash !== this.lastHash || !arraysEqual(childHashes, this.lastChildHashes)) && include.length > 0) {
if (this.isRunning && (hash !== this.lastHash || !arraysEqual(childHashes, this.lastChildHashes)) && updated.length > 0) {
this.lastHash = hash;
this.lastChildHashes = childHashes;
setTimeout(function onTimeout() {
this.server.notifyClients(include);
this.server.notifyClients(updated);
}.bind(this), this.delay);
}
};
Expand Down

0 comments on commit 5c2efa9

Please sign in to comment.