441

I'm trying to get the hang of ES6 imports in Node.js and am trying to use the syntax provided in this example:

Cheatsheet Link

I'm looking through the support table, but I was not able to find what version supports the new import statements (I tried looking for the text import/require). I'm currently running Node.js 8.1.2 and also believe that since the cheatsheet is referring to .js files it should work with .js files.

As I run the code (taken from the cheatsheet's first example):

import { square, diag } from 'lib';

I get the error:

SyntaxError: Unexpected token import.

Reference to library I'm trying to import:

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

What am I missing and how can I get node to recognize my import statement?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Jonathan002
  • 7,041
  • 4
  • 23
  • 46
  • 128
    So much for "same language everywhere" when you get stuck on the very first line in half the tutorials out there, using this syntax. – sudo Jul 06 '18 at 23:49
  • 27
    Are we there yet?? – larrydalmeida Sep 14 '18 at 13:39
  • 3
    @Larrydx kind of. Nodejs v13 requires to have package.json somewhere in current or parent directory and `{"type": "module"}` in it and you can use ES6 imports. From doc: `Files ending with .js or lacking any extension will be loaded as ES modules when the nearest parent package.json file contains a top-level field "type" with a value of "module".` Check more here: https://nodejs.org/api/esm.html#esm_package_json_type_field – Lukas Liesis Feb 09 '20 at 18:58
  • Don't, unless you are working in the frontend there is absolutely no need for ESM, they do not add any value whatsoever, nonetheless you will have to install 10 dependencies just to transpile them and for what? Just to use import in. your code? – Madeo Feb 05 '21 at 01:06
  • @Madeo no transpilation seems to be necessary anymore? – herman Mar 24 '21 at 08:24
  • Checkout support for import () the nodejs v16 - https://nodejs.org/api/packages.html – human Apr 26 '21 at 04:14

11 Answers11

440

Node.js has included experimental support for ES6 support. Read more about here: https://nodejs.org/docs/latest-v13.x/api/esm.html#esm_enabling.

TLDR;

Node.js >= v13

It's very simple in Node.js 13 and above. You need to either:

  • Save the file with .mjs extension, or
  • Add { "type": "module" } in the nearest package.json.

You only need to do one of the above to be able to use ECMAScript modules.

Node.js <= v12

If you are using Node.js version 8-12, save the file with ES6 modules with .mjs extension and run it like:

node --experimental-modules my-app.mjs
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
tbking
  • 6,826
  • 2
  • 20
  • 30
  • 3
    @tbking, thank you for the article. It is important to understand what we do. I am sorry, would you please to add the working example? It should be just 2-3 rows: the importing of dependencies, then the importing of the target class and then creating the instance. – Takesi Tokugawa YD Nov 09 '17 at 00:33
  • 2
    For working example see https://stackoverflow.com/a/50641589/984471 – Manohar Reddy Poreddy Jun 01 '18 at 10:40
  • 4
    well I tried this method but inside .mjs file there was a require statement which then threw an error 'require is not defined' or something – Squareoot Mar 04 '19 at 12:58
  • 14
    You can alternatively use .js file extensions if you include `"type": "module"` in your package.json along with the flag `node --experimental-modules my-app.js` – JT Houk Jun 08 '19 at 06:45
  • The .mjs extension is no longer necessary, just use .js straight away – Khaled Osman Jan 10 '20 at 09:21
  • 3
    That answer helped me! I've added an example using with Typescript if someone needs it: https://github.com/Urigo/typescript-node-es-modules-example – Urigo Jan 17 '20 at 11:44
  • a minimalistic example: https://github.com/Nexysweb/node13ecmaScript – John Jan 30 '20 at 05:13
  • 2
    @JTHouk the flag is needed only befor v13, now flag was dropped. Nodejs v13 requires to have package.json somewhere in current or parent directory and `{"type": "module"}` in it and you can use ES6 imports. From doc: `Files ending with .js or lacking any extension will be loaded as ES modules when the nearest parent package.json file contains a top-level field "type" with a value of "module".` Check more here: https://nodejs.org/api/esm.html#esm_package_json_type_field – Lukas Liesis Feb 09 '20 at 18:59
  • 2
    Just remark - when importing modules it is important to write full path and add .js extention to filenames (it is the same behaviour as browser, but not webpack where it completes extentions and /index.js for you) otherwise node with throw an error `code:'ERR_MODULE_NOT_FOUND'` – bFunc Mar 19 '20 at 12:56
  • yarn 2.0.0-rc.32 chokes on this... hm.... – Thom Apr 27 '20 at 20:35
  • Node 14 removed the warning . Still expremental as far as i know . – Hos Mercury Sep 11 '20 at 02:54
  • Adding the `{ "type": "module" }` doesn't work. Do you have a working example for that, and does that only works in Node.js >= v13 ? – Ahsan Farooq Apr 15 '21 at 08:29
315

You can also use npm package called esm which allows you to use ES6 modules in Node.js. It needs no configuration. With esm you will be able to use export/import in your JavaScript files.

Run the following command on your terminal

yarn add esm

or

npm install esm

After that, you need to require this package when starting your server with node. For example if your node server runs index.js file, you would use the command

node -r esm index.js

You can also add it in your package.json file like this

{
  "name": "My-app",
  "version": "1.0.0",
  "description": "Some Hack",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node -r esm index.js"
  },

}

