Is CommonJS 'require' still used or deprecated?
Asked Answered
C

2

26

Current Javascript adopts import from ES6 as a standard way to import modules. However, I sometimes see codes using CommonJS require instead of import.

I first wondered whether two can be used together, but it seems like two are not interchangeable. (releated stackoverflow question)

So, is CommonJS 'require' something that is still used in projects? Or is it slowly dying and needed only for maintaining legacy codes?

Circumcise answered 13/1, 2022 at 4:37 Comment(1)
Yes, it's slowly dying, and of course still used in environments that have not adopted ES6 modules. It's not like on the day of the release of ES6, every environment magically started supporting it and every codebase automatically got updated.Inalterable
R
39

CommonJS will likely be supported in nodejs for a long time as there are still millions of lines of code written using it. It is the original module loading mechanism in nodejs. It is not deprecated.

ESM modules (ECMAScript modules) that use import and export are the new Javascript standard for modules and we can expect that nodejs will support these for as long as they are the Javascript standard (probably forever).

These two modules standards are not entirely compatible so mixing and matching within the same project can lead to complications that you have to learn how to deal with.

New Projects

If I were starting a new project today, I'd choose to write my code using ESM modules as it is the future trajectory of the language and nodejs. And, you can still load CommonJS modules into ESM modules if you require backward compatibility with other modules, but you do have to know how to do it properly - it's not always seamless to mix and match module types.

When ESM modules were first supported in nodejs, the interoperability with CommonJS modules was not very full featured and created some difficulties. As of the more recent versions of nodejs, you can load a CommonJS module from an ESM module or vice versa- you just have to use the right techniques to do it. This makes working from an ESM project a lot more feasible now than it was when ESM support in nodejs first came out as you can still access libraries in NPM that only support CommonJS.

Note also that more and more libraries in NPM are supporting direct loading from either module type so they are equally easy to use whether your project is CommonJS or ESM.

Over time, I would expect that any actively developed, shared module on NPM will eventually support direct loading as an ESM module. But, we're in this transition period of time where many have not yet implemented that or there are particular challenges in implementing the new loading scheme (which comes with it's own and different set of rules). In the meantime, you can still load CommonJS modules into ESM projects.

Existing CommonJS Projects

If I had an existing project that was CommonJS, I would not be spending any time thinking about converting it to ESM because there is still widespread support for CommonJS and my developer time is probably better spent on adding features, testing, fixing bugs, etc... than converting module formats.

Interoperability

One of the important interoperability things to know is that you can load a CommonJS module from an ESM module in several different ways:

  1. With a static import: import { callMeCommon } from '../otherProject/common.js';
  2. With a dynamic import import(someFile).then(...)
  3. By using module.createRequire(import.meta.url) and then using that require() function to load your CommonJS module.

You can also use the dynamic import('someModule').then(...) to load an ESM module from a CommonJS module, but it's not synchronous like require() was so it has to be dealt with differently.

It's also useful to know that ESM modules do not have some nodejs niceties that CommonJS modules had. There's no automatic __dirname or __filename to tell you exactly where this file was loaded from. Instead, you have to parse those values out of import.meta.url and compute them. So, it's still possible to get that information, it's just not as convenient as it used to be.

On the other hand, ESM modules have some features like top level await that CommonJS do not which can be very useful.

Your Specific Questions

So, is CommonJS 'require' something that is still used in projects?

Yes, it is still used a lot.

Or is it slowly dying and needed only for maintaining legacy codes?

[Comments as of early 2022] I wouldn't so much say that it is dying as there are very few projects on NPM that don't still support CommonJS. In fact, when a project releases a new version that no longer supports CommonJS, it creates quite a problem for their user base (we see some of these issues here on stackoverflow) because of the significant prevalence of CommonJS projects still and people not familiar with how to load different module types.

So, I'd say that we're still in a very early stages of a transition from CommonJS to ESM and it's more like ESM is getting more and more adoption rather than CommonJS is dying. I'd hazard a guess that the majority of modules on NPM will, over the next few years, move to support direct loading from both module formats.

Transpiling

Lastly, developers often use transpiling to allow them to write code using the newest syntax and features, but using a transpiler to convert the code back to a lower, common denominator that runs anywhere. That can also be done for module architecture (write code in ESM, transpile to CommonJS).

A Few Interesting References

What does it take to support Node.js ESM?

Hybrid npm packages (ESM and CommonJS)

Node Modules at War: Why CommonJS and ES Modules Can’t Get Along

All you need to know to move from CommonJS to ECMAScript Modules (ESM) in Node.js

Rendering answered 13/1, 2022 at 4:47 Comment(9)
"you can load a CommonJS module from an ESM module with a dynamic import. This is a function-like import that is not synchronous" - I think you got that backwardsInalterable
I'm missing a mention of transpilingInalterable
@Inalterable - I'll figure out what to add about transpiling (not my cup of tea, but it does belong in here). When I mention dynamic import, I'm talking about import('someModule').then(...). This is asynchronous, right? Perhaps you were thinking about using module.createRequire()?Rendering
Yes, you're talking about asynchronous dynamic import, but it should be "you can load an ESM module from a CommonJS module with a dynamic import()", not the other way round. And yes, you can always import or createRequire()() a CommonJS module from an ES module, which I why I also would disagree with "If some critical module does not [support ESM], then you may need to go with a CommonJS project".Inalterable
@Inalterable - OK, I didn't not realize, you could use import('someModule').then(...) to load an ESM module into a CommonJS file. I've updated that whole section of the answer.Rendering
I actually meant that you can use an import declaration to load CJS from ESM, you don't need the asynchronous dynamic import() (or the synchronous require from createRequire) for that.Inalterable
@Inalterable - Updated again. I've also updated the tone of the answer as there really is no longer any reason not to use ESM modules for new projects as the backward compatibility for loading CommonJS modules is so much better now than it was even a year ago.Rendering
I've deleted my previous answer because I didn't realize ES6 has a dynamic import function. However, there is still one use case I can't think of a work around for: modifying how import works. There is a mocking/stubbing module called rewire. It replaces require() with rewire() so you can do things like replacing modules etc. which is useful for unit testing. How would one go about doing something like that other than rewriting the entire codebase to explicitly use dependency injection?Champlain
@Champlain - Perhaps, custom loaders: nodejs.org/api/esm.html#loaders or npmjs.com/package/esm-fake-loader.Rendering
R
1

In Nodejs it doesn't yet appear to be deprecated given the LTS v20 precursor (v19) documentation describes the feature: https://nodejs.org/api/esm.html#interoperability-with-commonjs https://github.com/nodejs/Release

If locked into Nodejs the above affords a length of time into 2025 for using LTS Nodejs runtimes, with legacy code, and possibly polyfill the same whenever the Nodejs product changes status (the module system or the whole runtime).

CommonJS is the legacy module system specific to Nodejs see https://wiki.commonjs.org/wiki/Modules/Meta

Generally outside of Nodejs Commonjs/require has been displaced in JS/TS runtimes by ESModules: https://developer.mozilla.org/docs/Web/JavaScript/Guide/Modules

As much as possible I've been moving away from Nodejs to Deno and haven't looked at the specifics and details of its npm and Nodejs compatibility. For those who're looking to implement software using contemporary web platform features it's the best forward-looking with current working solutions that I'm aware of (more at https://deno.land/).

import { createRequire } from "https://deno.land/std/node/module.ts";
// import.meta.url...like `__filename` ...
const require = createRequire(import.meta.url);
const path = require("path");
Rhythmandblues answered 3/1, 2023 at 22:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.