<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Daniel Bertolini</title>
    <description>The latest articles on Forem by Daniel Bertolini (@dan_bertolini).</description>
    <link>https://forem.com/dan_bertolini</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1166037%2F74169fc1-16ad-4955-89d3-1431a2dd8964.jpeg</url>
      <title>Forem: Daniel Bertolini</title>
      <link>https://forem.com/dan_bertolini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dan_bertolini"/>
    <language>en</language>
    <item>
      <title>Como o npm install resolve as dependências de um projeto</title>
      <dc:creator>Daniel Bertolini</dc:creator>
      <pubDate>Tue, 26 Sep 2023 01:07:47 +0000</pubDate>
      <link>https://forem.com/dan_bertolini/como-o-npm-install-resolve-as-dependencias-de-um-projeto-25cm</link>
      <guid>https://forem.com/dan_bertolini/como-o-npm-install-resolve-as-dependencias-de-um-projeto-25cm</guid>
      <description>&lt;p&gt;Se você é uma pessoa desenvolvedora de Front End ou Back End que utiliza JavaScript como sua linguagem de programação já deve ter, em algum momento de sua jornada, executado o famigerado comando &lt;code&gt;npm install&lt;/code&gt; algumas (muitas) vezes para baixar bibliotecas a serem usadas em seus projetos.&lt;/p&gt;

&lt;p&gt;Mas afinal, o que esse comando faz por baixo dos panos e como ele resolve as dependências de um projeto? Bora descobrir na prática e entender de uma vez por todas como o &lt;code&gt;npm install&lt;/code&gt; funciona&lt;/p&gt;

&lt;h2&gt;
  
  
  Pré Requisitos
&lt;/h2&gt;

&lt;p&gt;Caso for acompanhar os exemplos junto é fundamental ter o ambiente preparado, então garanta que possua:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node instalado minimamente na versão &lt;code&gt;18&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;NPM instalado minimamente na versão &lt;code&gt;9&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Até é possível utilizar versões anteriores mas acabaríamos perdendo as incríveis capacidades que as versões mais novas nos proporcionam &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E também para ter um ambiente de testes mais limpo vamos criar um projeto &lt;code&gt;npm&lt;/code&gt; através do comando abaixo&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse comando deve criar um arquivo &lt;code&gt;package.json&lt;/code&gt; no diretório a qual o comando foi executado mais ou menos com o conteúdo abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"projects"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"keywords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tudo pronto, vamos começar os nossos testes&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo o &lt;code&gt;npm install&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Como o próprio nome já indica, o comando tem o objetivo de instalar pacotes e torná-los disponíveis para uso em nossos projetos. Esses pacotes são baixados de um &lt;a href="https://docs.npmjs.com/cli/v9/using-npm/registry?v=true"&gt;npm-registry&lt;/a&gt; sendo o padrão o &lt;a href="https://registry.npmjs.org/"&gt;registry público do npm&lt;/a&gt; onde milhares de pacotes e suas versões são publicados diariamente&lt;/p&gt;

&lt;p&gt;Existem algumas formas de usar o comando, sendo as principais que abordaremos nesse artigo são as seguintes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install&lt;/code&gt;: Instalação de dependências definidas em um &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm install &amp;lt;nome-pacote&amp;gt;&lt;/code&gt; ou &lt;code&gt;npm install &amp;lt;nome-pacote&amp;gt;@&amp;lt;versão&amp;gt;&lt;/code&gt;: Instalação de um pacote especifico e opcionalmente em uma versão especifica&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Caso não seja especificado uma versão de pacote o &lt;code&gt;npm&lt;/code&gt; avaliará se já não foi definido o uso do pacote no &lt;code&gt;package.json&lt;/code&gt;. Caso positivo ele utilizará a versão definida no arquivo. Caso negativo ele tentará baixar a última versão do pacote no npm-registry&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Após a execução do comando os seguintes processos são executados:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Verifica se existe uma pasta &lt;code&gt;node_modules&lt;/code&gt; onde o comando foi executado&lt;/p&gt;

&lt;p&gt;1.1. Caso exista será criada uma cópia da árvore de dependências baseada na estrutura da &lt;code&gt;node_modules&lt;/code&gt;&lt;br&gt;
1.2. Caso não exista será criada uma árvore de dependências vazia&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Realiza o download das dependências definidas no &lt;code&gt;package.json&lt;/code&gt; e, caso tenham sidos definidos no comando, os pacotes específicos a serem baixados.&lt;/p&gt;

