DependĂȘncias do JavaScript do caminho para o inferno

Cada projeto JavaScript começa com boas intençÔes, a saber, que seus criadores prometem a si mesmos nĂŁo usar muitos pacotes NPM durante seu desenvolvimento. Mas, mesmo que os desenvolvedores façam esforços considerĂĄveis ​​para manter essa promessa, os pacotes NPM penetram gradualmente em seus projetos. O tamanho do arquivo package.jsonaumenta com o tempo. E desde a package-lock.jsoninstalação das dependĂȘncias, ocorre um verdadeiro horror, expresso em adiçÔes e exclusĂ”es de pacotes, especialmente perceptĂ­veis no prĂłximo PR ... "EstĂĄ tudo bem", diz o lĂ­der da equipe. O resto da equipe concorda com a cabeça. O que mais fazer? Todos gostamos do fato de que o ecossistema JavaScript estĂĄ vivo e bem. NĂŁo precisamos reinventar a roda todas as vezes e tentar resolver os problemas que jĂĄ foram resolvidos pela comunidade de cĂłdigo aberto.





Suponha que vocĂȘ crie um blog e deseje usar o Gatsby.js. Tente adicionar este gerador de sites com base no seu projeto. Agora parabĂ©ns. Seu projeto tinha apenas 19.000 dependĂȘncias adicionais. Isto Ă© normal? QuĂŁo complexa pode uma ĂĄrvore de dependĂȘncia de JavaScript se tornar? Como uma ĂĄrvore de dependĂȘncia se transforma em inferno? Vamos descobrir.

O que Ă© um pacote JavaScript?


O NPM (Node Package Manager, Node Package Manager) armazena o maior registro de pacotes do mundo. Estes sĂŁo pacotes JavaScript. O NPM Ă© mais do que RubyGems, PyPi e Maven combinados. Essa conclusĂŁo pode ser feita com base na anĂĄlise dos dados do projeto Module Counts , que monitora o nĂșmero de pacotes nos registros populares.


Dados sobre o nĂșmero de pacotes em registros populares

VocĂȘ pode pensar que quantidades muito grandes de cĂłdigo estĂŁo representadas neste grĂĄfico. Do jeito que Ă©. Para transformar um projeto em um pacote NPM, este pacote deve ter um arquivopackage.json. Esse pacote pode ser enviado ao registro do NPM.

O que Ă© o package.json?


Aqui estĂŁo as tarefas que ele resolve package.json:

  • Ele lista os pacotes dos quais seu projeto depende (esta Ă© uma lista de dependĂȘncias do projeto).
  • Nele, usando as regras do controle de versĂŁo semĂąntico, sĂŁo definidas as versĂ”es dos pacotes de dependĂȘncia que seu projeto pode usar.
  • Permite reproduzir o ambiente necessĂĄrio para o pacote funcionar e, como resultado, simplifica a transferĂȘncia do projeto para outros desenvolvedores.

Um arquivo package.jsonpode ser pensado como um arquivo READMEbombeado com esterĂłides. Aqui vocĂȘ pode descrever as dependĂȘncias do seu pacote, aqui vocĂȘ pode escrever scripts que sĂŁo executados durante a montagem e teste do projeto. O mesmo arquivo contĂ©m informaçÔes sobre a versĂŁo do projeto especificada pelo desenvolvedor e uma descrição do projeto. Estamos particularmente interessados ​​na possibilidade package.jsonde especificar dependĂȘncias do projeto.

Talvez o fato de as dependĂȘncias do projeto estarem indicadas neste arquivo pareça um tanto alarmante. Imagine que existe um pacote que depende de outro pacote e esse outro pacote depende de outro pacote. Essa cadeia de dependĂȘncias pode ser arbitrariamente longa. Por esse motivo, instalar o Ășnico pacote, Gatsby.js, significa equipar o projeto com 19.000 dependĂȘncias adicionais.

Tipos de dependĂȘncia em package.json


