Skip to content

Módulos: Módulos ECMAScript

[Historial]

VersiónCambios
v23.1.0Los atributos de importación ya no son experimentales.
v22.0.0Se eliminó el soporte para las aserciones de importación.
v21.0.0, v20.10.0, v18.20.0Se agregó soporte experimental para los atributos de importación.
v20.0.0, v18.19.0Los hooks de personalización de módulos se ejecutan fuera del hilo principal.
v18.6.0, v16.17.0Se agregó soporte para encadenar hooks de personalización de módulos.
v17.1.0, v16.14.0Se agregó soporte experimental para las aserciones de importación.
v17.0.0, v16.12.0Se consolidaron los hooks de personalización, se eliminaron los hooks getFormat, getSource, transformSource y getGlobalPreloadCode, se agregaron los hooks load y globalPreload, se permitió devolver format desde los hooks resolve o load.
v14.8.0Se eliminó la bandera de Top-Level Await.
v15.3.0, v14.17.0, v12.22.0Se estabilizó la implementación de módulos.
v14.13.0, v12.20.0Soporte para la detección de exportaciones nombradas de CommonJS.
v14.0.0, v13.14.0, v12.20.0Se eliminó la advertencia de módulos experimentales.
v13.2.0, v12.17.0La carga de módulos ECMAScript ya no requiere una bandera de línea de comandos.
v12.0.0Se agregó soporte para módulos ES usando la extensión de archivo .js a través del campo "type" de package.json.
v8.5.0Añadido en: v8.5.0

[Estable: 2 - Estable]

Estable: 2 Estabilidad: 2 - Estable

Introducción

Los módulos ECMAScript son el formato estándar oficial para empaquetar código JavaScript para su reutilización. Los módulos se definen utilizando una variedad de declaraciones import y export.

El siguiente ejemplo de un módulo ES exporta una función:

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

export { addTwo }

El siguiente ejemplo de un módulo ES importa la función de addTwo.mjs:

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

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

Node.js es totalmente compatible con los módulos ECMAScript tal y como se especifican actualmente y proporciona interoperabilidad entre ellos y su formato de módulo original, CommonJS.

Habilitación

Node.js tiene dos sistemas de módulos: módulos CommonJS y módulos ECMAScript.

Los autores pueden indicar a Node.js que interprete JavaScript como un módulo ES a través de la extensión de archivo .mjs, el campo "type" de package.json con el valor "module" o el flag --input-type con el valor "module". Estos son marcadores explícitos de código que se pretende ejecutar como un módulo ES.

A la inversa, los autores pueden indicar explícitamente a Node.js que interprete JavaScript como CommonJS a través de la extensión de archivo .cjs, el campo "type" de package.json con el valor "commonjs" o el flag --input-type con el valor "commonjs".

Cuando el código carece de marcadores explícitos para cualquiera de los sistemas de módulos, Node.js inspeccionará el código fuente de un módulo para buscar la sintaxis de módulos ES. Si se encuentra dicha sintaxis, Node.js ejecutará el código como un módulo ES; de lo contrario, ejecutará el módulo como CommonJS. Consulte Determinación del sistema de módulos para obtener más detalles.

Paquetes

Esta sección se ha movido a Módulos: Paquetes.

Especificadores import

Terminología

El especificador de una declaración import es la cadena después de la palabra clave from, por ejemplo, 'node:path' en import { sep } from 'node:path'. Los especificadores también se usan en las declaraciones export from y como argumento para una expresión import().

Existen tres tipos de especificadores:

  • Especificadores relativos como './startup.js' o '../config.mjs'. Hacen referencia a una ruta relativa a la ubicación del archivo que importa. La extensión del archivo siempre es necesaria para estos.
  • Especificadores sin ruta como 'some-package' o 'some-package/shuffle'. Pueden hacer referencia al punto de entrada principal de un paquete por el nombre del paquete, o a un módulo de característica específico dentro de un paquete con el nombre del paquete como prefijo, según los ejemplos respectivamente. Incluir la extensión del archivo solo es necesario para paquetes sin un campo "exports".
  • Especificadores absolutos como 'file:///opt/nodejs/config.js'. Hacen referencia directa y explícitamente a una ruta completa.

La resolución de especificadores sin ruta se maneja mediante el algoritmo de resolución y carga de módulos de Node.js. Todas las demás resoluciones de especificadores siempre se resuelven únicamente con la semántica de resolución de URL relativa estándar.

