Skip to content

Módulos: Paquetes

[Historial]

VersiónCambios
v14.13.0, v12.20.0Se agrega soporte para patrones "exports".
v14.6.0, v12.19.0Se agrega el campo "imports" del paquete.
v13.7.0, v12.17.0Se desactiva el indicador de exportaciones condicionales.
v13.7.0, v12.16.0Se elimina la opción --experimental-conditional-exports. En 12.16.0, las exportaciones condicionales todavía están detrás de --experimental-modules.
v13.6.0, v12.16.0Se desactiva el indicador de auto referencia a un paquete usando su nombre.
v12.7.0Se introduce el campo "exports" en package.json como una alternativa más potente al campo clásico "main".
v12.0.0Se agrega soporte para módulos ES usando la extensión de archivo .js a través del campo "type" de package.json.

Introducción

Un paquete es un árbol de carpetas descrito por un archivo package.json. El paquete consiste en la carpeta que contiene el archivo package.json y todas las subcarpetas hasta la siguiente carpeta que contiene otro archivo package.json o una carpeta llamada node_modules.

Esta página proporciona orientación para los autores de paquetes que escriben archivos package.json, junto con una referencia para los campos package.json definidos por Node.js.

Determinación del sistema de módulos

Introducción

Node.js tratará lo siguiente como módulos ES cuando se pase a node como entrada inicial, o cuando se haga referencia a ellos mediante sentencias import o expresiones import():

  • Archivos con extensión .mjs.
  • Archivos con extensión .js cuando el archivo package.json padre más cercano contenga un campo de nivel superior "type" con un valor de "module".
  • Cadenas de texto pasadas como argumento a --eval, o canalizadas a node a través de STDIN, con el flag --input-type=module.
  • Código que contenga sintaxis que solo se analiza con éxito como módulos ES, como las sentencias import o export o import.meta, sin un marcador explícito de cómo debe interpretarse. Los marcadores explícitos son las extensiones .mjs o .cjs, los campos "type" de package.json con los valores "module" o "commonjs", o el flag --input-type. Las expresiones dinámicas import() son compatibles tanto en CommonJS como en los módulos ES y no forzarían que un archivo se trate como un módulo ES. Consulte Detección de sintaxis.

Node.js tratará lo siguiente como CommonJS cuando se pase a node como entrada inicial, o cuando se haga referencia a ellos mediante sentencias import o expresiones import():

  • Archivos con extensión .cjs.
  • Archivos con extensión .js cuando el archivo package.json padre más cercano contenga un campo de nivel superior "type" con un valor de "commonjs".
  • Cadenas de texto pasadas como argumento a --eval o --print, o canalizadas a node a través de STDIN, con el flag --input-type=commonjs.
  • Archivos con extensión .js sin archivo package.json padre o donde el archivo package.json padre más cercano carezca de un campo type, y donde el código pueda evaluarse correctamente como CommonJS. En otras palabras, Node.js intenta ejecutar primero estos archivos "ambiguos" como CommonJS, y volverá a intentar evaluarlos como módulos ES si la evaluación como CommonJS falla porque el analizador encontró sintaxis de módulos ES.

Escribir sintaxis de módulos ES en archivos "ambiguos" incurre en un coste de rendimiento, por lo que se anima a los autores a que sean explícitos siempre que sea posible. En particular, los autores de paquetes siempre deben incluir el campo "type" en sus archivos package.json, incluso en los paquetes donde todas las fuentes son CommonJS. Ser explícito sobre el type del paquete protegerá el paquete en el futuro en caso de que el tipo predeterminado de Node.js cambie alguna vez, y también facilitará a las herramientas de construcción y a los cargadores la determinación de cómo deben interpretarse los archivos del paquete.

Detección de sintaxis

[Historial]

VersiónCambios
v22.7.0La detección de sintaxis está habilitada por defecto.
v21.1.0, v20.10.0Añadido en: v21.1.0, v20.10.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1.2 - Candidato a lanzamiento

Node.js inspeccionará el código fuente de la entrada ambigua para determinar si contiene sintaxis de módulo ES; si se detecta dicha sintaxis, la entrada se tratará como un módulo ES.

