Desenvolvimento

8 mai, 2015

Chega de pacotes npm globais

Publicidade

A comunidade de desenvolvimento JavaScript tem dado boas-vindas a dezenas de ferramentas novas e poderosas a cada ano. Isso tem acontecido quase rápido demais para os meros os mortais acompanharem. Muitas dessas ferramentas vêm com pacotes de npm que você pode instalar globalmente para que possa, assim, usar a ferramenta de linha de comando a partir de qualquer lugar em seu computador. Isso pode ser muito conveniente, mas é o caminho certo para fazê-lo? Existe uma alternativa melhor?

É o certo?

Sim, com certeza é… mas não. Bem, o que é isso!?!? Ambos. Se você estiver experimentando uma ferramenta ou apenas usando-a de um modo geral, não hesite em utilizá-la como uma ferramenta de linha de comando global. Se o seu projeto, na verdade, for dependente da ferramenta, então instalá-la globalmente, provavelmente, não é o melhor caminho a percorrer.

Por que não? Bem, assim como praticamente todos os pacotes em npm, as ferramentas de linha de comando recebem versões. Se a sua máquina local/de desenvolvimento possui uma versão diferente da ferramenta de outra máquina que precisa executar essa ferramenta para esse projeto, então pode haver incompatibilidades. Seria bom se tivéssemos uma opção para salvar as dependências globais dentro de package.json, mas, honestamente, um arquivo de pacote não deve incluir utilidades globais, como foi discutido no GitHub.

Grunt fez um bom trabalho de minimizar esse problema criando um pacote npm separado para a ferramenta cli que é extremamente mínima para que você possa mantê-la instalada globalmente com chances mínimas de incompatibilidade. Ainda não há garantias, no entanto.

Qual é a alternativa?

O npm é a alternativa. Mais especificamente, você deve incluir suas ferramentas de desenvolvimento em devDependencies em package.json. Dessa forma, elas podem ser versionadas para cada projeto. Mas, então, como é que você o executa na linha de comando? Ninguém quer ser obrigado a digitar node ./node_modules/.bin/grunt toda vez que quiser executar um comando Grunt. Enquanto isso funcionaria para a maioria das ferramentas, existe uma maneira um pouco mais simples: os scripts npm. Você pode usar npm run WHATEVER para executar qualquer comando que colocar na seção scripts do seu arquivo package.json, e se você fizer referência a um comando que não existe no PATH do seu computador, ele vai pesquisar através dos módulos do node de seu projeto para ele.

Por exemplo, se você não tiver o Grunt CLI instalado globalmente, mas tiver isto em seu arquivo package.json:

...
devDependencies: {
    "grunt-cli": "~0.1",
    "grunt": "~0.4",
    "grunt-contrib-jshint": "~0.6",
    ...
},
scripts {
    "lint": "grunt lint",
    ...
},
...

…você poderia executar npm run lint e ele executaria o grunt lint corretamente. Além disso, se você usar npm v2 ou superior, os seus scripts npm podem aceitar argumentos:

...
scripts {
    "grunt": "grunt",
    ...
},
...

Agora você pode executar npm run grunt — lint e ele vai executar grunt lint. Esse novo recurso com a versão 2 permite-nos usar para passar argumentos ao comando npm que estiver em execução. Você também pode usar isso para garantir que algumas opções sejam sempre passadas aos seus comandos favoritos. Por exemplo, você pode fazer com que o Grunt seja sempre executado no modo verboso, sem a necessidade de especificá-lo a cada vez:

...
scripts {
    "grunt": "grunt -v",
    ...
},
...

Você pode executá-lo da mesma forma, mas agora sempre ficará verboso. Essa é uma ótima maneira de certificar-se de que algumas opções que você deseja sempre estabelecer sejam usadas sem ter que especificá-las o tempo todo.

Eu me deparei com um obstáculo significativo, ainda que fazendo isso no PowerShell no Windows. Suponha ainda estamos usando o script setup “grunt”: “grunt”. Se eu tentar digitar npm run grunt — -h no PowerShell, então o argumento -h vai realmente ser enviado para npm run, em vez de para o comando grunt. Todos os argumentos que você tentar passar que comecem com ou não serão enviados para o comando na configuração scripts, mas para o comando npm run. Isso não parece ser um problema com o cmd padrão ou Git Bash no Windows, nem parece que os usuários Linux/Mac são afetados.

A partir de inúmeros comentários que li na Internet, não parece que existam muitos usuários de PowerShell + npm por aí, mas eu duvido muito que eu seja o único. De qualquer forma, essa questão não vem à tona com muita frequência, já que tendem a colocar todos os comandos que vou realmente executar nos scripts, então raramente preciso passar argumentos e eu tendo a acabar com aliases agradáveis que, na verdade, tendem a fazer com que npm run COMMAND seja mais curto do que o comando real que está sendo executado.

De fato, eu estou pesquisando e tentando ir além de Grunt, Gulp, e afins, então estou tentando usar ferramentas individualmente, em vez de seus plugins runners de tarefas (por exemplo, usando a ferramenta de linha de comando JSLint em vez do plugin Grunt). Para obter mais informações, consulte “How to use npm as a Build Tool”, mas ainda estou experimentando isso e eu certamente não estou tentando convencê-lo disso ainda. Este artigo é simplesmente sobre como remover a necessidade de pacotes npm globais.

Prós e contras

Existem, certamente, alguns prós e contras para esta abordagem, então vamos dar uma olhada em alguns:

Contras

  • É preciso digitar mais graças à necessidade de jogar npm run na frente de tudo e à necessidade de adicionar cada vez que você desejar jogar alguns argumentos extras nele. Claro, isso é negado quando você usar os scripts para converter nomes de comandos longos para um alias curto.
  • É preciso um pouco de trabalho extra para adicionar scripts para o arquivo package.json.

Prós

  • Você tem um lugar fácil de encontrar a documentação sobre o comando que deve ser executado. Basta digitar npm run e ele por si só irá mostrar todos os aliases e comandos que são executados por esses aliases.
  • Dependendo de como você configurar os scripts npm, eles podem atuar como uma camada de abstração para que, se você quiser mudar de Grunt para Gulp ou algo semelhante, você pode simplesmente alterar que comandos estão sendo executados dentro da configuração scripts e nunca precisa mudar os comandos que você, na verdade, digita na linha de comando.
  • Obviamente, um pró tem a ver com a forma como eu comecei este artigo: eliminando problemas em relação a conflitos de versão de pacotes globais.
  • Finalmente, quando alguém clona o seu projeto, ele aumenta a probabilidade de que todos eles terão que executar o npm install e aí vão, realmente, ter todas as dependências instaladas.

Perguntas finais

Se eu não mencionei algum pró ou contra, me avise. Em qualquer caso, o que você acha? Vale a pena a confusão para evitar conflitos de versão de pacote globais? Se você acha que sim, então como é que vamos contar com os scripts no npm versus deixar outras ferramentas, como task runners, fazer o trabalho real? Essas são perguntas que você certamente terá que responder por si mesmo ou discutir com sua equipe, e não algo que eu posso apenas te dizer a resposta “certa”.

***

Joe Zim faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://www.joezimjs.com/javascript/no-more-global-npm-packages/