diff --git a/client/reload-client.js b/client/reload-client.js index df448bb..1763411 100644 --- a/client/reload-client.js +++ b/client/reload-client.js @@ -97,7 +97,7 @@ class Util { class EleventyReload { constructor() { - this.isConnected = false; + this.connectionMessageShown = false; this.reconnectEventCallback = this.reconnect.bind(this); } @@ -140,8 +140,13 @@ class EleventyReload { if(!this.isConnected) { Util.log(Util.capitalize(data.status)); } - this.isConnected = true; + + this.connectionMessageShown = true; } else { + if(data.status === "disconnected") { + this.addReconnectListeners(); + } + Util.log(Util.capitalize(data.status)); } } else { @@ -154,13 +159,12 @@ class EleventyReload { socket.addEventListener("open", () => { // no reconnection when the connect is already open - this.applyReconnectListeners("remove"); + this.removeReconnectListeners(); }); socket.addEventListener("close", () => { - this.isConnected = false; - this.applyReconnectListeners("remove"); - this.applyReconnectListeners("add"); + this.connectionMessageShown = false; + this.addReconnectListeners(); }); } @@ -238,14 +242,16 @@ class EleventyReload { } } - applyReconnectListeners(mode) { - let method = "addEventListener"; - if (mode === "remove") { - method = "removeEventListener"; - } + addReconnectListeners() { + this.removeReconnectListeners(); + + window.addEventListener("focus", this.reconnectEventCallback); + window.addEventListener("visibilitychange", this.reconnectEventCallback); + } - window[method]("focus", this.reconnectEventCallback); - window[method]("visibilitychange", this.reconnectEventCallback); + removeReconnectListeners() { + window.removeEventListener("focus", this.reconnectEventCallback); + window.removeEventListener("visibilitychange", this.reconnectEventCallback); } } diff --git a/server.js b/server.js index ac6cb21..71d4abe 100644 --- a/server.js +++ b/server.js @@ -14,12 +14,11 @@ const debug = require("debug")("EleventyDevServer"); const wrapResponse = require("./server/wrapResponse.js"); -const serverCache = {}; const DEFAULT_OPTIONS = { port: 8080, - enabled: true, // Enable live reload at all + liveReload: true, // Enable live reload at all showAllHosts: false, // IP address based hosts (other than localhost) - folder: ".11ty", // Change the name of the special folder used for injected scripts + injectedScriptsFolder: ".11ty", // Change the name of the special folder used for injected scripts portReassignmentRetryCount: 10, // number of times to increment the port if in use https: {}, // `key` and `cert`, required for http/2 and https domdiff: true, // Use morphdom to apply DOM diffing delta updates to HTML @@ -41,10 +40,10 @@ class EleventyDevServer { } constructor(name, dir, options = {}) { + debug("Creating new Dev Server instance.") this.name = name; - this.options = Object.assign({}, DEFAULT_OPTIONS, options); - this.options.pathPrefix = this.cleanupPathPrefix(this.options.pathPrefix); - + this.normalizeOptions(options); + this.fileCache = {}; // Directory to serve if(!dir) { @@ -58,8 +57,28 @@ class EleventyDevServer { } } + normalizeOptions(options = {}) { + // better names for options https://github.com/11ty/eleventy-dev-server/issues/41 + if(options.folder) { + this.options.injectedScriptsFolder = options.folder; + delete options.folder; + } + if(options.domdiff) { + this.options.domDiff = options.domdiff; + delete options.domdiff; + } + if(options.enabled) { + this.options.liveReload = options.enabled; + delete options.enabled; + } + + this.options = Object.assign({}, DEFAULT_OPTIONS, options); + this.options.pathPrefix = this.cleanupPathPrefix(this.options.pathPrefix); + } + get watcher() { if(!this._watcher) { + debug("Watching %O", this.options.watch); // TODO if using Eleventy and `watch` option includes output folder (_site) this will trigger two update events! this._watcher = chokidar.watch(this.options.watch, { // TODO allow chokidar configuration extensions (or re-use the ones in Eleventy) @@ -93,7 +112,15 @@ class EleventyDevServer { } watchFiles(files) { - this.watcher.add(files); + if(files && (!Array.isArray(files) || files.length > 0)) { + if(typeof files === "string") { + files = [files]; + } + files = files.map(entry => TemplatePath.stripLeadingDotSlash(entry)); + + debug("Also watching %O", files); + this.watcher.add(files); + } } cleanupPathPrefix(pathPrefix) { @@ -111,7 +138,10 @@ class EleventyDevServer { // Allowed list of files that can be served from outside `dir` setAliases(aliases) { - this.passthroughAliases = aliases; + if(aliases) { + this.passthroughAliases = aliases; + debug( "Setting aliases (emulated passthrough copy) %O", aliases ); + } } matchPassthroughAlias(url) { @@ -307,7 +337,7 @@ class EleventyDevServer { } // This isn’t super necessary because it’s a local file, but it’s included anyway - let script = ``; + let script = ``; if (content.includes("")) { return content.replace("", `${script}`); @@ -361,13 +391,13 @@ class EleventyDevServer { } eleventyDevServerMiddleware(req, res, next) { - if(req.url === `/${this.options.folder}/reload-client.js`) { - if(this.options.enabled) { + if(req.url === `/${this.options.injectedScriptsFolder}/reload-client.js`) { + if(this.options.liveReload) { res.setHeader("Content-Type", mime.getType("js")); return res.end(this._getFileContents("./client/reload-client.js")); } - } else if(req.url === `/${this.options.folder}/morphdom.js`) { - if(this.options.domdiff) { + } else if(req.url === `/${this.options.injectedScriptsFolder}/morphdom.js`) { + if(this.options.domDiff) { res.setHeader("Content-Type", mime.getType("js")); return res.end(this._getFileContents("./node_modules/morphdom/dist/morphdom-esm.js", path.resolve("."))); } @@ -437,7 +467,7 @@ class EleventyDevServer { async onRequestHandler (req, res) { res = wrapResponse(res, content => { - if(this.options.enabled !== false) { + if(this.options.liveReload !== false) { let scriptContents = this._getFileContents("./client/reload-client.js"); let integrityHash = ssri.fromData(scriptContents); @@ -696,7 +726,7 @@ class EleventyDevServer { } let templates = []; - if(useDomDiffingForHtml && this.options.domdiff) { + if(useDomDiffingForHtml && this.options.domDiff) { for(let filePath of files) { if(!filePath.endsWith(".html")) { continue; @@ -721,7 +751,7 @@ class EleventyDevServer { if (build?.templates) { build.templates = build.templates .filter(entry => { - if(!this.options.domdiff) { + if(!this.options.domDiff) { // Don’t include any files if the dom diffing option is disabled return false; }