Skip to content

Módulos: Módulos ECMAScript

[Histórico]

VersãoMudanças
v23.1.0Os atributos de importação não são mais experimentais.
v22.0.0Remoção do suporte para declarações de importação.
v21.0.0, v20.10.0, v18.20.0Adicionado suporte experimental para atributos de importação.
v20.0.0, v18.19.0Os hooks de personalização de módulos são executados fora da thread principal.
v18.6.0, v16.17.0Adicionado suporte para encadeamento de hooks de personalização de módulos.
v17.1.0, v16.14.0Adicionado suporte experimental para declarações de importação.
v17.0.0, v16.12.0Consolidação dos hooks de personalização, remoção dos hooks getFormat, getSource, transformSource e getGlobalPreloadCode, adição dos hooks load e globalPreload, permitindo retornar format a partir dos hooks resolve ou load.
v14.8.0Remoção da sinalização Top-Level Await.
v15.3.0, v14.17.0, v12.22.0Estabilização da implementação de módulos.
v14.13.0, v12.20.0Suporte para detecção de exports nomeados do CommonJS.
v14.0.0, v13.14.0, v12.20.0Remoção do aviso de módulos experimentais.
v13.2.0, v12.17.0O carregamento de módulos ECMAScript não requer mais uma flag de linha de comando.
v12.0.0Adicionado suporte para módulos ES usando a extensão de arquivo .js via campo "type" em package.json.
v8.5.0Adicionado em: v8.5.0

[Estável: 2 - Estável]

Estável: 2 Estabilidade: 2 - Estável

Introdução

Módulos ECMAScript são o formato padrão oficial para empacotar código JavaScript para reutilização. Os módulos são definidos usando uma variedade de declarações import e export.

O exemplo a seguir de um módulo ES exporta uma função:

js
// addTwo.mjs
function addTwo(num) {
  return num + 2
}

export { addTwo }

O exemplo a seguir de um módulo ES importa a função de addTwo.mjs:

js
// app.mjs
import { addTwo } from './addTwo.mjs'

// Imprime: 6
console.log(addTwo(4))

O Node.js suporta totalmente os módulos ECMAScript como eles estão atualmente especificados e fornece interoperabilidade entre eles e seu formato de módulo original, CommonJS.

Habilitando

O Node.js possui dois sistemas de módulos: módulos CommonJS e módulos ECMAScript.

Os autores podem instruir o Node.js a interpretar JavaScript como um módulo ES através da extensão de arquivo .mjs, do campo "type" do package.json com o valor "module" ou da flag --input-type com o valor "module". Estes são marcadores explícitos de que o código pretende ser executado como um módulo ES.

Inversamente, os autores podem instruir explicitamente o Node.js a interpretar JavaScript como CommonJS através da extensão de arquivo .cjs, do campo "type" do package.json com o valor "commonjs" ou da flag --input-type com o valor "commonjs".

Quando o código não possui marcadores explícitos para nenhum dos sistemas de módulo, o Node.js inspecionará o código-fonte de um módulo em busca de sintaxe de módulo ES. Se tal sintaxe for encontrada, o Node.js executará o código como um módulo ES; caso contrário, executará o módulo como CommonJS. Consulte Determinando o sistema de módulos para mais detalhes.

Pacotes

Esta seção foi movida para Módulos: Pacotes.

Especificadores import

Terminologia

O especificador de uma instrução import é a string após a palavra-chave from, por exemplo, 'node:path' em import { sep } from 'node:path'. Especificadores também são usados em instruções export from e como argumento para uma expressão import().

Existem três tipos de especificadores:

  • Especificadores relativos, como './startup.js' ou '../config.mjs'. Eles se referem a um caminho relativo à localização do arquivo de importação. A extensão do arquivo é sempre necessária para estes.
  • Especificadores nus, como 'some-package' ou 'some-package/shuffle'. Eles podem se referir ao ponto de entrada principal de um pacote pelo nome do pacote, ou a um módulo de recurso específico dentro de um pacote prefixado pelo nome do pacote, conforme os exemplos respectivamente. Incluir a extensão do arquivo só é necessário para pacotes sem um campo "exports".
  • Especificadores absolutos, como 'file:///opt/nodejs/config.js'. Eles se referem direta e explicitamente a um caminho completo.