Para entender melhor como as listas de dependĂȘncias do projeto crescem com o tempo, vamos falar sobre os diferentes tipos de dependĂȘncias que um projeto pode ter. Nomeadamente, package.jsonpodem ser encontradas as seguintes seçÔes que descrevem vĂĄrias dependĂȘncias:

  • dependencies - sĂŁo dependĂȘncias comuns, cuja funcionalidade Ă© usada no projeto e que Ă© acessada a partir de seu cĂłdigo.
  • devDependencies- estas sĂŁo dependĂȘncias de desenvolvimento. Por exemplo, a biblioteca mais bonita usada para formatar o cĂłdigo.
  • peerDependencies - se as dependĂȘncias forem gravadas nesta seção, o desenvolvedor do pacote informarĂĄ quem irĂĄ instalĂĄ-lo que precisarĂĄ de uma versĂŁo especĂ­fica do pacote especificado nesta seção.
  • optionalDependencies - eles listam dependĂȘncias opcionais, como a incapacidade de instalar, que nĂŁo violarĂĄ o processo de instalação do pacote.
  • bundledDependencies — , . , NPM, , .

package-lock.json


Todos sabemos que o arquivo package-lock.json, no decorrer do trabalho no projeto, passa constantemente por mudanças. Algo Ă© removido, algo Ă© adicionado a ele. Isso Ă© especialmente perceptĂ­vel ao exibir o PR que contĂ©m uma versĂŁo atualizada desse arquivo. Muitas vezes tomamos isso como garantido. Um arquivo Ă© package-lock.jsongerado automaticamente sempre que um arquivo package.jsonou pasta Ă© alterado node_modules. Isso permite manter o conteĂșdo da ĂĄrvore de dependĂȘncias exatamente como era quando vocĂȘ instalou as dependĂȘncias do projeto. Isso permite, ao instalar o projeto, reproduzir a ĂĄrvore de dependĂȘncia. Isso resolve o problema de ter diferentes versĂ”es do mesmo pacote de diferentes desenvolvedores.

Considere um projeto cujas dependĂȘncias incluem React. A entrada correspondente estĂĄ disponĂ­vel em package.json. Se vocĂȘ olhar para o arquivopackage-lock.json deste projeto, vocĂȘ poderĂĄ ver algo como o seguinte:

    "react": {
      "version": "16.13.0",
      "resolved": "https://registry.npmjs.org/react/-/react-16.13.0.tgz",
      "integrity": "sha512-TSavZz2iSLkq5/oiE7gnFzmURKZMltmi193rm5HEoUDAXpzT9Kzw6oNZnGoai/4+fUnm7FqS5dwgUL34TujcWQ==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1",
        "prop-types": "^15.6.2"
      }
    }

Um arquivo package-lock.jsonĂ© uma grande lista de dependĂȘncias do projeto. Aqui estĂŁo as versĂ”es de dependĂȘncia, caminhos (URIs) para os mĂłdulos, hashes usados ​​para verificar a integridade do mĂłdulo e os pacotes necessĂĄrios para este mĂłdulo. Se vocĂȘ ler este arquivo, poderĂĄ encontrar registros de todos os pacotes que o React precisa. É aqui que estĂĄ o verdadeiro inferno das dependĂȘncias. Tudo o que o projeto precisa estĂĄ descrito aqui.

Compreendendo as dependĂȘncias do Gatsby.js


Como Ă© que, tendo estabelecido apenas uma dependĂȘncia, adicionamos atĂ© 19.000 dependĂȘncias ao projeto? É tudo sobre dependĂȘncias de dependĂȘncia. É por isso que temos o que temos:

$ npm install --save gatsby

...

+ gatsby@2.19.28
added 1 package from 1 contributor, removed 9 packages, updated 10 packages and audited 19001 packages in 40.382s

