Dependências opcionais em npm?

23

Eu tenho uma pergunta semelhante a isso , mas não exatamente a mesma.

Gostaria que o usuário do meu aplicativo o instalasse com as dependências necessárias para a maneira como ele gostaria de usá-lo. Portanto, por exemplo, se eles quiserem persistir no MongoDB, apenas as bibliotecas relacionadas ao Mongo serão instaladas, mas se quiserem persistir no Redis, somente as bibliotecas relacionadas ao Redis serão instaladas. Não quero fazê-los baixar e instalar bibliotecas que não usarão.

Eu sei que posso fazer isso para fins de desenvolvimento devDependencies, mas isso vai além disso. Como diz a resposta na pergunta acima, isso está mais estreitamente relacionado aos perfis de Python setuptools extras_requiree Clojure leiningen. Algo assim em NPM? Eu realmente sinto que devDependenciesdeveria ser um devperfil de uma maneira mais versátil de especificar dependências.

imiric
fonte
Apenas um pensamento, mas você pode usar vários pacotes. MyPackage-Core MyPackage-Db-Mongo MyPackage-Db-Redisetc ... da maneira como as pessoas fazem módulos bower, destinados a estender angularjs .
Mike
@ Mike: Hmm obrigado, vou considerar. Eu ainda acho que essa limitação package.jsonfoi resolvida em outros gerenciadores de pacotes.
Imiric
1
Essa é uma ótima pergunta, mas acho que é fora de tópico, porque se trata de usar alguma ferramenta. Essas questões são abordadas apenas se abordarem como a ferramenta se integra a algum processo de desenvolvimento - afinal, este site é sobre Engenharia de Software. Consulte nossa central de ajuda para obter detalhes. Leia: para onde vai a minha pergunta sobre a ferramenta? O uso de ferramentas de desenvolvimento como o NPM estaria no tópico Stack Overflow.
amon

Respostas:

9

O módulo de co-dependência pode ser o que você está procurando ou qualquer coisa que faça algo semelhante a:

  • declarar dependências opcionais package.jsonque não são instaladas automaticamente por npm install, digamosoptionalPeerDependencies
  • uma requirefunção de estilo personalizado que sabe optionalPeerDependenciese faz a coisa certa, incluindo lançar / aviso quando nada é encontrado que atenda a uma classe necessária de módulos (por exemplo redis, nem mongo, nem mysql, nem , etc. estão instalados).
  • documentar a expectativa de que os consumidores deste módulo instalem pelo menos 1 dos módulos pares opcionais

Uma variação seria se a funcionalidade principal do módulo funcionasse sem dependências opcionais (por exemplo, padrão de plug-in), sem erro / aviso quando nada for encontrado que atenda a uma dependência de pares.

Outra variação é fazer a lista acima enquanto considera as dependências de produção versus desenvolvimento, isto é, um análogo para dependenciese devDependencies.

Talvez combinado com uma demanda sob demanda, de modo que módulos opcionais sejam necessários preguiçosamente, por exemplo:

exports = {
    Core : require('./core'),
    get redis(){ return require('./redis'); },
    get mongo(){ return require('./mongo'); }
}
ferramenta
fonte
Não preciso disso há um tempo, mas acho que resolve o problema que tive. Obrigado!
Imiric>
2
Sim, eu imaginei que, com meses de idade, você provavelmente já descobriu ou seguiu em frente. Eu encontrei sua pergunta enquanto procurava respostas, então isso era principalmente para posteridade. Mais de uma vez eu fui procurar, apenas para encontrar uma resposta minha escrita alguns anos antes. Portanto, considere esse interesse próprio esclarecido. Além disso, atualizou a resposta para descrever em geral o que o codependencymódulo fornece no caso em que o módulo evapora do NPM e porque os links sem trechos são de má forma SO.
toolbear 24/02
9

Se você quiser dependências opcionais simples, como plug-ins, por exemplo, se você instalar o foo, será executado colorido, mas se não estiver instalado, você não terá nenhum problema e o verá em cinza, então poderá usar o Dependecies opcional no package.json :

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

E no 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 da documentação package.json .

PhoneixS
fonte
1

O que faço é configurar um script de instalação no meu package.json, por dentro scripts, assim:

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

Ele será executado logo após o término npm install. Eu o uso principalmente para gerar automaticamente um .envarquivo com padrões.

O my-install.jsscript pode executar comandos diferentes, criar arquivos, solicitar a entrada do usuário, para que você possa dizer "Deseja Redis ou 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 é uma resposta muito rápida, consulte a linha de leitura para ler a entrada do usuário corretamente e o processo filho para executar comandos e processar a saída, etc.

Observe também que o script de instalação pode ser o que você desejar (python, bash, etc)

aesede
fonte
2
Pedir entrada do usuário estragará as compilações automatizadas. A execução npm installnovamente dentro de um script de instalação também pode disparar um comportamento não intencional. Eu não recomendo esta solução.
Lambda Fairy
1

O npm realmente não foi projetado para isso, pois uma das partes mais difíceis do gerenciamento de dependências é garantir construções rápidas e reproduzíveis, fáceis e relativamente à prova de falhas. Mas acredito que há um caso de uso, e certamente havia para mim. Então, escrevi um pacote para fazer exatamente o que você está pedindo.

Meu pacote é install-subsete pode ser instalado globalmente comnpm install -g install-subset

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

Primeiro, você cria listas de permissões e listas negras para subconjuntos de instalação nomeados em seu package.json assim:

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

Em seguida, chame-o com, por exemplo, install-subset test

Isso reescreverá temporariamente o seu package.json para não instalar esses pacotes na lista negra e depois restaurá-lo, o que, dependendo dos pacotes, pode economizar muito tempo e largura de banda.

Também funciona com fios, é de código aberto e problemas / PRs são bem-vindos.

Em muitos casos, eu uso isso em nosso servidor ci para diminuir o tempo de compilação e, em nosso projeto mais recente do React Native, nossa instalação típica de desenvolvedor novo passou de 72 segundos para cerca de 20 segundos.

tabrindle
fonte