As resoluções de especificadores nus são tratadas pelo algoritmo de resolução e carregamento de módulos do Node.js. Todas as outras resoluções de especificadores são sempre resolvidas apenas com a semântica de resolução de URL relativa padrão.

Como no CommonJS, arquivos de módulos dentro de pacotes podem ser acessados anexando um caminho ao nome do pacote, a menos que o package.json do pacote contenha um campo "exports", caso em que os arquivos dentro dos pacotes só podem ser acessados através dos caminhos definidos em "exports".

Para detalhes sobre essas regras de resolução de pacotes que se aplicam a especificadores nus na resolução de módulos do Node.js, consulte a documentação de pacotes.

Extensões de arquivo obrigatórias

Uma extensão de arquivo deve ser fornecida ao usar a palavra-chave import para resolver especificadores relativos ou absolutos. Índices de diretório (por exemplo, './startup/index.js') também devem ser totalmente especificados.

Este comportamento corresponde ao comportamento do import em ambientes de navegador, assumindo um servidor configurado tipicamente.

URLs

Os módulos ES são resolvidos e armazenados em cache como URLs. Isso significa que caracteres especiais devem ser codificados por percentagem, como # com %23 e ? com %3F.

Esquemas de URL file:, node: e data: são suportados. Um especificador como 'https://example.com/app.js' não é suportado nativamente no Node.js, a menos que usando um carregador HTTPS personalizado.

URLs file:

Os módulos são carregados várias vezes se o especificador import usado para resolvê-los tiver uma consulta ou fragmento diferente.

js
import './foo.mjs?query=1' // carrega ./foo.mjs com a consulta "?query=1"
import './foo.mjs?query=2' // carrega ./foo.mjs com a consulta "?query=2"

A raiz do volume pode ser referenciada via /, // ou file:///. Dadas as diferenças entre URL e resolução de caminho (como detalhes de codificação de percentagem), é recomendado usar url.pathToFileURL ao importar um caminho.

Importações data:

Adicionado em: v12.10.0

URLs data: são suportados para importação com os seguintes tipos MIME:

  • text/javascript para módulos ES
  • application/json para JSON
  • application/wasm para Wasm
js
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' };

URLs data: resolvem apenas especificadores bare para módulos embutidos e especificadores absolutos. A resolução de especificadores relativos não funciona porque data: não é um esquema especial. Por exemplo, tentar carregar ./foo de data:text/javascript,import "./foo"; falha ao resolver porque não há conceito de resolução relativa para URLs data:.

node: imports

[Histórico]

VersãoMudanças
v16.0.0, v14.18.0Adicionado suporte a importação node: para require(...).
v14.13.1, v12.20.0Adicionado em: v14.13.1, v12.20.0

URLs node: são suportados como um meio alternativo para carregar módulos embutidos do Node.js. Esse esquema de URL permite que os módulos embutidos sejam referenciados por strings de URL absolutas válidas.

js
import fs from 'node:fs/promises'

Atributos de importação

[Histórico]

VersãoMudanças
v21.0.0, v20.10.0, v18.20.0Mudança de Import Assertions para Import Attributes.
v17.1.0, v16.14.0Adicionado em: v17.1.0, v16.14.0

[Estável: 2 - Estável]

Estável: 2 Estabilidade: 2 - Estável

Atributos de importação são uma sintaxe inline para instruções de importação de módulo para passar mais informações junto com o especificador de módulo.

js
import fooData from './foo.json' with { type: 'json' };

const { default: barData } =
  await import('./bar.json', { with: { type: 'json' } });

Node.js suporta apenas o atributo type, para o qual ele suporta os seguintes valores:

