modulos-commonjs-y-esm-nodejs

Diferencias entre módulos CommonJS y ESM en Node.js

Hemos visto que Node.js es compatible con ficheros con extensión .js y .mjs. Si te parece algo lioso, agarrate que vienen curvas ahora mismo te lo explico.

CommonJS y ECMAScript Modules (ESM) son dos sistemas de módulos, utilizados en JavaScript para organizar y reutilizar código en diferentes archivos.

Cuando aparece Node.js (2009), JavaScript no tenía un sistema de módulos nativos. Node.js decidió hacer el suyo propio, CommonJS. Decisión que seguimos arrastrando hasta hoy en día.

Posteriormente, JavaScript crea su sistema de ficheros “oficial”, ECMAScript Modules (ESM) (2015). Iban con retraso los pobres. Pero la sintaxis oficial tenía muchas mejoras respecto a las anteriores.

Dirás, pues… “¡todo perfecto, dejamos los CommonJS y usamos los ESM!“. A esas alturas había tanto código que ha costado años reemplazarlos.

De hecho, Node.js tardó hasta su versión 12 (2019) en ser compatible con ESM. Son 10 añazos de CommonJS. Por lo que aún existen muchas herramientas y bibliotecas sin portar.

Por eso hay dos extensiones:

  • .js funciona con módulos CommonsJS 🤔
  • .mjs funciona con módulos ECMAScript Modules (ESM) 👍

Siempre que sea posible, usad los ECMAScript Modules (ESM). Son la solución estándar, y deben sustituir a los CommonJS.

Usando módulos en JavaScript

Vamos a ver cómo usar módulos CommonJS y ECMAScript Modules. Lo dicho, salvo que alguna librería no tenga compatibilidad, o algún otro motivo de peso, vamos a preferir ECMAScript.

Pero vamos a ver la sintaxis de los dos, porque os vais a encontrar la mitad de documentación de internet, librerías, o proyectos que os toque, en cada sistema

CommonJS Legacy

CommonJS es un sistema de módulos que fue desarrollado para su uso en entornos de servidor, como Node.js. Utiliza las palabras clave require y module.exports para importar y exportar módulos.

Aquí un ejemplo de su uso.

// Importar un módulo
const otroModulo = require('./otroModulo');

// Exportar un módulo
module.exports = {
  foo: 'bar'
};

Node.js ha utilizado CommonJS como su sistema de módulos principal durante mucho tiempo.

ECMAScript Modules (ESM) Standard

Los módulos de ECMAScript son el estándar de módulos de JavaScript definido en el estándar ECMAScript (el estándar subyacente de JavaScript). Utiliza las palabras clave import y export para importar y exportar módulos, respectivamente.

Ejemplo de su uso:

// Importar un módulo
import otroModulo from './otroModulo';

// Exportar un módulo
export const foo = 'bar';

Los módulos ECMAScript están diseñados para funcionar tanto en navegadores web modernos como en entornos de servidor (como Node.js), lo que significa que son más portables.

Además, los módulos ECMAScript son cargados de forma asíncrona, lo que puede ser más eficiente en ciertos escenarios.

Extensiones JS, CJS, y MJS

Antes de la adopción de los módulos ECMAScript en Node.js, los archivos JavaScript se escribían con la extensión .js y utilizaban CommonJS para la importación y exportación de código.

A partir de Node.js v12, se introdujo el soporte para módulos ECMAScript, permitiendo así utilizar la misma sintaxis de import/export que se utiliza en los navegadores web modernos.

Como la extensión “buena” ya estaba pillada, le tuvieron que llamar .mjs. Para diferenciarlos, algunos (no muchos) pasaron a llamar .cjs a los ficheros que contenían módulos CommonJS, y que antes llamaban .js.

Pero, por otro lado, un fichero .js puede ser tratado como módulo ESM si en su fichero package.json tiene puesto tipo “módulo”. Asi.

{
  "name": "tu_modulo",
  "type": "module",
  "version": "1.0.0",
  // ...  mas cosas
}

Es decir, que si lo intentan liar más no lo consiguen ni queriendo 😅

Cómo usar módulos CommonJS en ESM

En general, deberías intentar usar módulos ESM. Son los estándard y son los que deberías usar siempre… y el mundo es maravilloso y está lleno de unicornios y arcoiris 🦄.

No, lo cierto es que lo de que existan dos sistemas es un engorro, y vas a tener que aprender a usar los dos, y a convivir con los dos. Usar uno u otro es demasiado dificil, pero cuando te toque mezclaros… igual alguna vez te tiras de los pelos.

En principio, en tus proyectos deberías usar módulos ESM. La propia documentación de Node.js se ha migrado a usar ESM. Pero aún existen muchas librerías que no se han portado aún, y algunas que no se portarán nunca.

Así que vamos a ver cómo importar un módulo CommonJS en ESM. Que, afortunadamente, es muy sencillo.

import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);

const tus_funciones = require('tu_modulo_commonjs');

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github github-full