Módulos: Paquetes
[Historial]
Versión | Cambios |
---|---|
v14.13.0, v12.20.0 | Se agrega soporte para patrones "exports" . |
v14.6.0, v12.19.0 | Se agrega el campo "imports" del paquete. |
v13.7.0, v12.17.0 | Se desactiva el indicador de exportaciones condicionales. |
v13.7.0, v12.16.0 | Se 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.0 | Se desactiva el indicador de auto referencia a un paquete usando su nombre. |
v12.7.0 | Se introduce el campo "exports" en package.json como una alternativa más potente al campo clásico "main" . |
v12.0.0 | Se 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 archivopackage.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 anode
a través deSTDIN
, 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
oexport
oimport.meta
, sin un marcador explícito de cómo debe interpretarse. Los marcadores explícitos son las extensiones.mjs
o.cjs
, los campos"type"
depackage.json
con los valores"module"
o"commonjs"
, o el flag--input-type
. Las expresiones dinámicasimport()
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 archivopackage.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 anode
a través deSTDIN
, con el flag--input-type=commonjs
. - Archivos con extensión
.js
sin archivopackage.json
padre o donde el archivopackage.json
padre más cercano carezca de un campotype
, 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ón | Cambios |
---|---|
v22.7.0 | La detección de sintaxis está habilitada por defecto. |
v21.1.0, v20.10.0 | Añ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 archivopackage.json
de control o uno que carezca de un campotype
. - Entrada de cadena (
--eval
oSTDIN
) 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 expresionesimport()
, 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 conprocess.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 expresionesimport()
. - 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()
.
// 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.
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
.
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
:
{
"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:
{
"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:
{
"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"
:
{
"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:
{
"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 "."
:
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
Ahora, solo la subruta definida en "exports"
puede ser importada por un consumidor:
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:
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"
.
{
"exports": {
".": "./index.js"
}
}
puede escribirse:
{
"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:
// 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ón | Cambios |
---|---|
v16.10.0, v14.19.0 | Soporte para patrones de cola en el campo "imports". |
v16.9.0, v14.19.0 | Soporte para patrones de cola. |
v14.13.0, v12.20.0 | Añ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:
// ./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 /
.
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
:
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null
}
}
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ón | Cambios |
---|---|
v13.7.0, v12.16.0 | Desactivar las exportaciones condicionales. |
v13.2.0, v12.16.0 | Agregado 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:
// 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 deimport
oimport()
, 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 derequire()
. El archivo al que se hace referencia debe poder cargarse conrequire()
, 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 deimport
,import()
orequire()
. 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 serequire()
-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:
{
"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:
{
"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
:
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ón | Cambios |
---|---|
v13.6.0, v12.16.0 | Se quitó la marca de auto-referencia a un paquete usando su nombre. |
v13.1.0, v12.16.0 | Agregado 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:
// 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:
// ./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:
// ./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á:
// ./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á:
// package.json
{
"name": "@mi/paquete",
"exports": "./index.js"
}
// ./index.js
module.exports = 42
// ./otro.js
console.log(require('@mi/paquete'))
$ 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ón | Cambios |
---|---|
v13.6.0, v12.16.0 | Eliminar la opción --experimental-resolve-self . |
v13.1.0, v12.16.0 | Agregado en: v13.1.0, v12.16.0 |
- Tipo: <string>
{
"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
- Tipo: <string>
{
"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()
.
// 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
- Tipo: <string>
{
"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ón | Cambios |
---|---|
v13.2.0, v12.17.0 | Se eliminó el flag --experimental-modules . |
v12.0.0 | Añadido en: v12.0.0 |
- Tipo: <string>
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.
// package.json
{
"type": "module"
}
# 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"
.
// 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ón | Cambios |
---|---|
v14.13.0, v12.20.0 | Se añade soporte para los patrones de "exports" . |
v13.7.0, v12.17.0 | Se elimina la bandera de los exports condicionales. |
v13.7.0, v12.16.0 | Se implementa el ordenamiento lógico de los exports condicionales. |
v13.7.0, v12.16.0 | Se 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.0 | Se implementan los exports condicionales. |
v12.7.0 | Añadido en: v12.7.0 |
- Tipo: <Object> | <string> | <string[]>
{
"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
- Tipo: <Objeto>
// 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.