How to convert a CJS module to an ESM
© https://nodejs.org/en/

How to convert a CJS module to an ESM

Converting CommonJS to EcmaScript modules

By Mario Kandut

Europe’s developer-focused job platform

Let companies apply to you

Developer-focused, salary and tech stack upfront.

Just one profile, no job applications!

This article is based on Node v16.14.0.

Since Node v14, there are two kinds of modules, CommonJS Modules (CJS) and EcmaScript Modules (ESM) . EcmaScript Modules (ESM) were introduced as part of ES6 (EcmaScript 2015). The main goal was for module includes to be statically analyzable, so browsers would be able to pre-parse out imports. This behaviour is similar to collecting all <script> tags as the web page loads.

💰 The Pragmatic Programmer: journey to mastery. 💰 One of the best books in software development, sold over 200,000 times.

It took more than three years for major browsers to implement this retrofitting of a static module system into a dynamic language. And even longer for ESM to be implemented in NodeJS, because of interoperability with the existing CJS module system, though there are still some issues.

The main difference between CJS and ESM is that CJS loads every module synchronously, and ESM loads every module asynchronously.

There are also two types of ESM: native ESM and "transpiled ESM". Transpiled ESM is ESM-like syntax that would be typically be transpiled with Babel. The syntax looks similar, but the behaviour can vary. "Transpiled ESM" compiles to CommonJS in Node and will be compiled in the browser using a bundled synchronous loader. This means, that "transpiled ESM" loads modules synchronously, native ESM loads modules asynchronously. A Node application (or module) can contain CJS and ESM files.

Let's convert the module created in How to create a CSJ module.

The CSJ module contains this code:

'use strict';

const toUpper = str => {
  if (typeof str === 'symbol') str = str.toString(); // convert to string if symbol is used as input
  str += '';
  return str.toUpperCase();
};

module.exports = { toUpper };

To convert it to an ESM module, the only we have to do change the file extension from .js to .mjs. If you are on linux you can use the mv command.

mv format.js format.mjs

CJS modules modify the module.exports object, in ESM its native syntax. Hence, to create a named export, we can just use the export keyword.

Let's update our module:

export const toUpper = str => {
  if (typeof str === 'symbol') str = str.toString(); // convert to string if symbol is used as input
  str += '';
  return str.toUpperCase();
};

The 'use strict' pragma to enforce strict mode is also not needed anymore, since ESM executes in strict-mode.

That's it for converting CSJ to ESM. There is only one thing, try to run the node application with node index. You will see an error.

internal/modules/cjs/loader.js:948
    throw new ERR_REQUIRE_ESM(filename);
    ^

This error occurs because the require function will not automatically resolve a filename without an extension ('./format') to an .mjs extension.

Ok, so let's fix this and try again.

node -p "require('./format.mjs')"

We will get an error again. Error [ERR_REQUIRE_ESM]: Must use import to load ES Module.

The major difference between CSJ and ESM, is that CJS is synchronous. CJS cannot require ESM since that would break the synchronous constraint, but ESM can import CJS.

There is a way around this constraint with dynamic import. Have a look at this article How to dynamically load an ESM Module in CJS.

TL;DR

  • Since Node v14, there are two kinds of modules, CommonJS Modules (CJS) and EcmaScript Modules (ESM) .
  • The main difference between CJS and ESM is that CJS loads every module synchronously, and ESM loads every module asynchronously.
  • To convert a CSJ module to ESM, change the file ending to .mjs and use the export keyword instead of module.exports.
  • CJS cannot require ESM since that would break the synchronous constraint, but ESM can import CJS.
  • Use dynamic import for importing ESM in CSJ.

Thanks for reading and if you have any questions, use the comment function or send me a message @mariokandut.

If you want to know more about Node, have a look at these Node Tutorials.

References (and Big thanks):

NodeJS, JSNAD, CommonJS

More node articles:

How to create a web server in Node.js

How to dynamically load ESM in CJS

How to convert a CJS module to an ESM

How to create a CJS module

How to stream to an HTTP response

How to handle binary data in Node.js?

How to use streams to ETL data?

How to connect streams with pipeline?

How to handle stream errors?

How to connect streams with pipe?

What Is a Node.js Stream?

Handling Errors in Node (asynchronous)

Handling Errors in Node.js (synchronous)

Introduction to errors in Node.js

Callback to promise-based functions

ETL: Load Data to Destination with Node.js

ETL: Transform Data with Node.js

ETL: Extract Data with Node.js

Event Emitters in Node.js

How to set up SSL locally with Node.js?

How to use async/await in Node.js

What is an API proxy?

How to make an API request in Node.js?

How does the Event Loop work in Node.js

How to wait for multiple Promises?

How to organize Node.js code

Understanding Promises in Node.js

How does the Node.js module system work?

Set up and test a .env file in Node

How to Use Environment Variables in Node

How to clean up node modules?

Restart a Node.js app automatically

How to update a Node dependency - NPM?

What are NPM scripts?

How to uninstall npm packages?

How to install npm packages?

How to create a package.json file?

What Is the Node.js ETL Pipeline?

What is data brokering in Node.js?

How to read and write JSON Files with Node.js?

What is package-lock.json?

How to install Node.js locally with nvm?

How to update Node.js?

How to check unused npm packages?

What is the Node.js fs module?

What is Semantic versioning?

The Basics of Package.json explained

How to patch an NPM dependency

What is NPM audit?

Beginner`s guide to NPM

Getting started with Node.js

Scroll to top ↑