Modules: Is `require` with ESM planned?

Created on 6 Apr 2019  Â·  9Comments  Â·  Source: nodejs/modules

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 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)

Most helpful comment

There also remains the possibility of dual modules, so you’d be able to require or import them.

All 9 comments

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 notifications@github.com 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
https://github.com/nodejs/modules/issues/308, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAecV5TL4vgOurOA-5m8zAN8rpCRKXxfks5veMZNgaJpZM4cgRwj
.

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

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.

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().

There also remains the possibility of dual modules, so you’d be able to require or import them.

Wow, 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);

299 has a proposal allowing require(esm) and this is a partial implementation (a proof of concept that integrating a require(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 within node itself 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.

@weswigham Thanks for highlighting the proposal. Let's continue main discussion there.

I look forwards to learning how you can eliminate the hazard 😉

Any update on this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

GeoffreyBooth picture GeoffreyBooth  Â·  4Comments

GeoffreyBooth picture GeoffreyBooth  Â·  5Comments

arlac77 picture arlac77  Â·  3Comments

Jamesernator picture Jamesernator  Â·  4Comments

GeoffreyBooth picture GeoffreyBooth  Â·  4Comments