"If a worker wants to do his job well, he must first sharpen his tools." - Confucius, "The Analects of Confucius. Lu Linggong"
Front page > Programming > Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

Published on 2024-07-29
Browse:787

It was a normal patching day. I patched and upgraded my npm dependencies without making code changes, and suddenly, some of my unit tests failed.
Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

Wtf!

Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS

My tests failed because Jest encountered an unexpected token; they failed because Jest cannot handle ESM-only packages out of the box. In fact, Jest is written in CommonJS.
But what does that mean? To do so, we need to understand why CommonJS and ESM exist.

Why Do We Need Module Systems?

In the early days of web development, JavaScript was mainly used to manipulate the Document Object Model (DOM) with libraries like jQuery. However, the introduction of Node.js also led to JavaScript being used for server-side programming. This shift increased the complexity and size of JavaScript codebases. As a result, there arose a need for a structured method to organize and manage JavaScript code. Module systems were introduced to meet this need, enabling developers to divide their code into manageable, reusable units1.

The Emergence of CommonJS

CommonJS was established in 2009, originally named ServerJS2. It was designed for server-side JavaScript, providing conventions for defining modules. Node.js adopted CommonJS as its default module system, making it prevalent among backend JavaScript developers. CommonJS uses require to import and module.exports to export modules. All operations in CommonJS are synchronous, meaning each module is loaded individually.

The Rise of ESM (ECMAScript Modules)

In 2015, ECMAScript introduced a new module system called ECMAScript Modules (ESM), primarily targeting client-side development. ESM uses import and export statements, and its operations are asynchronous, allowing modules to be loaded in parallel3. Initially, ESM was intended for browsers, whereas CommonJS was designed for servers. It became more and more a standard for the JS ecosystem. Nowadays, modern JavaScript runtimes support both module systems. Browsers began supporting ESM natively in 2017. Even Typescript adapted the ESM syntax, and whenever you learn it, you also learn ESM subconsciously.

How Are you not dead.jpg

CommonJS is here to stay

The truth is that there are many more CommonJS (CJS)- only packages than ESM-only packages4.
Oh CommonJS! Why are you mESMing with me?! Reasons to ditch CommonJS
However, there is a clear trend. The number of ESM-only or dual module packages is on the rise, while fewer CJS-only packages are being created. This trend underscores the growing preference for ESM and raises the question of how many of the CJS-only packages are actively maintained.

Comparison

An interesting comparison between CommonJS and ESM involves performance benchmarks. Due to its synchronous nature, CommonJS is faster when directly using require and import statements. Let's consider the following example:

// CommonJS -> s3-get-files.cjs
const s3 = require('@aws-sdk/client-s3');
new s3.S3Client({ region: 'eu-central-1' });

// ESM -> s3-get-files.mjs
import { S3Client } from '@aws-sdk/client-s3';

new S3Client({ region: 'eu-central-1' });

I used the aws-sdk S3-Client because it has dual module support. Here we instantiate the client and then execute it with node:

hyperfine --warmup 10 --style color 'node s3-get-files.cjs' 'node s3-get-files.mjs'

Benchmark 1: node s3-get-files.cjs
Time (mean ± σ): 82.6 ms ± 3.7 ms [User: 78.5 ms, System: 16.7 ms]
Range (min … max): 78.0 ms … 93.6 ms 37 runs
Benchmark 2: node s3-get-files.mjs
Time (mean ± σ): 93.9 ms ± 4.0 ms [User: 98.3 ms, System: 18.1 ms]
Range (min … max): 88.1 ms … 104.8 ms 32 runs

Summary
node s3-get-files.cjs ran
  1.14 ± 0.07 times faster than node s3-get-files.mjs

As you can see, the s3-get-files.cjs and, thus, CommonJS run faster.
I got inspired by Buns Blogpost.

However, when you want to productionize your JS library, you need to bundle it. Otherwise, you will ship all the node_modules. Is used esbuild because it is able to bundle to CJS and ESM. Now, let's run the same benchmark with the bundled version.

hyperfine --warmup 10 --style color 'node s3-bundle.cjs' 'node s3-bundle.mjs'

Benchmark 1: node s3-bundle.cjs
Time (mean ± σ): 62.1 ms ± 2.5 ms [User: 53.8 ms, System: 6.7 ms]
Range (min … max): 59.5 ms … 74.5 ms 45 runs

Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Benchmark 2: node s3-bundle.mjs
Time (mean ± σ): 45.3 ms ± 2.2 ms [User: 38.1 ms, System: 5.6 ms]
Range (min … max): 43.0 ms … 59.2 ms 62 runs

Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet system without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

Summary

  node s3-bundle.mjs ran
    1.37 ± 0.09 times faster than node s3-bundle.cjs

As you can see, the s3-bundle.mjs is now faster than the s3-bundle.cjs. The ESM file is now even faster than the unbundled CommonJS file because it results in smaller file sizes and faster load times due to efficient tree-shaking—a process that removes unused code.

Embrace ESM!

The future of JavaScript modules is undoubtedly leaning towards ESM. This starts when creating a new NodeJS project or even a React project. Every tutorial and article uses the import—statement, which is thus ESM. Despite many existing CommonJS packages, the trend is shifting as more developers and maintainers adopt ESM for its performance benefits and modern syntax. Another question is also how many of these CJS-only projects are still maintained.

ESM is a standard that works in any runtime, such as NodeJS, Bun, or Deno, and in the browser without running on a server. It is unnecessary to convert via Babel to CommonJS because the browser understands ESM. You can still use Babel to convert to a different ECMAScript version, but you shouldn't convert to CJS.

You should develop in ESM-only because every runtime now and browser newer than 2017 understands ESM.

If your code breaks, you may have legacy issues. Consider using different tooling or packages. For example, you can migrate from Jest to vitest or from ExpressJS to h3. The syntax remains the same; the only difference is the import statement.

Key Takeaways:

  • Smaller Bundles: ESM produces smaller bundles through tree-shaking, leading to faster load times.
  • Universal Support: ESM is supported natively by browsers and JavaScript runtimes (Node.js, Bun, Deno).
  • Future-Proof: With ongoing adoption, ESM is positioned as the standard for modern JavaScript modules.

To get started, you can follow this Gist or get an inspirational learning here.

For a better JavaScript Future, Embrace ESM!

The presentation

More Resources

  • https://dev.to/logto/migrate-a-60k-loc-typescript-nodejs-repo-to-esm-and-testing-become-4x-faster-22-4a4k
  • https://jakearchibald.com/2017/es-modules-in-browsers/
  • https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7dfcad3
  • https://gist.github.com/joepie91/bca2fda868c1e8b2c2caf76af7dfcad3

  1. https://www.freecodecamp.org/news/javascript-es-modules-and-module-bundlers/#why-use-modules ↩

  2. https://deno.com/blog/commonjs-is-hurting-javascript ↩

  3. https://tc39.es/ecma262/#sec-overview ↩

  4. https://twitter.com/wooorm/status/1759918205928194443 ↩

Release Statement This article is reproduced at: https://dev.to/jolodev/oh-commonjs-why-are-you-mesming-with-me-reasons-to-ditch-commonjs-enh?1 If there is any infringement, please contact study_golang@163 .comdelete
Latest tutorial More>

Disclaimer: All resources provided are partly from the Internet. If there is any infringement of your copyright or other rights and interests, please explain the detailed reasons and provide proof of copyright or rights and interests and then send it to the email: [email protected] We will handle it for you as soon as possible.

Copyright© 2022 湘ICP备2022001581号-3