Se vocĂȘ procurar package.json, poderĂĄ encontrar apenas uma dependĂȘncia. Mas se vocĂȘ observar package-lock.json, acontece que diante de nĂłs hĂĄ um monstro de quase 14 kilobytes. Uma resposta mais detalhada sobre o significado de todas essas linhas de cĂłdigo que package-lock.jsonpodem ser encontradas no arquivo package.jsonno repositĂłrio Gatsby.js . Existem muitas dependĂȘncias diretas, ou seja, de acordo com os cĂĄlculos da NPM , 132. Se cada uma dessas dependĂȘncias tiver pelo menos mais uma dependĂȘncia, o nĂșmero total de dependĂȘncias do projeto duplicarĂĄ - e haverĂĄ 264 dependĂȘncias. Claro que, no mundo real, nĂŁo Ă© assim. Cada dependĂȘncia direta do projeto possui mais de uma dependĂȘncia inerente. Como resultado, a lista de dependĂȘncias do projeto Ă© muito longa.

Por exemplo, teremos interesse em quantas vezes a biblioteca lodash Ă© usada como dependĂȘncia para outros pacotes :

$ npm ls lodash
example-js-package@1.0.0
└─┬ gatsby@2.19.28
  â”œâ”€â”Ź @babel/core@7.8.6
  â”‚ ├─┬ @babel/generator@7.8.6
  â”‚ │ └── lodash@4.17.15  deduped
  â”‚ ├─┬ @babel/types@7.8.6
  â”‚ │ └── lodash@4.17.15  deduped
  â”‚ └── lodash@4.17.15  deduped
  â”œâ”€â”Ź @babel/traverse@7.8.6
  â”‚ └── lodash@4.17.15  deduped
  â”œâ”€â”Ź @typescript-eslint/parser@2.22.0
  â”‚ └─┬ @typescript-eslint/typescript-estree@2.22.0
  â”‚   └── lodash@4.17.15  deduped
  â”œâ”€â”Ź babel-preset-gatsby@0.2.29
  â”‚ └─┬ @babel/preset-env@7.8.6
  â”‚   ├─┬ @babel/plugin-transform-block-scoping@7.8.3
  â”‚   │ └── lodash@4.17.15  deduped
  â”‚   ├─┬ @babel/plugin-transform-classes@7.8.6
  â”‚   │ └─┬ @babel/helper-define-map@7.8.3
  â”‚   │   └── lodash@4.17.15  deduped
  â”‚   ├─┬ @babel/plugin-transform-modules-amd@7.8.3
  â”‚   │ └─┬ @babel/helper-module-transforms@7.8.6
  â”‚   │   └── lodash@4.17.15  deduped
  â”‚   └─┬ @babel/plugin-transform-sticky-regex@7.8.3
  â”‚     └─┬ @babel/helper-regex@7.8.3
  â”‚       └── lodash@4.17.15  deduped
  ...

Felizmente, a maioria dessas dependĂȘncias Ă© representada pela mesma versĂŁo do lodash. E com essa abordagem, node_moduleshaverĂĄ apenas uma pasta da biblioteca lodash. É verdade que esse geralmente nĂŁo Ă© o caso. Às vezes, pacotes diferentes precisam de versĂ”es diferentes do mesmo pacote. É por isso que muitas piadas apareceram sobre o tamanho enorme da pasta node_modules. No nosso caso, no entanto, nem tudo Ă© tĂŁo ruim:

$ du -sh node_modules
200M    node_modules

200 megabytes nĂŁo Ă© tĂŁo ruim. Vi como o tamanho desta pasta atinge facilmente 700 MB. Se vocĂȘ estiver interessado em aprender quais mĂłdulos ocupam mais espaço, execute o seguinte comando:

