Node.js plans to support import/export ES6 (ECMAScript 2015) modules

93,620

Node.js 13.2.0 & Above

Node.js 13.2.0 now supports ES Modules without a flag 🎉. However, the implementation is still marked as experimental so use in production with caution.

To enable ECMAScript module (ESM) support in 13.2.0, add the following to your package.json:

{
  "type": "module"
}

All .js, .mjs (or files without an extension) will be treated as ESM.

There are a number of different options other than entire package.json opt-in, all of which are detailed in the documentation for 13.2.0.

Node.js 13.1.0 & Below

Those still using older versions of Node may want to try the [esm][3] module loader, which is a production-ready implementation of the ES Modules Specificaiton for Node.js:

node -r esm main.js

Detailed Updates...

23 April 2019

A PR recently landed to change the way ECMAScript modules are detected: https://github.com/nodejs/node/pull/26745

It's still behind the --experimental-modules flag, but there are major changes in the way modules can be loaded:

  • package.type which can be either module or commonjs
    • type: "commonjs":
      • .js is parsed as CommonJS
      • the default for an entry point without an extension is CommonJS
    • type: "module":
      • .js is parsed as an ECMAScript module
      • does not support loading JSON or a native module by default
      • the default for an entry point without an extension is ECMAScript module
  • --type=[mode] to let you set the type on entry point. Will override package.type for entry point.
  • A new file extension .cjs.
    • this is specifically to support importing CommonJS in the module mode.
    • this is only in the ECMAScript module loader, the CommonJS loader remains untouched, but the extension will work in the old loader if you use the full file path.
  • --es-module-specifier-resolution=[type]
    • options are explicit (default) and node
    • by default our loader will not allow for optional extensions in the import, the path for a module must include the extension if there is one
    • by default our loader will not allow for importing directories that have an index file
    • developers can use --es-module-specifier-resolution=node to enable the CommonJS specifier resolution algorithm
    • This is not a “feature”, but rather an implementation for experimentation. It is expected to change before the flag is removed
  • --experimental-json-loader
    • the only way to import JSON when "type": "module"
    • when enable all import 'thing.json' will go through the experimental loader independent of mode
    • based on whatwg/html#4315
  • You can use package.main to set an entry point for a module
    • the file extensions used in main will be resolved based on the type of the module

17 January 2019

Node.js 11.6.0 still lists ES Modules as experimental, behind a flag.

13 September 2017

Node.js 8.5.0 has been released with support for mjs files behind a flag:

node --experimental-modules index.mjs

The plan for this is to remove the flag for the v10.0 LTS release.

--Outdated Information. Kept here for historical purposes--

8 September 2017

The Node.js master branch has been updated with initial support for ESM modules: https://github.com/nodejs/node/commit/c8a389e19f172edbada83f59944cad7cc802d9d5

This should be available in the latest nightly (this can be installed via nvm to run alongside your existing install): https://nodejs.org/download/nightly/

And enabled behind the --experimental-modules flag:

package.json

{
  "name": "testing-mjs",
  "version": "1.0.0",
  "description": "",
  "main": "index.mjs" <-- Set this to be an mjs file
}

Then run:

node --experimental-modules .

February 2017:

An Update on ES6 Modules in Node.js

The Node.js guys have decided that the least bad solution is to use the .mjs file extension. The takeaway from this is:

In other words, given two files foo.js and bar.mjs , using import * from 'foo' will treat foo.js as CommonJS while import * from 'bar' will treat bar.mjs as an ES6 Module

And as for timelines...

At the current point in time, there are still a number of specification and implementation issues that need to happen on the ES6 and Virtual Machine side of things before Node.js can even begin working up a supportable implementation of ES6 modules. Work is in progress but it is going to take some time — We’re currently looking at around a year at least.

October 2016:

One of the developers on Node.js recently attended a TC-39 meeting and wrote up a superb article on the blockers to implementing for Node.js:

Node.js, TC-39, and Modules

The basic take-away from that is:

  • ECMAScript modules are statically analyzed, and CommonJS are evaluated
  • CommonJS modules allow for monkey-patching exports, and ECMAScript modules currently do not
  • It's difficult to detect what is an ECMAScript module and what is CommonJS without some form of user input, but they are trying.
  • *.mjs seems the most likely solution, unless they can accurately detect an ECMAScript module without user-input

-- Original Answer --

This has been a hot potato for quite some time. The bottom line is that yes, Node.js will eventually support the ES2015 syntax for importing/exporting modules - most likely when the specification for loading modules is finalized and agreed upon.

Here is a good overview of what's holding Node.js up. Essentially, they need to make sure that the new specification works for Node.js which is primarily conditional, synchronous loading and also HTML which is primarily asynchronous.

Nobody knows for sure right now, but I imagine Node.js will support import/export for static loading, in addition to the new System.import for dynamic loading - while still keeping require for legacy code.

Here's a few proposals on how Node might achieve this:

Share:
93,620

Related videos on Youtube

Zorgatone
Author by

Zorgatone