La entrada ambigua se define como:

  • Archivos con una extensión .js o sin extensión; y ya sea que no haya un archivo package.json de control o uno que carezca de un campo type.
  • Entrada de cadena (--eval o STDIN) cuando no se especifica --input-type.

La sintaxis del módulo ES se define como sintaxis que se lanzaría al evaluarse como CommonJS. Esto incluye lo siguiente:

  • Declaraciones import (pero no expresiones import(), que son válidas en CommonJS).
  • Declaraciones export.
  • Referencias import.meta.
  • await en el nivel superior de un módulo.
  • Redeclaraciones léxicas de las variables de envoltura CommonJS (require, module, exports, __dirname, __filename).

Cargadores de módulos

Node.js tiene dos sistemas para resolver un especificador y cargar módulos.

Existe el cargador de módulos CommonJS:

  • Es totalmente síncrono.
  • Es responsable de manejar las llamadas a require().
  • Se puede modificar mediante monkey patching.
  • Admite carpetas como módulos.
  • Al resolver un especificador, si no se encuentra una coincidencia exacta, intentará agregar extensiones (.js, .json y finalmente .node) y luego intentará resolver carpetas como módulos.
  • Trata .json como archivos de texto JSON.
  • Los archivos .node se interpretan como módulos complementarios compilados cargados con process.dlopen().
  • Trata todos los archivos que carecen de extensiones .json o .node como archivos de texto JavaScript.
  • Solo se puede utilizar para cargar módulos ECMAScript desde módulos CommonJS si el gráfico de módulos es síncrono (es decir, que no contiene await de nivel superior). Cuando se usa para cargar un archivo de texto JavaScript que no es un módulo ECMAScript, el archivo se cargará como un módulo CommonJS.

Existe el cargador de módulos ECMAScript:

  • Es asíncrono, a menos que se esté utilizando para cargar módulos para require().
  • Es responsable de manejar las declaraciones import y las expresiones import().
  • No se puede modificar mediante monkey patching, se puede personalizar utilizando ganchos de cargador.
  • No admite carpetas como módulos, los índices de directorio (por ejemplo, './startup/index.js') deben especificarse por completo.
  • No realiza búsquedas de extensión. Se debe proporcionar una extensión de archivo cuando el especificador es una URL de archivo relativa o absoluta.
  • Puede cargar módulos JSON, pero se requiere un atributo de tipo de importación.
  • Acepta solo las extensiones .js, .mjs y .cjs para archivos de texto JavaScript.
  • Se puede usar para cargar módulos JavaScript CommonJS. Dichos módulos se pasan a través de cjs-module-lexer para intentar identificar exportaciones con nombre, que están disponibles si se pueden determinar mediante análisis estático. Los módulos CommonJS importados tienen sus URL convertidas en rutas absolutas y luego se cargan a través del cargador de módulos CommonJS.

package.json y extensiones de archivo

Dentro de un paquete, el campo "type" de package.json define cómo Node.js debe interpretar los archivos .js. Si un archivo package.json no tiene un campo "type", los archivos .js se tratan como CommonJS.

Un valor "type" de "module" en package.json indica a Node.js que interprete los archivos .js dentro de ese paquete utilizando la sintaxis de módulos ES.

El campo "type" se aplica no solo a los puntos de entrada iniciales (node my-app.js), sino también a los archivos a los que se hace referencia mediante las declaraciones import y las expresiones import().

js
// my-app.js, tratado como un módulo ES porque hay un archivo package.json
// en la misma carpeta con "type": "module".

import './startup/init.js'
// Cargado como módulo ES ya que ./startup no contiene ningún archivo package.json,
// y por lo tanto hereda el valor "type" del nivel superior.

import 'commonjs-package'
// Cargado como CommonJS ya que ./node_modules/commonjs-package/package.json
// carece de un campo "type" o contiene "type": "commonjs".

import './node_modules/commonjs-package/index.js'
// Cargado como CommonJS ya que ./node_modules/commonjs-package/package.json
// carece de un campo "type" o contiene "type": "commonjs".

Los archivos que terminan con .mjs siempre se cargan como módulos ES independientemente del package.json principal más cercano.

Los archivos que terminan con .cjs siempre se cargan como CommonJS independientemente del package.json principal más cercano.

js
import './legacy-file.cjs'
// Cargado como CommonJS ya que .cjs siempre se carga como CommonJS.