&lt;p&gt;2.1. Os pacotes que faltam na cópia da árvore de dependências são adicionados a ela&lt;br&gt;
2.2. Os pacotes que tiverem versões modificadas serão atualizados na cópia da árvore de dependências&lt;br&gt;
2.3. Os pacotes presentes na cópia da árvore de dependências mas não forem resolvidos no download serão removidos da cópia&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Compara a estrutura da árvore de dependências original e a cópia da árvore de dependências e realiza as modificações necessárias&lt;/p&gt;

&lt;p&gt;3.1. Em caso de conflitos realiza o tratamento da maneira que o &lt;code&gt;npm&lt;/code&gt; achar mais eficaz e correta&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Algumas coisas também afetam o comportamento do comando como a presença de arquivos &lt;a href="https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json"&gt;&lt;code&gt;package-lock&lt;/code&gt;&lt;/a&gt; ou &lt;a href="https://docs.npmjs.com/cli/v9/configuring-npm/npm-shrinkwrap-json"&gt;&lt;code&gt;npm-shrinkwrap.json&lt;/code&gt;&lt;/a&gt; e também o &lt;a href="https://docs.npmjs.com/cli/v9/commands/npm-cache"&gt;npm-cache&lt;/a&gt;, mas não os abordaremos nesse momento, quem sabe em outro artigo ou uma sequência deste aqui&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Estrutura da &lt;code&gt;node_modules&lt;/code&gt; e a Resolução de Conflitos de Dependências
&lt;/h3&gt;

&lt;p&gt;Quando instalamos um pacote em nosso projeto é comum vir junto uma cacetada de outros pacotes junto, pois foram declarados como dependências pelo pacote que queremos usar. Então não é impossível que diferentes pacotes tenham como dependência um mesmo pacote, mas em versões distintas&lt;/p&gt;

&lt;p&gt;Vamos entender o que ocorre quando baixamos dois pacotes que tem a mesma dependência em comum porém com versões diferentes. Para esse teste iremos baixar dois pacotes: &lt;code&gt;prettier-eslint@15.0.0&lt;/code&gt; e &lt;code&gt;gulp-eslint@6.0.0&lt;/code&gt;. Ambos tem como dependência o &lt;code&gt;eslint&lt;/code&gt; porém o primeiro depende da versão &lt;code&gt;^8.7.0&lt;/code&gt; e o segundo da versão &lt;code&gt;^6.0.0&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A escolha de bibliotecas foram apenas para fins demonstrativos, não é uma recomendação de uso em seus projetos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nesse cenário é certo que haverá conflitos pois estamos lidando com duas versões diferentes de &lt;code&gt;eslint&lt;/code&gt; e o &lt;code&gt;npm&lt;/code&gt; precisa agir. Para isso deverá ser criado uma instância de &lt;code&gt;eslint&lt;/code&gt; geral que ficará na raíz da pasta &lt;code&gt;node_modules&lt;/code&gt; e também uma segunda instância exclusiva para o outro pacote ficando com uma estrutura mais ou menos assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── node_modules
│   ├── eslint@8.50.0
│   ├── gulp-eslint@6.0.0
│   │   ├── node_modules
│   │   │   ├── eslint@6.8.0
│   ├── prettier-eslint@15.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note que foi criada uma sub pasta &lt;code&gt;node_modules&lt;/code&gt; abaixo da pasta do pacote &lt;code&gt;gulp-eslint&lt;/code&gt;. Isso ocorre pois é a forma do &lt;code&gt;npm&lt;/code&gt; segregar os escopos e permitir que o &lt;code&gt;gulp-eslint&lt;/code&gt; utilize o &lt;code&gt;eslint&lt;/code&gt; na versão que precisa.&lt;/p&gt;

&lt;p&gt;Na prática, dentro de contexto de uso de pacotes em códigos JS quando realizamos a importação de uma dependência seja através de &lt;code&gt;import&lt;/code&gt; com &lt;code&gt;ESModules&lt;/code&gt; ou o uso de &lt;code&gt;require(...)&lt;/code&gt; o &lt;code&gt;npm&lt;/code&gt; resolve baseado na localização do pacote dentro da &lt;code&gt;node_modules/&amp;lt;nome-dependencia&amp;gt;&lt;/code&gt; mais próxima.&lt;/p&gt;

