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

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 localmente
  • npm i -g <package> -- instala um pacote globalmente
  • npm un <package> -- desinstala um pacote (-g para global)
  • npm up -- atualiza os pacotes
  • npm t -- executa testes
  • npm ls -- lista os módulos instalados (depth=0 para listar só os pacotes)
  • npm ll ou npm 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

Compartilhe esta postagem