Repository: 11ty/eleventy
Pull request #683 (2019-09-05 17:41:19)

Fix require cache cleanup on Windows (#596)


Patch:
diff --git a/src/Eleventy.js b/src/Eleventy.js
index e9fa1b06..1bc4fe40 100644
--- a/src/Eleventy.js
+++ b/src/Eleventy.js
@@ -11,6 +11,7 @@ const simplePlural = require("./Util/Pluralize");
 const config = require("./Config");
 const bench = require("./BenchmarkManager");
 const debug = require("debug")("Eleventy");
+const deleteRequireCache = require("./Util/DeleteRequireCache");
 
 /**
  * @module @11ty/eleventy/Eleventy
@@ -171,7 +172,7 @@ class Eleventy {
 
     // reload package.json values (if applicable)
     // TODO only reset this if it changed
-    delete require.cache[TemplatePath.absolutePath("package.json")];
+    deleteRequireCache(TemplatePath.absolutePath("package.json"));
 
     await this.init();
   }
diff --git a/src/EleventyWatchTargets.js b/src/EleventyWatchTargets.js
index 6965732a..87dc3bc9 100644
--- a/src/EleventyWatchTargets.js
+++ b/src/EleventyWatchTargets.js
@@ -1,5 +1,6 @@
 const dependencyTree = require("dependency-tree");
 const TemplatePath = require("./TemplatePath");
+const deleteRequireCache = require("./Util/DeleteRequireCache");
 
 class EleventyWatchTargets {
   constructor() {
@@ -109,7 +110,7 @@ class EleventyWatchTargets {
 
   clearDependencyRequireCache() {
     for (let path of this.dependencies) {
-      delete require.cache[TemplatePath.absolutePath(path)];
+      deleteRequireCache(TemplatePath.absolutePath(path));
     }
   }
 
diff --git a/src/Engines/JavaScript.js b/src/Engines/JavaScript.js
index 1a9869b9..ec676cab 100644
--- a/src/Engines/JavaScript.js
+++ b/src/Engines/JavaScript.js
@@ -1,6 +1,7 @@
 const TemplateEngine = require("./TemplateEngine");
 const TemplatePath = require("../TemplatePath");
 const EleventyBaseError = require("../EleventyBaseError");
+const deleteRequireCache = require("../Util/DeleteRequireCache");
 
 class JavaScriptTemplateInvalidDataFormatError extends EleventyBaseError {}
 
@@ -72,8 +73,8 @@ class JavaScript extends TemplateEngine {
   // only remove from cache once on startup (if it already exists)
   initRequireCache(inputPath) {
     let requirePath = TemplatePath.absolutePath(inputPath);
-    if (requirePath in require.cache) {
-      delete require.cache[requirePath];
+    if (requirePath) {
+      deleteRequireCache(requirePath);
     }
 
     if (inputPath in this.instances) {
diff --git a/src/TemplateConfig.js b/src/TemplateConfig.js
index dc395c01..1bcf39fe 100644
--- a/src/TemplateConfig.js
+++ b/src/TemplateConfig.js
@@ -5,6 +5,7 @@ const TemplatePath = require("./TemplatePath");
 const EleventyBaseError = require("./EleventyBaseError");
 const eleventyConfig = require("./EleventyConfig");
 const debug = require("debug")("Eleventy:TemplateConfig");
+const deleteRequireCache = require("./Util/DeleteRequireCache");
 
 class EleventyConfigError extends EleventyBaseError {}
 
@@ -76,7 +77,7 @@ class TemplateConfig {
       try {
         // remove from require cache so it will grab a fresh copy
         if (path in require.cache) {
-          delete require.cache[path];
+          deleteRequireCache(path);
         }
 
         localConfig = require(path);
diff --git a/src/TemplateData.js b/src/TemplateData.js
index 06912f8f..8aa5802d 100644
--- a/src/TemplateData.js
+++ b/src/TemplateData.js
@@ -14,6 +14,7 @@ const config = require("./Config");
 const debugWarn = require("debug")("Eleventy:Warnings");
 const debug = require("debug")("Eleventy:TemplateData");
 const debugDev = require("debug")("Dev:Eleventy:TemplateData");
+const deleteRequireCache = require("./Util/DeleteRequireCache");
 
 class TemplateDataParseError extends EleventyBaseError {}
 
@@ -233,7 +234,7 @@ class TemplateData {
       if (await fs.pathExists(localPath)) {
         let dataBench = bench.get(`\`${path}\``);
         dataBench.before();
-        delete require.cache[localPath];
+        deleteRequireCache(localPath);
         let returnValue = require(localPath);
         if (typeof returnValue === "function") {
           returnValue = await returnValue();
diff --git a/src/Util/DeleteRequireCache.js b/src/Util/DeleteRequireCache.js
new file mode 100644
index 00000000..5f94b716
--- /dev/null
+++ b/src/Util/DeleteRequireCache.js
@@ -0,0 +1,11 @@
+const path = require("path");
+
+/**
+ * Removes a nodejs module from the cache.
+ * The keys of the nodejs require cache are file paths based on the current operating system.
+ * @param {string} absoluteModulePath An absolute POSIX path to the module.
+ */
+module.exports = function deleteRequireCache(absoluteModulePath) {
+  const normalizedPath = path.normalize(absoluteModulePath);
+  delete require.cache[normalizedPath];
+};


Test patch:
diff --git a/test/DeleteRequireCacheTest.js b/test/DeleteRequireCacheTest.js
new file mode 100644
index 00000000..4393f5bd
--- /dev/null
+++ b/test/DeleteRequireCacheTest.js
@@ -0,0 +1,32 @@
+import test from "ava";
+import path from "path";
+import deleteRequireCache from "../src/Util/DeleteRequireCache";
+import template from "./stubs/function.11ty";
+
+test("deleteRequireCache", t => {
+  const modulePath = path.normalize(
+    path.join(__dirname, "./stubs/function.11ty.js")
+  );
+  const posixModulePath = useForwardSlashes(modulePath);
+  const windowsModulePath = useBackwardSlashes(modulePath);
+
+  deleteRequireCache(posixModulePath);
+
+  t.is(require.cache[windowsModulePath], undefined);
+});
+
+/**
+ * @param {string} path
+ * @returns {string}
+ */
+function useForwardSlashes(path) {
+  return path.replace(/\\/g, "/");
+}
+
+/**
+ * @param {string} path
+ * @returns {string}
+ */
+function useBackwardSlashes(path) {
+  return path.replace(/\//g, "\\");
+}