Atributo typeNecessário para
'json'Módulos JSON

O atributo type: 'json' é obrigatório ao importar módulos JSON.

Módulos embutidos

Módulos embutidos fornecem exports nomeados de sua API pública. Um export default também é fornecido, que é o valor dos exports CommonJS. O export default pode ser usado para, entre outras coisas, modificar os exports nomeados. Exports nomeados de módulos embutidos são atualizados apenas chamando module.syncBuiltinESMExports().

js
import EventEmitter from 'node:events'
const e = new EventEmitter()
js
import { readFile } from 'node:fs'
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err)
  } else {
    console.log(source)
  }
})
js
import fs, { readFileSync } from 'node:fs'
import { syncBuiltinESMExports } from 'node:module'
import { Buffer } from 'node:buffer'

fs.readFileSync = () => Buffer.from('Olá, ESM')
syncBuiltinESMExports()

fs.readFileSync === readFileSync

Expressões import()

O import dinâmico import() é suportado tanto em módulos CommonJS quanto em módulos ES. Em módulos CommonJS, ele pode ser usado para carregar módulos ES.

import.meta

A meta propriedade import.meta é um Object que contém as seguintes propriedades. Ela só é suportada em módulos ES.

import.meta.dirname

Adicionado em: v21.2.0, v20.11.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1.2 - Candidato a lançamento

import.meta.filename

Adicionado em: v21.2.0, v20.11.0

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1.2 - Candidato a lançamento

import.meta.url

  • <string> A URL file: absoluta do módulo.

Isso é definido exatamente da mesma forma que nos navegadores, fornecendo a URL do arquivo do módulo atual.

Isso possibilita padrões úteis, como o carregamento relativo de arquivos:

js
import { readFileSync } from 'node:fs'
const buffer = readFileSync(new URL('./data.proto', import.meta.url))

import.meta.resolve(specifier)

[Histórico]

VersãoAlterações
v20.6.0, v18.19.0Não está mais por trás da flag CLI --experimental-import-meta-resolve, exceto para o parâmetro não padrão parentURL.
v20.6.0, v18.19.0Esta API não lança mais exceções ao direcionar URLs file: que não mapeiam para um arquivo existente no sistema de arquivos local.
v20.0.0, v18.19.0Esta API agora retorna uma string de forma síncrona em vez de uma Promise.
v16.2.0, v14.18.0Adicionado suporte para objeto WHATWG URL ao parâmetro parentURL.
v13.9.0, v12.16.2Adicionado em: v13.9.0, v12.16.2

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1.2 - Candidato a lançamento

  • specifier <string> O especificador do módulo a ser resolvido em relação ao módulo atual.
  • Retorna: <string> A string URL absoluta para a qual o especificador seria resolvido.

import.meta.resolve é uma função de resolução relativa ao módulo, com escopo para cada módulo, retornando a string URL.

js
const dependencyAsset = import.meta.resolve('component-lib/asset.css')
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js')
// file:///app/dep.js

Todos os recursos da resolução de módulos do Node.js são suportados. As resoluções de dependências estão sujeitas às resoluções de exportações permitidas dentro do pacote.

Ressalvas:

  • Isso pode resultar em operações síncronas do sistema de arquivos, o que pode afetar o desempenho de forma semelhante a require.resolve.
  • Esse recurso não está disponível em carregadores personalizados (criaria um deadlock).

API não padrão:

Ao usar a flag --experimental-import-meta-resolve, essa função aceita um segundo argumento:

  • parent <string> | <URL> Uma URL de módulo pai absoluta opcional a partir da qual resolver. Padrão: import.meta.url

Interoperabilidade com CommonJS

Declarações import

Uma declaração import pode referenciar um módulo ES ou um módulo CommonJS. Declarações import são permitidas apenas em módulos ES, mas expressões dinâmicas import() são suportadas em CommonJS para carregar módulos ES.

