DEV Community

Cover image for How to Handle ES6 modules in Node.Js
Sabesan Sathananthan
Sabesan Sathananthan

Posted on • Updated on

How to Handle ES6 modules in Node.Js

Learning the JavaScript language, you will find that it has two types of modules.

One is the ES6 module, referred to as ESM; the other is Node.js dedicated CommonJS module, referred to as CJS. The two modules are not compatible.

Many people use Node.js and only require() load modules, but don't know what to do when they encounter ES6 modules. This article will talk about how to use ES6 modules in Node.js.

Alt Text

Source: https://www.zymr.com/es6-impact-node-js/

The difference between the two modules

There is a big difference between ES6 modules and CommonJS modules. The syntax of the module being loaded is different. CJS modules are loaded with require() and exported with module.exports. But ESM modules are loaded with import and exported with export.
require() is doing synchronous loading, the following statements must wait for this statement to be executed before it is executed. But import is doing asynchronous loading, to be precise it has a static code analysis and dependency resolution phase.

Alt Text

Source: https://twitter.com/Manz/status/1265341200007036929

The distinction of Node.js

Node.js requires ES6 modules to use the .mjs file extension. In other words, as long as the import or export is used in the script file, the .mjs file extension should be used. When Node.js encounters a .mjs file, it considers it to be an ES6 module, and strict mode is enabled by default, and it is not necessary to specify it at the top of each module file "use strict". If you do not want to change the file extension to .mjs, you can specify the type field as a module in the project file's package.json.

{
   "type": "module"
}
Enter fullscreen mode Exit fullscreen mode

Once set, the JS scripts in the directory will be interpreted as ES6 modules.

# Interpreted as ES6 Module
$ node my-app.js
Enter fullscreen mode Exit fullscreen mode

If you still want to use the CommonJS module at this time, you need to change the file extension of the CommonJS script to .cjs. If there is no type field, or the type field is commonjs, the .js script will be interpreted as a CommonJS module.
Summarized in one sentence: .mjs files are always loaded as ES6 modules, .cjs files are always loaded as CommonJS modules, and the loading of .js files depends on the settings of the type field inside the package.json.

Note: Try not to mix ES6 modules and CommonJS modules. The require() cannot load the .mjs file, and an error will be reported. Only the import can load the .mjs file. Conversely, require() cannot be used in .mjs files, they should use import.


CommonJS module loads ES6 modules

require() cannot load ES6 modules and an error will be reported in the CommonJS module. You can only use the import() method to load ESM.

(async () => {
  await import('./my-app.mjs');
})();
Enter fullscreen mode Exit fullscreen mode

The above code can be run in the CommonJS module.

One reason why require() does not support ES6 modules is that, it is loaded synchronously, and the top-level await statement can be used inside ES6 modules, which would cause the module not to be able to be loaded synchronously.


ES6 modules load CommonJS modules

In the ES6 module, import can load CommonJS modules, but the CJS module should be loaded altogether, and can not just load a specific single exported object.

// Correct
import packageMain from 'commonjs-package';

// Report an error
import { method } from 'commonjs-package';
Enter fullscreen mode Exit fullscreen mode

This is because ES6 modules need to support static code analysis, and the export interface of CommonJS modules is module.exports, which is an object that cannot be statically analyzed, so it can only be loaded as a whole.
To load a specific single exported object, it can be written as follows.

import packageMain from 'commonjs-package';
const { method } = packageMain;
Enter fullscreen mode Exit fullscreen mode

Alt Text


Support two formats of modules at the same time

It is easy for a module to support both CommonJS and ES6 formats. If the original module is in ES6 format, you need to give an overall output interface, such as export default obj, so that CommonJS can be loaded with import().
If the original module is in CommonJS format, a packaging layer can be added.

import cjsModule from '../index.js';
export const foo = cjsModule.foo; 
Enter fullscreen mode Exit fullscreen mode

The above code first enters the CommonJS module as a whole and then exports the named interface as needed. You can change the extension of this file to .mjs, or put it in a subdirectory, and put a separate package.json file in this subdirectory, specifying {type: "module" }.
Another approach is to specify the respective loading entry of the two format modules in the exports field of the package.json file.

"exports"{ 
    "require": "./index.js"
    "import": "./esm/wrapper.js" 
}
Enter fullscreen mode Exit fullscreen mode

The above code specifies require() and import, and loading the module will automatically switch to a different entry file.

Top comments (0)