import 'commonjs-package/src/index.mjs'
// Cargado como módulo ES ya que .mjs siempre se carga como módulo ES.

Las extensiones .mjs y .cjs se pueden usar para mezclar tipos dentro del mismo paquete:

  • Dentro de un paquete "type": "module", se puede indicar a Node.js que interprete un archivo en particular como CommonJS nombrándolo con la extensión .cjs (ya que tanto los archivos .js como los .mjs se tratan como módulos ES dentro de un paquete "module").
  • Dentro de un paquete "type": "commonjs", se puede indicar a Node.js que interprete un archivo en particular como un módulo ES nombrándolo con la extensión .mjs (ya que tanto los archivos .js como los .cjs se tratan como CommonJS dentro de un paquete "commonjs").

Bandera --input-type

Agregado en: v12.0.0

Las cadenas que se pasan como argumento a --eval (o -e), o se envían a node a través de STDIN, se tratan como módulos ES cuando se establece la bandera --input-type=module.

bash
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module

Para completar, también existe --input-type=commonjs, para ejecutar explícitamente la entrada de cadenas como CommonJS. Este es el comportamiento predeterminado si no se especifica --input-type.

Determinación del administrador de paquetes

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

Si bien se espera que todos los proyectos de Node.js se puedan instalar con todos los administradores de paquetes una vez publicados, sus equipos de desarrollo a menudo deben usar un administrador de paquetes específico. Para facilitar este proceso, Node.js incluye una herramienta llamada Corepack que tiene como objetivo hacer que todos los administradores de paquetes estén disponibles de forma transparente en su entorno, siempre que tenga Node.js instalado.

De forma predeterminada, Corepack no aplicará ningún administrador de paquetes específico y utilizará las versiones genéricas "Última conocida buena" asociadas con cada versión de Node.js, pero puede mejorar esta experiencia configurando el campo "packageManager" en el package.json de su proyecto.

Puntos de entrada del paquete

En el archivo package.json de un paquete, dos campos pueden definir los puntos de entrada para un paquete: "main" y "exports". Ambos campos se aplican tanto a los puntos de entrada de módulos ES como de módulos CommonJS.

El campo "main" es compatible con todas las versiones de Node.js, pero sus capacidades son limitadas: solo define el punto de entrada principal del paquete.

El campo "exports" proporciona una alternativa moderna a "main" que permite definir múltiples puntos de entrada, soporte de resolución de entrada condicional entre entornos y previene cualquier otro punto de entrada además de los definidos en "exports". Esta encapsulación permite a los autores de módulos definir claramente la interfaz pública de su paquete.

Para nuevos paquetes dirigidos a las versiones actualmente compatibles de Node.js, se recomienda el campo "exports". Para paquetes que admiten Node.js 10 y versiones anteriores, se requiere el campo "main". Si se definen tanto "exports" como "main", el campo "exports" tiene prioridad sobre "main" en las versiones compatibles de Node.js.

Se pueden usar exportaciones condicionales dentro de "exports" para definir diferentes puntos de entrada del paquete por entorno, incluyendo si se hace referencia al paquete a través de require o a través de import. Para obtener más información sobre cómo admitir módulos CommonJS y ES en un solo paquete, consulte la sección de paquetes duales CommonJS/ES module.

Los paquetes existentes que introducen el campo "exports" evitarán que los consumidores del paquete utilicen cualquier punto de entrada que no esté definido, incluyendo el package.json (por ejemplo, require('your-package/package.json')). Es probable que esto sea un cambio disruptivo.

Para que la introducción de "exports" no sea disruptiva, asegúrese de que se exporte cada punto de entrada compatible previamente. Es mejor especificar explícitamente los puntos de entrada para que la API pública del paquete esté bien definida. Por ejemplo, un proyecto que previamente exportaba main, lib, feature y el package.json podría usar el siguiente package.exports:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

Alternativamente, un proyecto podría optar por exportar carpetas completas con y sin subrutas con extensión utilizando patrones de exportación:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./package.json": "./package.json"
  }
}

Con lo anterior que proporciona compatibilidad con versiones anteriores para cualquier versión de paquete menor, un futuro cambio importante para el paquete puede entonces restringir correctamente las exportaciones solo a las exportaciones de características específicas expuestas:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}

