Dependencias opcionales en npm?


23

Tengo una pregunta similar a esta , pero no la misma.

Me gustaría que el usuario de mi aplicación la instale con las dependencias necesarias para la forma en que le gustaría usarla. Entonces, por ejemplo, si quieren persistir en MongoDB, solo se instalarán las bibliotecas relacionadas con Mongo, pero si quieren persistir en Redis, solo se instalarán las bibliotecas relacionadas con Redis. No quiero hacer que descarguen e instalen bibliotecas que no usarán.

Sé que puedo hacerlo con fines de desarrollo devDependencies, pero esto va más allá de eso. Como dice la respuesta en la pregunta anterior, esto está más estrechamente relacionado con setuptools extras_requirelos leiningenperfiles de Python y Clojure . ¿Algo así en npm? Realmente siento que devDependenciesdebería ser un devperfil de una forma más versátil de especificar dependencias.


Solo un pensamiento, pero podría ir con múltiples paquetes. MyPackage-Core MyPackage-Db-Mongo MyPackage-Db-Redisetc ... de la misma manera que la gente hace módulos de bower que están destinados a extender angularjs .
Mike

@ Mike: Hmm, gracias, lo consideraré. Sigo pensando que esto es una limitación de lo package.jsonque se ha resuelto en otros gestores de paquetes.
Imiric

1
Esta es una gran pregunta, pero creo que está fuera de tema porque se trata de usar alguna herramienta. Tales preguntas son sobre el tema solo si cubren cómo la herramienta se integra en algún proceso de desarrollo ; después de todo, este sitio trata sobre Ingeniería de Software. Consulte nuestro centro de ayuda para más detalles. Por favor lea: ¿A dónde va mi pregunta sobre herramientas? El uso de herramientas de desarrollo como NPM sería un tema en Stack Overflow.
amon

Respuestas:


9

El módulo de codependencia puede ser lo que está buscando, o cualquier cosa que haga algo similar a:

  • declarar dependencias opcionales package.jsonque no son instaladas automáticamente pornpm install , digamosoptionalPeerDependencies
  • una requirefunción de estilo personalizado que conoce optionalPeerDependenciesy hace lo correcto, incluyendo lanzar / advertencia cuando no se encuentra nada que cumpla con una clase de módulos requeridos (por ejemplo, ningunoredis , ni mongo, ni mysql, ni , etc. están instalados).
  • documentar la expectativa de que los consumidores de este módulo instalen al menos 1 de los módulos pares opcionales

Una variación sería si la funcionalidad principal del módulo funciona sin dependencias opcionales (por ejemplo, patrón de complemento), sin error / advertencia cuando no se encuentra nada que cumpla con una dependencia de igual.

Otra variación es hacer la lista anterior mientras se tienen en cuenta las dependencias de producción versus desarrollo, es decir, un análogo para dependenciesy devDependencies.

Quizás combinado con un requerimiento bajo demanda de tal manera que los módulos opcionales se requieran perezosamente, por ejemplo:

exports = {
    Core : require('./core'),
    get redis(){ return require('./redis'); },
    get mongo(){ return require('./mongo'); }
}

No he necesitado esto por un tiempo, pero creo que resuelve el problema que tenía. ¡Gracias!
Imiric

2
Sí, pensé que con meses de edad, probablemente lo habías descifrado o seguido. Encontré tu pregunta mientras buscaba respuestas, así que esto fue principalmente para la posteridad. Más de una vez he ido a buscar, solo para encontrar una respuesta mía escrita unos años antes. Así que considere este interés propio iluminado. Además, actualicé la respuesta para describir en general lo codependencyque proporciona el módulo en caso de que el módulo se evapore de NPM y porque los enlaces sin extractos tienen una forma SO incorrecta.
toolbear

9

Si desea dependencias opcionales simples como complementos, por ejemplo, si instala foo, lo ejecutará colorido, pero si no está instalado, no tiene ningún problema y lo verá en gris, entonces podría usar dependencias opcionales en el paquete .json :

{
  "name": "watchit",
  "version": "1.2.3",
  "optionalDependencies": {
    "foo": "^2.0.0"
  }
}

Y en el código:

try {
  var foo = require('foo')
  var fooVersion = require('foo/package.json').version
} catch (er) {
  foo = null
}
if ( notGoodFooVersion(fooVersion) ) {
  foo = null
}

// .. then later in your program ..

if (foo) {
  foo.doFooThings()
}

Extraído de la documentación de package.json .


1

Lo que hago es configurar un script de instalación en mi package.json, dentro scripts, de esta manera:

"install": "node ./my-tools/my-install.js",

Se ejecutará justo después de los npm installacabados. Lo uso principalmente para generar automáticamente un .envarchivo con valores predeterminados.

El my-install.jsscript podría ejecutar diferentes comandos, crear archivos, solicitar la entrada del usuario, por lo que podría decir "¿Quieres Redis o Mongo?":

const exec = require('child_process').exec;
const readline = require('readline');

// Insert "Ask question script" here
// using readline core module

if ( option == 'mongo' )
  exec('npm install mongoose');

if ( option == 'redis' )
  exec('npm install redis');

Esta es una respuesta muy rápida, echa un vistazo a readline para leer la entrada del usuario correctamente y el proceso hijo para ejecutar comandos y procesamiento de salida, etc.

También tenga en cuenta que el script de instalación podría ser lo que desee (python, bash, etc.)


2
Pedir la entrada del usuario arruinará las compilaciones automatizadas. Ejecutar npm installnuevamente dentro de un script de instalación también puede desencadenar un comportamiento no deseado. No recomiendo esta solución.
Lambda Fairy

1

npm realmente no fue diseñado para esto, ya que una de las partes más difíciles de la administración de dependencias es garantizar compilaciones rápidas y reproducibles que sean fáciles y relativamente a prueba de fallas. Pero creo que hay un caso de uso, y ciertamente lo hubo para mí. Entonces escribí un paquete para hacer exactamente lo que está pidiendo.

Mi paquete es install-subsety puede instalarse globalmente connpm install -g install-subset

https://www.npmjs.com/package/install-subset

Primero, crea listas blancas y negras para subconjuntos de instalación con nombre en su package.json de esta manera:

"subsets": {
    "build": {
        "whitelist": [
            "babel-cli",
            "dotenv"
        ]
    },
    "test": {
        "blacklist": [
            "eslint",
            "lint-rules",
            "prettier"
        ]
    }
}

Luego llámalo con, por ejemplo, install-subset test

Esto reescribirá temporalmente su package.json para no instalar esos paquetes en la lista negra, luego lo restaurará, lo que dependiendo de los paquetes puede ahorrar mucho tiempo y ancho de banda.

También funciona con hilo, es de código abierto y los problemas / relaciones públicas son bienvenidos.

En muchos casos, utilizo esto en nuestro servidor ci para reducir el tiempo de compilación, y en nuestro último proyecto React Native, tomamos nuestra típica instalación nueva de desarrollador de 72 segundos a aproximadamente 20 segundos.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.