Ao importar módulos CommonJS, o objeto module.exports é fornecido como a exportação padrão. Exportações nomeadas podem estar disponíveis, fornecidas por análise estática como uma conveniência para melhor compatibilidade com o ecossistema.

require

O módulo CommonJS require atualmente suporta apenas o carregamento síncrono de módulos ES (ou seja, módulos ES que não usam await de nível superior).

Consulte Carregando módulos ECMAScript usando require() para detalhes.

Namespaces CommonJS

[Histórico]

VersãoMudanças
v23.0.0Adicionada a marca de exportação 'module.exports' para namespaces CJS.
v14.13.0Adicionada em: v14.13.0

Módulos CommonJS consistem em um objeto module.exports que pode ser de qualquer tipo.

Para dar suporte a isso, ao importar CommonJS de um módulo ECMAScript, um invólucro de namespace para o módulo CommonJS é construído, que sempre fornece uma chave de exportação default apontando para o valor module.exports do CommonJS.

Além disso, uma análise estática heurística é realizada no texto de origem do módulo CommonJS para obter uma lista estática de melhor esforço de exportações para fornecer no namespace a partir de valores em module.exports. Isso é necessário, pois esses namespaces devem ser construídos antes da avaliação do módulo CJS.

Esses objetos de namespace CommonJS também fornecem a exportação default como uma exportação nomeada 'module.exports', a fim de indicar inequivocamente que sua representação em CommonJS usa esse valor e não o valor do namespace. Isso reflete a semântica do tratamento do nome de exportação 'module.exports' no suporte de interop require(esm).

Ao importar um módulo CommonJS, ele pode ser importado de forma confiável usando a importação padrão do módulo ES ou sua sintaxe de açúcar correspondente:

js
import { default as cjs } from 'cjs'
// Idêntico ao acima
import cjsSugar from 'cjs'

console.log(cjs)
console.log(cjs === cjsSugar)
// Imprime:
//   <module.exports>
//   true

Este Objeto Exótico de Namespace de Módulo pode ser observado diretamente ao usar import * as m from 'cjs' ou uma importação dinâmica:

js
import * as m from 'cjs'
console.log(m)
console.log(m === (await import('cjs')))
// Imprime:
//   [Module] { default: <module.exports>, 'module.exports': <module.exports> }
//   true

Para melhor compatibilidade com o uso existente no ecossistema JS, o Node.js também tenta determinar as exportações nomeadas CommonJS de cada módulo CommonJS importado para fornecê-las como exportações separadas de módulos ES usando um processo de análise estática.

Por exemplo, considere um módulo CommonJS escrito:

js
// cjs.cjs
exports.name = 'exported'

O módulo anterior suporta importações nomeadas em módulos ES:

js
import { name } from './cjs.cjs'
console.log(name)
// Imprime: 'exported'

import cjs from './cjs.cjs'
console.log(cjs)
// Imprime: { name: 'exported' }

import * as m from './cjs.cjs'
console.log(m)
// Imprime:
//   [Module] {
//     default: { name: 'exported' },
//     'module.exports': { name: 'exported' },
//     name: 'exported'
//   }

Como pode ser visto no último exemplo do Objeto Exótico de Namespace de Módulo sendo registrado, a exportação name é copiada do objeto module.exports e definida diretamente no namespace do módulo ES quando o módulo é importado.

Atualizações de vinculação dinâmica ou novas exportações adicionadas a module.exports não são detectadas para essas exportações nomeadas.

A detecção de exportações nomeadas é baseada em padrões de sintaxe comuns, mas nem sempre detecta corretamente as exportações nomeadas. Nesses casos, usar o formulário de importação padrão descrito acima pode ser uma opção melhor.

A detecção de exportações nomeadas cobre muitos padrões de exportação comuns, padrões de reexportação e saídas de ferramentas de construção e transpiladores. Consulte cjs-module-lexer para a semântica exata implementada.

Diferenças entre módulos ES e CommonJS

Sem require, exports ou module.exports