Al igual que en CommonJS, se puede acceder a los archivos de módulos dentro de los paquetes agregando una ruta al nombre del paquete, a menos que el package.json del paquete contenga un campo "exports", en cuyo caso solo se puede acceder a los archivos dentro de los paquetes a través de las rutas definidas en "exports".

Para obtener detalles sobre estas reglas de resolución de paquetes que se aplican a los especificadores sin ruta en la resolución de módulos de Node.js, consulte la documentación de paquetes.

Extensiones de archivo obligatorias

Se debe proporcionar una extensión de archivo al usar la palabra clave import para resolver especificadores relativos o absolutos. Los índices de directorio (por ejemplo, './startup/index.js') también deben especificarse completamente.

Este comportamiento coincide con cómo se comporta import en entornos de navegador, asumiendo un servidor configurado de manera típica.

URLs

Los módulos ES se resuelven y almacenan en caché como URLs. Esto significa que los caracteres especiales deben estar codificados por porcentaje, como # con %23 y ? con %3F.

Se admiten los esquemas de URL file:, node: y data:. Un especificador como 'https://example.com/app.js' no se admite de forma nativa en Node.js a menos que se utilice un cargador HTTPS personalizado.

URLs file:

Los módulos se cargan varias veces si el especificador import utilizado para resolverlos tiene una consulta o fragmento diferente.

js
import './foo.mjs?query=1' // carga ./foo.mjs con la consulta "?query=1"
import './foo.mjs?query=2' // carga ./foo.mjs con la consulta "?query=2"

La raíz del volumen se puede referenciar a través de /, // o file:///. Dadas las diferencias entre la URL y la resolución de rutas (como los detalles de la codificación por porcentaje), se recomienda usar url.pathToFileURL al importar una ruta.

Imports data:

Agregado en: v12.10.0

Las data: URLs son compatibles para importar con los siguientes tipos MIME:

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

Las data: URLs solo resuelven especificadores desnudos para módulos integrados y especificadores absolutos. Resolver especificadores relativos no funciona porque data: no es un esquema especial. Por ejemplo, intentar cargar ./foo desde data:text/javascript,import "./foo"; no se resuelve porque no existe el concepto de resolución relativa para las data: URLs.

Imports node:

[Historial]

VersiónCambios
v16.0.0, v14.18.0Se agregó soporte para importación node: a require(...).
v14.13.1, v12.20.0Agregado en: v14.13.1, v12.20.0

Las node: URLs son compatibles como un medio alternativo para cargar módulos incorporados de Node.js. Este esquema de URL permite que los módulos incorporados se referencien mediante cadenas de URL absolutas válidas.

js
import fs from 'node:fs/promises'

Importar atributos

[Historial]

VersiónCambios
v21.0.0, v20.10.0, v18.20.0Cambio de Afirmaciones de Importación a Atributos de Importación.
v17.1.0, v16.14.0Agregado en: v17.1.0, v16.14.0

[Estable: 2 - Estable]

Estable: 2 Estabilidad: 2 - Estable

Los atributos de importación son una sintaxis en línea para las declaraciones de importación de módulos para pasar más información junto con el especificador del módulo.

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

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

Node.js solo admite el atributo type, para el cual admite los siguientes valores:

Atributo typeNecesario para
'json'Módulos JSON

El atributo type: 'json' es obligatorio al importar módulos JSON.

Módulos integrados

Los módulos integrados proporcionan exportaciones con nombre de su API pública. También se proporciona una exportación predeterminada que es el valor de las exportaciones de CommonJS. La exportación predeterminada se puede utilizar, entre otras cosas, para modificar las exportaciones con nombre. Las exportaciones con nombre de los módulos integrados se actualizan solo llamando a 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('Hola, ESM')
syncBuiltinESMExports()

fs.readFileSync === readFileSync

Expresiones import()

El import dinámico import() es compatible tanto en CommonJS como en los módulos ES. En los módulos CommonJS, se puede utilizar para cargar módulos ES.

import.meta

La metapropiedad import.meta es un Object que contiene las siguientes propiedades. Solo se admite en módulos ES.

import.meta.dirname

Agregado en: v21.2.0, v20.11.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1.2 - Candidato a lanzamiento

import.meta.filename

Agregado en: v21.2.0, v20.11.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1.2 - Candidato a lanzamiento