Punto de entrada principal de exportación

Al escribir un nuevo paquete, se recomienda usar el campo "exports":

json
{
  "exports": "./index.js"
}

Cuando el campo "exports" está definido, todas las subrutas del paquete se encapsulan y ya no están disponibles para los importadores. Por ejemplo, require('pkg/subpath.js') arroja un error ERR_PACKAGE_PATH_NOT_EXPORTED.

Esta encapsulación de exportaciones proporciona garantías más fiables sobre las interfaces de los paquetes para las herramientas y al manejar las actualizaciones semver para un paquete. No es una encapsulación fuerte, ya que una solicitud directa de cualquier subruta absoluta del paquete como require('/path/to/node_modules/pkg/subpath.js') seguirá cargando subpath.js.

Todas las versiones actualmente compatibles de Node.js y las herramientas de compilación modernas admiten el campo "exports". Para los proyectos que usan una versión anterior de Node.js o una herramienta de compilación relacionada, la compatibilidad se puede lograr incluyendo el campo "main" junto con "exports" apuntando al mismo módulo:

json
{
  "main": "./index.js",
  "exports": "./index.js"
}

Subpath exports

Agregado en: v12.7.0

Cuando se usa el campo "exports", se pueden definir subrutas personalizadas junto con el punto de entrada principal tratando el punto de entrada principal como la subruta ".":

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}

Ahora, solo la subruta definida en "exports" puede ser importada por un consumidor:

js
import submodule from 'es-module-package/submodule.js'
// Carga ./node_modules/es-module-package/src/submodule.js

Mientras que otras subrutas generarán un error:

js
import submodule from 'es-module-package/private-module.js'
// Lanza ERR_PACKAGE_PATH_NOT_EXPORTED

Extensiones en subrutas

Los autores de paquetes deben proporcionar subrutas con extensión (import 'pkg/subpath.js') o sin extensión (import 'pkg/subpath') en sus exports. Esto asegura que haya solo una subruta para cada módulo exportado para que todos los dependientes importen el mismo especificador consistente, manteniendo claro el contrato del paquete para los consumidores y simplificando las finalizaciones de subrutas del paquete.

Tradicionalmente, los paquetes tendían a usar el estilo sin extensión, que tiene los beneficios de la legibilidad y de enmascarar la ruta real del archivo dentro del paquete.

Con mapas de importación ahora proporcionando un estándar para la resolución de paquetes en navegadores y otros tiempos de ejecución de JavaScript, usar el estilo sin extensión puede resultar en definiciones de mapas de importación infladas. Las extensiones de archivo explícitas pueden evitar este problema al permitir que el mapa de importación utilice un mapeo de carpeta de paquetes para mapear múltiples subrutas donde sea posible en lugar de una entrada de mapa separada por exportación de subruta de paquete. Esto también refleja el requisito de usar la ruta completa del especificador en especificadores de importación relativos y absolutos.

Azúcar para exportaciones

Añadido en: v12.11.0

Si la exportación "." es la única exportación, el campo "exports" proporciona azúcar para este caso siendo el valor directo del campo "exports".

json
{
  "exports": {
    ".": "./index.js"
  }
}

puede escribirse:

json
{
  "exports": "./index.js"
}

Importaciones de subruta

Añadido en: v14.6.0, v12.19.0

Además del campo "exports", existe un campo "imports" en el paquete para crear asignaciones privadas que solo se aplican a los especificadores de importación desde dentro del propio paquete.

Las entradas en el campo "imports" siempre deben comenzar con # para asegurar que se diferencien de los especificadores de paquetes externos.

Por ejemplo, el campo de importaciones se puede utilizar para obtener los beneficios de las exportaciones condicionales para los módulos internos:

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

donde import '#dep' no obtiene la resolución del paquete externo dep-node-native (incluidas sus exportaciones a su vez), y en cambio obtiene el archivo local ./dep-polyfill.js relativo al paquete en otros entornos.

A diferencia del campo "exports", el campo "imports" permite mapear a paquetes externos.

Las reglas de resolución para el campo de importaciones son análogas al campo de exportaciones.

Patrones de subrutas

[Historial]