Then run this command from the terminal to start your node server

npm start

Check this link for more details.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Seunope
  • 4,136
  • 2
  • 19
  • 27
170

I just wanted to use the import and export in JavaScript files.

Everyone says it's not possible. But, as of May 2018, it's possible to use above in plain Node.js, without any modules like Babel, etc.

Here is a simple way to do it.

Create the below files, run, and see the output for yourself.

Also don't forget to see Explanation below.

File myfile.mjs

function myFunc() {
    console.log("Hello from myFunc")
}

export default myFunc;

File index.mjs

import myFunc from "./myfile.mjs"  // Simply using "./myfile" may not work in all resolvers

myFunc();

Run

node  --experimental-modules  index.mjs

Output

(node:12020) ExperimentalWarning: The ESM module loader is experimental.

Hello from myFunc

Explanation:

  1. Since it is experimental modules, .js files are named .mjs files
  2. While running you will add --experimental-modules to the node index.mjs
  3. While running with experimental modules in the output you will see: "(node:12020) ExperimentalWarning: The ESM module loader is experimental. "
  4. I have the current release of Node.js, so if I run node --version, it gives me "v10.3.0", though the LTE/stable/recommended version is 8.11.2 LTS.
  5. Someday in the future, you could use .js instead of .mjs, as the features become stable instead of Experimental.
  6. More on experimental features, see: https://nodejs.org/api/esm.html
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Manohar Reddy Poreddy
  • 16,412
  • 7
  • 111
  • 98
59

Using Node.js v12.2.0, I can import all standard modules like this:

import * as Http from 'http'
import * as Fs from 'fs'
import * as Path from 'path'
import * as Readline from 'readline'
import * as Os from 'os'

Versus what I did before:

const
  Http = require('http')
  ,Fs = require('fs')
  ,Path = require('path')
  ,Readline = require('readline')
  ,Os = require('os')

Any module that is an ECMAScript module can be imported without having to use an .mjs extension as long as it has this field in its package.json file:

"type": "module"

So make sure you put such a package.json file in the same folder as the module you're making.

And to import modules not updated with ECMAScript module support, you can do like this:

// Implement the old require function
import { createRequire } from 'module'
const require = createRequire(import.meta.url)

// Now you can require whatever
const
  WebSocket = require('ws')
  ,Mime = require('mime-types')
  ,Chokidar = require('chokidar')

And of course, do not forget that this is needed to actually run a script using module imports (not needed after v13.2):

node --experimental-modules my-script-that-use-import.js

And that the parent folder needs this package.json file for that script to not complain about the import syntax:

{
  "type": "module"
}

If the module you want to use has not been updated to support being imported using the import syntax then you have no other choice than using require (but with my solution above that is not a problem).

I also want to share this piece of code which implements the missing __filename and __dirname constants in modules:

