NPM Básico
Publicado em 07/10/2017
O Node.js foi criado em 2009 por um funcionário da empresa Joyent, Inc. chamado Ryan Dahl. Trata-se de um interpretador de código JavaScript baseado no V8 JavaScript Engine desenvolvido pelo Google e utilizado no seu famoso navegador Chrome.
Foi graças a ele que o uso do JavaScript, tanto no desenvolvimento backend, no lado do servidor, como no frontend, no lado do cliente, teve um vertiginoso crescimento dos últimos anos, culminando nesta verdadeira explosão que presenciamos agora em 2017.
Instalação
Há instaladores do Node.js para inúmeras plataformas, automatizando o processo de instalação. Juntamente com o Node, é instalado seu gerenciador de pacotes, o NPM (Node package manager), assunto objeto deste artigo.
Para verificar a instalação no seu sistema, use os comandos:
$ node -v
v8.5.0
$ npm -v
5.4.2
Neste caso, estou usando a última versão disponível na data em que escrevo este artigo, mas existe também uma versão LTS (Long Term Support), estando atualmente disponível, a versão 6.11.4.
O Node possui um REPL (Read–Eval–Print Loop) que é um programa simples e interativo que lê expressões ou trechos de programa, avalia (ou executa) e imprime o resultado. Podemos testá-lo:
$ node
> console.log('O Node está em execução.');
O Node está em execução.
undefined
> .help
.break Sometimes you get stuck, this gets you out
.clear Alias for .break
.editor Enter editor mode
.exit Exit the repl
.help Print this help message
.load Load JS from a file into the REPL session
.save Save all evaluated commands in this REPL session to a file
> .exit
Para descobrir onde o npm está instalado no sistema, usamos o comando:
$ which npm
/usr/bin/npm
No sistema operacional Linux que estou usando nestes exemplos, o diretório de instalação do npm é /usr
.
Módulos NPM
O NPM pode instalar os pacotes localmente ou globalmente. No primeiro caso, eles são instalados numa pasta denominada node_modules, no diretório de trabalho raiz do projeto.
Já no caso dos pacotes instalados globalmente, com a opção -g
ou --global
, os arquivos são copiados na pasta {prefixo}/lib/node_modules/
. O {prefixo}
é o diretório onde o npm foi instalado, ou seja, /usr
no meu caso, como visto acima.
Isso significa que as instalações globais devem ser feitas usando o comando sudo
para obter privilégios de administrador. Isso pode causar erros de permissão de acesso no caso de pacotes de terceiros, além da questão de segurança.
Podemos resolver isso alterando o proprietário do diretório {prefixo}/lib/node_modules/
, mas essa não é uma boa solução. O melhor é mudar a localização dos pacotes globais para o nosso diretório pessoal. Veremos como fazer isso a seguir.
Mudando o Diretório de Instalação dos Pacotes Globais
Entre os comandos:
$ cd ~ && mkdir .node_modules_global
$ npm config set prefix=$HOME/.node_modules_global
Com o primeiro comando entramos no nosso diretório pessoal de usuário (~/
) e criamos a pasta .node_modules_global
. O segundo, configura o prefixo usado pelo npm para apontar a pasta criada, criando o arquivo .npmrc
no nosso diretório de usuário. Podemos conferir a configuração com o comando npm config list
:
$ npm config list
; cli configs
metrics-registry = "https://registry.npmjs.org/"
scope = ""
user-agent = "npm/5.4.2 node/v8.5.0 linux x64"
; userconfig /home/gaeta/.npmrc
prefix = "/home/gaeta/.node_modules_global"
; node bin location = /usr/bin/node
; cwd = /home/gaeta
; HOME = /home/gaeta
; "npm config ls -l" to show all defaults.
Este é o conteúdo do arquivo .npmrc
:
$ cat .npmrc
prefix=/home/gaeta/.node_modules_global
O npm ainda está instalado no diretório /usr
de propriedade do usuário root
, vamos então instalar uma versão atualizada dele no novo diretório, de propriedade do usuário comum:
$ npm install npm --global
/home/gaeta/.node_modules_global/bin/npx -> /home/gaeta/.node_modules_global/lib/node_modules/npm/bin/npx-cli.js
/home/gaeta/.node_modules_global/bin/npm -> /home/gaeta/.node_modules_global/lib/node_modules/npm/bin/npm-cli.js
+ npm@5.4.2
added 410 packages in 9.774s
O último passo consiste em acrescentar o diretório .node_modules_global/bin
à variável de ambiente $PATH
. Para isso, acrescente a linha abaixo a um dos seguintes arquivos: .profile
, .bash_profile
ou .bashrc
e reinicie o terminal.
export PATH="$HOME/.node_modules_global/bin:$PATH"
Dessa forma o diretório .node_modules_global/bin
será encontrado primeiro e a versão correta do npm será executada. Pode conferir:
$ which npm
/home/gaeta/.node_modules_global/bin/npm
Instalação de Pacotes Globais
Para instalar pacotes globalmente, usamos a flag --global
, que pode ser abreviada como -g
(aliás, o comando install
também pode ser abreviado para i
). Vamos instalar o pacote gulp-cli
, que precisa ser instalado globalmente. O comando completo seria npm install --global gulp-cli
, mas podemos abreviar, conforme abaixo:
$ npm i -g gulp-cli
/home/gaeta/.node_modules_global/bin/gulp -> /home/gaeta/.node_modules_global/lib/node_modules/gulp-cli/bin/gulp.js
+ gulp-cli@1.4.0
added 139 packages in 13.043s
Observe que foram instalados 139 pacotes adicionais, que são as dependências do gulp-cli.
Listagem dos Pacotes Globais Instalados
Para listar todos os pacotes instalados globalmente, use o comando npm --global list
:
$ npm --global list
/home/gaeta/.node_modules_global/lib
├─┬ gulp-cli@1.4.0
│ ├── archy@1.0.0
│ ├─┬ chalk@1.1.3
│ │ ├── ansi-styles@2.2.1
│ │ ├── escape-string-regexp@1.0.5
│ │ ├─┬ has-ansi@2.0.0
│ │ │ └── ansi-regex@2.1.1
(...)
│ └── xtend@4.0.1
├── wrappy@1.0.2
└─┬ write-file-atomic@2.1.0
├── graceful-fs@4.1.11 deduped
├── imurmurhash@0.1.4 deduped
└── slide@1.1.6 deduped
A lista é bem grande e no exemplo acima eu cortei a maior parte dela. Para listar apenas os pacotes, sem suas dependências, acrescente a opção --depth=0
:
$ npm list --global --depth=0
/home/gaeta/.node_modules_global/lib
├── gulp-cli@1.4.0
└── npm@5.4.2
Instalação de Pacotes Locais
Vamos criar uma pasta para um projeto denominado, por exemplo, npm-basico
, e entrar neste diretório (o til ~
, nos sistemas baseados em UNIX, representa sempre o diretório local do usuário):
~ mkdir npm-basico
~ cd npm-basico
~/npm-basico
A primeira coisa a fazer é criar um arquivo package.json
onde serão armazenadas as dependências e outras configurações do projeto. Ao invés de fazer isso manualmente, podemos utilizar o comando npm init
. Serão feitas algumas perguntas sobre as configurações básicas, bastante intuitivas. Vejam o exemplo abaixo:
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.
See `npm help json` for definitive documentation on these fields
and exactly what they do.
Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.
Press ^C at any time to quit.
package name: (npm-basico)
version: (1.0.0)
description: Instalação e uso do NPM (Gerenciador de Pacotes do NodeJS)
entry point: (index.js)
test command:
git repository:
keywords:
author: J. A. Gaeta Mendes
license: (ISC) MIT
About to write to /home/gaeta/temp/npm-basico/package.json:
{
"name": "npm-basico",
"version": "1.0.0",
"description": "Instalação e uso do NPM (Gerenciador de Pacotes do NodeJS)",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "J. A. Gaeta Mendes",
"license": "MIT"
}
Is this ok? (yes) y
Se quiser criar o arquivo package.json
sem passar pelo questionário, use a opção --y
. Isso cria o arquivo com as opções padrão e você poderá editá-lo no seu editor preferido.
$ npm init --y
Wrote to /home/gaeta/temp/npm-basico/package.json:
{
"name": "npm-basico",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Vamos instalar o pacote gulp
localmente. Para registrar as dependências no arquivo package.json
, usamos a opção --save
se for uma dependência geral do projeto, ou --save-dev
, se a dependência for usada apenas durante o desenvolvimento. Neste exemplo, vamos usar esta última opção, uma vez que o gulp
é uma ferramenta de desenvolvimento:
$ npm install --save-dev gulp
npm WARN deprecated minimatch@2.0.10: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated minimatch@0.2.14: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue
npm WARN deprecated graceful-fs@1.2.3: graceful-fs v3.0.0 and before will fail on node releases >= v7.0. Please update to graceful-fs@^4.0.0 as soon as possible. Use 'npm ls graceful-fs' to find it in the tree.
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN npm-basico@1.0.0 No description
npm WARN npm-basico@1.0.0 No repository field.
+ gulp@3.9.1
added 186 packages in 24.143s
Não se preocupe com os avisos (WARN), que são normais e indicam apenas, neste caso, pacotes desatualizados e falta de alguns campos, mas isso não tem nenhum problema.
Observe, também, o aviso de que foi criado o arquivo package-lock.json
. Essa funcionalidade foi introduzida na versão 5 do npm e veio resolver muitas dores de cabeça dos desenvolvedores, decorrentes da atualização de versões incompatíveis com aquela usada no desenvolvimento. Veremos mais sobre isso adiante.
Vejam como ficou o arquivo package.json
, depois da instalação local do gulp
:
{
"name": "npm-basico",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^3.9.1"
}
}
Como podem observar, o gulp
foi acrescentado na seção devDependencies
no package.json
, como "gulp": "^3.9.1"
. O acento circunflexo (^
) na frente do número da versão indica que o npm irá buscar sempre a última versão disponível, respeitando apenas a dependência para a versão maior. Estamos nos referindo aqui ao versionamento semântico, que obedece a forma MAJOR.MINOR.PATCH. No caso deste exemplo, portanto, o npm buscaria a última versão disponível, abaixo da 4.0.0.
Como dissemos linhas atrás, essa atualização automática para versões MINOR pode criar incompatibilidades. O arquivo package-lock.json
, criado automaticamente a partir do npm v5, "trava", por assim dizer, nas versões instaladas durante o desenvolvimento. Assim, quando o projeto for reinstalado, serão baixadas as versões idênticas àquelas.
A principal razão de adotarmos o package.json
para controle das dependências de um projeto é a portabilidade. Dessa forma, quando clonamos um projeto do Github, por exemplo, basta executarmos o comando npm install
e todas as dependências necessárias serão instaladas num piscar de olhos (ou quase...).
Instalação de uma versão específica
Um pacote muito interessante é o underscore.js
. O undescore é uma pequena biblioteca com diversos códigos utilitários em JavaScript. Com apenas 4k, podemos usar recursos como map, select e invoke, além de uma engine de templating. Além disso, o underscore usa recursos nativos dos navegadores modernos, tirando proveito de funções forEach, indexOf, filter, etc.
No momento em que escrevo este artigo, a última versão disponível do underscore.js é a 1.8.3.
Digamos, contudo, que queiramos instalar a versão 1.8.2. Simples:
$ npm install --save underscore@1.8.2
npm WARN npm-basico@1.0.0 No description
npm WARN npm-basico@1.0.0 No repository field.
+ underscore@1.8.2
added 1 package in 2.854s
Vamos fazer um teste com o utilitário underscore.js
. Crie um arquivo chamado teste-underscore.js
e inclua as seguintes linhas:
const _ = require("underscore")
const resultado = _.map([1, 2, 3], function(num) {
return num * 3
})
console.log(resultado)
Execute o programa com o node.js:
$ node teste-underscore.js
[ 3, 6, 9 ]
Observem que instalamos o pacote underscore
com a opção --save
, o que o inclui na seção "dependencies" do package.json
, e não na seção "devDependencies", como ocorreu quando usamos a opção --save-dev
. Vejam também que foi instalada a versão que especificamos (1.8.2):
{
"name": "npm-basico",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^3.9.1"
},
"dependencies": {
"underscore": "^1.8.2"
}
}
Desinstalação de Pacotes
Vamos agora desinstalar o pacote underscore
:
$ npm uninstall underscore
npm WARN npm-basico@1.0.0 No description
npm WARN npm-basico@1.0.0 No repository field.
removed 1 package in 0.893s
Vamos conferir o package.json
:
{
"name": "npm-basico",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"gulp": "^3.9.1"
},
"dependencies": {}
}
Atualização de Pacotes
Para demonstrar a atualização de pacotes usando o npm, reinstalaremos a versão mais antiga do underscore
. Feito isso, vamos primeiramente verificar quais são os pacotes que estão desatualizados, com o comando npm outdated
:
$ npm outdated
Package Current Wanted Latest Location
underscore 1.8.2 1.8.3 1.8.3 npm-basico
Se nós deletarmos a pasta node_modules
e reinstalarmos os pacotes com o comando npm install
, a versão do underscore
reinstalada será a 1.8.2, em virtude da trava do arquivo package-lock.json
, conforme já vimos anteriormente.
Para atualizar para a versão 1.8.3, previsamos fazer isso explícitamente, com o comando npm update underscore
:
$ npm update underscore
npm WARN npm-basico@1.0.0 No description
npm WARN npm-basico@1.0.0 No repository field.
+ underscore@1.8.3
updated 1 package in 1.107s
$ npm list --depth=0
npm-basico@1.0.0 /home/gaeta/temp/npm-basico
├── gulp@3.9.1
└── underscore@1.8.3
Gerenciamento do Cache
Toda vez que um pacote é instalado, o npm guarda uma cópia no diretório ~/.npm
(onde ~
representa o diretório-raiz do usuário). Dessa forma, caso um daqueles pacotes venha ser solicitado numa outra instalação, o npm usa essa cópia local, sem necessidade de acessar a rede.
A partir da versão 5, o npm possui a opção npm cache verify
, que verifica a consistência do cache, limpa e compacta seu conteúdo:
$ npm cache verify
Cache verified and compressed (~/.npm/_cacache):
Content verified: 4411 (142646623 bytes)
Content garbage-collected: 1124 (31731644 bytes)
Index entries: 7119
Finished in 143.123s
Caso você queira mesmo limpar todo o cache, use o comando npm cache clean --force
.
Quando temos muitos projetos no nosso workspace, os node_modules
ocupam também muito espaço. O script a seguir permite limpar todos eles de uma vez:
$ find . -name "node_modules" -type d -exec rm -rf '{}' +
Aliases
Como já vimos, existem aliases para os comandos mais comuns do npm, que simplificam a digitação:
npm i <package>
-- instala um pacote localmentenpm i -g <package>
-- instala um pacote globalmentenpm un <package>
-- desinstala um pacote (-g
para global)npm up
-- atualiza os pacotesnpm t
-- executa testesnpm ls
-- lista os módulos instalados (depth=0
para listar só os pacotes)npm ll
ounpm la
-- lista detalhes dos pacotes (depth=0
para listar só os pacotes)
Podemos instalar vários pacotes de uma só vez, conforme a seguir:
$ npm i express momemt lodash mongoose body-parser webpack
Ajuda
Para acessar o sistema de ajuda do npm, basta digitar npm help
, para obter uma visão geral dos comandos. Para ajuda de um comando específico, digite npm -h <comando>
para uma explicação resumida, ou npm help <comando>
para uma explicação detalhada.
Definição de padrões para o npm init
Para não precisar responder aquele questionário toda vez que iniciamos um projeto com o comando npm init
, basta usar npm init -y
, como já vimos.
Podemos também personalizar algumas opções:
$ npm config set init.author.name <nome>
$ npm config set init.author.email <email>
Outros Comandos Úteis
Acessar a Homepage de um pacote:
npm home <pacote>
Acessar o repositório de um pacote no GitHub:
npm repo <pacote>
Acessar a documentação de um pacote:
npm docs <pacote>
Crédito: Baseado no artigo A Beginner’s Guide to npm — the Node Package Manager