VersiónCambios
v16.10.0, v14.19.0Soporte para patrones de cola en el campo "imports".
v16.9.0, v14.19.0Soporte para patrones de cola.
v14.13.0, v12.20.0Añadido en: v14.13.0, v12.20.0

Para paquetes con un pequeño número de exportaciones o importaciones, recomendamos listar explícitamente cada entrada de subruta de exportaciones. Pero para paquetes que tienen un gran número de subrutas, esto podría causar un inflado de package.json y problemas de mantenimiento.

Para estos casos de uso, se pueden utilizar patrones de exportación de subrutas en su lugar:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}

* mapea la exposición de subrutas anidadas, ya que es solo una sintaxis de sustitución de cadenas.

Todas las instancias de * en el lado derecho se reemplazarán con este valor, incluso si contiene algún separador /.

js
import featureX from 'es-module-package/features/x.js'
// Carga ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js'
// Carga ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js'
// Carga ./node_modules/es-module-package/src/internal/z.js

Esta es una coincidencia estática directa y un reemplazo sin ningún manejo especial para las extensiones de archivo. Incluir "*.js" en ambos lados del mapeo restringe las exportaciones de paquetes expuestos solo a archivos JS.

La propiedad de que las exportaciones sean estáticamente enumerables se mantiene con los patrones de exportaciones, ya que las exportaciones individuales para un paquete se pueden determinar tratando el patrón de destino del lado derecho como un glob ** contra la lista de archivos dentro del paquete. Debido a que las rutas node_modules están prohibidas en los destinos de las exportaciones, esta expansión depende solo de los archivos del paquete en sí.

Para excluir subcarpetas privadas de los patrones, se pueden usar destinos null:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js'
// Lanza: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js'
// Carga ./node_modules/es-module-package/src/features/x.js

Exportaciones condicionales

[Historial]

VersiónCambios
v13.7.0, v12.16.0Desactivar las exportaciones condicionales.
v13.2.0, v12.16.0Agregado en: v13.2.0, v12.16.0

Las exportaciones condicionales proporcionan una forma de mapear a diferentes rutas dependiendo de ciertas condiciones. Son compatibles tanto para importaciones de CommonJS como para módulos ES.

Por ejemplo, un paquete que desea proporcionar diferentes exportaciones de módulos ES para require() e import se puede escribir:

json
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

Node.js implementa las siguientes condiciones, listadas en orden de más específica a menos específica, ya que las condiciones deben definirse:

  • "node-addons" - similar a "node" y coincide con cualquier entorno de Node.js. Esta condición se puede usar para proporcionar un punto de entrada que usa complementos nativos de C++ en lugar de un punto de entrada que es más universal y no depende de complementos nativos. Esta condición se puede desactivar mediante el indicador [--no-addons][--no-addons].
  • "node" - coincide con cualquier entorno de Node.js. Puede ser un archivo CommonJS o de módulo ES. En la mayoría de los casos, no es necesario llamar explícitamente a la plataforma Node.js.
  • "import" - coincide cuando el paquete se carga a través de import o import(), o a través de cualquier operación de importación o resolución de nivel superior mediante el cargador de módulos ECMAScript. Se aplica independientemente del formato de módulo del archivo de destino. Siempre mutuamente excluyente con "require".
  • "require" - coincide cuando el paquete se carga a través de require(). El archivo al que se hace referencia debe poder cargarse con require(), aunque la condición coincida independientemente del formato de módulo del archivo de destino. Los formatos esperados incluyen CommonJS, JSON, complementos nativos y módulos ES. Siempre mutuamente excluyente con "import".
  • "module-sync" - coincide sin importar si el paquete se carga a través de import, import() o require(). Se espera que el formato sea módulos ES que no contengan await de nivel superior en su gráfico de módulos; si lo hace, se lanzará ERR_REQUIRE_ASYNC_MODULE cuando el módulo se require()-ed.
  • "default" - la alternativa genérica que siempre coincide. Puede ser un archivo CommonJS o de módulo ES. Esta condición siempre debe ir al final.

Dentro del objeto ["exports"][exports], el orden de las claves es importante. Durante la coincidencia de condiciones, las entradas anteriores tienen mayor prioridad y tienen preferencia sobre las entradas posteriores. La regla general es que las condiciones deben ser de las más específicas a las menos específicas en el orden del objeto.

