Skip to content

Commit

Permalink
Fix a bunch of issues with dev server reloads and reconnection. Fixes #…
Browse files Browse the repository at this point in the history
…41 but maintains aliases for old names
  • Loading branch information
zachleat committed Jan 13, 2023
1 parent bad59f5 commit ca8c8e9
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 29 deletions.
32 changes: 19 additions & 13 deletions client/reload-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class Util {

class EleventyReload {
constructor() {
this.isConnected = false;
this.connectionMessageShown = false;
this.reconnectEventCallback = this.reconnect.bind(this);
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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();
});
}

Expand Down Expand Up @@ -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);
}
}

Expand Down
62 changes: 46 additions & 16 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -307,7 +337,7 @@ class EleventyDevServer {
}

// This isn’t super necessary because it’s a local file, but it’s included anyway
let script = `<script type="module" integrity="${integrityHash}"${inlineContents ? `>${scriptContents}` : ` src="/${this.options.folder}/reload-client.js">`}</script>`;
let script = `<script type="module" integrity="${integrityHash}"${inlineContents ? `>${scriptContents}` : ` src="/${this.options.injectedScriptsFolder}/reload-client.js">`}</script>`;

if (content.includes("</head>")) {
return content.replace("</head>", `${script}</head>`);
Expand Down Expand Up @@ -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(".")));
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down

0 comments on commit ca8c8e9

Please sign in to comment.