-
-
Notifications
You must be signed in to change notification settings - Fork 35.1k
Description
In a loader module I'm handling sources with omitted type import attribute on JSON imports,
by assigning context.importAttributes.type in the load hook, while caching the outputs.
When the attributes are consistently omitted from JSON imports, or consistently aren't, this works well.
But when the import signature changes on one occasion, the process breaks
with an empty error object ({}) on returning the cached output.
I can't tell the reason for this, for as far as the loader is concerned,
the load hook output should be idempotent whether the input context.importAttributes.type is defined or not:
[Object: null prototype] {
format: 'json',
responseURL: 'file:///home/ranger/loaded.json',
source: <Buffer 7b 22 61 22 3a 31 7d 0a>,
shortCircuit: true
}Here's a loader module to reproduce:
// loader.js
export const address = new URL(import.meta.url).pathname;
export const file = address.replace(/.*\//, "");
// register loader
if (
!globalThis.window &&
process.execArgv.some((flag) =>
new RegExp("^--import[= ][^ ]*" + file).test(flag),
)
)
Promise.all(
["module", "worker_threads"].map((module) => import(module)),
).then(
([{ register }, { MessageChannel, isMainThread }]) =>
isMainThread &&
[new MessageChannel(), import.meta.url].reduce(
({ port1, port2 }, parentURL) =>
register(address, parentURL, {
data: { socket: port2 },
transferList: [port2],
}) || port1.on("message", console.log),
),
);
function initialize({ socket }) {
socket.postMessage(" Registered loader module:\n " + import.meta.url + ".");
}
const load = function (source, context, next) {
if (this[source])
// return cached value.
return console.log(this[source]) || this[source];
if (source.endsWith("json")) context.importAttributes.type = "json";
return next(source, context).then(
(context) =>
// cache output
(this[source] = Object.assign(context, { shortCircuit: true })),
);
}.bind({});
export { initialize, load };and here's a file with inconsistent attribute signatures to load
(in my real use case the inconsistency was in separate modules, one importing the other):
// loaded.js
import json1 from "./loaded.json";
import json2 from "./loaded.json" with {type:"json"};
console.log(json1===json2);
// loaded.json
{"a":1}Result with node 21.1.0 (only notable difference is <Buffer > being empty in nextLoad's output already on the first import):
node --watch --import=./loader.js ./loaded.js
Registered loader module:
file:///home/ranger/loader.js.
(node:1498) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
[Object: null prototype] {
format: 'json',
responseURL: 'file:///home/ranger/loaded.json',
source: <Buffer >,
shortCircuit: true
}
node:internal/process/esm_loader:40
internalBinding('errors').triggerUncaughtException(
^
{}
Node.js v21.1.0
Failed running './loaded.js'