import.meta.url

  • <string> La URL file: absoluta del módulo.

Esto se define exactamente igual que en los navegadores, proporcionando la URL del archivo del módulo actual.

Esto permite patrones útiles, como la carga de archivos relativos:

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

import.meta.resolve(specifier)

[Historial]

VersiónCambios
v20.6.0, v18.19.0Ya no está detrás del indicador CLI --experimental-import-meta-resolve, excepto para el parámetro no estándar parentURL.
v20.6.0, v18.19.0Esta API ya no lanza un error cuando se dirigen a URLs file: que no se mapean a un archivo existente en el sistema de archivos local.
v20.0.0, v18.19.0Esta API ahora devuelve una cadena de forma síncrona en lugar de una Promesa.
v16.2.0, v14.18.0Se agregó soporte para el objeto WHATWG URL al parámetro parentURL.
v13.9.0, v12.16.2Agregado en: v13.9.0, v12.16.2

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1.2 - Candidato a lanzamiento

  • specifier <string> El especificador de módulo para resolver en relación con el módulo actual.
  • Devuelve: <string> La cadena de URL absoluta a la que se resolvería el especificador.

import.meta.resolve es una función de resolución relativa al módulo con ámbito en cada módulo, que devuelve la cadena de 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

Todas las características de la resolución de módulos de Node.js son compatibles. Las resoluciones de dependencias están sujetas a las resoluciones de exportaciones permitidas dentro del paquete.

Advertencias:

  • Esto puede resultar en operaciones síncronas del sistema de archivos, lo que puede afectar el rendimiento de manera similar a require.resolve.
  • Esta característica no está disponible dentro de los cargadores personalizados (crearía un interbloqueo).

API no estándar:

Cuando se utiliza el indicador --experimental-import-meta-resolve, esa función acepta un segundo argumento:

  • parent <string> | <URL> Una URL de módulo principal absoluta opcional desde la cual resolver. Predeterminado: import.meta.url

Interoperabilidad con CommonJS

Sentencias import

Una sentencia import puede hacer referencia a un módulo ES o a un módulo CommonJS. Las sentencias import solo están permitidas en módulos ES, pero las expresiones dinámicas import() son compatibles con CommonJS para cargar módulos ES.

Al importar módulos CommonJS, el objeto module.exports se proporciona como la exportación por defecto. Las exportaciones con nombre pueden estar disponibles, proporcionadas por un análisis estático como una conveniencia para una mejor compatibilidad con el ecosistema.

require

El módulo CommonJS require actualmente solo admite la carga de módulos ES síncronos (es decir, módulos ES que no utilizan await de nivel superior).

Consulte Cargar módulos ECMAScript usando require() para obtener más detalles.

Espacios de nombres CommonJS

[Historial]

VersiónCambios
v23.0.0Se agregó el marcador de exportación 'module.exports' a los espacios de nombres CJS.
v14.13.0Agregado en: v14.13.0

Los módulos CommonJS consisten en un objeto module.exports que puede ser de cualquier tipo.

Para admitir esto, al importar CommonJS desde un módulo ECMAScript, se construye un envoltorio de espacio de nombres para el módulo CommonJS, que siempre proporciona una clave de exportación default que apunta al valor module.exports de CommonJS.

Además, se realiza un análisis estático heurístico en el texto fuente del módulo CommonJS para obtener una lista estática de exportaciones del mejor esfuerzo para proporcionar en el espacio de nombres a partir de los valores de module.exports. Esto es necesario ya que estos espacios de nombres deben construirse antes de la evaluación del módulo CJS.

Estos objetos de espacio de nombres CommonJS también proporcionan la exportación default como una exportación con nombre 'module.exports', para indicar inequívocamente que su representación en CommonJS utiliza este valor y no el valor del espacio de nombres. Esto refleja la semántica del manejo del nombre de exportación 'module.exports' en el soporte de interop require(esm).

Al importar un módulo CommonJS, se puede importar de forma fiable utilizando la importación predeterminada del módulo ES o su sintaxis de azúcar correspondiente:

js
import { default as cjs } from 'cjs'
// Idéntico a lo anterior
import cjsSugar from 'cjs'

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