Na maioria dos casos, o import do módulo ES pode ser usado para carregar módulos CommonJS.

Se necessário, uma função require pode ser construída dentro de um módulo ES usando module.createRequire().

Sem __filename ou __dirname

Essas variáveis CommonJS não estão disponíveis em módulos ES.

Casos de uso de __filename e __dirname podem ser replicados via import.meta.filename e import.meta.dirname.

Sem Carregamento de Addon

Addons não são atualmente suportados com imports de módulos ES.

Eles podem, em vez disso, ser carregados com module.createRequire() ou process.dlopen.

Sem require.resolve

A resolução relativa pode ser tratada via new URL('./local', import.meta.url).

Para uma substituição completa de require.resolve, existe a API import.meta.resolve.

Alternativamente, module.createRequire() pode ser usado.

Sem NODE_PATH

NODE_PATH não faz parte da resolução de especificadores import. Por favor, use symlinks se este comportamento for desejado.

Sem require.extensions

require.extensions não é usado por import. Hooks de customização de módulos podem fornecer uma substituição.

Sem require.cache

require.cache não é usado por import já que o carregador de módulos ES tem seu próprio cache separado.

Módulos JSON

[Histórico]

VersãoMudanças
v23.1.0Módulos JSON não são mais experimentais.

[Estável: 2 - Estável]

Estável: 2 Estabilidade: 2 - Estável

Arquivos JSON podem ser referenciados por import:

js
import packageConfig from './package.json' with { type: 'json' };

A sintaxe with { type: 'json' } é obrigatória; veja Atributos de Importação.

O JSON importado expõe apenas um export default. Não há suporte para exports nomeados. Uma entrada de cache é criada no cache CommonJS para evitar duplicação. O mesmo objeto é retornado no CommonJS se o módulo JSON já tiver sido importado do mesmo caminho.

Módulos Wasm

[Estável: 1 - Experimental]

Estável: 1 Estabilidade: 1 - Experimental

A importação de módulos WebAssembly é suportada sob a flag --experimental-wasm-modules, permitindo que quaisquer arquivos .wasm sejam importados como módulos normais, enquanto também suportam suas importações de módulos.

Essa integração está em linha com a Proposta de Integração de Módulos ES para WebAssembly.

Por exemplo, um index.mjs contendo:

js
import * as M from './module.wasm'
console.log(M)

executado em:

bash
node --experimental-wasm-modules index.mjs

forneceria a interface de exportação para a instanciação de module.wasm.

await de nível superior

Adicionado em: v14.8.0

A palavra-chave await pode ser usada no corpo de nível superior de um módulo ECMAScript.

Assumindo um a.mjs com

js
export const five = await Promise.resolve(5)

E um b.mjs com

js
import { five } from './a.mjs'

console.log(five) // Imprime `5`
bash
node b.mjs # funciona

Se uma expressão await de nível superior nunca for resolvida, o processo node sairá com um código de status 13.

js
import { spawn } from 'node:child_process'
import { execPath } from 'node:process'

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // Promise que nunca resolve:
  'await new Promise(() => {})',
]).once('exit', code => {
  console.log(code) // Imprime `13`
})

Carregadores

A documentação anterior dos Carregadores agora está em Módulos: Hooks de customização.

Algoritmo de resolução e carregamento

Recursos

O resolvedor padrão tem as seguintes propriedades:

  • Resolução baseada em FileURL como usado por módulos ES
  • Resolução de URL relativa e absoluta
  • Sem extensões padrão
  • Sem mains de pastas
  • Pesquisa de resolução de pacote de especificador vazio por meio de node_modules
  • Não falha em extensões ou protocolos desconhecidos
  • Pode opcionalmente fornecer uma dica do formato para a fase de carregamento

O carregador padrão tem as seguintes propriedades

  • Suporte para carregamento de módulo embutido via URLs node:
  • Suporte para carregamento de módulo "inline" via URLs data:
  • Suporte para carregamento de módulo file:
  • Falha em qualquer outro protocolo de URL
  • Falha em extensões desconhecidas para carregamento file: (suporta apenas .cjs, .js e .mjs)