Usar las condiciones "import" y "require" puede generar algunos peligros, que se explican con más detalle en [la sección de paquetes duales CommonJS/módulo ES][dual-commonjses-module-packages].

La condición "node-addons" se puede usar para proporcionar un punto de entrada que use complementos nativos de C++. Sin embargo, esta condición se puede desactivar mediante el indicador [--no-addons][--no-addons]. Cuando se usa "node-addons", se recomienda tratar "default" como una mejora que proporciona un punto de entrada más universal, p. ej., usar WebAssembly en lugar de un complemento nativo.

Las exportaciones condicionales también se pueden extender a subrutas de exportaciones, por ejemplo:

json
{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}

Define un paquete donde require('pkg/feature.js') e import 'pkg/feature.js' podrían proporcionar diferentes implementaciones entre Node.js y otros entornos JS.

Cuando se usan ramas de entorno, siempre incluye una condición "default" siempre que sea posible. Proporcionar una condición "default" asegura que cualquier entorno JS desconocido pueda usar esta implementación universal, lo que ayuda a evitar que estos entornos JS tengan que fingir ser entornos existentes para admitir paquetes con exportaciones condicionales. Por esta razón, usar ramas de condición "node" y "default" suele ser preferible a usar ramas de condición "node" y "browser".

Condiciones anidadas

Además de las asignaciones directas, Node.js también admite objetos de condiciones anidadas.

Por ejemplo, para definir un paquete que solo tenga puntos de entrada de modo dual para su uso en Node.js pero no en el navegador:

json
{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}

Las condiciones se siguen emparejando en orden como con las condiciones planas. Si una condición anidada no tiene ninguna asignación, seguirá comprobando las condiciones restantes de la condición principal. De esta forma, las condiciones anidadas se comportan de forma análoga a las sentencias if anidadas de JavaScript.

Resolver condiciones de usuario

Añadido en: v14.9.0, v12.19.0

Al ejecutar Node.js, se pueden añadir condiciones de usuario personalizadas con el flag --conditions:

bash
node --conditions=development index.js

lo que resolvería entonces la condición "development" en las importaciones y exportaciones de paquetes, mientras que resolvería las condiciones existentes "node", "node-addons", "default", "import" y "require" según corresponda.

Se puede establecer cualquier número de condiciones personalizadas con flags repetidos.

Las condiciones típicas solo deben contener caracteres alfanuméricos, utilizando ":", "-" o "=" como separadores si es necesario. Cualquier otra cosa puede generar problemas de compatibilidad fuera de node.

En node, las condiciones tienen muy pocas restricciones, pero específicamente estas incluyen:

Definiciones de Condiciones de la Comunidad

Las cadenas de condición que no sean las condiciones "import", "require", "node", "module-sync", "node-addons" y "default" implementadas en el núcleo de Node.js se ignoran de forma predeterminada.

Otras plataformas pueden implementar otras condiciones y las condiciones de usuario se pueden habilitar en Node.js a través del flag --conditions / -C.

Dado que las condiciones de paquete personalizadas requieren definiciones claras para garantizar un uso correcto, se proporciona a continuación una lista de condiciones de paquete conocidas comunes y sus definiciones estrictas para ayudar con la coordinación del ecosistema.

  • "types" - puede ser utilizado por sistemas de tipado para resolver el archivo de tipado para la exportación dada. Esta condición siempre debe incluirse primero.
  • "browser" - cualquier entorno de navegador web.
  • "development" - se puede utilizar para definir un punto de entrada de entorno solo de desarrollo, por ejemplo, para proporcionar contexto de depuración adicional, como mejores mensajes de error al ejecutarse en modo de desarrollo. Siempre debe ser mutuamente exclusivo con "production".
  • "production" - se puede utilizar para definir un punto de entrada de entorno de producción. Siempre debe ser mutuamente exclusivo con "development".

Para otros entornos de ejecución, las definiciones de claves de condición específicas de la plataforma son mantenidas por el WinterCG en la especificación de propuesta Runtime Keys.

