From f70a68376bf5f6cb23e61f3df4296d9a2119c29d Mon Sep 17 00:00:00 2001 From: Michael Orenstein Date: Tue, 9 Oct 2018 16:26:43 +1000 Subject: [PATCH] feat: Leading and trailing edge debounce for the file watch feat: Hash the files and don't re-run the compilation if they're the same. feat: Ignore initial file watch events. feat: Different hash feat: Removed timer --- lib/dev-middleware.js | 75 ++++++++++++++++++++++++++++++++----------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/lib/dev-middleware.js b/lib/dev-middleware.js index 2a39a8f..043a7ef 100644 --- a/lib/dev-middleware.js +++ b/lib/dev-middleware.js @@ -3,6 +3,7 @@ let chokidar = require('chokidar'); let expressws = require('express-ws'); let fs = require('fs'); let url = require('url'); +let crypto = require('crypto'); const MIME_TYPES = { 'mjs': 'application/javascript', @@ -13,6 +14,7 @@ const MIME_TYPES = { module.exports = function (app, config, options) { expressws(app); let files = {}; + const fileHashes = {}; let sockets = []; if (options.hot) { @@ -105,28 +107,65 @@ module.exports = function (app, config, options) { console.log('\x1b[32m%s\x1b[0m', `Compiled in ${response.stats.time}ms.`); } - async function compiler () { - let bundle = await nollup(config); - let watcher = chokidar.watch(options.watch); - let watcherTimeout; + function debounceLeadingAndTrailing(func, wait) { + let timeout; + let needsCalling = false; + + return function executedFunction() { + let context = this; + let args = arguments; + + let later = function() { + timeout = null; + if (needsCalling) { + func.apply(context, args) + needsCalling = false; + }; + }; + + let callNow = !timeout; + + clearTimeout(timeout); + + timeout = setTimeout(later, wait); + + if (callNow) { + func.apply(context, args) + } else { + needsCalling = true; + } + }; + }; - const onChange = async (path) => { - if (fs.lstatSync(path).isFile()) { - files = {}; - bundle.invalidate(path); + const onChange = async (path, bundle) => { + if (fs.lstatSync(path).isFile()) { + // read out the file and generate a hash + const file = fs.readFileSync(path); + const hash = crypto.createHash('md5').update(file).digest("hex"); + + // if the hash is the same as it was last time, bail and don't recompile + if (fileHashes[path] === hash) { + return + } - if (watcherTimeout) { - clearTimeout(watcherTimeout); - } + // set the hash so that next time we can bail early + fileHashes[path] = hash; + + files = {}; + bundle.invalidate(path); - watcherTimeout = setTimeout(async () => { - handleGeneratedBundle(await bundle.generate()); - }, 100); - } - }; + handleGeneratedBundle(await bundle.generate()); + } + }; + + async function compiler () { + let bundle = await nollup(config); + let watcher = chokidar.watch(options.watch, { ignoreInitial: true }); + + const debouncedOnChange = debounceLeadingAndTrailing(path => onChange(path, bundle), 300); - watcher.on('add', onChange); - watcher.on('change', onChange); + watcher.on('add', debouncedOnChange); + watcher.on('change', debouncedOnChange); handleGeneratedBundle(await bundle.generate()); };