Este Objeto Exótico del Espacio de Nombres del Módulo se puede observar directamente ya sea cuando se usa import * as m from 'cjs' o una importación 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 una mejor compatibilidad con el uso existente en el ecosistema JS, Node.js además intenta determinar las exportaciones con nombre CommonJS de cada módulo CommonJS importado para proporcionarlas como exportaciones de módulos ES separadas utilizando un proceso de análisis estático.

Por ejemplo, considere un módulo CommonJS escrito:

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

El módulo anterior admite importaciones con nombre en 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 se puede ver en el último ejemplo del Objeto Exótico del Espacio de Nombres del Módulo que se está registrando, la exportación name se copia del objeto module.exports y se establece directamente en el espacio de nombres del módulo ES cuando se importa el módulo.

Las actualizaciones de enlace en vivo o las nuevas exportaciones agregadas a module.exports no se detectan para estas exportaciones con nombre.

La detección de exportaciones con nombre se basa en patrones de sintaxis comunes, pero no siempre detecta correctamente las exportaciones con nombre. En estos casos, usar el formulario de importación predeterminado descrito anteriormente puede ser una mejor opción.

La detección de exportaciones con nombre cubre muchos patrones de exportación comunes, patrones de reexportación y salidas de herramientas de construcción y transpiladores. Consulte cjs-module-lexer para conocer la semántica exacta implementada.

Diferencias entre los módulos ES y CommonJS

No require, exports ni module.exports

En la mayoría de los casos, el import del módulo ES se puede usar para cargar módulos CommonJS.

Si es necesario, se puede construir una función require dentro de un módulo ES usando module.createRequire().

No __filename ni __dirname

Estas variables de CommonJS no están disponibles en los módulos ES.

Los casos de uso de __filename y __dirname se pueden replicar a través de import.meta.filename e import.meta.dirname.

No se admite la carga de complementos (Addons)

Actualmente, los complementos no son compatibles con las importaciones de módulos ES.

En su lugar, se pueden cargar con module.createRequire() o process.dlopen.

No require.resolve

La resolución relativa se puede manejar a través de new URL('./local', import.meta.url).

Para un reemplazo completo de require.resolve, existe la API import.meta.resolve.

Alternativamente, se puede usar module.createRequire().

Sin NODE_PATH

NODE_PATH no es parte de la resolución de especificadores de import. Por favor, usa enlaces simbólicos si deseas este comportamiento.

Sin require.extensions

require.extensions no es usado por import. Los hooks de personalización de módulos pueden proporcionar un reemplazo.

Sin require.cache

require.cache no es usado por import ya que el cargador de módulos ES tiene su propia caché separada.

Módulos JSON

[Historia]

VersiónCambios
v23.1.0Los módulos JSON ya no son experimentales.

[Estable: 2 - Estable]

Estable: 2 Estabilidad: 2 - Estable

Se puede hacer referencia a los archivos JSON con import:

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

La sintaxis with { type: 'json' } es obligatoria; ver Atributos de Importación.

El JSON importado solo expone una exportación default. No hay soporte para exportaciones con nombre. Se crea una entrada en la caché de CommonJS para evitar la duplicación. El mismo objeto se devuelve en CommonJS si el módulo JSON ya ha sido importado desde la misma ruta.

Módulos Wasm

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

La importación de módulos WebAssembly es compatible bajo la bandera --experimental-wasm-modules, permitiendo que cualquier archivo .wasm sea importado como módulos normales, a la vez que soporta las importaciones de sus propios módulos.

Esta integración está en línea con la Propuesta de Integración de Módulos ES para WebAssembly.

Por ejemplo, un index.mjs que contenga:

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

ejecutado bajo:

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

proporcionaría la interfaz de exportaciones para la instanciación de module.wasm.

await de nivel superior

Añadido en: v14.8.0

La palabra clave await puede usarse en el cuerpo de nivel superior de un módulo ECMAScript.

Asumiendo un a.mjs con

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

Y un b.mjs con

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

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

Si una expresión await de nivel superior nunca se resuelve, el proceso node saldrá con un código de estado 13.

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

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

Cargadores

La documentación anterior sobre Cargadores ahora se encuentra en Módulos: Hooks de personalización.

Algoritmo de resolución y carga

Características

El resolutor predeterminado tiene las siguientes propiedades:

  • Resolución basada en FileURL tal como se utiliza en los módulos ES
  • Resolución de URL relativa y absoluta
  • Sin extensiones predeterminadas
  • Sin principales de carpeta
  • Búsqueda de resolución de paquetes de especificadores sin formato a través de node_modules
  • No falla en extensiones o protocolos desconocidos
  • Opcionalmente puede proporcionar una sugerencia del formato para la fase de carga

