Módulos: Módulos ECMAScript
[Histórico]
Versão | Mudanças |
---|---|
v23.1.0 | Os atributos de importação não são mais experimentais. |
v22.0.0 | Remoção do suporte para declarações de importação. |
v21.0.0, v20.10.0, v18.20.0 | Adicionado suporte experimental para atributos de importação. |
v20.0.0, v18.19.0 | Os hooks de personalização de módulos são executados fora da thread principal. |
v18.6.0, v16.17.0 | Adicionado suporte para encadeamento de hooks de personalização de módulos. |
v17.1.0, v16.14.0 | Adicionado suporte experimental para declarações de importação. |
v17.0.0, v16.12.0 | Consolidaçã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.0 | Remoção da sinalização Top-Level Await. |
v15.3.0, v14.17.0, v12.22.0 | Estabilização da implementação de módulos. |
v14.13.0, v12.20.0 | Suporte para detecção de exports nomeados do CommonJS. |
v14.0.0, v13.14.0, v12.20.0 | Remoção do aviso de módulos experimentais. |
v13.2.0, v12.17.0 | O carregamento de módulos ECMAScript não requer mais uma flag de linha de comando. |
v12.0.0 | Adicionado suporte para módulos ES usando a extensão de arquivo .js via campo "type" em package.json . |
v8.5.0 | Adicionado 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:
// 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
:
// 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.
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 ESapplication/json
para JSONapplication/wasm
para Wasm
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ão | Mudanças |
---|---|
v16.0.0, v14.18.0 | Adicionado suporte a importação node: para require(...) . |
v14.13.1, v12.20.0 | Adicionado 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.
import fs from 'node:fs/promises'
Atributos de importação
[Histórico]
Versão | Mudanças |
---|---|
v21.0.0, v20.10.0, v18.20.0 | Mudança de Import Assertions para Import Attributes. |
v17.1.0, v16.14.0 | Adicionado 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.
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 type | Necessá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()
.
import EventEmitter from 'node:events'
const e = new EventEmitter()
import { readFile } from 'node:fs'
readFile('./foo.txt', (err, source) => {
if (err) {
console.error(err)
} else {
console.log(source)
}
})
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
- <string> O nome do diretório do módulo atual. É o mesmo que o
path.dirname()
doimport.meta.filename
.
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
- <string> O caminho absoluto completo e o nome do arquivo do módulo atual, com os symlinks resolvidos.
- É o mesmo que o
url.fileURLToPath()
doimport.meta.url
.
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:
import { readFileSync } from 'node:fs'
const buffer = readFileSync(new URL('./data.proto', import.meta.url))
import.meta.resolve(specifier)
[Histórico]
Versão | Alterações |
---|---|
v20.6.0, v18.19.0 | Nã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.0 | Esta 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.0 | Esta API agora retorna uma string de forma síncrona em vez de uma Promise. |
v16.2.0, v14.18.0 | Adicionado suporte para objeto WHATWG URL ao parâmetro parentURL . |
v13.9.0, v12.16.2 | Adicionado 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.
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ão | Mudanças |
---|---|
v23.0.0 | Adicionada a marca de exportação 'module.exports' para namespaces CJS. |
v14.13.0 | Adicionada 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:
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:
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:
// cjs.cjs
exports.name = 'exported'
O módulo anterior suporta importações nomeadas em módulos ES:
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ão | Mudanças |
---|---|
v23.1.0 | Mó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
:
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:
import * as M from './module.wasm'
console.log(M)
executado em:
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
export const five = await Promise.resolve(5)
E um b.mjs
com
import { five } from './a.mjs'
console.log(five) // Imprime `5`
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
.
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.