O package.json é um elemento-chave em muitas aplicações do ecossistema Node.js. Se você trabalha com JavaScript, ou se você já interagiu com um projeto JavaScript antes, Node.js ou front-end, você certamente já se deparou com um arquivo package.json.
Para quê ele serve?
O que você deveria saber sobre ele e que coisas legais pode fazer com ele?
O package.json é uma espécie de manifesto do seu projeto. Ele pode fazer várias coisas, completamente não relacionadas. Ele é um repositório central de configurações de ferramentas, por exemplo. Ele também é onde npm armazena os nomes e versões dos pacotes instalados.
Neste artigo você vai ver:
Caso prefira, o vídeo abaixo resume os principais pontos deste artigo.
Vamos lá!
A estrutura do arquivo
Aqui temos um exemplo de arquivo package.json válido:
1 2 3 4 |
{ } |
Está vazio Não existem exigências fixas do que deve estar em um arquivo package.json para uma aplicação. O único requisito é que ele respeite o formato JSON, de outra maneira ele não poderá ser lido pelos programas que tentarem acessar suas propriedades programaticamente. Se você está construindo um pacote Node.js que você quer distribuir através do npm, as coisas mudam radicalmente e você deve ter um conjunto de propriedades que ajudem outras pessoas a usá-lo. Veremos mais sobre isso mais tarde. Este é outro package.json válido:
1 2 3 4 5 |
{ "name": "test-project" } |
Ele define uma propriedade name, que diz o nome da aplicação ou pacote, que está contido na mesma pasta onde o arquivo vive. Aqui temos um exemplo mais complexo, o qual eu extraí de uma aplicação de exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
{ "name": "test-project", "version": "1.0.0", "description": "A Node.js project", "main": "src/index.js", "private": true, "scripts": { "dev": "nodemon ./src/index", "start": "node ./src/index", "test": "jest", "compile": "tsc" }, "dependencies": { "axios": "^0.21.1", "cors": "^2.8.5", "dotenv": "^8.2.0", "express": "^4.17.1", "joi": "^17.3.0", "pg": "^8.5.1", "sequelize": "^6.4.0", "uuid": "^8.3.2", "winston": "^3.3.3" }, "devDependencies": { "jest": "^26.6.3", "nock": "^13.0.5", "nodemon": "^2.0.6", "sequelize-mock-v5": "^1.2.0" }, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } } |
Existem muitas coisas acontecendo aqui:
- name define o nome da aplicação ou pacote;
- version indica a versão atual;
- description é um resumo da sua aplicação/pacote;
- main define o ponto de entrada da aplicação (o que vai ser retornado se alguém der um require no nosso pacote);
- private (true) previne a sua aplicação de ser publicada acidentalmente no npm;
- scripts define um conjunto de scripts Node para você executar;
- dependencies define uma lista de pacotes npm instalados como dependências de produção;
- devDependencies define uma lista de pacotes npm instalados como dependências de desenvolvimento;
- engines define quais versões de Node este pacote/aplicação funciona;
Todas estas propriedades são usadas tanto pelo npm quanto por outras ferramentas que podemos usar.
Detalhando as propriedades
Esta seção descreve as propriedades que você pode usar em detalhes. Eu refiro várias vezes a pacote, mas a mesma coisa se aplica a aplicações locais que você não use como pacote. A maioria destas propriedades são usadas somente no site do npm. Outras são usadas por scripts que interagem com seu código, como npm ou outros.
name define o nome do pacote, caso você venha a publicá-lo no NPM algum dia. Ex:
1 2 3 |
"name": "test-project" |
O nome deve ser menor que 214 caracteres, não pode ter espaços e somente pode conter letras minúsculas, hífens (-) ou underscore (_).
Isto tudo porque quando um pacote é publicado no npm, ele ganha sua URL baseado nesta propriedade. Se você publicou este pacote publicamente no GitHub, um bom valor para esta propriedade é o nome do repositório.
author exibe o nome do autor do pacote (útil para o NPM também). Ex:
1 2 3 |
Também pode ser usado neste formato:
1 2 3 4 5 6 7 8 9 |
contributors
Assim como o autor, o projeto pode ter um ou mais contribuidores. Esta propriedade é uma array que os lista:
1 2 3 4 5 6 7 |
Pode também ser usado neste formato:
1 2 3 4 5 6 7 8 9 10 11 |
{ "contributors": [ { "name": "Luiz Duarte", "url": "http://184.73.67.74" } ] } |
bugs
Links para o issue tracker do pacote, geralmente uma página de issues no GitHub. Ex:
1 2 3 4 5 |
{ "bugs": "https://github.com/luiztools/package/issues" } |
homepage
Define o site do pacote, geralmente onde tem a documentação do mesmo. Ex:
1 2 3 4 5 |
{ "homepage": "https://www.luiztools.com.br/meu-pacote" } |
version
Indica a versão atual do pacote. Ex:
1 2 3 |
"version": "1.0.0" |
Esta propriedade segue a notação semântica de versionamento (semver), o que significa que a versão é sempre expressada com três números: x.x.x
.
O primeiro número é a versão principal, o segundo é a versão secundária e o terceiro é a versão do patch.
Existe um significado nestes números: uma release que somente corrija bugs é uma release patch, uma release que introduza mudanças na compatibilidade retroativa é uma release secundária e uma release principal é aquela que pode quebrar compatibilidade.
license
Indica a licença do pacote. Ex:
1 2 3 |
"license": "MIT" |
keywords
Esta propriedade contém um array ou palavras-chave associados com o que seu pacote faz. Ex:
1 2 3 4 5 6 7 |
"keywords": [ "email", "machine learning", "ai" ] |
Isto ajuda as pessoas a encontrar o seu pacote quando estiverem procurando pacotes similares ou quando navegam pelo site do npm.
description
Esta propriedade contém um resumo do pacote. Ex:
1 2 3 |
"description": "A package to work with strings" |
Isto é especialmente útil se você decidir publicar seu pacote para o npm, visando que outras pessoas possam descobrir o que este pacote faz.
repository
Esta propriedade especifica onde o repositório do pacote está localizado. Ex:
1 2 3 |
"repository": "github:teste/testing", |
Note o prefixo github. Existem outros serviços populares também:
1 2 3 |
"repository": "gitlab:teste/testing", |
1 2 3 |
"repository": "bitbucket:teste/testing", |
Você pode explicitamente definir o controle de versão do sistema:
1 2 3 4 5 6 |
"repository": { "type": "git", "url": "https://github.com/teste/testing.git" } |
Você também pode usar diferentes sistemas de controle de versão:
1 2 3 4 5 6 |
"repository": { "type": "svn", "url": "..." } |
main
Define o ponto de entrada do pacote. Quando você importa este pacote em uma aplicação, é essa aplicação que a aplicação irá buscar pelo module.exports. Ex:
1 2 3 |
"main": "src/main.js" |
private
Se definido como true, previne que a aplicação/pacote seja acidentalmente publicada no npm. Ex:
1 2 3 |
"private": true |
scripts
Define um conjunto de scripts node que você pode executar. Ex:
1 2 3 4 5 6 7 8 9 10 |
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "start": "npm run dev", "unit": "jest --config test/unit/jest.conf.js --coverage", "test": "npm run unit", "lint": "eslint --ext .js,.vue src test/unit", "build": "node build/build.js" } |
Estes scripts são aplicações de linha de comando. Você pode rodá-los usando npm run XXXX, onde XXXX é o nome do comando. Ex:
1 2 3 |
npm run dev |
Você pode usar qualquer nome que você quiser para um comando, e os scripts podem ser literalmente qualquer coisa que você quiser.
dependencies
Define uma lista de pacotes npm instalados como dependências. Quando você instalar um pacote usando npm:
1 2 3 |
npm install <PACKAGENAME> |
o pacote será automaticamente inserido nesta lista. Exemplo:
1 2 3 4 5 |
"dependencies": { "vue": "^2.5.2" } |
devDependencies
Define uma lista de pacotes npm instalados como dependências de desenvolvimento. Eles diferem de dependencies porque eles serão instalados somente em máquinas de desenvolvimento, não necessárias para executar o código em produção.
Quando você instalar um pacote usando npm:
1 2 3 |
npm install --dev <PACKAGENAME> |
o pacote será automaticamente inserido nessa lista. Exemplo:
1 2 3 4 5 6 |
"devDependencies": { "autoprefixer": "^7.1.2", "babel-core": "^6.22.1" } |
engines
Define quais versões de Node.js e outros comandos que este pacote ou aplicação suporta. Exemplo:
1 2 3 4 5 6 |
"engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } |
browserslist
É usado para dizer quais browsers (e suas versões) você quer suportar. Ele é referenciado pelo Babel, Autoprefixer, e outras ferramentas, para somente adicionar os polyfills e fallbacks necessários aos navegadores-alvo. Exemplo:
1 2 3 4 5 6 7 |
"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] |
Esta configuração significa que você quer suportar ao menos as duas versões principais de todos os browsers com ao menos 1% de uso no mercado (as estatísticas são do CanIUse.com ), exceto IE8 e inferiores (saiba mais no browserslist do NPM).
Comandos específicos de propriedades
O arquivo package.json também pode hospedar configurações de comandos específicos, por exemplo Babel, ESLint, e muito mais.
Cada um possui uma propriedade específica, como eslintConfig
, babel
e outros. Estes são comandos específicos e você pode encontrar como usá-los na documentação do respectivo comando ou projeto.
Versões de Pacote
Então você abre o packages.json e rapidamente entende que as dependencies são os pacotes que sua aplicação usa, mas onde deveriam estar listadas as versões dos pacotes tem um monte de símbolos que não lhe dizem muita coisa…Por exemplo, qual a diferença entre o til (˜) e o circunflexo (^) no package.json?
- O til garante que o pacote seja sempre carregado respeitando o número do meio da versão. Ex: ˜1.2.3 pega o pacote mais recente da versão 1.2.x, mas não vai atualizar para 1.3. Geralmente garante que correções de bugs sejam atualizados no seu pacote.
- O circunflexo garante que o pacote seja sempre carregado respeitando o primeiro número da versão. Ex: ˆ1.2.3 pega o pacote mais recente da versão 1.x, mas não vai atualizar para 2.0. Garante que bugs e novas funcionalidades do seu pacote sejam atualizados, mas não novas versões “major” dele.
A imagem abaixo ajuda a entender o template de versões dos pacotes do NPM, que aliás usa um padrão bem comum da indústria de software:
Outros símbolos incluem:
- >, >=, <, <=1.0: a versão deve ser superior, superior ou igual, inferior, inferior ou igual à 1.0, respectivamente.
- 1.2.x: equivalente a ˜1.2.0
- *: qualquer versão do pacote
- latest: a versão mais recente do pacote
Agora se você não tiver símbolo algum, aí o pacote deve ser sempre carregado usando a versão especificada.
Uma dica bem valiosa aqui (quem não gosta de um bônus?) para quando se quer atualizar todos pacotes é colocar * na versão de todos e rodar o comando “npm update –save” sobre a pasta do projeto.
O arquivo package-lock.json
Na versão 5 o NPM introduziu o arquivo package-lock.json, que é automaticamente gerado quando você instala pacotes Node.
O que ele faz? Você provavelmente sabe tudo sobre o arquivo package.json a essa altura, o que é bem mais comum e está aí há bem mais tempo.
O objetivo deste arquivo é manter registro das versões exatas de cada pacote que é instalado para que um produto possa ser 100% reproduzível da mesma maneira mesmo se os pacotes forem atualizados por seus mantenedores.
Isto soluciona um problema bem específico que o package.json não resolvia. No package.json você pode definir quais versões você quer atualizar, usando a notação semver, por exemplo:
- se você escrever
~0.13.0
, você quer somente atualizar patch releases:0.13.1
está ok, mas0.14.0
não. - se você escrever
^0.13.0
, você quer atualizar patches e minor releases:0.13.1
,0.14.0
e assim por diante. - se você escrevere
0.13.0
, você somente usará esta versão exata do pacote.
Você não commita no Git a sua pasta node_modules, uma vez que ela é geralmente muito grande, e quando você tenta replicar o projeto em outra máquina usando o comando npm install, se você especificar a sintaxe ~
e uma patch release de um pacote tiver sido lançada, ela será instalada. O mesmo para ^
e minor releases.
Se você especificar as versões exatas, como 0.13.0
neste exemplo, você não sofre desse problema.
Além poder ser um problema para você, pode ser um problema para outra pessoa qualquer querendo rodar o seu projeto com npm install, podendo acontecer do seu projeto original ficar diferente da nova instalação. Isto implica obviamente em possíveis bugs, mesmo que seja um patch ou minor release.
O arquivo package-lock.json
define as versões instaladas de cada pacote de maneira irreversível e o npm usará exatamente estas versões quando você rodar npm install.
Este conceito não é novo e outros gerenciadores de pacotes de linguagens de programação (como o Composer do PHP) usam um sistema semelhante por anos.
O arquivo package-lock.json
precisa ser commitado no Git para que possa ser baixado por outras pessoas. E se precisar atualizar as versões das dependências no package-lock.json, basta rodar npm update.
Espero que este artigo tenha sido útil para você que está aprendendo Node.js Para conteúdo mais aprofundado, recomendo meus livros. Para videoaulas, recomendo o meu curso online (abaixo).
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]
[…] O guia completo do package.json do Node.js […]