$ du -sh ./node_modules/* | sort -nr | grep '\dM.*'
 17M    ./node_modules/rxjs
8.4M    ./node_modules/@types
7.4M    ./node_modules/core-js
6.8M    ./node_modules/@babel
5.4M    ./node_modules/gatsby
5.2M    ./node_modules/eslint
4.8M    ./node_modules/lodash
3.6M    ./node_modules/graphql-compose
3.6M    ./node_modules/@typescript-eslint
3.5M    ./node_modules/webpack
3.4M    ./node_modules/moment
3.3M    ./node_modules/webpack-dev-server
3.2M    ./node_modules/caniuse-lite
3.1M    ./node_modules/graphql
...

Sim, o rxjs Ă© um pacote insidioso.

Aqui estĂĄ um comando simples que ajuda a reduzir o tamanho da pasta node_modulese simplificar sua estrutura:

$ npm dedup
moved 1 package and audited 18701 packages in 4.622s

51 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Durante a desduplicação, o npm tenta simplificar a estrutura da ĂĄrvore de dependĂȘncias localizando as dependĂȘncias usadas por outras dependĂȘncias e movendo-as para que possam ser compartilhadas. Isso se aplica ao nosso exemplo de lodash. Muitos pacotes sĂŁo usados lodash @4.17.15, como resultado, para garantir sua operacionalidade, basta instalar esta versĂŁo da biblioteca apenas uma vez. Obviamente, essa Ă© a situação em que entramos desde o inĂ­cio, apenas estabelecendo dependĂȘncias. Se, no processo de trabalho em um projeto, package.jsonadicionar novas dependĂȘncias, Ă s vezes Ă© recomendĂĄvel lembrar a equipe npm dedup. Se vocĂȘ usar o gerenciador de pacotes do yarn, um comando semelhante serĂĄ semelhante ao do yarn dedupe. Mas, de fato, nĂŁo hĂĄ necessidade, pois a otimização de dependĂȘncia Ă© realizada automaticamente quando o comando Ă© executado yarn install.

Visualização de DependĂȘncias


Interessado em uma representação grĂĄfica das dependĂȘncias do seu projeto? Nesse caso, vocĂȘ pode criar uma apresentação usando ferramentas especiais. Vamos considerar alguns deles.

A seguir, Ă© apresentado um resultado de visualização de dependĂȘncia obtido em npm.anvaka.com/ .


Visualização de dependĂȘncia usando npm.anvaka.com

Aqui vocĂȘ pode ver as dependĂȘncias das dependĂȘncias do pacote de projeto Gatsby.js. O resultado Ă© semelhante a uma enorme web. O projeto Gatsby.js tem tantas dependĂȘncias que essa "web" quase "travou" meu navegador. Agora , se estiver interessado, um link para este diagrama. Pode ser apresentado em forma 3D.

Aqui estå uma visualização feita usando o npm.broofa.com .


Um fragmento de uma visualização de dependĂȘncia feita usando o npm.broofa.com

É semelhante a um fluxograma. Ela, para o Gatsby.js, acaba sendo muito complicada. VocĂȘ pode dar uma olhada aqui . Elementos do circuito podem ser coloridas com base em estimativas de npms.io . VocĂȘ pode enviar seu prĂłprio arquivo para o sitepackage.json.

A ferramenta Package Phobia permite descobrir quanto espaço ele precisa antes de instalar um pacote.


InformaçÔes sobre o pacote recebidas usando a fobia do pacote

Aqui vocĂȘ pode descobrir sobre o tamanho do pacote publicado e quanto espaço em disco serĂĄ necessĂĄrio apĂłs a instalação.

ConclusĂŁo: com grande poder vem grande responsabilidade


No final, quero dizer que JavaScript e NPM sĂŁo Ăłtimas ferramentas. O bom Ă© que os desenvolvedores modernos tĂȘm a oportunidade de usar um conjunto enorme de dependĂȘncias. Executar o comando npm installpara evitar que vocĂȘ escreva algumas linhas de cĂłdigo Ă© tĂŁo fĂĄcil que Ă s vezes esquecemos as consequĂȘncias.

Agora que vocĂȘ leu atĂ© esse ponto, deve ter um entendimento mais completo dos recursos da estrutura da ĂĄrvore de dependĂȘncia do npm-project. Se vocĂȘ adicionar uma biblioteca ao projeto que Ă© muito grande ou apenas explorar as dependĂȘncias do seu projeto, sempre poderĂĄ tirar proveito do que discutimos aqui e analisar as dependĂȘncias.

Queridos leitores! Deseja usar o menor nĂșmero possĂ­vel de dependĂȘncias em seus projetos npm?


All Articles