Módulos ESM com NodeJS
Até antes da versão 12.0.0
do NodeJS, nós usávamos o comando require
e module.exports
para exportar/importar um determinado módulo dentro de um outro arquivo.
E foi o que fizemos durante toda a nossa jornada:
const db = require('../db/connection.js');
....
module.exports = app;
Até os dias de hoje, o NodeJS ainda utiliza (por padrão) o CommonJS (CJS), que por sua vez, nos obriga a importar/exportar nosso módulos por meio do module.export
e também do comando require
.
No entanto, a partir da versão 12.0.0
, o NodeJS passou a dar suporte aos módulos ESM, o que permitiu o uso dos comandos export default
, export
e import
😉
Habilitando o uso do import/export
Quando temos uma aplicação setada com NodeJS, existem duas formas diferentes para fazermos o uso dos comandos import
e export
dos módulos ESM.
A primeira delas, envolve alterar a extenção dos seus arquivos, que estão como .js
para .mjs
, o que faz com que o NodeJS entenda que você está lidando com os módulos ECMAScript, o que permite o uso dos comandos import
e export
.
Caso você queria manter a extenção .js
nos seus arquivos, você vai precisar adicionar uma nova configuração dentro do package.json
, indicando ao NodeJS que estamos usando módulos:
{
"type": "module"
}
Com a configuração acima, o NodeJS será instruído a tratar todos os arquivos .js
como módulos ECMAScript, permitindo o uso dos comandos import
e export
.
Vejamos um exemplo de um arquivo package.json
completo, que faz a implementação do módulo ECMAScript:
{
"name": "22)-testes",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"axios": "^1.7.7",
"body-parser": "^1.20.3",
"chai": "^4.4.0",
"chai-http": "^4.4.0",
"dotenv": "^16.4.5",
"express": "^4.21.1",
"express-handlebars": "^8.0.1",
"mocha": "^10.7.3",
"mysql2": "^3.11.3",
"sequelize": "^6.37.4"
}
}
Note que a chave "type"
foi adicionada logo após a "description"
, mas ela poderia estar declarada em qualquer lugar, desde que não tinha sido aninhada dentro de qualquer outra chave.
Usando os comandos Import e Export
Agora que já habilitamos o uso do ESM no NodeJS, vejamos como usar os módulos ESM!
O uso desses novos comandos são bem simples, basta que você troque seus antigos require
para import
, da seguinte forma:
const express = require('express');//Usa o CommomJS
...
import express from 'express';//Usa o ESM
Caso a sua biblioteca ou módulo faça a exportação de mais de um módulo por vez, você pode utilizar:
import { db, alter, status } from '../db/connection.js';//Usa o ESM
Já para fazer exportações, basta trocar os comandos module.exports
para export default
da seguinte forma:
module.exports = app;//Usa o CommomJS
...
export default app;
Caso você queria exportar mais coisas, basta usar o comando export
sem o default
:
// Exportando múltiplos objetos, funções ou variáveis
export const PI = 3.14159;
export const square = (x) => x * x;
export function add(x, y) {
return x + y;
}
Já para fazer a importação, você pode usar:
// Importando objetos exportados de forma nomeada
import { PI, square, add } from './math.js';
console.log(PI); // 3.14159
console.log(square(2)); // 4
console.log(add(2, 3)); // 5
Observação: o uso do export default
também pode ser usado em conjunto com o export
, desde que exista apenas um único export default
por módulo.
Realizando exportações nomeadas
Com o ESM, você também consegue realizar exportações nomeadas, isto é, exportar de forma direta uma função ou uma determinada classe:
exports.myFunction = () => {};//Usando o CommomJS
...
export const myFunction = () => {};//Usando o ESM
Importando módulos dinâmicos
Durante a sua jornada como desenvolvedor NodeJS, por vezes, será necessário a importação de módulos dinâmicos, que nada mais são do que módulos que carregam uma função assíncrona:
const module = await import('module-name');
É importante ressaltar que no ambiente ESM, __dirname
e __filename
não estão disponíveis para acesso direito, o que nos obriga a importá-los dessa maneira:
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
Export Default VS Export
No caso do export default
, ele é usado exclusivamente para exportar um valor único por módulo. A partir do momento que você tem um export default
dentro de um arquivo Javascript, o compilador do NodeJS começa a apresentar problemas.
É importante destacar que ao realizar a importação, você não precisa usar o nome exato do valor que foi exportado em outro arquivo, por exemplo:
export default function add(x, y) {
return x + y;
}
...
import addFunction from './math.js';
console.log(addFunction(2, 3)); // 5
Já o export
é usado para exportar múltiplos valores (variáveis, funções, objetos...) de um módulo. Como por exemplo:
export const PI = 3.14159;
export function add(x, y) {
return x + y;
}
...
import { PI, add } from './math.js';
console.log(PI); // 3.14159
console.log(add(2, 3)); // 5
Posso ter um export default dentro de um módulo em conjunto com varios export?
Sim, mas desde que exista apenas um único export default
dentro do seu arquivo, vamos ver um exemplo:
// Exportação default (apenas um por módulo)
const circleArea = (radius) => Math.PI * radius * radius;
export default circleArea;
// Várias exportações nomeadas
export const rectangleArea = (width, height) => width * height;
export const triangleArea = (base, height) => 0.5 * base * height;
A importação de todos esses elementos, aconteceria da seguinte forma:
// Importando a exportação default e exportações nomeadas
import circleArea, { rectangleArea, triangleArea } from './shapes.js';
console.log(circleArea(3)); // Área do círculo com raio 3
console.log(rectangleArea(4, 5)); // Área do retângulo
console.log(triangleArea(4, 5)); // Área do triângulo
Portanto, o export default
te permite exportar um único valor como padrão do módulo, ja o export
sozinho, te permite exportar múltiplos valores nomeados do mesmo módulo.
Conclusão
Nesta lição, nós aprendemos a utilizar os comandos import
, export
e export default
do ECMAScript 😉
Até a próxima!