Skip to content
This repository was archived by the owner on Sep 2, 2023. It is now read-only.
This repository was archived by the owner on Sep 2, 2023. It is now read-only.

Is require with ESM planned? #308

Description

@tjcrowder

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)

Activity

  1. MylesBorins commented on Apr 6, 2019

    @MylesBorins
    Contributor
  2. devsnek commented on Apr 6, 2019

    @devsnek
    Member

    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

  3. robpalme commented on Apr 6, 2019

    @robpalme
    Contributor

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

  4. ljharb commented on Apr 6, 2019

    @ljharb
    SponsorMember

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

  5. tjcrowder commented on Apr 6, 2019

    @tjcrowder
    Author

    Wow, never expected to get such quick and complete answers. Thank you all!!

  6. tjcrowder commented on Apr 6, 2019

    @tjcrowder
    Author

    @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...

  7. weswigham commented on Apr 8, 2019

    @weswigham
    Contributor

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

  8. robpalme commented on Apr 8, 2019

    @robpalme
    Contributor

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

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

  9. added a commit that references this issue on Dec 10, 2019
  10. stevenvachon commented on Feb 27, 2021

    @stevenvachon

    Any update on this?

  11. NullDev commented on Oct 29, 2021

    @NullDev

    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.

  12. ljharb commented on Oct 29, 2021

    @ljharb
    SponsorMember

    … or, you could drop those dependencies and find replacements that maintain compatibility.

  13. NullDev commented on Oct 29, 2021

    @NullDev

    @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 to import()'s...

    let { default: fetch } = await import("node-fetch");

    Not really a solution either ^^

  14. ljharb commented on Oct 29, 2021

    @ljharb
    SponsorMember

    @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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions