HMR maximum call stack errors + other issues

Bug report

What is the current behavior?

I have a reproduction repository here. I described the behavior in a twitter thread here. Copying that to here:

I get a full page reload on changes. Here’s the console output:

hot-reload

I’m fairly confident that this is a webpack bug. Those hotAddUpdateChunk and webpackHotUpdateCallback functions are in webpack/bootstrap. Here’s what it looks like for me: https://gist.github.com/kentcdodds/8542e0c27a2cc0cca5ab8dc24c874dfc

click to expand
 	function hotDisposeChunk(chunkId) {
 		delete installedChunks[chunkId];
 	}
 	var parentHotUpdateCallback = window["webpackHotUpdate"];
 	window["webpackHotUpdate"] =   // eslint-disable-next-line no-unused-vars
 	  function webpackHotUpdateCallback(chunkId, moreModules) {
 	    hotAddUpdateChunk(chunkId, moreModules)
 	    if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules)
 	  } ;

 	  // eslint-disable-next-line no-unused-vars
 	  function hotDownloadUpdateChunk(chunkId) {
 	    var head = document.getElementsByTagName('head')[0]
 	    var script = document.createElement('script')
 	    script.charset = 'utf-8'
 	    script.src = __webpack_require__.p + "" + chunkId + "." + hotCurrentHash + ".hot-update.js"
 	    
 	    head.appendChild(script)
 	  }

 	  // eslint-disable-next-line no-unused-vars
 	  function hotDownloadManifest(requestTimeout) {
 	    requestTimeout = requestTimeout || 10000
 	    return new Promise(function(resolve, reject) {
 	      if (typeof XMLHttpRequest === 'undefined')
 	        return reject(new Error('No browser support'))
 	      try {
 	        var request = new XMLHttpRequest()
 	        var requestPath = __webpack_require__.p + "" + hotCurrentHash + ".hot-update.json"
 	        request.open('GET', requestPath, true)
 	        request.timeout = requestTimeout
 	        request.send(null)
 	      } catch (err) {
 	        return reject(err)
 	      }
 	      request.onreadystatechange = function() {
 	        if (request.readyState !== 4) return
 	        if (request.status === 0) {
 	          // timeout
 	          reject(
 	            new Error('Manifest request to ' + requestPath + ' timed out.')
 	          )
 	        } else if (request.status === 404) {
 	          // no update available
 	          resolve()
 	        } else if (request.status !== 200 && request.status !== 304) {
 	          // other failure
 	          reject(new Error('Manifest request to ' + requestPath + ' failed.'))
 	        } else {
 	          // success
 	          try {
 	            var update = JSON.parse(request.responseText)
 	          } catch (e) {
 	            reject(e)
 	            return
 	          }
 	          resolve(update)
 	        }
 	      }
 	    })
 	  }

 	var hotApplyOnUpdate = true;
 	var hotCurrentHash = "7d4d1bb64e50c4217c9c"; // eslint-disable-line no-unused-vars
 	var hotRequestTimeout = 10000;
 	var hotCurrentModuleData = {};
 	var hotCurrentChildModule; // eslint-disable-line no-unused-vars
 	var hotCurrentParents = []; // eslint-disable-line no-unused-vars
 	var hotCurrentParentsTemp = []; // eslint-disable-line no-unused-vars

 	// eslint-disable-next-line no-unused-vars
 	function hotCreateRequire(moduleId) {
 		var me = installedModules[moduleId];
 		if (!me) return __webpack_require__;
 		var fn = function(request) {
 			if (me.hot.active) {
 				if (installedModules[request]) {
 					if (installedModules[request].parents.indexOf(moduleId) === -1)
 						installedModules[request].parents.push(moduleId);
 				} else {
 					hotCurrentParents = [moduleId];
 					hotCurrentChildModule = request;
 				}
 				if (me.children.indexOf(request) === -1) me.children.push(request);
 			} else {
 				console.warn(
 					"[HMR] unexpected require(" +
 						request +
 						") from disposed module " +
 						moduleId
 				);
 				hotCurrentParents = [];
 			}
 			return __webpack_require__(request);
 		};
 		var ObjectFactory = function ObjectFactory(name) {
 			return {
 				configurable: true,
 				enumerable: true,
 				get: function() {
 					return __webpack_require__[name];
 				},
 				set: function(value) {
 					__webpack_require__[name] = value;
 				}
 			};
 		};
 		for (var name in __webpack_require__) {
 			if (
 				Object.prototype.hasOwnProperty.call(__webpack_require__, name) &&
 				name !== "e"
 			) {
 				Object.defineProperty(fn, name, ObjectFactory(name));
 			}
 		}
 		fn.e = function(chunkId) {
 			if (hotStatus === "ready") hotSetStatus("prepare");
 			hotChunksLoading++;
 			return __webpack_require__.e(chunkId).then(finishChunkLoading, function(err) {
 				finishChunkLoading();
 				throw err;
 			});

 			function finishChunkLoading() {
 				hotChunksLoading--;
 				if (hotStatus === "prepare") {
 					if (!hotWaitingFilesMap[chunkId]) {
 						hotEnsureUpdateChunk(chunkId);
 					}
 					if (hotChunksLoading === 0 && hotWaitingFiles === 0) {
 						hotUpdateDownloaded();
 					}
 				}
 			}
 		};
 		return fn;
 	}

 	// eslint-disable-next-line no-unused-vars
 	function hotCreateModule(moduleId) {
 		var hot = {
 			// private stuff
 			_acceptedDependencies: {},
 			_declinedDependencies: {},
 			_selfAccepted: false,
 			_selfDeclined: false,
 			_disposeHandlers: [],
 			_main: hotCurrentChildModule !== moduleId,

 			// Module API
 			active: true,
 			accept: function(dep, callback) {
 				if (typeof dep === "undefined") hot._selfAccepted = true;
 				else if (typeof dep === "function") hot._selfAccepted = dep;
 				else if (typeof dep === "object")
 					for (var i = 0; i < dep.length; i++)
 						hot._acceptedDependencies[dep[i]] = callback || function() {};
 				else hot._acceptedDependencies[dep] = callback || function() {};
 			},
 			decline: function(dep) {
 				if (typeof dep === "undefined") hot._selfDeclined = true;
 				else if (typeof dep === "object")
 					for (var i = 0; i < dep.length; i++)
 						hot._declinedDependencies[dep[i]] = true;
 				else hot._declinedDependencies[dep] = true;
 			},
 			dispose: function(callback) {
 				hot._disposeHandlers.push(callback);
 			},
 			addDisposeHandler: function(callback) {
 				hot._disposeHandlers.push(callback);
 			},
 			removeDisposeHandler: function(callback) {
 				var idx = hot._disposeHandlers.indexOf(callback);
 				if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
 			},

 			// Management API
 			check: hotCheck,
 			apply: hotApply,
 			status: function(l) {
 				if (!l) return hotStatus;
 				hotStatusHandlers.push(l);
 			},
 			addStatusHandler: function(l) {
 				hotStatusHandlers.push(l);
 			},
 			removeStatusHandler: function(l) {
 				var idx = hotStatusHandlers.indexOf(l);
 				if (idx >= 0) hotStatusHandlers.splice(idx, 1);
 			},

 			//inherit from previous dispose call
 			data: hotCurrentModuleData[moduleId]
 		};
 		hotCurrentChildModule = undefined;
 		return hot;
 	}

 	var hotStatusHandlers = [];
 	var hotStatus = "idle";

 	function hotSetStatus(newStatus) {
 		hotStatus = newStatus;
 		for (var i = 0; i < hotStatusHandlers.length; i++)
 			hotStatusHandlers[i].call(null, newStatus);
 	}

 	// while downloading
 	var hotWaitingFiles = 0;
 	var hotChunksLoading = 0;
 	var hotWaitingFilesMap = {};
 	var hotRequestedFilesMap = {};
 	var hotAvailableFilesMap = {};
 	var hotDeferred;

 	// The update info
 	var hotUpdate, hotUpdateNewHash;

 	function toModuleId(id) {
 		var isNumber = +id + "" === id;
 		return isNumber ? +id : id;
 	}

 	function hotCheck(apply) {
 		if (hotStatus !== "idle")
 			throw new Error("check() is only allowed in idle status");
 		hotApplyOnUpdate = apply;
 		hotSetStatus("check");
 		return hotDownloadManifest(hotRequestTimeout).then(function(update) {
 			if (!update) {
 				hotSetStatus("idle");
 				return null;
 			}
 			hotRequestedFilesMap = {};
 			hotWaitingFilesMap = {};
 			hotAvailableFilesMap = update.c;
 			hotUpdateNewHash = update.h;

 			hotSetStatus("prepare");
 			var promise = new Promise(function(resolve, reject) {
 				hotDeferred = {
 					resolve: resolve,
 					reject: reject
 				};
 			});
 			hotUpdate = {};
 			var chunkId = "main";
 			{
 				// eslint-disable-line no-lone-blocks
 				/*globals chunkId */
 				hotEnsureUpdateChunk(chunkId);
 			}
 			if (
 				hotStatus === "prepare" &&
 				hotChunksLoading === 0 &&
 				hotWaitingFiles === 0
 			) {
 				hotUpdateDownloaded();
 			}
 			return promise;
 		});
 	}

 	// eslint-disable-next-line no-unused-vars
 	function hotAddUpdateChunk(chunkId, moreModules) {
 		if (!hotAvailableFilesMap[chunkId] || !hotRequestedFilesMap[chunkId])
 			return;
 		hotRequestedFilesMap[chunkId] = false;
 		for (var moduleId in moreModules) {
 			if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 				hotUpdate[moduleId] = moreModules[moduleId];
 			}
 		}
 		if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
 			hotUpdateDownloaded();
 		}
 	}

 	function hotEnsureUpdateChunk(chunkId) {
 		if (!hotAvailableFilesMap[chunkId]) {
 			hotWaitingFilesMap[chunkId] = true;
 		} else {
 			hotRequestedFilesMap[chunkId] = true;
 			hotWaitingFiles++;
 			hotDownloadUpdateChunk(chunkId);
 		}
 	}

 	function hotUpdateDownloaded() {
 		hotSetStatus("ready");
 		var deferred = hotDeferred;
 		hotDeferred = null;
 		if (!deferred) return;
 		if (hotApplyOnUpdate) {
 			// Wrap deferred object in Promise to mark it as a well-handled Promise to
 			// avoid triggering uncaught exception warning in Chrome.
 			// See https://bugs.chromium.org/p/chromium/issues/detail?id=465666
 			Promise.resolve()
 				.then(function() {
 					return hotApply(hotApplyOnUpdate);
 				})
 				.then(
 					function(result) {
 						deferred.resolve(result);
 					},
 					function(err) {
 						deferred.reject(err);
 					}
 				);
 		} else {
 			var outdatedModules = [];
 			for (var id in hotUpdate) {
 				if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
 					outdatedModules.push(toModuleId(id));
 				}
 			}
 			deferred.resolve(outdatedModules);
 		}
 	}

 	function hotApply(options) {
 		if (hotStatus !== "ready")
 			throw new Error("apply() is only allowed in ready status");
 		options = options || {};

 		var cb;
 		var i;
 		var j;
 		var module;
 		var moduleId;

 		function getAffectedStuff(updateModuleId) {
 			var outdatedModules = [updateModuleId];
 			var outdatedDependencies = {};

 			var queue = outdatedModules.slice().map(function(id) {
 				return {
 					chain: [id],
 					id: id
 				};
 			});
 			while (queue.length > 0) {
 				var queueItem = queue.pop();
 				var moduleId = queueItem.id;
 				var chain = queueItem.chain;
 				module = installedModules[moduleId];
 				if (!module || module.hot._selfAccepted) continue;
 				if (module.hot._selfDeclined) {
 					return {
 						type: "self-declined",
 						chain: chain,
 						moduleId: moduleId
 					};
 				}
 				if (module.hot._main) {
 					return {
 						type: "unaccepted",
 						chain: chain,
 						moduleId: moduleId
 					};
 				}
 				for (var i = 0; i < module.parents.length; i++) {
 					var parentId = module.parents[i];
 					var parent = installedModules[parentId];
 					if (!parent) continue;
 					if (parent.hot._declinedDependencies[moduleId]) {
 						return {
 							type: "declined",
 							chain: chain.concat([parentId]),
 							moduleId: moduleId,
 							parentId: parentId
 						};
 					}
 					if (outdatedModules.indexOf(parentId) !== -1) continue;
 					if (parent.hot._acceptedDependencies[moduleId]) {
 						if (!outdatedDependencies[parentId])
 							outdatedDependencies[parentId] = [];
 						addAllToSet(outdatedDependencies[parentId], [moduleId]);
 						continue;
 					}
 					delete outdatedDependencies[parentId];
 					outdatedModules.push(parentId);
 					queue.push({
 						chain: chain.concat([parentId]),
 						id: parentId
 					});
 				}
 			}

 			return {
 				type: "accepted",
 				moduleId: updateModuleId,
 				outdatedModules: outdatedModules,
 				outdatedDependencies: outdatedDependencies
 			};
 		}

 		function addAllToSet(a, b) {
 			for (var i = 0; i < b.length; i++) {
 				var item = b[i];
 				if (a.indexOf(item) === -1) a.push(item);
 			}
 		}

 		// at begin all updates modules are outdated
 		// the "outdated" status can propagate to parents if they don't accept the children
 		var outdatedDependencies = {};
 		var outdatedModules = [];
 		var appliedUpdate = {};

 		var warnUnexpectedRequire = function warnUnexpectedRequire() {
 			console.warn(
 				"[HMR] unexpected require(" + result.moduleId + ") to disposed module"
 			);
 		};

 		for (var id in hotUpdate) {
 			if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
 				moduleId = toModuleId(id);
 				/** @type {TODO} */
 				var result;
 				if (hotUpdate[id]) {
 					result = getAffectedStuff(moduleId);
 				} else {
 					result = {
 						type: "disposed",
 						moduleId: id
 					};
 				}
 				/** @type {Error|false} */
 				var abortError = false;
 				var doApply = false;
 				var doDispose = false;
 				var chainInfo = "";
 				if (result.chain) {
 					chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
 				}
 				switch (result.type) {
 					case "self-declined":
 						if (options.onDeclined) options.onDeclined(result);
 						if (!options.ignoreDeclined)
 							abortError = new Error(
 								"Aborted because of self decline: " +
 									result.moduleId +
 									chainInfo
 							);
 						break;
 					case "declined":
 						if (options.onDeclined) options.onDeclined(result);
 						if (!options.ignoreDeclined)
 							abortError = new Error(
 								"Aborted because of declined dependency: " +
 									result.moduleId +
 									" in " +
 									result.parentId +
 									chainInfo
 							);
 						break;
 					case "unaccepted":
 						if (options.onUnaccepted) options.onUnaccepted(result);
 						if (!options.ignoreUnaccepted)
 							abortError = new Error(
 								"Aborted because " + moduleId + " is not accepted" + chainInfo
 							);
 						break;
 					case "accepted":
 						if (options.onAccepted) options.onAccepted(result);
 						doApply = true;
 						break;
 					case "disposed":
 						if (options.onDisposed) options.onDisposed(result);
 						doDispose = true;
 						break;
 					default:
 						throw new Error("Unexception type " + result.type);
 				}
 				if (abortError) {
 					hotSetStatus("abort");
 					return Promise.reject(abortError);
 				}
 				if (doApply) {
 					appliedUpdate[moduleId] = hotUpdate[moduleId];
 					addAllToSet(outdatedModules, result.outdatedModules);
 					for (moduleId in result.outdatedDependencies) {
 						if (
 							Object.prototype.hasOwnProperty.call(
 								result.outdatedDependencies,
 								moduleId
 							)
 						) {
 							if (!outdatedDependencies[moduleId])
 								outdatedDependencies[moduleId] = [];
 							addAllToSet(
 								outdatedDependencies[moduleId],
 								result.outdatedDependencies[moduleId]
 							);
 						}
 					}
 				}
 				if (doDispose) {
 					addAllToSet(outdatedModules, [result.moduleId]);
 					appliedUpdate[moduleId] = warnUnexpectedRequire;
 				}
 			}
 		}

 		// Store self accepted outdated modules to require them later by the module system
 		var outdatedSelfAcceptedModules = [];
 		for (i = 0; i < outdatedModules.length; i++) {
 			moduleId = outdatedModules[i];
 			if (
 				installedModules[moduleId] &&
 				installedModules[moduleId].hot._selfAccepted
 			)
 				outdatedSelfAcceptedModules.push({
 					module: moduleId,
 					errorHandler: installedModules[moduleId].hot._selfAccepted
 				});
 		}

 		// Now in "dispose" phase
 		hotSetStatus("dispose");
 		Object.keys(hotAvailableFilesMap).forEach(function(chunkId) {
 			if (hotAvailableFilesMap[chunkId] === false) {
 				hotDisposeChunk(chunkId);
 			}
 		});

 		var idx;
 		var queue = outdatedModules.slice();
 		while (queue.length > 0) {
 			moduleId = queue.pop();
 			module = installedModules[moduleId];
 			if (!module) continue;

 			var data = {};

 			// Call dispose handlers
 			var disposeHandlers = module.hot._disposeHandlers;
 			for (j = 0; j < disposeHandlers.length; j++) {
 				cb = disposeHandlers[j];
 				cb(data);
 			}
 			hotCurrentModuleData[moduleId] = data;

 			// disable module (this disables requires from this module)
 			module.hot.active = false;

 			// remove module from cache
 			delete installedModules[moduleId];

 			// when disposing there is no need to call dispose handler
 			delete outdatedDependencies[moduleId];

 			// remove "parents" references from all children
 			for (j = 0; j < module.children.length; j++) {
 				var child = installedModules[module.children[j]];
 				if (!child) continue;
 				idx = child.parents.indexOf(moduleId);
 				if (idx >= 0) {
 					child.parents.splice(idx, 1);
 				}
 			}
 		}

 		// remove outdated dependency from module children
 		var dependency;
 		var moduleOutdatedDependencies;
 		for (moduleId in outdatedDependencies) {
 			if (
 				Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
 			) {
 				module = installedModules[moduleId];
 				if (module) {
 					moduleOutdatedDependencies = outdatedDependencies[moduleId];
 					for (j = 0; j < moduleOutdatedDependencies.length; j++) {
 						dependency = moduleOutdatedDependencies[j];
 						idx = module.children.indexOf(dependency);
 						if (idx >= 0) module.children.splice(idx, 1);
 					}
 				}
 			}
 		}

 		// Not in "apply" phase
 		hotSetStatus("apply");

 		hotCurrentHash = hotUpdateNewHash;

 		// insert new code
 		for (moduleId in appliedUpdate) {
 			if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
 				modules[moduleId] = appliedUpdate[moduleId];
 			}
 		}

 		// call accept handlers
 		var error = null;
 		for (moduleId in outdatedDependencies) {
 			if (
 				Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
 			) {
 				module = installedModules[moduleId];
 				if (module) {
 					moduleOutdatedDependencies = outdatedDependencies[moduleId];
 					var callbacks = [];
 					for (i = 0; i < moduleOutdatedDependencies.length; i++) {
 						dependency = moduleOutdatedDependencies[i];
 						cb = module.hot._acceptedDependencies[dependency];
 						if (cb) {
 							if (callbacks.indexOf(cb) !== -1) continue;
 							callbacks.push(cb);
 						}
 					}
 					for (i = 0; i < callbacks.length; i++) {
 						cb = callbacks[i];
 						try {
 							cb(moduleOutdatedDependencies);
 						} catch (err) {
 							if (options.onErrored) {
 								options.onErrored({
 									type: "accept-errored",
 									moduleId: moduleId,
 									dependencyId: moduleOutdatedDependencies[i],
 									error: err
 								});
 							}
 							if (!options.ignoreErrored) {
 								if (!error) error = err;
 							}
 						}
 					}
 				}
 			}
 		}

 		// Load self accepted modules
 		for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
 			var item = outdatedSelfAcceptedModules[i];
 			moduleId = item.module;
 			hotCurrentParents = [moduleId];
 			try {
 				__webpack_require__(moduleId);
 			} catch (err) {
 				if (typeof item.errorHandler === "function") {
 					try {
 						item.errorHandler(err);
 					} catch (err2) {
 						if (options.onErrored) {
 							options.onErrored({
 								type: "self-accept-error-handler-errored",
 								moduleId: moduleId,
 								error: err2,
 								originalError: err
 							});
 						}
 						if (!options.ignoreErrored) {
 							if (!error) error = err2;
 						}
 						if (!error) error = err;
 					}
 				} else {
 					if (options.onErrored) {
 						options.onErrored({
 							type: "self-accept-errored",
 							moduleId: moduleId,
 							error: err
 						});
 					}
 					if (!options.ignoreErrored) {
 						if (!error) error = err;
 					}
 				}
 			}
 		}

 		// handle errors in accept handlers and self accepted module load
 		if (error) {
 			hotSetStatus("fail");
 			return Promise.reject(error);
 		}

 		hotSetStatus("idle");
 		return new Promise(function(resolve) {
 			resolve(outdatedModules);
 		});
 	}
 	function hotDisposeChunk(chunkId) {
 		delete installedChunks[chunkId];
 	}
 	var parentHotUpdateCallback = window["webpackHotUpdate"];
 	window["webpackHotUpdate"] =   // eslint-disable-next-line no-unused-vars
 	  function webpackHotUpdateCallback(chunkId, moreModules) {
 	    hotAddUpdateChunk(chunkId, moreModules)
 	    if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules)
 	  } ;

 	  // eslint-disable-next-line no-unused-vars
 	  function hotDownloadUpdateChunk(chunkId) {
 	    var head = document.getElementsByTagName('head')[0]
 	    var script = document.createElement('script')
 	    script.charset = 'utf-8'
 	    script.src = __webpack_require__.p + "" + chunkId + "." + hotCurrentHash + ".hot-update.js"
 	    
 	    head.appendChild(script)
 	  }

 	  // eslint-disable-next-line no-unused-vars
 	  function hotDownloadManifest(requestTimeout) {
 	    requestTimeout = requestTimeout || 10000
 	    return new Promise(function(resolve, reject) {
 	      if (typeof XMLHttpRequest === 'undefined')
 	        return reject(new Error('No browser support'))
 	      try {
 	        var request = new XMLHttpRequest()
 	        var requestPath = __webpack_require__.p + "" + hotCurrentHash + ".hot-update.json"
 	        request.open('GET', requestPath, true)
 	        request.timeout = requestTimeout
 	        request.send(null)
 	      } catch (err) {
 	        return reject(err)
 	      }
 	      request.onreadystatechange = function() {
 	        if (request.readyState !== 4) return
 	        if (request.status === 0) {
 	          // timeout
 	          reject(
 	            new Error('Manifest request to ' + requestPath + ' timed out.')
 	          )
 	        } else if (request.status === 404) {
 	          // no update available
 	          resolve()
 	        } else if (request.status !== 200 && request.status !== 304) {
 	          // other failure
 	          reject(new Error('Manifest request to ' + requestPath + ' failed.'))
 	        } else {
 	          // success
 	          try {
 	            var update = JSON.parse(request.responseText)
 	          } catch (e) {
 	            reject(e)
 	            return
 	          }
 	          resolve(update)
 	        }
 	      }
 	    })
 	  }

 	var hotApplyOnUpdate = true;
 	var hotCurrentHash = "7d4d1bb64e50c4217c9c"; // eslint-disable-line no-unused-vars
 	var hotRequestTimeout = 10000;
 	var hotCurrentModuleData = {};
 	var hotCurrentChildModule; // eslint-disable-line no-unused-vars
 	var hotCurrentParents = []; // eslint-disable-line no-unused-vars
 	var hotCurrentParentsTemp = []; // eslint-disable-line no-unused-vars

 	// eslint-disable-next-line no-unused-vars
 	function hotCreateRequire(moduleId) {
 		var me = installedModules[moduleId];
 		if (!me) return __webpack_require__;
 		var fn = function(request) {
 			if (me.hot.active) {
 				if (installedModules[request]) {
 					if (installedModules[request].parents.indexOf(moduleId) === -1)
 						installedModules[request].parents.push(moduleId);
 				} else {
 					hotCurrentParents = [moduleId];
 					hotCurrentChildModule = request;
 				}
 				if (me.children.indexOf(request) === -1) me.children.push(request);
 			} else {
 				console.warn(
 					"[HMR] unexpected require(" +
 						request +
 						") from disposed module " +
 						moduleId
 				);
 				hotCurrentParents = [];
 			}
 			return __webpack_require__(request);
 		};
 		var ObjectFactory = function ObjectFactory(name) {
 			return {
 				configurable: true,
 				enumerable: true,
 				get: function() {
 					return __webpack_require__[name];
 				},
 				set: function(value) {
 					__webpack_require__[name] = value;
 				}
 			};
 		};
 		for (var name in __webpack_require__) {
 			if (
 				Object.prototype.hasOwnProperty.call(__webpack_require__, name) &&
 				name !== "e"
 			) {
 				Object.defineProperty(fn, name, ObjectFactory(name));
 			}
 		}
 		fn.e = function(chunkId) {
 			if (hotStatus === "ready") hotSetStatus("prepare");
 			hotChunksLoading++;
 			return __webpack_require__.e(chunkId).then(finishChunkLoading, function(err) {
 				finishChunkLoading();
 				throw err;
 			});

 			function finishChunkLoading() {
 				hotChunksLoading--;
 				if (hotStatus === "prepare") {
 					if (!hotWaitingFilesMap[chunkId]) {
 						hotEnsureUpdateChunk(chunkId);
 					}
 					if (hotChunksLoading === 0 && hotWaitingFiles === 0) {
 						hotUpdateDownloaded();
 					}
 				}
 			}
 		};
 		return fn;
 	}

 	// eslint-disable-next-line no-unused-vars
 	function hotCreateModule(moduleId) {
 		var hot = {
 			// private stuff
 			_acceptedDependencies: {},
 			_declinedDependencies: {},
 			_selfAccepted: false,
 			_selfDeclined: false,
 			_disposeHandlers: [],
 			_main: hotCurrentChildModule !== moduleId,

 			// Module API
 			active: true,
 			accept: function(dep, callback) {
 				if (typeof dep === "undefined") hot._selfAccepted = true;
 				else if (typeof dep === "function") hot._selfAccepted = dep;
 				else if (typeof dep === "object")
 					for (var i = 0; i < dep.length; i++)
 						hot._acceptedDependencies[dep[i]] = callback || function() {};
 				else hot._acceptedDependencies[dep] = callback || function() {};
 			},
 			decline: function(dep) {
 				if (typeof dep === "undefined") hot._selfDeclined = true;
 				else if (typeof dep === "object")
 					for (var i = 0; i < dep.length; i++)
 						hot._declinedDependencies[dep[i]] = true;
 				else hot._declinedDependencies[dep] = true;
 			},
 			dispose: function(callback) {
 				hot._disposeHandlers.push(callback);
 			},
 			addDisposeHandler: function(callback) {
 				hot._disposeHandlers.push(callback);
 			},
 			removeDisposeHandler: function(callback) {
 				var idx = hot._disposeHandlers.indexOf(callback);
 				if (idx >= 0) hot._disposeHandlers.splice(idx, 1);
 			},

 			// Management API
 			check: hotCheck,
 			apply: hotApply,
 			status: function(l) {
 				if (!l) return hotStatus;
 				hotStatusHandlers.push(l);
 			},
 			addStatusHandler: function(l) {
 				hotStatusHandlers.push(l);
 			},
 			removeStatusHandler: function(l) {
 				var idx = hotStatusHandlers.indexOf(l);
 				if (idx >= 0) hotStatusHandlers.splice(idx, 1);
 			},

 			//inherit from previous dispose call
 			data: hotCurrentModuleData[moduleId]
 		};
 		hotCurrentChildModule = undefined;
 		return hot;
 	}

 	var hotStatusHandlers = [];
 	var hotStatus = "idle";

 	function hotSetStatus(newStatus) {
 		hotStatus = newStatus;
 		for (var i = 0; i < hotStatusHandlers.length; i++)
 			hotStatusHandlers[i].call(null, newStatus);
 	}

 	// while downloading
 	var hotWaitingFiles = 0;
 	var hotChunksLoading = 0;
 	var hotWaitingFilesMap = {};
 	var hotRequestedFilesMap = {};
 	var hotAvailableFilesMap = {};
 	var hotDeferred;

 	// The update info
 	var hotUpdate, hotUpdateNewHash;

 	function toModuleId(id) {
 		var isNumber = +id + "" === id;
 		return isNumber ? +id : id;
 	}

 	function hotCheck(apply) {
 		if (hotStatus !== "idle")
 			throw new Error("check() is only allowed in idle status");
 		hotApplyOnUpdate = apply;
 		hotSetStatus("check");
 		return hotDownloadManifest(hotRequestTimeout).then(function(update) {
 			if (!update) {
 				hotSetStatus("idle");
 				return null;
 			}
 			hotRequestedFilesMap = {};
 			hotWaitingFilesMap = {};
 			hotAvailableFilesMap = update.c;
 			hotUpdateNewHash = update.h;

 			hotSetStatus("prepare");
 			var promise = new Promise(function(resolve, reject) {
 				hotDeferred = {
 					resolve: resolve,
 					reject: reject
 				};
 			});
 			hotUpdate = {};
 			var chunkId = "main";
 			{
 				// eslint-disable-line no-lone-blocks
 				/*globals chunkId */
 				hotEnsureUpdateChunk(chunkId);
 			}
 			if (
 				hotStatus === "prepare" &&
 				hotChunksLoading === 0 &&
 				hotWaitingFiles === 0
 			) {
 				hotUpdateDownloaded();
 			}
 			return promise;
 		});
 	}

 	// eslint-disable-next-line no-unused-vars
 	function hotAddUpdateChunk(chunkId, moreModules) {
 		if (!hotAvailableFilesMap[chunkId] || !hotRequestedFilesMap[chunkId])
 			return;
 		hotRequestedFilesMap[chunkId] = false;
 		for (var moduleId in moreModules) {
 			if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
 				hotUpdate[moduleId] = moreModules[moduleId];
 			}
 		}
 		if (--hotWaitingFiles === 0 && hotChunksLoading === 0) {
 			hotUpdateDownloaded();
 		}
 	}

 	function hotEnsureUpdateChunk(chunkId) {
 		if (!hotAvailableFilesMap[chunkId]) {
 			hotWaitingFilesMap[chunkId] = true;
 		} else {
 			hotRequestedFilesMap[chunkId] = true;
 			hotWaitingFiles++;
 			hotDownloadUpdateChunk(chunkId);
 		}
 	}

 	function hotUpdateDownloaded() {
 		hotSetStatus("ready");
 		var deferred = hotDeferred;
 		hotDeferred = null;
 		if (!deferred) return;
 		if (hotApplyOnUpdate) {
 			// Wrap deferred object in Promise to mark it as a well-handled Promise to
 			// avoid triggering uncaught exception warning in Chrome.
 			// See https://bugs.chromium.org/p/chromium/issues/detail?id=465666
 			Promise.resolve()
 				.then(function() {
 					return hotApply(hotApplyOnUpdate);
 				})
 				.then(
 					function(result) {
 						deferred.resolve(result);
 					},
 					function(err) {
 						deferred.reject(err);
 					}
 				);
 		} else {
 			var outdatedModules = [];
 			for (var id in hotUpdate) {
 				if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
 					outdatedModules.push(toModuleId(id));
 				}
 			}
 			deferred.resolve(outdatedModules);
 		}
 	}

 	function hotApply(options) {
 		if (hotStatus !== "ready")
 			throw new Error("apply() is only allowed in ready status");
 		options = options || {};

 		var cb;
 		var i;
 		var j;
 		var module;
 		var moduleId;

 		function getAffectedStuff(updateModuleId) {
 			var outdatedModules = [updateModuleId];
 			var outdatedDependencies = {};

 			var queue = outdatedModules.slice().map(function(id) {
 				return {
 					chain: [id],
 					id: id
 				};
 			});
 			while (queue.length > 0) {
 				var queueItem = queue.pop();
 				var moduleId = queueItem.id;
 				var chain = queueItem.chain;
 				module = installedModules[moduleId];
 				if (!module || module.hot._selfAccepted) continue;
 				if (module.hot._selfDeclined) {
 					return {
 						type: "self-declined",
 						chain: chain,
 						moduleId: moduleId
 					};
 				}
 				if (module.hot._main) {
 					return {
 						type: "unaccepted",
 						chain: chain,
 						moduleId: moduleId
 					};
 				}
 				for (var i = 0; i < module.parents.length; i++) {
 					var parentId = module.parents[i];
 					var parent = installedModules[parentId];
 					if (!parent) continue;
 					if (parent.hot._declinedDependencies[moduleId]) {
 						return {
 							type: "declined",
 							chain: chain.concat([parentId]),
 							moduleId: moduleId,
 							parentId: parentId
 						};
 					}
 					if (outdatedModules.indexOf(parentId) !== -1) continue;
 					if (parent.hot._acceptedDependencies[moduleId]) {
 						if (!outdatedDependencies[parentId])
 							outdatedDependencies[parentId] = [];
 						addAllToSet(outdatedDependencies[parentId], [moduleId]);
 						continue;
 					}
 					delete outdatedDependencies[parentId];
 					outdatedModules.push(parentId);
 					queue.push({
 						chain: chain.concat([parentId]),
 						id: parentId
 					});
 				}
 			}

 			return {
 				type: "accepted",
 				moduleId: updateModuleId,
 				outdatedModules: outdatedModules,
 				outdatedDependencies: outdatedDependencies
 			};
 		}

 		function addAllToSet(a, b) {
 			for (var i = 0; i < b.length; i++) {
 				var item = b[i];
 				if (a.indexOf(item) === -1) a.push(item);
 			}
 		}

 		// at begin all updates modules are outdated
 		// the "outdated" status can propagate to parents if they don't accept the children
 		var outdatedDependencies = {};
 		var outdatedModules = [];
 		var appliedUpdate = {};

 		var warnUnexpectedRequire = function warnUnexpectedRequire() {
 			console.warn(
 				"[HMR] unexpected require(" + result.moduleId + ") to disposed module"
 			);
 		};

 		for (var id in hotUpdate) {
 			if (Object.prototype.hasOwnProperty.call(hotUpdate, id)) {
 				moduleId = toModuleId(id);
 				/** @type {TODO} */
 				var result;
 				if (hotUpdate[id]) {
 					result = getAffectedStuff(moduleId);
 				} else {
 					result = {
 						type: "disposed",
 						moduleId: id
 					};
 				}
 				/** @type {Error|false} */
 				var abortError = false;
 				var doApply = false;
 				var doDispose = false;
 				var chainInfo = "";
 				if (result.chain) {
 					chainInfo = "\nUpdate propagation: " + result.chain.join(" -> ");
 				}
 				switch (result.type) {
 					case "self-declined":
 						if (options.onDeclined) options.onDeclined(result);
 						if (!options.ignoreDeclined)
 							abortError = new Error(
 								"Aborted because of self decline: " +
 									result.moduleId +
 									chainInfo
 							);
 						break;
 					case "declined":
 						if (options.onDeclined) options.onDeclined(result);
 						if (!options.ignoreDeclined)
 							abortError = new Error(
 								"Aborted because of declined dependency: " +
 									result.moduleId +
 									" in " +
 									result.parentId +
 									chainInfo
 							);
 						break;
 					case "unaccepted":
 						if (options.onUnaccepted) options.onUnaccepted(result);
 						if (!options.ignoreUnaccepted)
 							abortError = new Error(
 								"Aborted because " + moduleId + " is not accepted" + chainInfo
 							);
 						break;
 					case "accepted":
 						if (options.onAccepted) options.onAccepted(result);
 						doApply = true;
 						break;
 					case "disposed":
 						if (options.onDisposed) options.onDisposed(result);
 						doDispose = true;
 						break;
 					default:
 						throw new Error("Unexception type " + result.type);
 				}
 				if (abortError) {
 					hotSetStatus("abort");
 					return Promise.reject(abortError);
 				}
 				if (doApply) {
 					appliedUpdate[moduleId] = hotUpdate[moduleId];
 					addAllToSet(outdatedModules, result.outdatedModules);
 					for (moduleId in result.outdatedDependencies) {
 						if (
 							Object.prototype.hasOwnProperty.call(
 								result.outdatedDependencies,
 								moduleId
 							)
 						) {
 							if (!outdatedDependencies[moduleId])
 								outdatedDependencies[moduleId] = [];
 							addAllToSet(
 								outdatedDependencies[moduleId],
 								result.outdatedDependencies[moduleId]
 							);
 						}
 					}
 				}
 				if (doDispose) {
 					addAllToSet(outdatedModules, [result.moduleId]);
 					appliedUpdate[moduleId] = warnUnexpectedRequire;
 				}
 			}
 		}

 		// Store self accepted outdated modules to require them later by the module system
 		var outdatedSelfAcceptedModules = [];
 		for (i = 0; i < outdatedModules.length; i++) {
 			moduleId = outdatedModules[i];
 			if (
 				installedModules[moduleId] &&
 				installedModules[moduleId].hot._selfAccepted
 			)
 				outdatedSelfAcceptedModules.push({
 					module: moduleId,
 					errorHandler: installedModules[moduleId].hot._selfAccepted
 				});
 		}

 		// Now in "dispose" phase
 		hotSetStatus("dispose");
 		Object.keys(hotAvailableFilesMap).forEach(function(chunkId) {
 			if (hotAvailableFilesMap[chunkId] === false) {
 				hotDisposeChunk(chunkId);
 			}
 		});

 		var idx;
 		var queue = outdatedModules.slice();
 		while (queue.length > 0) {
 			moduleId = queue.pop();
 			module = installedModules[moduleId];
 			if (!module) continue;

 			var data = {};

 			// Call dispose handlers
 			var disposeHandlers = module.hot._disposeHandlers;
 			for (j = 0; j < disposeHandlers.length; j++) {
 				cb = disposeHandlers[j];
 				cb(data);
 			}
 			hotCurrentModuleData[moduleId] = data;

 			// disable module (this disables requires from this module)
 			module.hot.active = false;

 			// remove module from cache
 			delete installedModules[moduleId];

 			// when disposing there is no need to call dispose handler
 			delete outdatedDependencies[moduleId];

 			// remove "parents" references from all children
 			for (j = 0; j < module.children.length; j++) {
 				var child = installedModules[module.children[j]];
 				if (!child) continue;
 				idx = child.parents.indexOf(moduleId);
 				if (idx >= 0) {
 					child.parents.splice(idx, 1);
 				}
 			}
 		}

 		// remove outdated dependency from module children
 		var dependency;
 		var moduleOutdatedDependencies;
 		for (moduleId in outdatedDependencies) {
 			if (
 				Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
 			) {
 				module = installedModules[moduleId];
 				if (module) {
 					moduleOutdatedDependencies = outdatedDependencies[moduleId];
 					for (j = 0; j < moduleOutdatedDependencies.length; j++) {
 						dependency = moduleOutdatedDependencies[j];
 						idx = module.children.indexOf(dependency);
 						if (idx >= 0) module.children.splice(idx, 1);
 					}
 				}
 			}
 		}

 		// Not in "apply" phase
 		hotSetStatus("apply");

 		hotCurrentHash = hotUpdateNewHash;

 		// insert new code
 		for (moduleId in appliedUpdate) {
 			if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
 				modules[moduleId] = appliedUpdate[moduleId];
 			}
 		}

 		// call accept handlers
 		var error = null;
 		for (moduleId in outdatedDependencies) {
 			if (
 				Object.prototype.hasOwnProperty.call(outdatedDependencies, moduleId)
 			) {
 				module = installedModules[moduleId];
 				if (module) {
 					moduleOutdatedDependencies = outdatedDependencies[moduleId];
 					var callbacks = [];
 					for (i = 0; i < moduleOutdatedDependencies.length; i++) {
 						dependency = moduleOutdatedDependencies[i];
 						cb = module.hot._acceptedDependencies[dependency];
 						if (cb) {
 							if (callbacks.indexOf(cb) !== -1) continue;
 							callbacks.push(cb);
 						}
 					}
 					for (i = 0; i < callbacks.length; i++) {
 						cb = callbacks[i];
 						try {
 							cb(moduleOutdatedDependencies);
 						} catch (err) {
 							if (options.onErrored) {
 								options.onErrored({
 									type: "accept-errored",
 									moduleId: moduleId,
 									dependencyId: moduleOutdatedDependencies[i],
 									error: err
 								});
 							}
 							if (!options.ignoreErrored) {
 								if (!error) error = err;
 							}
 						}
 					}
 				}
 			}
 		}

 		// Load self accepted modules
 		for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
 			var item = outdatedSelfAcceptedModules[i];
 			moduleId = item.module;
 			hotCurrentParents = [moduleId];
 			try {
 				__webpack_require__(moduleId);
 			} catch (err) {
 				if (typeof item.errorHandler === "function") {
 					try {
 						item.errorHandler(err);
 					} catch (err2) {
 						if (options.onErrored) {
 							options.onErrored({
 								type: "self-accept-error-handler-errored",
 								moduleId: moduleId,
 								error: err2,
 								originalError: err
 							});
 						}
 						if (!options.ignoreErrored) {
 							if (!error) error = err2;
 						}
 						if (!error) error = err;
 					}
 				} else {
 					if (options.onErrored) {
 						options.onErrored({
 							type: "self-accept-errored",
 							moduleId: moduleId,
 							error: err
 						});
 					}
 					if (!options.ignoreErrored) {
 						if (!error) error = err;
 					}
 				}
 			}
 		}

 		// handle errors in accept handlers and self accepted module load
 		if (error) {
 			hotSetStatus("fail");
 			return Promise.reject(error);
 		}

 		hotSetStatus("idle");
 		return new Promise(function(resolve) {
 			resolve(outdatedModules);
 		});
 	}

 	// The module cache
 	var installedModules = {};

 	// The require function
 	function __webpack_require__(moduleId) {

 		// Check if module is in cache
 		if(installedModules[moduleId]) {
 			return installedModules[moduleId].exports;
 		}
 		// Create a new module (and put it into the cache)
 		var module = installedModules[moduleId] = {
 			i: moduleId,
 			l: false,
 			exports: {},
 			hot: hotCreateModule(moduleId),
 			parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),
 			children: [],
 			hot: hotCreateModule(moduleId),
 			parents: (hotCurrentParentsTemp = hotCurrentParents, hotCurrentParents = [], hotCurrentParentsTemp),
 			children: []
 		};

 		// Execute the module function
 		modules[moduleId].call(module.exports, module, module.exports, hotCreateRequire(moduleId));

 		// Flag the module as loaded
 		module.l = true;

 		// Return the exports of the module
 		return module.exports;
 	}


 	// expose the modules object (__webpack_modules__)
 	__webpack_require__.m = modules;

 	// expose the module cache
 	__webpack_require__.c = installedModules;

 	// define getter function for harmony exports
 	__webpack_require__.d = function(exports, name, getter) {
 		if(!__webpack_require__.o(exports, name)) {
 			Object.defineProperty(exports, name, {
 				configurable: false,
 				enumerable: true,
 				get: getter
 			});
 		}
 	};

 	// define __esModule on exports
 	__webpack_require__.r = function(exports) {
 		Object.defineProperty(exports, '__esModule', { value: true });
 	};

 	// getDefaultExport function for compatibility with non-harmony modules
 	__webpack_require__.n = function(module) {
 		var getter = module && module.__esModule ?
 			function getDefault() { return module['default']; } :
 			function getModuleExports() { return module; };
 		__webpack_require__.d(getter, 'a', getter);
 		return getter;
 	};

 	// Object.prototype.hasOwnProperty.call
 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

 	// __webpack_public_path__
 	__webpack_require__.p = "/";

 	// __webpack_hash__
 	__webpack_require__.h = function() { return hotCurrentHash; };

 	// __webpack_hash__
 	__webpack_require__.h = function() { return hotCurrentHash; };


 	// Load entry module and return exports
 	return hotCreateRequire(1)(__webpack_require__.s = 1);