Algoritmo de resolução

O algoritmo para carregar um especificador de módulo ES é fornecido através do método ESM_RESOLVE abaixo. Ele retorna a URL resolvida para um especificador de módulo relativo a um parentURL.

O algoritmo de resolução determina a URL resolvida completa para um carregamento de módulo, juntamente com seu formato de módulo sugerido. O algoritmo de resolução não determina se o protocolo de URL resolvido pode ser carregado ou se as extensões de arquivo são permitidas; em vez disso, essas validações são aplicadas pelo Node.js durante a fase de carregamento (por exemplo, se foi solicitado o carregamento de uma URL que tem um protocolo que não seja file:, data: ou node:).

O algoritmo também tenta determinar o formato do arquivo com base na extensão (consulte o algoritmo ESM_FILE_FORMAT abaixo). Se não reconhecer a extensão do arquivo (por exemplo, se não for .mjs, .cjs ou .json), um formato de undefined será retornado, o que causará um erro durante a fase de carregamento.

O algoritmo para determinar o formato do módulo de uma URL resolvida é fornecido por ESM_FILE_FORMAT, que retorna o formato de módulo único para qualquer arquivo. O formato "module" é retornado para um Módulo ECMAScript, enquanto o formato "commonjs" é usado para indicar o carregamento através do carregador CommonJS legado. Formatos adicionais, como "addon", podem ser estendidos em futuras atualizações.

Nos algoritmos a seguir, todos os erros de sub-rotina são propagados como erros dessas rotinas de nível superior, a menos que indicado de outra forma.

defaultConditions é o array de nomes de ambiente condicional, ["node", "import"].

O resolvedor pode lançar os seguintes erros:

  • Especificador de Módulo Inválido: O especificador de módulo é uma URL, nome de pacote ou especificador de subcaminho de pacote inválido.
  • Configuração de Pacote Inválida: A configuração do package.json é inválida ou contém uma configuração inválida.
  • Destino de Pacote Inválido: As exportações ou importações do pacote definem um módulo de destino para o pacote que é um tipo inválido ou destino de string.
  • Caminho do Pacote Não Exportado: As exportações do pacote não definem ou permitem um subcaminho de destino no pacote para o determinado módulo.
  • Importação de Pacote Não Definida: As importações de pacote não definem o especificador.
  • Módulo Não Encontrado: O pacote ou módulo solicitado não existe.
  • Importação de Diretório Não Suportada: O caminho resolvido corresponde a um diretório, que não é um destino suportado para importações de módulo.

Especificação do Algoritmo de Resolução

ESM_RESOLVE(especificador, parentURL)

PACKAGE_RESOLVE(especificadorDePacote, parentURL)

PACKAGE_SELF_RESOLVE(nomeDoPacote, subcaminhoDoPacote, parentURL)

PACKAGE_EXPORTS_RESOLVE(packageURL, subcaminho, exports, condições)

PACKAGE_IMPORTS_RESOLVE(especificador, parentURL, condições)

PACKAGE_IMPORTS_EXPORTS_RESOLVE(chaveDeCorrespondência, objetoDeCorrespondência, packageURL, éImports, condições)

PATTERN_KEY_COMPARE(chaveA, chaveB)

PACKAGE_TARGET_RESOLVE(packageURL, alvo, correspondênciaDePadrão, éImports, condições)

ESM_FILE_FORMAT(url)

LOOKUP_PACKAGE_SCOPE(url)

READ_PACKAGE_JSON(packageURL)

DETECT_MODULE_SYNTAX(fonte)

Personalizando o algoritmo de resolução de especificador ESM

Ganchos de personalização de módulo fornecem um mecanismo para personalizar o algoritmo de resolução de especificador ESM. Um exemplo que fornece resolução no estilo CommonJS para especificadores ESM é commonjs-extension-resolution-loader.