Gabriel / Ramos

⟨ pintor de pixel ⟩

  • Blog
  • Entendendo o package.json

Entendendo o package.json

Dependências, scripts e configurações. Você já parou para entender o que cada parte desse arquivo faz?

10 min. de leitura

Foto por Elevate

Praticamente qualquer projeto que envolva JS hoje em dia vai possuir um arquivo na sua raíz chamado package.json, e saber lidar com esse arquivo da melhor maneira possível pode ajudar nosso trabalho de muitas maneiras. Da realização de algumas automações até aos dados dos desenvolvedores responsáveis por algum projeto, esse arquivo está repleto de informações e significados muito úteis.

Tá, mas o que é esse arquivo?

O package.json é um arquivo de configuração utilizado para estipular e configurar dependências do seu projeto (quais outros pacotes ele vai precisar para ser executado) e scripts automatizados. Através dele conseguimos deixar claro uma "receita" para executar um projeto.

Pra facilitar o entendimento, vamos dividir ele em algumas partes, que vamos ver a seguir e vamos "construindo" um package.json ao longo do caminho.


Criando um package.json

Exitem algumas formas de criar um arquivo package.json. A maneira mais simples é executar npm init via terminal na pasta do seu projeto. Algumas perguntas aparecerão para você responder (como nome, descrição, versão) e após confirmar tudo isso, um arquivo básico será gerado para você.

Você consegue configurar algumas informações para seu usuário, assim não precisa ficar inserindo essas informações a cada projeto que for criar. Se quiser configurar isso, basta criar um arquivo .npmrc na raíz do seu usuário na sua máquina ou executar os comandos abaixo no terminal

npm set init.author.email "example-user@example.com"
npm set init.author.name "example_user"
npm set init.license "MIT"

O que esses comandos fazem, cada um, é:

  • insere o email do usuário
  • insere o nome do usuário
  • insere uma licença padrão (se quiser, você pode ler um pouco mais sobre isso)

Com essa informações configuradas, na próxima vez que for executar um npm init no seu terminal, você pode passar a flag -y (executando npm init -y ou npm init --yes) e o package.json já será criado para você com essas informações por padrão.

Você também pode criar esse arquivo na mão, sem problemas nenhum. Mas, se podemos automatizar esse processo, não tem necessidade de ficar repetindo trabalho, né?


Informações

Vamos pensar no seguinte package.json

{
  "name": "@gabrieluizramos/utils",
  "description": "Alguns utilitários pessoais utilizados entre vários projetos",
  "keywords": [],
  "author": "Gabriel Ramos <gabriel.luiz.ramos@gmail.com> (https://gabrieluizramos.com.br)",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "url-do-github"
  },
  "bugs": {
    "url": "url-do-github"
  },
  "homepage": "url"
}

Ao gerar um arquivo desses automaticamente, algumas dessas estruturaas já virão preenchidas (outras eu ocultei pois vamos ver mais pra frente). E, o que cada uma delas significa é:

Name

É o nome do projeto e pode ser o nome que você quiser.

Pode possuir um prefixo com @nome (como é o caso do @gabrieluizramos) que deixa claro qual o time responsável pelo pacote, inclusive facilita na publicação desse pacote outros pacotes com o mesmo nome.

Ou seja, nesse cenário, seria um pacote de utilitários (utils), mantido por um usuário (@gabrieluizramos).

Description

Uma breve descrição do seu projeto e o que seu pacote realiza. Nesse ponto não há necessidade de se alongar na descrição já que o intuito desse campo não é fornecer uma documentação completa do seu projeto. Uma frase bem descritiva já é o suficiente.

Keywords

É um array de palavras-chave do seu projeto e também é de livre preenchimento. Insira nessa parte as palavras que mais estão de acordo com seu projeto.

Author

Como o própro nome indica, as informações sobre os desenvolvedores desse pacote. Pode ser dividida dentro da própria string e conter algumas partes como Nome <email> (site) deixando claro e facilitando qualquer eventual contato necessário com as pessoas envolvidas no projeto.

License

É a licença escolhida para o projeto. Você consegue definir a padrão que será útilizada ao criar um novo arquivo package.json. Não vale a pena nos aprofundarmos nesse assunto por agora mas imagine que a licença é o "tópico" responsável por indicar como seu software/pacote deve ser distribuído e utilizado.

Bugs, homepage e repository