I’m pretty sure that’s a generated file from both JsonMainTemplate.runtime.js and WebWorkerMainTemplate.runtime.js.

And I think that’s what’s causing the infinite loop. Because we get to this first block of code (from WebWorkerMainTemplate.runtime.js I think) and window["webpackHotUpdate"] is undefined, then set.

first-webpackhotupdate

Then we get to the second block of code (from JsonpMainTemplate.runtime.js) and it’s now defined.

second-webpackhotupdate

Infinite loop!

If I comment out the line in both places:

if (parentHotUpdateCallback) parentHotUpdateCallback(chunkId, moreModules);

Then the maximum call stack error goes away. However, HMR still only reloads the page saying “Cannot apply update. Please refresh the page. Error: Aborted because ./path/to/file.js is not accepted.”

If the current behavior is a bug, please provide the steps to reproduce.

As mentioned, I have a small reproduction repository.

What is the expected behavior?

As mentioned in the README of my reproduction, I want to be able to use require.context to load in and hot-swap certain files within my project.

Other relevant information:

  "devDependencies": {
    "webpack": "^4.8.3",
    "webpack-cli": "^2.1.4",
    "webpack-serve": "^1.0.2"
  }

Node.js version: 8.9.4
Operating System: macOS 10.12.6
Additional tools: N/A

Author: Fantashit

1 thought on “HMR maximum call stack errors + other issues

Comments are closed.