El cargador predeterminado tiene las siguientes propiedades

  • Soporte para la carga de módulos incorporados a través de URL node:
  • Soporte para la carga de módulos "en línea" a través de URL data:
  • Soporte para la carga de módulos file:
  • Falla en cualquier otro protocolo de URL
  • Falla en extensiones desconocidas para la carga file: (solo admite .cjs, .js y .mjs)

Algoritmo de resolución

El algoritmo para cargar un especificador de módulo ES se proporciona a través del método ESM_RESOLVE a continuación. Devuelve la URL resuelta para un especificador de módulo en relación con una parentURL.

El algoritmo de resolución determina la URL resuelta completa para una carga de módulo, junto con su formato de módulo sugerido. El algoritmo de resolución no determina si el protocolo de URL resuelto se puede cargar, o si las extensiones de archivo están permitidas, en cambio, estas validaciones son aplicadas por Node.js durante la fase de carga (por ejemplo, si se solicitó cargar una URL que tiene un protocolo que no es file:, data: o node:).

El algoritmo también intenta determinar el formato del archivo en función de la extensión (consulte el algoritmo ESM_FILE_FORMAT a continuación). Si no reconoce la extensión del archivo (por ejemplo, si no es .mjs, .cjs o .json), entonces se devuelve un formato de undefined, que se lanzará durante la fase de carga.

El algoritmo para determinar el formato del módulo de una URL resuelta se proporciona mediante ESM_FILE_FORMAT, que devuelve el formato de módulo único para cualquier archivo. El formato "module" se devuelve para un Módulo ECMAScript, mientras que el formato "commonjs" se utiliza para indicar la carga a través del cargador CommonJS heredado. En futuras actualizaciones se pueden ampliar formatos adicionales como "addon".

En los siguientes algoritmos, todos los errores de subrutina se propagan como errores de estas rutinas de nivel superior a menos que se indique lo contrario.

defaultConditions es la matriz de nombres de entorno condicional, ["node", "import"].

El resolutor puede lanzar los siguientes errores:

  • Especificador de módulo no válido: el especificador de módulo es una URL, un nombre de paquete o un especificador de subruta de paquete no válidos.
  • Configuración de paquete no válida: la configuración de package.json no es válida o contiene una configuración no válida.
  • Destino de paquete no válido: las exportaciones o importaciones de paquetes definen un módulo de destino para el paquete que es un tipo no válido o un destino de cadena.
  • Ruta de paquete no exportada: las exportaciones de paquetes no definen o permiten una subruta de destino en el paquete para el módulo dado.
  • Importación de paquete no definida: las importaciones de paquetes no definen el especificador.
  • Módulo no encontrado: el paquete o módulo solicitado no existe.
  • Importación de directorio no admitida: la ruta resuelta corresponde a un directorio, que no es un destino admitido para las importaciones de módulos.

Especificación del Algoritmo de Resolución

ESM_RESOLVE(especificador, URLpadre)

PACKAGE_RESOLVE(especificadorPaquete, URLpadre)

PACKAGE_SELF_RESOLVE(nombrePaquete, subrutaPaquete, URLpadre)

PACKAGE_EXPORTS_RESOLVE(URLpaquete, subruta, exports, condiciones)

PACKAGE_IMPORTS_RESOLVE(especificador, URLpadre, condiciones)

PACKAGE_IMPORTS_EXPORTS_RESOLVE(claveCoincidente, objetoCoincidente, URLpaquete, esImports, condiciones)

PATTERN_KEY_COMPARE(claveA, claveB)

PACKAGE_TARGET_RESOLVE(URLpaquete, objetivo, patrónCoincidente, esImports, condiciones)

ESM_FILE_FORMAT(url)

LOOKUP_PACKAGE_SCOPE(url)

READ_PACKAGE_JSON(URLpaquete)

DETECT_MODULE_SYNTAX(fuente)

Personalización del algoritmo de resolución de especificadores ESM

Los hooks de personalización de módulos proporcionan un mecanismo para personalizar el algoritmo de resolución de especificadores ESM. Un ejemplo que proporciona la resolución al estilo CommonJS para los especificadores ESM es commonjs-extension-resolution-loader.