Make way for async import.
jdalton opened this issue · comments
The esm built-in import()
will be async. I ❤️ the module.import
namespace as a userland alternative. The current module.import
is synchronous so I propose renaming module.import
to module.importSync
(similar to the *Sync forms of other Node built-in methods) leaving room for module.import
to be the async form to align more closely with built-in import()
.
Meteor has module.import
and module.dynamicImport
, for what it's worth. If the names were changed, I would suggest module.importSync
and module.importAsync
(since "dynamic" doesn't have a good antonym in this context).
I think aligning with Node's naming scheme makes more sense here. JS is going with an async import()
so it's a nice fit to have a module.import
be async too (it fits the JS and Node naming scheme).
I'm not up on Meteor's current use of reify but maybe a more Meteor-centric fork would be a way to continue to support them (like meteor-reify
). That way instead of trying to ride the line of Node's and Meteor's ecosystems, they could have a package more tailored for their ecosystem and they won't take priority when it comes to decisions for something in Node's ecosystem.
Any idea if Node will implement a Module.prototype.import
method? If they did that (or even if they seemed likely to), I'd be sold.
Any idea if Node will implement a
Module.prototype.import
method?
I pinged Bradley and he says probably not. I think that's a good thing because that leaves a clear user-land alternative that looks built-in enough without worrying about conflicts with built-ins.
Update:
Talking more to Bradley it looks like they plan to freeze all JS prototypes/objs including those of Module
which presents problems for reify's current implementation.
Actually talking more to Bradley they plan to freeze all JS prototypes/objs including those of Module which presents problems for reify's current implementation.
That's seems unnecessarily hostile to existing JS conventions allowing .prototype
extensions for built-in constructors. I strongly oppose that idea, @bmeck! What problems do you believe you're solving?
@benjamn preventing tampering which was heavily prevalent in CJS. In particular loading guarantees/isolation on a per package level are needed if we want to guarantee loading of signed packages. .prototype
has global affects, use hooks instead.
You can always provide a wrapper object that has mutable prototypes and works on the frozen object in a Proxy-like behavior.
To put this into perspective, Meteor will literally ship a fork of Node that does not freeze those prototypes, if it comes to that. What you call "tampering," I call thoughtful interoperation with the existing CommonJS module system.
Edit: Looks like this won't be necessary. 🎉
@benjamn no clue on context of
"thoughtful interoperation with the existing CommonJS module system"
like I said, you can just wrap if you want to ship a mutable system. Default is aiming for tamper resistant to prevent the issues that were problematic from global mutation of the CJS loader. Even then, I view this as similar to mutating Element.prototype.*
in browsers, which isn't always reliable if you are trying to change default behaviors.
Is a discussion of freezing prototypes happening somewhere currently so I can familiarize myself with what's motivating it?
@benjamn it has been talked about in CTC meetings, no issue open on it though.
you can just wrap if you want to ship a mutable system
Where would I do that wrapping? Currently Node offers no hooks for transforming the module
object, short of recompiling the module via something like require.extensions[".js"]
. If you're going to make it easier to use a custom Module
wrapper simultaneously with this freezing plan, that would be somewhat reassuring.
Even then, I view this as similar to mutating
Element.prototype.*
in browsers, which isn't always reliable if you are trying to change default behaviors.
I view it as more like extending Array.prototype
, which has a long and relatively happy history.
tamper resistant
If you can make it tamper-proof, then it may be a good idea. If the best you can do is "tamper resistant," please don't even bother.
Where would I do that wrapping? Currently Node offers no hooks for transforming the module object, short of recompiling the module via something like require.extensions[".js"]. If you're going to make it easier to use a custom Module wrapper simultaneously with this freezing plan, that would be somewhat reassuring.
In your entry point / bootstrap code you would wrap any loading operation and ship your own loader. Loader hooks are currently in discussion as ESM is a bit tricky to sort out isolation of hooks.
I view it as more like extending Array.prototype, which has a long and relatively happy history.
Disagree since this affects how host objects from the environment itself function. We also have the option of what ECMAScript spec did of setting a null
prototype for ModuleNamespaceObject if that seems preferable. Though that would be a bit slower/memory hungry.
If you can make it tamper-proof, then it may be a good idea. If the best you can do is "tamper resistant," please don't even bother.
The goal is to be tamper-proof, there is no 100% proof of this though. I stick to resistant as the term until then.
In your entry point / bootstrap code you would wrap any loading operation and ship your own loader. Loader hooks are currently in discussion as ESM is a bit tricky to sort out isolation of hooks.
The problem with global loader hooks is that they don't automatically know the current module.id
, which is what makes the Module.prototype
object such a convenient place to put new methods that need that information.
We also have the option of what ECMAScript spec did of setting a null prototype for ModuleNamespaceObject if that seems preferable.
The namespace object is not what I'm talking about. In CommonJS terms, the namespace object is analogous to module.exports
, whereas I'm interested in module
itself.
I'm honestly surprised that Node wouldn't want to implement import(...)
in terms of Module.prototype.import
, since require
is implemented in terms of Module.prototype.require
.
The goal is to be tamper-proof, there is no 100% proof of this though. I stick to resistant as the term until then.
I won't keep arguing this now, because I know you understand my point. Breaking backwards compatibility for the sake of preventing something you can't truly prevent is a murky value proposition. Whenever you have a plan for this up for discussion, I would love to participate.
@bmeck is the plan to freeze them in esm only, leaving cjs as is?
By the way, @bmeck, I apologize if my tone seems hostile. I'm really just a bit surprised, and eager to understand the details. Thanks for all your responses here.
The problem with global loader hooks is that they don't automatically know the current module.id, which is what makes the Module.prototype object such a convenient place to put new methods that need that information.
ESM is not based upon the CJS require('module').Module
. We are not changing any parts of the CJS loader (including not adding support to require(esm)
[in light of import()
]).
The namespace object is not what I'm talking about. In CommonJS terms, the namespace object is analogous to module.exports, whereas I'm interested in module itself.
I'm honestly surprised that Node wouldn't want to implement import(...) in terms of Module.prototype.import, since require is implemented in terms of Module.prototype.require.
Think this is covered by the above statement, not sure though.
I won't keep arguing this now, because I know you understand my point. Breaking backwards compatibility for the sake of preventing something you can't really fully prevent is a murky value proposition. Whenever you have a plan for this up for discussion, I would love to participate.
The goal is to not break any existing CJS system, this mostly means we cannot re-use much of the existing system since it was mutable and/or has different constraints than ESM.
Ok, I think I'm feeling better now! The Module.prototype.*
extensions that I've added are all about simulating ES modules in CommonJS, and I have full confidence Reify won't be needed once everything is native ES modules. In the meantime, I would really appreciate it if require("module").Module.prototype
remained mutable.
All CJS loader components (except CLI resolution on startup) will be untouched. require("module").Module.prototype
will remain mutable.
Moving to #85.