Se pueden agregar nuevas definiciones de condiciones a esta lista creando una solicitud de extracción a la documentación de Node.js para esta sección. Los requisitos para listar una nueva definición de condición aquí son que:

  • La definición debe ser clara e inequívoca para todos los implementadores.
  • El caso de uso de por qué se necesita la condición debe estar claramente justificado.
  • Debe existir un uso de implementación existente suficiente.
  • El nombre de la condición no debe entrar en conflicto con otra definición de condición o condición de uso generalizado.
  • La lista de la definición de la condición debe proporcionar un beneficio de coordinación al ecosistema que de otra manera no sería posible. Por ejemplo, este no sería necesariamente el caso de las condiciones específicas de la empresa o de la aplicación.
  • La condición debe ser tal que un usuario de Node.js esperaría que estuviera en la documentación principal de Node.js. La condición "types" es un buen ejemplo: en realidad no pertenece a la propuesta de Runtime Keys pero encaja bien aquí en la documentación de Node.js.

Las definiciones anteriores pueden trasladarse a un registro de condiciones dedicado a su debido tiempo.

Auto-referencia a un paquete usando su nombre

[Historial]

VersiónCambios
v13.6.0, v12.16.0Se quitó la marca de auto-referencia a un paquete usando su nombre.
v13.1.0, v12.16.0Agregado en: v13.1.0, v12.16.0

Dentro de un paquete, los valores definidos en el campo "exports" del package.json del paquete se pueden referenciar a través del nombre del paquete. Por ejemplo, asumiendo que el package.json es:

json
// package.json
{
  "name": "un-paquete",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}

Entonces cualquier módulo en ese paquete puede referenciar una exportación en el propio paquete:

js
// ./un-modulo.mjs
import { something } from 'un-paquete' // Importa "something" desde ./index.mjs.

La auto-referencia solo está disponible si package.json tiene "exports", y permitirá importar solo lo que ese "exports" (en el package.json) permite. Así que el código de abajo, dado el paquete anterior, generará un error en tiempo de ejecución:

js
// ./otro-modulo.mjs

// Importa "another" desde ./m.mjs. Falla porque
// el campo "exports" del "package.json"
// no proporciona una exportación llamada "./m.mjs".
import { another } from 'un-paquete/m.mjs'

La auto-referencia también está disponible al usar require, tanto en un módulo ES, como en uno CommonJS. Por ejemplo, este código también funcionará:

js
// ./un-modulo.js
const { something } = require('un-paquete/foo.js') // Carga desde ./foo.js.

Finalmente, la auto-referencia también funciona con paquetes con ámbito. Por ejemplo, este código también funcionará:

json
// package.json
{
  "name": "@mi/paquete",
  "exports": "./index.js"
}
js
// ./index.js
module.exports = 42
js
// ./otro.js
console.log(require('@mi/paquete'))
bash
$ node otro.js
42

Paquetes duales CommonJS/módulo ES

Consulta el repositorio de ejemplos de paquetes para más detalles.

Definiciones de campos package.json de Node.js

Esta sección describe los campos utilizados por el entorno de ejecución de Node.js. Otras herramientas (como npm) utilizan campos adicionales que Node.js ignora y que no se documentan aquí.

Los siguientes campos en los archivos package.json se utilizan en Node.js:

  • "name" - Relevante cuando se utilizan importaciones con nombre dentro de un paquete. También utilizado por los administradores de paquetes como el nombre del paquete.
  • "main" - El módulo predeterminado al cargar el paquete, si no se especifica exports, y en versiones de Node.js anteriores a la introducción de exports.
  • "packageManager" - El administrador de paquetes recomendado al contribuir al paquete. Aprovechado por los shims de Corepack.
  • "type" - El tipo de paquete que determina si se cargan los archivos .js como CommonJS o módulos ES.
  • "exports" - Exportaciones de paquetes y exportaciones condicionales. Cuando está presente, limita qué submódulos se pueden cargar desde dentro del paquete.
  • "imports" - Importaciones de paquetes, para uso de módulos dentro del propio paquete.

"name"

[Historial]

VersiónCambios
v13.6.0, v12.16.0Eliminar la opción --experimental-resolve-self.
v13.1.0, v12.16.0Agregado en: v13.1.0, v12.16.0
json
{
  "name": "nombre-del-paquete"
}

El campo "name" define el nombre de su paquete. La publicación en el registro npm requiere un nombre que cumpla con ciertos requisitos.

El campo "name" se puede utilizar además del campo "exports" para autorreferenciar un paquete utilizando su nombre.