I just love code, videogames and TTRPGs, and I like C/C++, C# and JavaScript. At the moment I'm a student at the University of Turin, and I also work as a Senior Full Stack Web Developer / Consultant at Orbyta Srl. I am studying to switch to videogames programming and try to one day make my own videogame business a reality :) helloworld.c: /* * File: helloworld.c * Description: Greets the StackExchange community */ #include &lt;stdio.h&gt; int main(void) { puts("Hello, World!"); return 0; }

Updated on July 11, 2022

Comments

  • Zorgatone
    Zorgatone almost 2 years

    I've been looking all over the Internet without a clear answer for this.

    Currently Node.js uses only CommonJS syntax to load modules, and if you really want to use the standard ECMAScript 2015 modules syntax, you either have to transpile it beforehand or use an external module loader at runtime.

    Currently I'm not too positive to use either of those two methods, are the Node.js maintainers even planning to support ECMAScript 2015 modules or not? I haven't found an hint at all about this.

    At the moment Node.js 6.x claims to support 96% of the ECMAScript 2015 features, but there isn't any reference to modules (Node.js ECMAScript 2015 support link).

    Do you know if Node.js will support these modules out of the box, in the near future?

    • Felix Kling
      Felix Kling almost 8 years
      Googling for node es2015 modules, shows the following as one of the top results: github.com/nodejs/node/wiki/ES6-Module-Detection-in-Node .
    • Felix Kling
      Felix Kling almost 8 years
      Personally I would vote to close this question as "too localized", but that close reason doesn't exist anymore. Let's say Node manages to implement ES6 modules tomorrow. Are you going to delete your question then, because it is not relevant anymore? Or will you at least update it? I don't think a question is suitable for SO if you already know that it will be outdated or needs to be updated "soon". But that's just my opinion and there seem to be others who think the opposite, which is ok for me :) (fwiw, by itself the question is of course important and interesting)
    • Wonko the Sane
      Wonko the Sane about 7 years
      I wish there was a place in the Stack Universe for questions like this, though. It may not technically fit here, but I don't know that I agree that it's really "opinion based" either. OP is looking for a specific answer to a specific question, but one that will be (at some point) outdated.
    • Joe Lapp
      Joe Lapp over 6 years
      Alternative phrasing of this question: "What is the state of node.js support for ES6 modules?" The answer to many SO questions change over time as tech evolves. I too had trouble finding the answer until I landed here.
    • Muhammad Umer
      Muhammad Umer about 6 years
      @FelixKling i guess you wanting to close this question would have been a very wrong decision as it's a problem 211 people so far found to be problematic enough to vote up.
  • Jeewes
    Jeewes about 7 years
    About .mjs extension: We have affectionately called these “Michael Jackson Script” files in the past. Just in case you hear someone talking about pop artists during JS talk.
  • Corey Alix
    Corey Alix about 7 years
    I'm not seeing why changing the import syntax isn't enough. One syntax for importing es (the 'correct' one) and one for importing cjs? In other words, given two files foo.js and bar.js , import * from 'foo' will treat foo.js as CommonJS import * as bar from 'bar' will treat bar.js as an ES6 Module Can someone explain?
  • CodingIntrigue
    CodingIntrigue almost 7 years
    @CoreyAlix Syntax generally won't be changed in order to support an environment. This really only affects Node after all. Also, the syntax is a bit non-intuitive. How do I access the exports in your proposed syntax?
  • Corey Alix
    Corey Alix almost 7 years
    To be clear, it's not "my" proposal, I'm copying what typescript is already doing. To answer your question, you access the exports with "bar": bar.foobar() import {foo as Foo} from “./foo” is the mechanism for identifying an ES6 module. var Foo = require(“./foo”) is the mechanism for identifying the CJS module. In Typescript the commonjs output looks like this: var mod1_1 = require("./mod1"); exports.mod1 = mod1; The ES6 output looks like this: import { mod1 } from “./mod1”; export { mod1 }
  • Corey Alix
    Corey Alix almost 7 years
    @CodingIntrigue, Let me try again. If typescript is configured to output "commonjs" then import * as Counter from './counter'; is transpiled as var Counter = require("./counter"); and if I change typescript to output "es6" the same import is transpiled as import * as Counter from './counter';. So when I said the 'correct' one I meant the standard ES6 syntax using Typescript as my authority. If that standard ES6 syntax doesn't conflict with nodejs then perhaps that is all that is needed to identify a module as an ES6 module?
  • Mikhail
    Mikhail over 6 years
    So, we will not have ES6 modules. Only with .mjs files? We'll still need to use Wepback and es2015 presets only to use es6 modules in the normal form. I think most of modules developers (modules like express) will not migrating to ES6 modules. Thus for what we need ES6 modules in Node.js in this case?
  • CodingIntrigue
    CodingIntrigue over 6 years
    @Mikhail It's only renaming a file. Additionally, import dependency from "./file" will work if the file is file.mjs. Most module developers will eventually migrate to ESModules, as they provide some serious benefits.
  • Enrico Giurin
    Enrico Giurin almost 6 years
    Thanks, it works. Just makes sure to have both files (that one which export variables, const and that one which use them) with extension .mjs