-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal: Module Loader API #8327
Comments
@kitsonk did you ever come across a solution for |
This is broken because Deno won't reload transitively imported modules. See denoland/deno#8327
This comment has been minimized.
This comment has been minimized.
This issue is being discussed on the TC39 side. The following slide from the 8 October 2024 TC39 meeting is helpful. |
The position of Deno team is a bit strange:
So we follow or not node standards, or we follow only TC39 |
I think that statement is incorrect. it wasn't neglected for 4 years, just that the experimental (unstable) API of Node.js is not implemented. I understand the frustration of not getting the features you want, but if you want the features you want included, you can contribute in some way by discussing their usefulness for compatibility or actually implementing them to Deno 🙂 |
For example, it's quite useful to report that the widely used npm package does not work properly due to the lack of implementation of the Node.js experimental Loader API. |
We know this is a experimental feature If you check most of the issues above mentioned, are related to example: #25742 |
Please calm down. If you want to fix a problem with
(This discussion is obviously not directly related to experimental Module Loader API in Node.js. It might be better to hide their comments as unrelated discussions. It is better to report this problem in a new issue) |
The issue which was closed has a clear description with reproduction steps, please re-read it
|
Check the timeline. The issue was closed on September 20; a Deno team member requested a repository to reproduce your issue on September 21. Again, could you please provide a repository to reproduce the issue? |
The reproduction was requested for another thing here #25742 (comment) Thx |
The usefulness was discussed, in many issues. Those discussions were closed and redirected here. The only discussion here was minimized (read: censored). I had plans to implement something that could help but which would ultimately be a workaround, but seeing that discussion is censored by the Deno team if it is critical, but fair, to the Deno team drives me to not implement the feature in Deno. The emoji usage comes off as passive aggressive, at least to me. I also personally don't understand what Node.js or TC39 have to do with considering the functionality for Deno. It should not be required, for some feature, for it to be implemented somewhere else before it is considered for Deno. Otherwise, why use Deno? |
Could you folks list which packages you are trying to run that is blocked by not having access to loaders in Deno? Most recently we saw an issue with Playwright that uses TypeScript loader (that shouldn't really be necessary in Deno, but alas), but I'd like to gauge for a wider range of use cases before we undertake this. Keep in mind that solving this issue will most likely result in worse performance for loading modules as Deno would have to check for registered loaders and potentially hopping multiple times between Rust and JavaScript code to actually load a file before handing it off to V8 for actual execution. |
In my case, we build a new package which will need to load user code, and this code may change at runtime, and since This package will be used by a big tech company (I can't say the name due to the contract) For example in |
Since caching is defined in the language specification when using ES Modules (Cyclic Module Records), this seems to be the only way to accomplish this in CommonJS (Abstract Module Records).
My understanding is that Bun's implementation deviates from JavaScript... By the way, this issue could be solved by implementing I confirmed that the |
At the moment in Deno, In node you at least can compile by yourself But in Deno there is no compile step and everything stays as |
You can interact between them just like in Node.js.
You can compile these files yourself in Deno as well and you can Let's see a short example: // main.cjs
(async () => {
const fs = require("fs");
fs.writeFileSync("./import.mjs", "export default { foo: 'bar' };");
const foo = await import("./import.mjs");
console.log(foo);
console.log(require.cache);
Object.keys(require.cache).forEach(key => delete require.cache[key]);
console.log(require.cache);
fs.writeFileSync("./import.mjs", "export default { foo: 'fizz' };");
const foo1 = await import("./import.mjs");
console.log(foo1);
})();
You can have a dedicated compilation/transpilation step like a lot of frameworks do (Vite, Next.js, etc). It's the transpilation output that determines if file uses Altering While I can see why loaders might be a useful feature, there's a whole ecosystem that worked without loaders by handling transpilation it self. It is expected that it will work exactly the same in Deno as it does in Node.js. If you have some example code that works in Node, but has wrong behavior in Deno I'll be glad to take a look and fix the bug.
Deno supports |
I didn't know that. Thanks! |
I mean, why then use Deno? If in the end I can run compiled files with Node, that doesn't make sense So the idea is just to move back to node and do the compilation to And that's the problem, we apply a Node mindset to Deno |
In @cordisjs/loader (https://github.com/cordiverse/cordis), |
This idea (thought experiment) may seem unusual, but it could open the door to some pragmatic solutions. What if Deno offered a mechanism to “inline” another module directly into the current one? Instead of a standard import, a special directive could copy the referenced module’s code right into the importing file. // file1.ts
export const moduleLevelState = { state: 'moduleLevelState' };
export const act1 = () => {};
export const act2 = () => {}; // file2.ts
import inline('./file1.ts');
// overwrites the definition for act2
export const act2 = () => {};
// and duplicates/copies the definition for moduleLevelState & act1
// so moduleLevelState in file1.ts is not related to moduleLevelState in file2.ts It seems in most cases people need to reload a module a specific number of times. If this assumption is backed by reality, inlining modules could be a valid option - that breaks the least number of things (for example deno's security system should not face many problems while handling this). |
We should have something like this // mod1.ts
console.log("Hello World") // user.ts
await import("./mod1.ts")
await import("./mod1.ts")
console.log("delete the cache")
delete Module.Loader.cache["./mod1.ts"]
await import("./mod1.ts") $ deno run user.ts
Hello World
delete the cache
Hello World Thinking about that |
With ES Modules, it is effectively impossible to do "hot module reloading" of the modules, as ES Module specification is fairly strict that once the module is resolve and its two pass instantiation is done, that is the end of the road for the module and cannot be replaced.
When you are mocking/testing/etc. though, it is potentially valuable to make changes to a module and "reload" it via dynamic import. In Node.js, they have solved this problem with an experimental loader API. For an example of how this gets integrated to provide a mechanism for module replacements of imports that are still modules, quibble provides ESM support. It basically creates a unique specifier for every time the module is requested to get around the challenge of ES modules being static once resolved.
This would also support solving things like #1739, which we have long wanted to do.
The text was updated successfully, but these errors were encountered: