How can i use es6 modules in node.js - node.js

I'm trying to import express into my project using ES6 syntax, but it keeps giving me the error:
import express from "express";
SyntaxError: Unexpected identifier
I have done a bit of research and found where people were saying to add:
"type":"module"
to my package.json file, which I have done:
...
"description": "Shopping list to learn the MERN stack",
"type": "module",
"main": "server.js",
"scripts": {
"start": "node server.js",
"server": "nodemon server.js"
},
...
But I'm still getting the same issue. I would prefer to use the import ES6 syntax rather than this syntax.
const express = require('express')

Solution:
For me the best, easiest and most compatible way to resolve (dated to September 2019) was to:
npm install esm
ensure that the script is run by node with the -r esm option.
Simple as that.
The Node way:
(according to the ECMAScript Modules Documentation)
Other than the "type": "module" property into package.json NodeJS requires the scripts to be run via node --experimental-modules <your_script.js>.
Code into either .mjs or .js extension files will be treated with the support for new ES6. Renaming that I find pretty inconvenient.
Note: "type": "module" is particularly important for supporting .js files. Read the linked documentation.
The solution provided by NodeJS with the --experimental-modules flag didn't work for me in all cases. 🤷‍♂️
Adding the esm packages is more maintainable; and reliable. And can be easily/quickly removed once the ESM support won't be experimental anymore.
Offtopic tip:
If you want to run tests on your ES6 import/export code, I suggest to rely on a solid/flexible framework, like Mocha.
You can follow this instructions to run Mocha tests supporting ES6.
Trying with Jest I wasn't able to run test successfully. Apparently they don't want to directly support the experimental ES6 and suggest to transpile the code (thing that I hate).
References:
https://alxgbsn.co.uk/2019/02/22/testing-native-es-modules-mocha-esm/
https://timonweb.com/tutorials/how-to-enable-ecmascript-6-imports-in-nodejs/
https://nodejs.org/api/esm.html#esm_ecmascript_modules

If you try to run Node js app with ES6 syntax, you will encounter this kind of error
SyntaxError: Cannot use import statement outside a module
let's fix that
install three main packages
npm install --save-dev #babel/core #babel/preset-env #babel/node
npm install --save-dev nodemon
add .babelrc file with this
{ "presets": ["#babel/preset-env"] }
and this in package.json
"start": "nodemon --exec babel-node app.js"

Ok, so, after doing even more research on the ES6 way, this is what worked for me. It's a combination of an answer above and the following:
package.json
node --experimental-modules server.mjs
server.mjs
import dotenv from 'dotenv'
dotenv.config()
const db = process.env.MONGO_URI

I've created a nodejs boilerplate supporting es6 module.
https://github.com/jasonjin220/es6-express-rest-api-boilerplate

You need Babel transpiler to do this, as Node.js doesn't support es6.
First add these packages to your project :
npm install --save-dev babel-cli babel-preset-es2015 rimraf
Then, create a .babelrc file , this is used by Babel transpiler to know some information during transpilation.
touch .babelrc
Then, add this line to .babelrc :
{
"presets": ["es2015"]
}
This tells your Babel to use es6 presets as per 2015 standard.
Then, modify the package.json and tell it to use transpiler.
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "rimraf dist/ && babel ./ --out-dir dist/ --ignore ./node_modules,./.babelrc,./package.json,./npm-debug.log --copy-files",
"start": "npm run build && node dist/index.js"
},
You have to add the following to script section, this line tells when you run npm run start , the transpiler will first convert the es6 syntax to minmal JavaScript one, later that is used.
Check your dependencies :
"dependencies": {
"express": "^4.15.2",
"morgan": "^1.8.1",
"rimraf": "^2.6.1"
}
And Dev dependencies :
"devDependencies": {
"babel-cli": "^6.24.0",
"babel-preset-es2015": "^6.24.0"
}
This should work, comment if there is any issue.

You need to use Babel,
npm install --save-dev #babel/plugin-proposal-class-properties
then for usage create a .babelrc file if you don't have it, with options:
{"plugins": ["#babel/plugin-proposal-class-properties"]}
then ad devDependencies in package.json
"#babel/plugin-proposal-class-properties": "^7.5.5",
that should do the job.

Related

Using Sequelize CLI with the ESM package

Is there a way to execute Sequelize migrations with the ESM package? It requires the -r esm command line argument to be passed, but I don't seem to find that in their docs
I had the same issue and the way I got around it is by calling sequelize-cli from the node_modules folder.
In my package.json
{
"scripts": {
"migrate": "node --require esm ./node_modules/.bin/sequelize-cli db:migrate"
}
}
then if you run npm run migrate it should work with es6 imports.

How do I globally install a node-cli tool with es6 import support?

I am creating a simple project-generator tool and I am using es6 imports in my main index.js file. I have a babel-node installed with a .babelrc at the root of my project.
To install the tool globally I have this in my package.json:
"bin": {
"generate": "./node_modules/.bin/babel-node ./index.js"
},
When I run npm link or npm install -g at the root of my project, I get the error:
ENOENT: no such file or directory, chmod '/usr/local/lib/node_modules/node-project-generator/node_modules/.bin/babel-node ./index.js'
I have also tried:
"bin": {
"generate": "babel-node ./index.js"
},
and
"bin": {
"generate": "npx babel-node ./index.js"
},
This tool is working just fine in the context of my project. So how do I add es6 support to install it globally?
Have you added babel-node as a dependency to your package?
Anyway, the more proper way of doing it is to compile your program with babel into some dist directory in advance so that it can be run without any additional pre-processor.
"bin": {
"generate": "./dist/index.js"
}
Less problematic and faster command execution.

How to make babel transpile modules linked using "npm link"

I have a lot of code in my currently project(my-project) that will be needed in a new one. This code has been written in ES6 and transpiled after with babel.
I've created a module called "my-module" with this shared code and linked it to "my-project" using npm link
The problem is that when I start the project, the code from "my-module" is not beening tranpiled and throws an error right at the import statement.
The code inside my-module will be edited a lot. How to make it works?
package.json
"scripts": {
"start": "nodemon bin/dev",
"clean": "rm -rf dist",
"build": "yarn run clean && mkdir dist && babel src -s -d dist",
"production": "yarn run build && node bin/production"
},
.babelrc
{
"presets": ["es2015", "stage-2"]
}
The linked project will not find your .babelrc file. You have a few options:
Place a .babelrc in my-project or a higher level directory. This will require the babel plugins to be installed in that module though (or globally)
If you're using a build tool (i.e. webpack, browserify, etc.) you can declare the babel config there.
This comment helped me out: https://github.com/babel/gulp-babel/issues/93#issuecomment-249172581
My project uses browserify, so declaring the same plugins in the build script that are in .babelrc and invoking require.resolve caused the npm linked project to be transpiled correctly.
return browserify.transform('babelify', {
sourceMaps: true,
global:true,
plugins: ['babel-preset-es2015'].map(require.resolve)
})

How do I deploy my Typescript Node.js app to Heroku?

When testing locally I was previously running:
"build-live": "nodemon --exec ./node_modules/.bin/ts-node -r dotenv/config -- ./index.ts"
I then figured my Procfile should be something like:
web: ./node_modules/.bin/ts-node -- ./index.ts
But it says module 'typescript' not found, even when it is in package.json. I read in a few places that ts-node is not the way to go to deploy to Heroku, so I am not sure what to do.
UPDATE: I think I am supposed to compile it, so I tried:
web: ./node_modules/.bin/tsc --module commonjs --allowJs --outDir build/ --sourceMap --target es6 index.ts && node build/index.js
This succeeds, however when actually running it, a bunch of the libs I'm using get "Cannot find module '...'".
Alternatively you can have the TypeScript compile as a postinstall hook and run node build/index.js as the only Procfile command:
Your package.json should contain a postinstall hint that gets executed after npm install and before the node process launches:
"scripts": {
"start": "node build/index.js",
"build": "tsc",
"postinstall": "npm run build"
}
You can then leave your Procfile as is:
web: npm start
This 'build on deploy' approach is documented by Heroku here.
The command you've given Heroku is to launch the web "process" by compiling index.ts and dependencies and starting node at index.js. Depending on how things are timed, index.js might or might not exist at the time node starts.
You need to already have your sources compiled by the time you want to start your app. For example, web should just be web: node index.js or similar.
Each build process is different, so you need to figure that out for your own setup. But, suppose you have a classical setup where you push to git and then Heroku picks up that change and updates the app with the new slug. You could just compile things locally and include index.js and any other build output in the repository, for it to be available in the slug for Heroku to use.
A better approach is to use a build server which has an integration with Heroku. After you do the build there, configure it to send the build results to Heroku. Travis has a straighforward setup like this. This way you don't need to include build outputs in your repository, which is considered an anti-pattern.
On a sidenode, try using a tsconfig.json to keep the tsc configuration. It will save you from having to write such long command lines all over the place.
Fabian said that we could do something like:
"scripts": {
"start": "node build/index.js",
"build": "tsc",
"postinstall": "npm run build"
}
As of me writing this, I tested this and can state: postinstall is not required since build script is ran by Heroku. If you want to do it without build script, then you can use heroku-postbuild which will run after dependencies are installed there you run tsc to compile.
My problem was about missing Typescript npm modules. The Typescript compiler tsc was not found when deployed the app to Heroku.
The Heroku deploy process (rightly) does not install development dependencies, in my case the Typescript module was part of devDependencies and thus the tsc command was not running on the Heroku platform.
Solution 1
Add typescript to dependencies: npm i typescript -s
Solution 2
Open Heroku console:
Select console type:
Run the command npm i typescript && npm run tsc
Install typescript as a dev dependency (cf. https://www.typescriptlang.org/download). Once built, your app does not need typescript anymore!
npm install -D typescript
Then in your package.json:
{
"main": "index.js", // <- file will be generated at build time with `tsc`
"scripts": {
"build": "tsc",
"start": "node ."
"start:dev": "ts-node index.ts" // idem, install ts-node as a dev dependency
}
}
The key point here is "build": "tsc".
Why?
Heroku does install all dependencies during build and remove the dev dependencies before the app is deployed (source here).
Node.js deployments will automatically execute an app’s build script during build (since March 11. 2019 source here)
In package.json
"scripts": {
"tsc": "./node_modules/typescript/bin/tsc",
"postinstall": "npm run tsc"
},
Works for me for Heroku deployment.
Installing typescript npm install -D typescript and writing tsc in the build script "build": "tsc", does not work for me. Also, try to run npm i typescript && npm run tsc in the Heroku console which also does not work.
In my case, I remove some dependencies from "devDependencies" to "dependencies", so it goes like this:
"dependencies": {
// The other dependencies goes here, I don't touch them.
// But all TS dependencies I remove to here.
"ts-node": "^9.1.1",
"tsconfig-paths": "^3.9.0",
"typescript": "^4.2.3",
"ts-loader": "^8.0.18"
},

Node error: SyntaxError: Unexpected token import

I don't understand what is wrong. I checked other forum talking about transpilation and babel. What do I have to do?
node -v
v5.5.0
my code:
import recast from 'recastai'
and the error
(function (exports, require, module, __filename, __dirname) { import recast from 'module1'
^^^^^^
SyntaxError: Unexpected token import
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:387:25)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:139:18)
at node.js:999:3
ES6 imports are a recently introduced feature and the current stable version of Node does not support them yet. Node.js issue tracker has an open issue for this - but until V8 and Node add support for this feature, you will need to use a transpiler (most popular one being babel) to be able to use imports.
For quickly trying out transpilation, babel provides a web based REPL. This one demonstrates your code being transpiled.
The babel project homepage points to the relevant resources for getting started with Babel and integrating it with your development workflow.
For the simplest setup, visit this setup page and select CLI in the Babel built-ins section.
This basically involves three simple steps:
Install babel-cli : npm install --save-dev babel-cli babel-preset-es2015
Create .babelrc configuration file: echo '{ "presets": ["es2015"] }' > .babelrc
Use the installed module to transpile your source code: ./node_modules/.bin/babel src -d lib
The aforementioned setup page also illustrates how to add an npm script to simplify the last step. Alternatively you can integrate babel with your editor or build chain so that your files are automatically compiled on change.
In case you don't want to deal with babel. This one worked for me.
const calc = require('./my_calc');
let {add, multiply} = calc;
1) Install the latest presets
yarn add --dev babel-preset-latest
2) Create .babelrc and add the following
{
"presets": ["latest"]
}
3) Run your script
npx babel-node yourscript.js
Or in your package.json file add
"scripts": {
"start": "babel-node index.js"
}
Getting Started
First we'll install babel-cli.
$ npm install --save-dev babel-cli
Along with some presets.
$ npm install --save-dev babel-preset-es2015 babel-preset-stage-2
package.json:
"scripts": {
"start": "babel-node index.js --presets es2015,stage-2"
}
run:
$ npm start
Watching file changes with nodemon:
We can improve our npm start script with nodemon.
$ npm install --save-dev nodemon
Then we can update our npm start script.
package.json:
"scripts": {
"start": "nodemon index.js --exec babel-node --presets es2015,stage-2"
}
run:
$ npm start
If you are using pm2, then follow these steps:
$ pm2 start app.js --interpreter babel-node
Its very simple to resolve this issue, import is ES6 syntax and Node has difficulty in supporting it, you need to add Babel as a transpiler, go to package.json and add the following
First add a script tag to use babel while running the JS code for transpiling.
"scripts": {
"start": "nodemon ./app.js --exec babel-node -e js"
}
And then add the following as the Babel devDependencies
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-0": "^6.24.1"
}
after this you also need to configure the babel presets file, therefore create .babelrc file at the root directory and define the presets as follows
{
"presets": [
"es2015",
"stage-0"
]
}
Don't forget to do an npm install in the end
Thanks to a NodeJS enhancement proposal we have a path forward. You can use #standard-things/esm
Find the announcement here Simply run
npm i --save #std/esm
In your packaged today.

Resources