"main"

Agregado en: v0.4.0

json
{
  "main": "./index.js"
}

El campo "main" define el punto de entrada de un paquete cuando se importa por nombre a través de una búsqueda de node_modules. Su valor es una ruta.

Cuando un paquete tiene un campo "exports", este tendrá prioridad sobre el campo "main" al importar el paquete por nombre.

También define el script que se utiliza cuando se carga el directorio del paquete mediante require().

js
// Esto se resuelve como ./ruta/al/directorio/index.js.
require('./ruta/al/directorio')

"packageManager"

Añadido en: v16.9.0, v14.19.0

[Estable: 1 - Experimental]

Estable: 1 Estabilidad: 1 - Experimental

json
{
  "packageManager": "<nombre del gestor de paquetes>@<versión>"
}

El campo "packageManager" define qué gestor de paquetes se espera que se utilice al trabajar en el proyecto actual. Puede establecerse en cualquiera de los gestores de paquetes soportados, y garantizará que sus equipos utilicen exactamente las mismas versiones del gestor de paquetes sin tener que instalar nada más que Node.js.

Este campo es actualmente experimental y necesita ser activado; consulte la página de Corepack para obtener detalles sobre el procedimiento.

"type"

[Historial]

VersiónCambios
v13.2.0, v12.17.0Se eliminó el flag --experimental-modules.
v12.0.0Añadido en: v12.0.0

El campo "type" define el formato de módulo que Node.js utiliza para todos los archivos .js que tienen ese archivo package.json como su padre más cercano.

Los archivos que terminan con .js se cargan como módulos ES cuando el archivo package.json padre más cercano contiene un campo de nivel superior "type" con un valor de "module".

El package.json padre más cercano se define como el primer package.json encontrado al buscar en la carpeta actual, la carpeta padre y así sucesivamente hasta que se alcanza una carpeta node_modules o la raíz del volumen.

json
// package.json
{
  "type": "module"
}
bash
# En la misma carpeta que el package.json anterior {#in-same-folder-as-preceding-packagejson}
node my-app.js # Se ejecuta como módulo ES

Si el package.json padre más cercano carece de un campo "type", o contiene "type": "commonjs", los archivos .js se tratan como CommonJS. Si se alcanza la raíz del volumen y no se encuentra ningún package.json, los archivos .js se tratan como CommonJS.

Las declaraciones import de los archivos .js se tratan como módulos ES si el package.json padre más cercano contiene "type": "module".

js
// my-app.js, parte del mismo ejemplo anterior
import './startup.js' // Cargado como módulo ES debido a package.json

Independientemente del valor del campo "type", los archivos .mjs siempre se tratan como módulos ES y los archivos .cjs siempre se tratan como CommonJS.

"exports"

[Historial]

VersiónCambios
v14.13.0, v12.20.0Se añade soporte para los patrones de "exports".
v13.7.0, v12.17.0Se elimina la bandera de los exports condicionales.
v13.7.0, v12.16.0Se implementa el ordenamiento lógico de los exports condicionales.
v13.7.0, v12.16.0Se elimina la opción --experimental-conditional-exports. En la versión 12.16.0, los exports condicionales siguen estando detrás de --experimental-modules.
v13.2.0, v12.16.0Se implementan los exports condicionales.
v12.7.0Añadido en: v12.7.0
json
{
  "exports": "./index.js"
}

El campo "exports" permite definir los puntos de entrada de un paquete cuando se importa por nombre, ya sea cargado a través de una búsqueda en node_modules o una auto-referencia a su propio nombre. Es compatible con Node.js 12+ como una alternativa al "main" que puede soportar la definición de exports de subruta y exports condicionales mientras se encapsulan los módulos internos no exportados.

Los Exports Condicionales también se pueden usar dentro de "exports" para definir diferentes puntos de entrada de paquetes por entorno, incluyendo si el paquete es referenciado a través de require o a través de import.

Todas las rutas definidas en "exports" deben ser URLs de archivos relativas que comiencen con ./.

"imports"

Añadido en: v14.6.0, v12.19.0

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

Las entradas en el campo imports deben ser cadenas que comiencen con #.

Las importaciones de paquetes permiten mapear a paquetes externos.

Este campo define importaciones de subrutas para el paquete actual.