Repository navigation
Is require with ESM planned? #308
Description
Activity
- There are currently no plans on the road map to support requiring esm Some members of the team are exploring the possibility but I am personally skeptical of the ability to do so The biggest challenge is moving from and async loader to a sync loader and back or sync to async to sync. Something we have referred to as zebra striping. It introduces all sorts of problems including deadlock and cache duplication.…On Sat, Apr 6, 2019, 12:07 PM T.J. Crowder ***@***.***> wrote: I'm embarrassed to say that I can't quite tell from the Plan for New Modules Implementation <https://github.com/nodejs/modules/blob/master/doc/plan-for-new-modules-implementation.md> . Suppose I have a project where I'm using the CommonJS default, and I install a module with "type": "module" in its package.json from npm. Is the current plan that I'll be able to use require to load exports from that ESM module? (Or similarly, if I just try to require an .mjs file...) (To be clear: This is purely a question, not a veiled suggestion or criticism. Here's an unveiled comment, though: *Thank you* for the updated ESM stuff!) It doesn't work with the v12 nightlies, but hey, it's nightlies, stuff is in flux. :-) My test setup, in case I'm just doing it wrong: index.js: const { foo } = require("foo"); foo("Hi"); node_modules/foo/index.js: export function foo(...args) { console.log("foo:", ...args); } node_modules/foo/package.json: { "type": "module", "name": "foo", "version": "1.0.0", "main": "index.js", "license": "MIT" } Command: node12 --experimental-modules index.js Result: (node:20699) ExperimentalWarning: The ESM module loader is experimental. /home/blah/blah/node_modules/foo/index.js:1 export function foo(...args) { ^^^^^^ SyntaxError: Unexpected token export at Module._compile (internal/modules/cjs/loader.js:768:23) at Object.Module._extensions..js (internal/modules/cjs/loader.js:835:10) at Module.load (internal/modules/cjs/loader.js:693:32) at Function.Module._load (internal/modules/cjs/loader.js:620:12) at Module.require (internal/modules/cjs/loader.js:731:19) at require (internal/modules/cjs/helpers.js:14:16) at Object. (/home/tjc/temp/esmcheck/index.js:1:17) at Module._compile (internal/modules/cjs/loader.js:824:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:835:10) at Module.load (internal/modules/cjs/loader.js:693:32) — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub <#308>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AAecV5TL4vgOurOA-5m8zAN8rpCRKXxfks5veMZNgaJpZM4cgRwj> .Reacted by T.J. Crowder, nopeless and Tom GReacted by Wesley Wigham, tommyZZM and Serhii
we do have a design for allowing require(esm) but it's still in the early stages.
as a side note, i would not advise mixing cjs and esm in the same extension within a package
Reacted by T.J. CrowderEven if
require(esm)was provided today it becomes hazardous to use over time as async-to-eval modules get introduced. For example a JS module using top-level await or a WebAssembly module. This can happen in dependencies you don't control, so it's not easy to defend against this hazard.For this reason I'm also skeptical that this feature could be provided in a safe way that guaranteed future compatibility.
The safe way for CJS to import ESM is dynamic
import().Reacted by T.J. Crowder and snekThere also remains the possibility of dual modules, so you’d be able to require or import them.
Reacted by T.J. Crowder and Rob PalmerWow, never expected to get such quick and complete answers. Thank you all!!
@devsnek -
as a side note, i would not advise mixing cjs and esm in the same extension within a package
LOL no. That would be...bad...
Even if require(esm) was provided today it becomes hazardous to use over time as async-to-eval modules get introduced. For example a JS module using top-level await or a WebAssembly module. This can happen in dependencies you don't control, so it's not easy to defend against this hazard.
This is baseless fearmongering, IMO - the synchronicity (or lack thereof) of the execution of a module has no bearing on the synchronicity of the resolution of the module graph containing it; which is all that matters for pulling on the correct namespace objects. Introducing async evaluation does introduce the possibility of witnessing namespaces whose members do not yet have final values, but this is already possible in CJS today, eg with
setTimeout(() => module.exports = {x: 12}, 100);
nodejs/node#49450 has a proposal allowing
require(esm)and this is a partial implementation (a proof of concept that integrating arequire(esm)is certainly possible), with full support of "zebra striping" as it is called. Cache duplication isn't an issue when there's only one canonical loader for each cache entry (unlike a dual impl model where "cache duplication" is replaced with "implementation duplication" which is even worse), and deadlocking isn't a problem provided the code withinnodeitself that's doing the syncification doesn't introduce async dependencies that can deadlock (user code cannot produce a deadlock, pending the final design of loaders with might be within the syncificed pipeline and thus may need some extra (already desired) context separation).The fears around it are overblown, IMO.
Reacted by Alex@weswigham Thanks for highlighting the proposal. Let's continue main discussion there.
I look forwards to learning how you can eliminate the hazard 😉
- added a commit that references this issue
on Dec 10, 2019 Any update on this?
This would be really nice. Many dependencies are dropping require for ESM. So once we update, we'd have to change all the CommonJS imports to ESM.
… or, you could drop those dependencies and find replacements that maintain compatibility.
@ljharb So, rewriting the whole code instead of changing the imports? 😄
Why not just add the ability to load ESM with commonjs.I mean, while at it why not just replace all the
require()'s toimport()'s...let { default: fetch } = await import("node-fetch");
Not really a solution either ^^
Reacted by Eric, João Lenon and Gabriel Moreira@NullDev no, rewriting the code is what you'd need to do to keep using a dep in CJS or transpiled ESM that dropped support for CJS. Changing the imports is the easy path - iow, using a different dependency. TLA isn't available outside of native ESM, so your solution wouldn't work.
I'm embarrassed to say that I can't quite tell from the Plan for New Modules Implementation.
Suppose I have a project where I'm using the CommonJS default, and I install a module with
"type": "module"in itspackage.jsonfromnpm. Is the current plan that I'll be able to userequireto load exports from that ESM module? (Or similarly, if I just try torequirean.mjsfile...)(To be clear: This is purely a question, not a veiled suggestion or criticism. Here's an unveiled comment, though: Thank you for the updated ESM stuff!)
It doesn't work with the v12 nightlies, but hey, it's nightlies, stuff is in flux. :-)
My test setup, in case I'm just doing it wrong:
index.js:node_modules/foo/index.js:node_modules/foo/package.json:{ "type": "module", "name": "foo", "version": "1.0.0", "main": "index.js", "license": "MIT" }Command:
Result:
(node:20699) ExperimentalWarning: The ESM module loader is experimental. /home/blah/blah/node_modules/foo/index.js:1 export function foo(...args) { ^^^^^^ SyntaxError: Unexpected token export at Module._compile (internal/modules/cjs/loader.js:768:23) at Object.Module._extensions..js (internal/modules/cjs/loader.js:835:10) at Module.load (internal/modules/cjs/loader.js:693:32) at Function.Module._load (internal/modules/cjs/loader.js:620:12) at Module.require (internal/modules/cjs/loader.js:731:19) at require (internal/modules/cjs/helpers.js:14:16) at Object. (/home/tjc/temp/esmcheck/index.js:1:17) at Module._compile (internal/modules/cjs/loader.js:824:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:835:10) at Module.load (internal/modules/cjs/loader.js:693:32)