O npm (Node Package Manager) é o gerenciador de pacotes padrão do Node.js e uma das ferramentas mais importantes no ecossistema JavaScript. Ele permite que você instale, compartilhe e gerencie bibliotecas e ferramentas de código aberto, facilitando a reutilização de código e a colaboração em projetos. Com o npm, você pode rapidamente adicionar funcionalidades ao seu projeto sem precisar escrever tudo do zero.

Nesta aula, vamos mergulhar nos conceitos fundamentais do npm: o arquivo package.json, que é o coração de qualquer projeto Node.js; como instalar dependências de forma correta; como usar scripts para automatizar tarefas; e o versionamento semântico, que garante que as atualizações de pacotes sejam previsíveis. Ao final, você terá uma base sólida para trabalhar com qualquer projeto JavaScript moderno.

package.json

O arquivo package.json é o manifesto do seu projeto. Ele contém metadados como nome, versão, descrição, ponto de entrada, scripts, e, principalmente, a lista de dependências do projeto. Sem ele, o npm não sabe quais pacotes instalar ou como executar comandos personalizados. Todo projeto Node.js deve ter um package.json na raiz.

Você pode criar um package.json manualmente ou usando o comando npm init. O comando interativo faz perguntas e gera o arquivo com os campos básicos. Para um processo mais rápido, use npm init -y para aceitar os padrões.

Exemplo de um package.json típico:

{
  "name": "meu-projeto",
  "version": "1.0.0",
  "description": "Um projeto de exemplo para aprender npm",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "test": "jest"
  },
  "keywords": ["npm", "javascript", "exemplo"],
  "author": "Seu Nome",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2"
  },
  "devDependencies": {
    "jest": "^29.5.0"
  }
}

Os campos mais importantes são dependencies e devDependencies. O primeiro lista os pacotes necessários para o projeto rodar em produção; o segundo, apenas para desenvolvimento (como testes e ferramentas de build). O npm usa essas informações para instalar as versões corretas quando outra pessoa clona o projeto e roda npm install.

Instalando dependências

Para adicionar um pacote ao seu projeto, use o comando npm install <pacote>. Por padrão, isso adiciona o pacote à lista de dependencies e o instala na pasta node_modules. Se o pacote for usado apenas em desenvolvimento, use a flag --save-dev (ou -D) para adicioná-lo a devDependencies.

Exemplos:

# Instala uma dependência de produção
npm install express

# Instala uma dependência de desenvolvimento
npm install --save-dev jest

# Instala uma versão específica
npm install lodash@4.17.21

# Instala todas as dependências listadas no package.json
npm install

A pasta node_modules é criada automaticamente e contém todos os pacotes instalados. Ela não deve ser versionada no Git; em vez disso, usamos o arquivo package-lock.json (ou npm-shrinkwrap.json) para travar as versões exatas. Isso garante que todos os desenvolvedores tenham as mesmas versões, evitando surpresas.

Quando você clona um projeto, basta rodar npm install para instalar todas as dependências. O npm lê o package-lock.json e instala exatamente as versões ali registradas.

Scripts

Os scripts são comandos personalizados definidos no campo scripts do package.json. Eles permitem automatizar tarefas comuns, como iniciar o servidor, rodar testes, fazer build, etc. Você pode executá-los com npm run <nome-do-script> (ou npm <nome> para scripts especiais como start e test).

Exemplo de scripts:

"scripts": {
  "start": "node index.js",
  "dev": "nodemon index.js",
  "test": "jest",
  "build": "webpack --mode production",
  "lint": "eslint ."
}

Para executar, use:

npm run start
npm run dev
npm test
npm run build
npm run lint

Os scripts podem ser combinados com o operador && (executa em sequência) ou & (em paralelo). Por exemplo: "build": "npm run lint && webpack --mode production". Além disso, você pode usar variáveis de ambiente e argumentos, mas isso é mais avançado.

Versionamento semântico

O versionamento semântico (SemVer) é um padrão para atribuir versões a pacotes, no formato MAJOR.MINOR.PATCH. Cada número tem um significado específico:

  • MAJOR: incrementado quando há mudanças incompatíveis com versões anteriores (quebra de API).
  • MINOR: incrementado quando são adicionadas funcionalidades compatíveis com versões anteriores.
  • PATCH: incrementado quando são feitas correções de bugs compatíveis com versões anteriores.

No package.json, as versões das dependências podem ser especificadas com intervalos usando símbolos como ^, ~, >=, etc. O símbolo ^ permite atualizações que não mudam o número mais à esquerda não-zero. Por exemplo, ^4.18.2 aceita qualquer versão 4.x.x maior ou igual a 4.18.2, mas não 5.x.x. O ~ permite apenas atualizações de patch (ex.: ~4.18.2 aceita 4.18.x).

O arquivo package-lock.json registra as versões exatas instaladas, garantindo que todos os ambientes usem as mesmas versões. É importante entender essas regras para evitar que atualizações quebrem seu projeto.

Boas práticas

Algumas boas práticas ao trabalhar com npm:

  • Mantenha o package.json e o package-lock.json versionados no Git, mas ignore node_modules.
  • Use npm update para atualizar dependências dentro dos intervalos permitidos, e npm outdated para verificar versões desatualizadas.
  • Evite instalar pacotes globalmente, a menos que seja necessário (como ferramentas CLI). Prefira usar npx para executar pacotes sem instalá-los.
  • Documente os scripts no README.md para que outros desenvolvedores saibam como rodar o projeto.

Referências

Exercícios

  1. Crie um package.json manualmente para um projeto chamado exercicio-npm, com versão 1.0.0, e adicione uma dependência lodash na versão ^4.17.21.

    ✓ Resposta: O arquivo deve conter:
    {
      "name": "exercicio-npm",
      "version": "1.0.0",
      "dependencies": {
        "lodash": "^4.17.21"
      }
    }
  2. Explique a diferença entre dependencies e devDependencies e dê um exemplo de cada tipo.

    ✓ Resposta: dependencies são pacotes necessários para o funcionamento em produção (ex.: express), enquanto devDependencies são usados apenas em desenvolvimento (ex.: jest).
  3. Qual comando você usaria para instalar o pacote axios como dependência de produção? E para instalar o eslint como dependência de desenvolvimento?

    ✓ Resposta: Para produção: npm install axios. Para desenvolvimento: npm install --save-dev eslint.
  4. Escreva um script chamado start que execute node server.js e um script dev que execute nodemon server.js.

    ✓ Resposta:
    "scripts": {
      "start": "node server.js",
      "dev": "nodemon server.js"
    }
  5. O que significa a versão ^2.3.1 em uma dependência? Quais atualizações são permitidas?

    ✓ Resposta: O ^ permite atualizações que não mudam o número mais à esquerda não-zero. Para ^2.3.1, são permitidas versões 2.x.x maiores ou iguais a 2.3.1, mas não 3.x.x.