&lt;p&gt;Em nosso exemplo, para o pacote &lt;code&gt;prettier-eslint&lt;/code&gt; e até mesmo o nosso projeto, a referência mais próxima acaba sendo o &lt;code&gt;eslint&lt;/code&gt; definido na raiz da &lt;code&gt;node_modules&lt;/code&gt; pois não há outra pasta &lt;code&gt;node_modules/eslint&lt;/code&gt; no caminho. Porém para o &lt;code&gt;gulp-eslint&lt;/code&gt; ele acabaria resolvendo com o &lt;code&gt;eslint&lt;/code&gt; dentro da pasta &lt;code&gt;node_modules&lt;/code&gt; que se encontra abaixo da própria pasta do &lt;code&gt;gulp-eslint&lt;/code&gt; pois é a referência relativamente mais próxima &lt;/p&gt;

&lt;p&gt;Mas afinal, baseado em que o &lt;code&gt;npm&lt;/code&gt; decidiu criar a sub pasta de &lt;code&gt;node_modules&lt;/code&gt; abaixo do pacote &lt;code&gt;gulp-eslint&lt;/code&gt; e não do &lt;code&gt;prettier-eslint&lt;/code&gt;? Bem, existem alguns fatores determinantes em ordem de importância:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Quão próximo está na declaração direta do pacote - Quanto mais distante da ramificação principal menor a prioridade de definir a versão da dependência como dominante&lt;/p&gt;

&lt;p&gt;1.1.  No exemplo, caso seja instalado no projeto diretamente o &lt;code&gt;eslint&lt;/code&gt; na versão &lt;code&gt;7.0.0&lt;/code&gt; seria criado na raiz da &lt;code&gt;node_modules&lt;/code&gt; o &lt;code&gt;eslint&lt;/code&gt; na versão &lt;code&gt;7.0.0&lt;/code&gt; e dentro das pastas dos pacotes &lt;code&gt;gulp-eslint&lt;/code&gt; e &lt;code&gt;prettier-eslint&lt;/code&gt; seriam criadas sub pastas &lt;code&gt;node_modules&lt;/code&gt; com as respectivas versões de &lt;code&gt;eslint&lt;/code&gt; a qual eles dependem. Isso ocorre pois o projeto está no nível 1 enquanto os pacotes estão no nível 2 (&lt;code&gt;projeto &amp;gt; eslint&lt;/code&gt; vs &lt;code&gt;projeto &amp;gt; gulp-eslint &amp;gt; eslint&lt;/code&gt;)&lt;br&gt;
1.2. Caso seja instalado, invés do pacote &lt;code&gt;prettier-eslint&lt;/code&gt;, o pacote &lt;code&gt;eslint-config-celebrate&lt;/code&gt; que depende do &lt;code&gt;prettier-eslint&lt;/code&gt; a prioridade seria da versão de &lt;code&gt;eslint&lt;/code&gt; definida pelo &lt;code&gt;gulp-eslint&lt;/code&gt;. Isso ocorre pois o pacote &lt;code&gt;gulp-eslint&lt;/code&gt; está no nível 2 enquanto o &lt;code&gt;prettier-eslint&lt;/code&gt; agora está no nível 3 (&lt;code&gt;projeto &amp;gt; gulp-eslint &amp;gt; eslint&lt;/code&gt; vs &lt;code&gt;projeto &amp;gt; eslint-config-celebrate &amp;gt; prettier-eslint &amp;gt; eslint&lt;/code&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Se mesmo assim ordem de prioridade for a mesma, será utilizado como fator de desempate a ordem de instalação&lt;/p&gt;

&lt;p&gt;2.1. No exemplo, caso seja instalado primeiro o &lt;code&gt;prettier-eslint&lt;/code&gt; e depois o &lt;code&gt;gulp-eslint&lt;/code&gt; a prioridade será da versão de &lt;code&gt;eslint&lt;/code&gt; definida pelo &lt;code&gt;prettier-eslint&lt;/code&gt;&lt;br&gt;
2.2. Caso seja instalado primeiro o &lt;code&gt;gulp-eslint&lt;/code&gt; e depois o &lt;code&gt;prettier-eslint&lt;/code&gt; a prioridade será da versão de &lt;code&gt;eslint&lt;/code&gt; definida pelo &lt;code&gt;gulp-eslint&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusões
&lt;/h2&gt;

&lt;p&gt;No exemplo que trabalhamos foram criadas duas instâncias da dependência em comum mas em projetos reais é comum ter muito mais instâncias. Se ter instâncias duplicadas não for algo que prejudique o funcionamento do uso da dependência não há com que se preocupar. Mas caso seja fundamental apenas uma instância é possível aplicar algumas práticas para alcançar isso. Quem sabe em um outro momento falamos sobre isso ;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Referências
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v9/configuring-npm/folders"&gt;NPM Folders&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v9/commands/npm-install?v=true#algorithm"&gt;NPM Install Algorithm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>npm</category>
      <category>javascript</category>
      <category>node</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