São algumas informações que podem estar contidas no seu package.json que indicam uma homepage padrão do seu projeto, eventuais formas de contato para caso alguém encontre um bug e a forma como esse projeto é versionado.


Dependências

O próximo ponto são as dependências do nosso projeto e quais os tipos de dependência que podemos ter. Vamos pensar num arquivo como:

{
  "name": "@gabrieluizramos/utils",
  "description": "Alguns utilitários pessoais utilizados entre vários projetos",
  "keywords": [],
  "author": "Gabriel Ramos <gabriel.luiz.ramos@gmail.com> (https://gabrieluizramos.com.br)",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "url-do-github"
  },
  "bugs": {
    "url": "url-do-github"
  },
  "homepage": "url",
  "dependencies": {
    "package": "^0.1.1"
  },
  "devDependencies": {
    "testpackage": "~0.1.1"
  }
}

Agora temos as informações de dependencies e devDependencies no nosso projeto e elas se diferem da seguinte maneira:

  • dependencies são geralmente utilizadas para declarar os pacotes necessários para executar seu projeto em um ambiente de execução como produção;
  • devDependencies são utilizadas para indicar pacotes necessários para executar seu projeto em um cenário de desenvolvimento e testes (como pacotes relacionados a teste e formatação geral do código-fonte do seu projeto).

Dentro dessas duas estruturas, segue-se um padrão de "nome": "versão" dos seus pacotes e, se reparou com cuidado, verá que um deles utiliza o prefixo ^ para instalar uma versão de um pacote e outro está com o prefixo ~.

Separando suas dependências dessa forma, em um cenário onde você vai publicar e executar sua aplicação em um ambiente de produção ou irá disponibilizar seu pacote para que alguém realiza a instalação, somente as dependências declaradas dentro de dependencies serão instaladas junto com seu projeto. Afinal, quem for utilizar esse pacote não precisa ficar responsável por realizar nenhuma configuração ou teste no seu projeto, já que isso é uma responsabilidade do time que o desenvolveu.

Para instalar e adicionar dependências nessa duas categorias, conseguimos passar um argumento para o comando npm install, fazendo com que a inserção de um novo pacote no package.json seja feita automaticamente ao realizar uma instalação, assim não precisamos nos preocupar de inserir isso manualmente. Para realizar uma instalação dessa forma, basta rodar no terminal:

npm install --save nome-do-pacote
npm install --save-dev nome-do-pacote

A linha que utiliza --save salvará as dependências dentro do nó de dependencies enquanto a que utiliza --save-dev salvará o pacote em devDependencies. Você também pode especificar uma versão do pacote ao instalar, após o seu nome, basta adicionar @versao-desejada para instalar algo em alguma versão específica.

Você também consegue instalar dependências de maneira global no seu computador, passando a opção -g ou --global ao instalar uma dependência via terminal.

peerDependencies é outro ponto que vale a pena ser comentado e é bem utilizado quando você está criando algum plugin ou biblioteca reutilizável entre projetos. É uma categoria que serve para lidar com dependências compartilhadas entre projetos.

Sem nos alongar muito no assunto, vamos imaginar que você fez um projeto que depende de um pacote X na versão Y e alguém que instalou seu projeto também instalou o pacote X, só que na versão Z. Isso pode trazer alguns problemas.

É bem comum que esses problemas sejam resolvidos (após uma análise e identificação dessas dependências problemáticas) com essa categoria. O que ela faz basicamente é informar ao time que for utilizar o seu pacote que seu projeto precisa de alguma versão específica de outro pacote, mas deixa a instalação desse outro pacote específico à cargo das pessoas que forem desenvolver.

Se quiser, você pode ler um pouco mais sobre essa categoria de dependências em específio, caso tenha necessidade.


Scripts

Por último, mas não menos importante, temos a parte de scripts do nosso arquivo. Seguindo a criação dele, agora teremos uma configuração mais ou menos assim:

