Skip to content

Commit

Permalink
Fixes #3270
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed Jun 19, 2024
1 parent 8c348d8 commit b5571f0
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 3 deletions.
49 changes: 49 additions & 0 deletions src/Util/EsmResolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import debugUtil from "debug";

const debug = debugUtil("Eleventy:EsmResolver");

let lastModifiedPaths = new Map();
export async function initialize({ port }) {
// From `eleventy.importCacheReset` event in Require.js
port.on("message", ({ path, newDate }) => {
lastModifiedPaths.set(path, newDate);
});
}

// Fixes issue https://github.com/11ty/eleventy/issues/3270
// Docs: https://nodejs.org/docs/latest/api/module.html#resolvespecifier-context-nextresolve
export async function resolve(specifier, context, nextResolve) {
try {
// Not a relative import and not a file import
// Or from node_modules (perhaps better to check if the specifier is in the project directory instead)
if (
(!specifier.startsWith("./") && !specifier.startsWith("file:")) ||
context.parentURL.includes("/node_modules/")
) {
return nextResolve(specifier);
}

let fileUrl = new URL(specifier, context.parentURL);
if (fileUrl.searchParams.has("_cache_bust")) {
// already is cache busted outside resolver (wider compat, url was changed prior to import, probably in Require.js)
return nextResolve(specifier);
}

let absolutePath = fileUrl.pathname;
// Bust the import cache if this is a recently modified file
if (lastModifiedPaths.has(absolutePath)) {
fileUrl.search = ""; // delete existing searchparams
fileUrl.searchParams.set("_cache_bust", lastModifiedPaths.get(absolutePath));
debug("Cache busting %o to %o", specifier, fileUrl.toString());

return nextResolve(fileUrl.toString());
}
} catch (e) {
debug("EsmResolver Error parsing specifier (%o): %o", specifier, e);
}

return nextResolve(specifier);
}

// export async function load(url, context, nextLoad) {
// }
27 changes: 24 additions & 3 deletions src/Util/Require.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { createRequire } from "node:module";
import module from "node:module";
import { MessageChannel } from "node:worker_threads";

import { TemplatePath } from "@11ty/eleventy-utils";

import eventBus from "../EventBus.js";

const { port1, port2 } = new MessageChannel();

// ESM Cache Buster is an enhancement that works in Node 18.19+
// https://nodejs.org/docs/latest/api/module.html#moduleregisterspecifier-parenturl-options
// Fixes https://github.com/11ty/eleventy/issues/3270
if ("register" in module) {
module.register("./EsmResolver.js", import.meta.url, {
data: {
port: port2,
},
transferList: [port2],
});
}

// important to clear the require.cache in CJS projects
const require = createRequire(import.meta.url);
const require = module.createRequire(import.meta.url);

// Used for JSON imports, suffering from Node warning that import assertions experimental but also
// throwing an error if you try to import() a JSON file without an import assertion.
Expand Down Expand Up @@ -37,7 +52,13 @@ let lastModifiedPaths = new Map();
eventBus.on("eleventy.importCacheReset", (fileQueue) => {
for (let filePath of fileQueue) {
let absolutePath = TemplatePath.absolutePath(filePath);
lastModifiedPaths.set(absolutePath, Date.now());
let newDate = Date.now();
lastModifiedPaths.set(absolutePath, newDate);

// post to EsmResolver worker thread
if (port1) {
port1.postMessage({ path: absolutePath, newDate });
}

// ESM Eleventy when using `import()` on a CJS project file still adds to require.cache
if (absolutePath in (require?.cache || {})) {
Expand Down

0 comments on commit b5571f0

Please sign in to comment.