Feature: Import CommonJS without needing asynchronous syntax
GeoffreyBooth opened this issue · 10 comments
Split off from #114:
Updated feature request description:
Imagine code like this in current Node:
eval(fs.readFileSync('./underscore.min.js')); // Creates `_` variable in global scope
console.log(`Using Underscore version ${_.VERSION}`);This can also be written as:
const _ = require('underscore'); // CommonJS module
console.log(`Using Underscore version ${_.VERSION}`);Both of these examples are written without asynchronous syntax on the user’s part: there are no callbacks, no promises, no await. Perhaps Node is doing asynchronous stuff behind the scenes, but the user is unaware of it. require appears to be as synchronous as readFileSync.
This feature request is that a user’s code to import CommonJS modules via ESM should be like these examples: no callbacks, no promises, no await. This feature request doesn’t concern Node’s implementation under the hood, and whether it is synchronous or asynchronous; the request is only that the user’s code be able to be written in a synchronous style.
Use case 48. Part of #100.
Original feature request description:
ESM imports of CommonJS can be written in a synchronous style:
import { keys } from 'underscore'; // CommonJS package
const dateKeys = keys(Date);The point of this feature is that users can use the same synchronous style of coding (in the vein of fs.readFileSync as opposed to callback- or promise-based alternatives) when the import is of CommonJS. Or put another way, users aren’t required to use await or promises or other asynchronous constructs in code in order to import CommonJS.
Use case 48. Part of #100.
once again... use of static import syntax doesn't infer the handling is happening synchronously...
Note that this feature request isn't asking for imports to work here (like they do in Node.js today) - you're asking for named exports.
Not necessarily; this seems like it’s asking for importing CJS to not always produce a promise - the named import syntax is simply a distraction from this issue.
the commonjs system is entirely synchronous so i'm not sure how one would conclude that a promise would be required to use it
Not necessarily; this seems like it’s asking for importing CJS to not always produce a promise - the named import syntax is simply a distraction from this issue.
@GeoffreyBooth would it be possible to phrase the issue to reflect that if you agree?
Yes, I agree. Once we settle on good wording for #114 I’ll update this one, unless you think we no longer need this issue.
the commonjs system is entirely synchronous so i’m not sure how one would conclude that a promise would be required to use it
In an all-CommonJS environment, sure. This issue refers to importing CommonJS into ESM. As I wrote in #114, during the last meeting someone suggested CommonJS would be imported via import() which would return a promise. This feature request is to ask that that not be the solution, but an “appears to be synchronous” solution be chosen instead.
@GeoffreyBooth I think we can rephrase it as "CJS can be imported statically" if that makes sense. The syntax being used is untimed but appears to fit your criteria.
From this:
CJS can be imported statically
While CJS is "entirely synchronous" it is designed to be imported dynamically (to use the same phrasing), that is it must be executed in order for exported bindings to be determined.
If the aim is to have CJS "imported statically" then the exported bindings will need to be "statically analyzed" without actually executing the code (@bmeck I recall some earlier efforts for opt-in declarative exports via directives).
this seems like it’s asking for importing CJS to not always produce a promise
import()which would return a promise
In essence, the original CJS module system has been operating synchronously, however import('…') and import … from '…' predicate that the loaded resource be asynchronously loaded and hence promise-wrapping the namespaces (even if they are synchronously loadable CJS modules) whereas require('…') synchronously returns the namespace for CJS and cannot do the same for ES modules.
I feel that there is a lot of room for assumptions when using those terms that might require a refinement of terminologies step, like creating a "Stipulative Definitions.md" or just "Terminology.md" something for the group.
@GeoffreyBooth I would say that my main confusion is around the term asynchronous syntax, since the asynchronous bit is more about the required behavior to access something, but some workflows like Promise are not syntax. The feature request is still about being able to use static imports instead of requiring extra glue in order to unwrap the bindings. My presumption is that this feature would still be the same even if it was synchronous in runtime behavior. For example, I think if we had to call a synchronous API .unwrap() to get values of bindings it would fall under the same intended feature:
import {x} from 'y';
console.log('x is', x.unwrap());