{
  "name": "@gabrieluizramos/utils",
  "description": "Alguns utilitários pessoais utilizados entre vários projetos",
  "keywords": [],
  "author": "Gabriel Ramos <gabriel.luiz.ramos@gmail.com> (https://gabrieluizramos.com.br)",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "url-do-github"
  },
  "bugs": {
    "url": "url-do-github"
  },
  "homepage": "url",
  "dependencies": {
    "package": "^0.1.1"
  },
  "devDependencies": {
    "testpackage": "~0.1.1"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

Se você criou esse arquivo com npm init -y muito provavelmente o script de test já vira preenchido para você.

O que os scripts nos permitem é realmente configurar rotinas e comandos a serem executados no nosso projeto. Você pode configurar comandos e rodar trechos de aplicações em JavaScript, Shell ou qualquer outra coisa que desejar.

É bem comum encontrar nos projetos algumas configurações de start e test, que são responsáveis por executarem ou testarem suas aplicações, respectivamente. Mas você pode criar qualquer script que for necessário.

Por padrão, existe um "ciclo" (se é que podemos chamar assim) de execução dos scripts. O que o npm faz é, automaticamente, ao executar algum script, procurar por outros dois scripts para executarem antes e depois da execução de qualquer script definido.

Por exemplo: ao executar npm install você consegue configurar um script para ser executado automaticamente antes desse próprio install (chamado preinstall) e outro pra executar automaticamente após a execução (chamado postinstall).

É algo bem útil para instalar algumas pacotes necessários globalmente ou realizar alguma consulta necessária antes de realizar a instalação do projeto. Utilizando o Gatsby como exemplo, você pode criar um script de preinstall, como:

{
  "name": "@gabrieluizramos/utils",
  "description": "Alguns utilitários pessoais utilizados entre vários projetos",
  "keywords": [],
  "author": "Gabriel Ramos <gabriel.luiz.ramos@gmail.com> (https://gabrieluizramos.com.br)",
  "license": "ISC",
  "repository": {
    "type": "git",
    "url": "url-do-github"
  },
  "bugs": {
    "url": "url-do-github"
  },
  "homepage": "url",
  "dependencies": {
    "package": "^0.1.1"
  },
  "devDependencies": {
    "testpackage": "~0.1.1"
  },
  "scripts": {
    "preinstall": "sh preinstall.sh"
  }
}

E no script preinstall.sh , "pular" a instalação do pacote gatsby-cli caso ele já esteja instalado globalmente na sua máquina, da seguinte maneira:

# arquivo preinstall.sh

# Mensagem que indica que o script está sendo executado
echo "Executando script de preinstall"

# Checa se o comando gatsby já existe
if [ -z $(which gatsby) ]; then
  # Se não existir, mostra a mensagem e inicia a instalação do pacote globalmente
  echo "O gatsby-cli não está instalado globalmente, instalando agora..."
  npm i -g gatsby-cli
else
  # Se já existir, mostra a mensagem que o pacote já foi instalado
  echo "O gatsby-cli já está instalado, prosseguindo com o restante da instalação..."
fi

Outro exemplo prático: aqui no blog eu deixei dois scripts prontos. Um para criação de rascunhos de novos posts e outro para publicação de rascunhos existentes. Também deixei um template para os rascunhos separado, assim eu não preciso criar toda a estrutura de arquivos/pastas a cada vez que for começar a escrever algo novo.

Dessa forma, pra criar um post com uma data e nome, basta executar no terminal:

npm run draft:create 2020-01-01.nome-do-novo-post

E para publicar esse mesmo post, basta executar:

npm run draft:publish 2020-01-01.nome-do-novo-post

Onde o valor de 2020-01-01.nome-do-novo-post é manipulado internamente por esses scripts e utilizado para substituir as informações no template que já está separado e criar os arquivos e pastas necessários. Bem mais prático, não acha?

Vale lembrar os scripts customizados (que não sejam os de start, test e alguns outros que estão na documentação oficial), precisam ser executados com o run, como os exemplos que citei acima: npm run script-customizado

Se quiser se aprofundar um pouco mais no que os scripts te oferecem, você pode ver também a documentação oficial no NPM sobre esses scripts e sobre o run scripts que permite que você execute scripts customizados.


Informações extras

Essa é só a parte mais fundamental e necessária de um package.json e está longe de conter tudo o que você pode utilizar.

Existem algumas outras opções e informações extras que podem te auxiliar ao escrever e configurar seu package.json (como, por exemplo, configurações sobre engine que definem em qual versão do Node/NPM seu pacote deve ser executado) e vários outros detalhes que você pode precisar futuramente.

Vale a pena ter sempre em mão o link da documentação oficial do NPM para eventuais e futuras consultas.

A documentação é extremamente completa e com certeza vai te ajudar muito! Assim como eu espero que esse post possa ter te ajudado a entender um pouco mais sobre o que é necessário para organizar e as imensas possibilidades que existem dentro de um package.json no seu projeto.