import {fileURLToPath} from 'url'
import {dirname} from 'path'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • 1
    v12.11.1 doesn't support import without tweaks – Alexey Sh. Oct 09 '19 at 10:08
  • @AlexeySh. The methods I described above works fine in that version, I just tested. – Joakim L. Christiansen Oct 11 '19 at 17:05
  • I tested it too. And it doesn't work. – Alexey Sh. Oct 11 '19 at 21:31
  • Did you use the --experimental-modules flag? And did you put a package.json file in the same folder or a parent folder with {"type": "module"} ? What is the error you get? – Joakim L. Christiansen Oct 12 '19 at 08:19
  • 2
    if you will read my initial comment carefully, you can see there the following words "import without tweaks". that means no flags and no configs. this is the main point of my comment. – Alexey Sh. Oct 12 '19 at 20:10
  • 5
    @AlexeySh. Well, it was a very weird thing to comment on what I wrote, since I didn't state anything else. – Joakim L. Christiansen Oct 15 '19 at 06:18
  • 3
    As of Node v. 13 the experimental flag is not needed. https://nodejs.org/api/esm.html – Robin Nov 25 '19 at 10:58
  • FYI: As of Node.js 12.17.0, the `--experimental-modules` flag is no longer necessary to use ECMAScript modules [source](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V12.md#2020-05-26-version-12170-erbium-lts-targos) – Mark Thomson Aug 14 '20 at 19:27
  • this is not working: `internal/modules/esm/resolve.js:61 let url = moduleWrapResolve(specifier, parentURL); ` – Madeo Feb 05 '21 at 01:14
  • @Madeo I would need to see your script if you want help with this, or at least the full error message / stack trace. Does this help? https://stackoverflow.com/questions/56723678/node-experimental-modules-error-cannot-find-module – Joakim L. Christiansen Feb 06 '21 at 21:18
23

If you are using the modules system on the server side, you do not need to use Babel at all. To use modules in Node.js ensure that:

  1. Use a version of node that supports the --experimental-modules flag
  2. Your *.js files must then be renamed to *.mjs

That's it.

However and this is a big however, while your shinny pure ES6 code will run in an environment like Node.js (e.g., 9.5.0) you will still have the craziness of transpilling just to test. Also bear in mind that Ecma has stated that release cycles for JavaScript are going to be faster, with newer features delivered on a more regular basis. Whilst this will be no problems for single environments like Node.js, it's a slightly different proposition for browser environments. What is clear is that testing frameworks have a lot to do in catching up. You will still need to probably transpile for testing frameworks. I'd suggest using Jest.

Also be aware of bundling frameworks. You will be running into problems there.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Zardoz
  • 334
  • 2
  • 7
  • 2
    Do you have any documentation for this? – js1568 Mar 22 '18 at 18:09
  • This is for node 9.X but will apply down to v8.6 I believe https://nodejs.org/dist/latest-v9.x/docs/api/all.html#esm_enabling – Zardoz Mar 26 '18 at 17:02
  • Why do you suggest Jest? I've had this same problem with Jest. Can't for example use this library with Jest: https://github.com/mrichar1/jsonapi-vuex/issues/104. Do you have a way to make it work? – geoidesic Nov 05 '19 at 21:06
20

Use:

  "devDependencies": {
    "@babel/core": "^7.2.0",
    "@babel/preset-env": "^7.2.0",
    "@babel/register": "^7.0.0"
  }

File .babelrc

{
  "presets": ["@babel/preset-env"]
}

Entry point for the Node.js application:

require("@babel/register")({})

// Import the rest of our application.
module.exports = require('./index.js')

See How To Enable ES6 Imports in Node.js

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
zloctb
  • 8,712
  • 5
  • 60
  • 76
15

You may try esm.

Here is some introduction: esm

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Losses Don
  • 773
  • 9
  • 12
9

Using the .mjs extension (as suggested in the accepted answer) in order to enable ECMAScript modules works. However, with Node.js v12, you can also enable this feature globally in your package.json file.

The official documentation states:

import statements of .js and extensionless files are treated as ES modules if the nearest parent package.json contains "type": "module".

{
  "type": "module",
  "main": "./src/index.js"
}

(Of course you still have to provide the flag --experimental-modules when starting your application.)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
B12Toaster
  • 8,944
  • 5
  • 48
  • 48
8

Back to Jonathan002's original question about

"... what version supports the new ES6 import statements?"

based on the article by Dr. Axel Rauschmayer, there is a plan to have it supported by default (without the experimental command line flag) in Node.js 10.x LTS. According to node.js's release plan as it is on 3/29, 2018, it's likely to become available after Apr 2018, while LTS of it will begin on October 2018.

codeful.element
  • 232
  • 2
  • 7
  • 2
    has it been added yet :? – Rana Tallal Apr 10 '18 at 18:26
  • 5
    @RanaTallal Node 10 api still shows the flag – tbking Apr 30 '18 at 13:34
  • @RanaTallal: I was just wondering too. node indeed still has the flag. But I'm currently studying for an Angular 6 course, and I use imports like `import { FormsModule } from '@angular/forms';` all the time. And Angular runs on node. I'm confused. – Michael Oct 21 '18 at 13:25
  • these kind of imports known as es6 are supported from 10x onwards. Which is now in LTS so you probably are using 10x or 11x. Thats the reason for you using these imports without any complications... So I am guessing they removed it somewhere in 10."x" – Rana Tallal Nov 01 '18 at 07:02
8

Solution

https://www.npmjs.com/package/babel-register

// This is to allow ES6 export syntax
// to be properly read and processed by node.js application
require('babel-register')({
  presets: [
    'env',
  ],
});

// After that, any line you add below that has typical ES6 export syntax
// will work just fine

const utils = require('../../utils.js');
const availableMixins = require('../../../src/lib/mixins/index.js');

Below is definition of file *mixins/index.js

export { default as FormValidationMixin } from './form-validation'; // eslint-disable-line import/prefer-default-export

That worked just fine inside my Node.js CLI application.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
DmitrySemenov
  • 6,544
  • 10
  • 41
  • 64
5

I don't know if this will work for your case, but I am running an Express.js server with this:

nodemon --inspect ./index.js --exec babel-node --presets es2015,stage-2

This gives me the ability to import and use spread operator even though I'm only using Node.js version 8.

You'll need to install babel-cli, babel-preset-es2015, and babel-preset-stage-2 to do what I'm doing.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
unflores
  • 1,444
  • 14
  • 29