Módulos: Paquetes
[Historia]
Versión | Cambios |
---|---|
v14.13.0, v12.20.0 | Se agrega soporte para patrones de "exports" . |
v14.6.0, v12.19.0 | Se agrega el campo "imports" del paquete. |
v13.7.0, v12.17.0 | Se quitan las banderas de las exportaciones condicionales. |
v13.7.0, v12.16.0 | Se elimina la opción --experimental-conditional-exports . En la versión 12.16.0, las exportaciones condicionales todavía están detrás de --experimental-modules . |
v13.6.0, v12.16.0 | Se quita la bandera a la autorreferencia de un paquete usando su nombre. |
v12.7.0 | Se introduce el campo "exports" de package.json como una alternativa más potente al campo "main" clásico. |
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 pasen a node
como la entrada inicial, o cuando se haga referencia a ellos mediante declaraciones import
o expresiones import()
:
- Archivos con una extensión
.mjs
. - Archivos con una extensión
.js
cuando el archivopackage.json
principal más cercano contiene un campo de nivel superior"type"
con un valor de"module"
. - Cadenas que se pasan como argumento a
--eval
, o se envían anode
a través deSTDIN
, con la bandera--input-type=module
. - Código que contiene sintaxis que solo se analiza correctamente como módulos ES, como declaraciones
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 valores"module"
o"commonjs"
, o la bandera--input-type
. Las expresionesimport()
dinámicas se admiten en módulos CommonJS o ES y no forzarían que un archivo se tratara como un módulo ES. Consulte Detección de sintaxis.
Node.js tratará lo siguiente como CommonJS cuando se pasen a node
como la entrada inicial, o cuando se haga referencia a ellos mediante declaraciones import
o expresiones import()
:
- Archivos con una extensión
.cjs
. - Archivos con una extensión
.js
cuando el archivopackage.json
principal más cercano contiene un campo de nivel superior"type"
con un valor de"commonjs"
. - Cadenas que se pasan como argumento a
--eval
o--print
, o se envían anode
a través deSTDIN
, con la bandera--input-type=commonjs
. - Archivos con una extensión
.js
sin un archivopackage.json
principal o donde el archivopackage.json
principal más cercano no tiene un campotype
, y donde el código se puede evaluar correctamente como CommonJS. En otras palabras, Node.js intenta ejecutar primero tales 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ódulo ES.
Escribir sintaxis de módulo ES en archivos "ambiguos" implica un costo de rendimiento y, por lo tanto, se recomienda que los autores 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 paquetes donde todas las fuentes son CommonJS. Ser explícito sobre el type
del paquete protegerá el paquete en caso de que el tipo predeterminado de Node.js cambie alguna vez, y también facilitará que las herramientas de compilación y los cargadores determinen cómo se deben interpretar 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 la sintaxis que arrojaría un error al evaluarse como CommonJS. Esto incluye lo siguiente:
- Declaraciones
import
(pero no expresionesimport()
, que son válidas en CommonJS). - Declaraciones
export
. - Referencias a
import.meta
. await
en el nivel superior de un módulo.- Redeclaraciones léxicas de las variables de envoltura de 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 gestionar las llamadas
require()
. - Es parcheable con monkey patch.
- 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 usar para cargar módulos ECMAScript desde módulos CommonJS si el gráfico del módulo es síncrono (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 gestionar las declaraciones
import
y las expresionesimport()
. - No es parcheable con monkey patch, se puede personalizar usando 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 extensiones
.js
,.mjs
y.cjs
para archivos de texto JavaScript. - Se puede utilizar para cargar módulos CommonJS de JavaScript. Dichos módulos se pasan a través de
cjs-module-lexer
para intentar identificar las exportaciones nombradas, que están disponibles si se pueden determinar mediante análisis estático. Los módulos CommonJS importados tienen sus URL convertidas a 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"
del 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
le dice a Node.js que interprete los archivos .js
dentro de ese paquete como si estuvieran utilizando la sintaxis de módulo ES.
El campo "type"
se aplica no solo a los puntos de entrada iniciales (node mi-aplicacion.js
) sino también a los archivos a los que hacen referencia las sentencias import
y las expresiones import()
.
// mi-aplicacion.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 un archivo package.json,
// y por lo tanto hereda el valor "type" de un 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 le puede indicar a Node.js que interprete un archivo en particular como CommonJS nombrándolo con una extensión.cjs
(ya que tanto los archivos.js
como.mjs
se tratan como módulos ES dentro de un paquete"module"
). - Dentro de un paquete
"type": "commonjs"
, se le puede indicar a Node.js que interprete un archivo en particular como un módulo ES nombrándolo con una extensión.mjs
(ya que tanto los archivos.js
como.cjs
se tratan como CommonJS dentro de un paquete"commonjs"
).
Flag --input-type
Agregado en: v12.0.0
Las cadenas de texto pasadas como argumento a --eval
(o -e
), o canalizadas a node
a través de STDIN
, se tratan como módulos ES cuando se establece el flag --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 que sea completo, también existe --input-type=commonjs
, para ejecutar explícitamente la entrada de cadena 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 se envía con 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 buena conocida" 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 de un paquete: "main"
y "exports"
. Ambos campos se aplican a los puntos de entrada de los módulos ES y de los 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 prevenir 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 los paquetes nuevos dirigidos a las versiones de Node.js actualmente compatibles, se recomienda el campo "exports"
. Para los paquetes que admiten Node.js 10 e inferior, 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.
Las exportaciones condicionales se pueden usar dentro de "exports"
para definir diferentes puntos de entrada de paquete por entorno, incluso 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, incluido el package.json
(por ejemplo, require('your-package/package.json')
). Esto probablemente será un cambio importante.
Para que la introducción de "exports"
no sea un cambio importante, asegúrese de que se exporte cada punto de entrada admitido anteriormente. 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 anteriormente 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 enteras tanto con como sin subrutas extendidas 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 cambio importante futuro para el paquete puede restringir adecuadamente 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
}
}
Exportación del punto de entrada principal
Al escribir un nuevo paquete, se recomienda utilizar el campo "exports"
:
{
"exports": "./index.js"
}
Cuando se define el campo "exports"
, todas las subrutas del paquete se encapsulan y ya no están disponibles para los importadores. Por ejemplo, require('pkg/subpath.js')
lanza un error ERR_PACKAGE_PATH_NOT_EXPORTED
.
Esta encapsulación de las exportaciones proporciona garantías más fiables sobre las interfaces de los paquetes para las herramientas y al gestionar las actualizaciones semver de un paquete. No es una encapsulación fuerte, ya que un require directo de cualquier subruta absoluta del paquete, como require('/path/to/node_modules/pkg/subpath.js')
, seguirá cargando subpath.js
.
Todas las versiones compatibles actualmente de Node.js y las herramientas de compilación modernas admiten el campo "exports"
. Para los proyectos que utilizan una versión anterior de Node.js o una herramienta de compilación relacionada, se puede lograr la compatibilidad incluyendo el campo "main"
junto con "exports"
apuntando al mismo módulo:
{
"main": "./index.js",
"exports": "./index.js"
}
Exportaciones de subrutas
Añadido en: v12.7.0
Al utilizar 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 darán 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 exportaciones. Esto asegura que haya solo una subruta para cada módulo exportado de manera que todos los dependientes importen el mismo especificador consistente, manteniendo el contrato del paquete claro para los consumidores y simplificando las finalizaciones de las 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 verdadera ruta del archivo dentro del paquete.
Con los mapas de importación que ahora proporcionan un estándar para la resolución de paquetes en los navegadores y otros tiempos de ejecución de JavaScript, el uso del estilo sin extensión puede resultar en definiciones de mapas de importación infladas. Las extensiones de archivo explícitas pueden evitar este problema permitiendo que el mapa de importación utilice una asignación de carpetas de paquetes para asignar 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.
Exports sugar
Añadido en: v12.11.0
Si la exportación "."
es la única exportación, el campo "exports"
proporciona una sintaxis simplificada para este caso, siendo el valor directo del campo "exports"
.
{
"exports": {
".": "./index.js"
}
}
puede escribirse:
{
"exports": "./index.js"
}
Subpath imports
Añadido en: v14.6.0, v12.19.0
Además del campo "exports"
, existe un campo "imports"
en el paquete para crear mappings privados 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 garantizar que se distingan de los especificadores de paquetes externos.
Por ejemplo, el campo imports se puede utilizar para obtener los beneficios de las exportaciones condicionales para 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 su lugar obtiene el archivo local ./dep-polyfill.js
relativo al paquete en otros entornos.
A diferencia del campo "exports"
, el campo "imports"
permite el mapping a paquetes externos.
Las reglas de resolución para el campo imports son, por lo demás, análogas al campo exports.
Subpath patterns
[Historial]
Versión | Cambios |
---|---|
v16.10.0, v14.19.0 | Soporte para trailers de patrones en el campo "imports". |
v16.9.0, v14.19.0 | Soporte para trailers de patrones. |
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 subpath de exportaciones. Pero para los paquetes que tienen un gran número de subpaths, esto podría causar hinchazón y problemas de mantenimiento en package.json
.
Para estos casos de uso, se pueden utilizar patrones de exportación de subpath en su lugar:
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
*
los mapas exponen subpaths anidados ya que es solo una sintaxis de reemplazo 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 y reemplazo estáticos directos sin ningún manejo especial para las extensiones de archivo. Incluir "*.js"
en ambos lados del mapping restringe las exportaciones de paquetes expuestas solo a archivos JS.
La propiedad de que las exportaciones sean enumerables estáticamente se mantiene con los patrones de exportación, 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 exportación, esta expansión depende solo de los archivos del propio paquete.
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 | Desmarcar las exportaciones condicionales. |
v13.2.0, v12.16.0 | Añadido en: v13.2.0, v12.16.0 |
Las exportaciones condicionales proporcionan una forma de mapear a diferentes rutas dependiendo de ciertas condiciones. Se admiten tanto para las importaciones de CommonJS como para las de módulos ES.
Por ejemplo, un paquete que quiera proporcionar diferentes exportaciones de módulos ES para require()
e import
puede escribirse:
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js implementa las siguientes condiciones, enumeradas 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 utilizar para proporcionar un punto de entrada que utilice complementos nativos de C++ en lugar de un punto de entrada que sea más universal y no dependa de complementos nativos. Esta condición se puede desactivar mediante el flag--no-addons
."node"
- coincide con cualquier entorno de Node.js. Puede ser un archivo CommonJS o un módulo ES. En la mayoría de los casos, llamar explícitamente a la plataforma Node.js no es necesario."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 por parte del cargador de módulos ECMAScript. Se aplica independientemente del formato de módulo del archivo de destino. Siempre mutuamente exclusivo 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 exclusivo 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"
- el fallback genérico que siempre coincide. Puede ser un archivo CommonJS o un módulo ES. Esta condición siempre debe ir al final.
Dentro del objeto "exports"
, el orden de las claves es significativo. Durante la coincidencia de condiciones, las entradas anteriores tienen mayor prioridad y tienen precedencia sobre las entradas posteriores. La regla general es que las condiciones deben ser de más específicas a menos específicas en el orden de los objetos.
El uso de las condiciones "import"
y "require"
puede conllevar algunos riesgos, que se explican con más detalle en la sección de paquetes duales CommonJS/ES module.
La condición "node-addons"
se puede utilizar para proporcionar un punto de entrada que utilice complementos nativos de C++. Sin embargo, esta condición se puede desactivar mediante el flag --no-addons
. Cuando se utiliza "node-addons"
, se recomienda tratar "default"
como una mejora que proporciona un punto de entrada más universal, por ejemplo, utilizando WebAssembly en lugar de un complemento nativo.
Las exportaciones condicionales también se pueden extender a subrutas de exportación, 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 utilizan ramas de entorno, siempre incluya una condición "default"
siempre que sea posible. Proporcionar una condición "default"
garantiza que cualquier entorno JS desconocido pueda utilizar esta implementación universal, lo que ayuda a evitar que estos entornos JS tengan que pretender ser entornos existentes para admitir paquetes con exportaciones condicionales. Por esta razón, el uso de ramas de condición "node"
y "default"
suele ser preferible al uso de ramas de condición "node"
y "browser"
.
Condiciones anidadas
Además de las asignaciones directas, Node.js también admite objetos de condición anidados.
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 continúan coincidiendo en orden al igual que con las condiciones planas. Si una condición anidada no tiene ninguna asignación, continuará comprobando las condiciones restantes de la condición principal. De esta manera, las condiciones anidadas se comportan de forma análoga a las instrucciones if
anidadas de JavaScript.
Resolución de condiciones de usuario
Añadido en: v14.9.0, v12.19.0
Al ejecutar Node.js, se pueden agregar condiciones de usuario personalizadas con el indicador --conditions
:
node --conditions=development index.js
que luego resolvería 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 indicadores 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 indicador --conditions
/ -C
.
Dado que las condiciones de paquete personalizadas requieren definiciones claras para garantizar un uso correcto, a continuación se proporciona 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 los sistemas de escritura para resolver el archivo de escritura 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 cuando se ejecuta 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 tiempos de ejecución, las definiciones de clave de condición específicas de la plataforma son mantenidas por WinterCG en la especificación de la propuesta de 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 enumerar una nueva definición de condición aquí son:
- 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 suficiente uso de implementación existente.
- El nombre de la condición no debe entrar en conflicto con otra definición de condición o condición de uso amplio.
- La lista de la definición de la condición debe proporcionar un beneficio de coordinación al ecosistema que de otro modo 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 central 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 se pueden mover a un registro de condiciones dedicado a su debido tiempo.
Autorreferencia a un paquete usando su nombre
[Historia]
Versión | Cambios |
---|---|
v13.6.0, v12.16.0 | Se eliminó el indicador de autorreferencia 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": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
Entonces, cualquier módulo en ese paquete puede referenciar una exportación en el propio paquete:
// ./a-module.mjs
import { something } from 'a-package'; // Importa "something" desde ./index.mjs.
La autorreferencia solo está disponible si package.json
tiene "exports"
, y permitirá importar solo lo que ese "exports"
(en el package.json
) permita. Entonces, el código a continuación, dado el paquete anterior, generará un error en tiempo de ejecución:
// ./another-module.mjs
// Importa "another" desde ./m.mjs. Falla porque
// el campo "exports" de "package.json"
// no proporciona una exportación llamada "./m.mjs".
import { another } from 'a-package/m.mjs';
La autorreferencia también está disponible cuando se usa require
, tanto en un módulo ES como en uno CommonJS. Por ejemplo, este código también funcionará:
// ./a-module.js
const { something } = require('a-package/foo.js'); // Carga desde ./foo.js.
Finalmente, la autorreferencia también funciona con paquetes con ámbito. Por ejemplo, este código también funcionará:
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42;
// ./other.js
console.log(require('@my/package'));
$ node other.js
42
Paquetes duales CommonJS/ES module
Consulte el repositorio de ejemplos de paquetes para obtener más detalles.
Definiciones de campos package.json
de Node.js
Esta sección describe los campos utilizados por el tiempo de ejecución de Node.js. Otras herramientas (como npm) utilizan campos adicionales que Node.js ignora y no se documentan aquí.
Los siguientes campos en los archivos package.json
se utilizan en Node.js:
"name"
- Relevante al usar 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 deben cargar 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 de npm requiere un nombre que cumpla con ciertos requisitos.
El campo "name"
se puede usar además del campo "exports"
para auto-referenciar 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 en 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 el directorio del paquete se carga a través de require()
.
// Esto se resuelve a ./path/to/directory/index.js.
require('./path/to/directory');
"packageManager"
Agregado 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. Se puede establecer en cualquiera de los gestores de paquetes admitidos, y garantizará que sus equipos utilicen exactamente las mismas versiones de gestores de paquetes sin tener que instalar nada más que Node.js.
Este campo es actualmente experimental y necesita ser habilitado; consulte la página Corepack para obtener detalles sobre el procedimiento.
"type"
[Historial]
Versión | Cambios |
---|---|
v13.2.0, v12.17.0 | Eliminar el indicador --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 usa 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, el padre de esa carpeta, 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 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 que el anterior
import './startup.js'; // Se carga como módulo ES debido al 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 | Agregar soporte para patrones "exports" . |
v13.7.0, v12.17.0 | Eliminar el indicador de exportaciones condicionales. |
v13.7.0, v12.16.0 | Implementar el ordenamiento lógico de las exportaciones condicionales. |
v13.7.0, v12.16.0 | Eliminar la opción --experimental-conditional-exports . En 12.16.0, las exportaciones condicionales aún están detrás de --experimental-modules . |
v13.2.0, v12.16.0 | Implementar exportaciones 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 cargado ya sea a través de una búsqueda de node_modules
o una auto-referencia a su propio nombre. Se admite en Node.js 12+ como una alternativa a "main"
que puede admitir la definición de exportaciones de subruta y exportaciones condicionales mientras se encapsulan módulos internos no exportados.
Las Exportaciones Condicionales también se pueden usar dentro de "exports"
para definir diferentes puntos de entrada de paquetes por entorno, incluso si se hace referencia al paquete a través de require
o a través de import
.
Todas las rutas definidas en "exports"
deben ser URL de archivo relativas que comiencen con ./
.
"imports"
Añadido en: v14.6.0, v12.19.0
- Tipo: <Object>
// 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 strings que comiencen con #
.
Las importaciones de paquetes permiten el mapeo a paquetes externos.
Este campo define las importaciones de subrutas para el paquete actual.