NodeJs - alandrade21/docsCompartilhados GitHub Wiki

Nesta página:

Instalação

Instalar nodeJs

Dê preferência por utilizar o NVM, listado na próxima seção.

curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt install nodejs
sudo apt install build-essential

Testa a instalação

node -v
npm -v

Várias versões de Node na máquina

Informações retiradas dessa página.

Vamos utilizar o NVM.

Pacotes NPM em escopo global

Pode haver versão de Angular e de Typescript instalada globalmente. Essa instalação global faz com que os comandos ng ou tsc sejam reconhecidos em todo o operacional.

Para verificar se esse é o caso, simplesmente digite ng ou tsc no terminal. Se o comando for reconhecido, então essas ferramentas estão instaladas no escopo global.

Angular e Typescript são pacotes NPM. Se eles estão em escopo global é porque eles foram instalados com npm i -g <nome do pacote>.

Para verificar todos os pacotes instalados em escopo global, rode o comando npm list -g.

No linux, utilizando uma instalação global de NodeJs, os pacotes instalados em escopo global ficam na pasta /usr/lib/node_modules.

Desinstalando pacotes globais

Os pacotes de Angular e Typescript que estejam no escopo global devem ser removidos. Para isso utilize os comandos abaixo:

sudo npm un -g @angular/cli
sudo npm un -g typescript

Outra opção é simplesmente apagar as pastas destes pacotes de /usr/lib/node_modules.

Node em Escopo Global

Para verificar se o NodeJS está em escopo global, rode o comando node -v. Se o comando for reconhecido, ele responde qual é a versão instalada.

O comando which node vai responder onde o node está instalado. Você deve obter a resposta /usr/bin/node (ou qualquer outra pasta de sistema, fora do seu domínio de usuário).

Desinstalando a Versão Global do Node

sudo apt remove nodejs

Só o pacote do node deve aparecer como candidato à remoção.

Instalando o NVM

O NVM é um gerenciador/chaveador de versões do node, que utiliza a sua pasta de usuário ao invés da área do sistema.

Veja qual a versão mais recente no repositório github da ferramenta. Para instalar:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh | bash

Esse comando baixa o script de instalação e instala o NVM em ~/.nvm/. As versões diferentes do node serão instaladas nesse diretório tb.

Essa instalação acrescenta as linhas abaixo no arquivo ~/.bashrc:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

Logo após a instalação, pode ser necessário abrir um novo terminal para o NVM ser reconhecido.

Comandos NVM

  • Versão do nvm instalada:
nvm --version
  • Listar versões instaladas do node:
nvm ls
  • Listar versões disponíveis do node para instalação:
nvm ls-remote
  • Instalar uma versão do node:
nvm install vX.X.X
  • Instalar a versão mais recente do node:
nvm install node
  • Desinstalar uma versão do node:
nvm uninstall vX.X.X
  • Usar uma versão do node (vale para aquela seção de shell):
nvm use vX.X.X
  • Usar a versão mais recente do node:
nvm use node
  • Definir nome para uma versão do node:
nvm alias meunome vX.X.X
nvm use meunome
  • Remover um nome de versão do node:
nvm unalias meunome
  • Definir uma versão padrão do node:
nvm alias default vX.X.X
  • Definir a versão mais nova versão padrão:
nvm alias default node
  • Saber a versão atual:
nvm current

Novos Escopos Globais

Cada versão instalada pelo comando nvm install vX.X.X é colocada em um subdiretório da pasta ~/.nvm/versions/node/.

Assim, se instalamos nvm install v15.11.0, esta versão estará instalada em ~/.nvm/versions/node/v15.11.0/.

O escopo global desta versão estará em ~/.nvm/versions/node/v15.11.0/lib/node_modules/.

Definir a versão do node por projeto

Informações retiradas da página How to run multiple Node and Angular versions simultaneously.

Versões específicas do angular tem dependências de versões específicas de node e de typescript. Esta é a tabela de versões compatíveis).

Crie um arquivo .nvmrc na raiz do projeto e coloque dentro dele a versão do node. Ex: 12.4.0.

Todos os comandos node agora tem que ser rodados com nvm:

  • O que era rodado com node [arguments], agora precisa ser rodado com nvm run [arguments].
  • O que era rodado com npm run [nome do script] [arguments], agora precisa ser rodado com nvm exec npm run [nome do script] [arguments].

Configurar npm

Roda os comandos abaixo depois de instalar o npm.

npm set init.author.name 'André Andrade'
npm set init.author.email '[email protected]'
npm set init.author.url 'https://github.com/alandrade21'
npm adduser

Esse último comando vai pedir seu usuário e sua senha no repositório npm, e será utilizado na hora de publicar pacotes.

Verifica as configurações feitas com o comando

npm config ls

NPM

Node Package Manager.

O repositório de pacotes é o NPM registry.

Inicializando o Projeto

Na pasta raiz do projeto, no terminal, roda npm init. Ele vai pedir vários dados, vai criar o arquivo de entry point e o package.json.

Instalar Dependência

Dependências de desenvolvimento

npm i <pacote> -D

ou

npm install @types/node --save-dev

Dependências de RunTime

npm i <pacote> -S

Instalar pacote em escopo global:

npm i <pacote> -g

Instalar todas as dependências do projeto:

npm install

Atualizar as dependências de um projeto:

npm update

Números de Versão no package.json

Número de versão composto por Major.Minor.Patch.

Incrementa a patch quando algum bug for corrigido. Não tem quebra de funcionalidade para os clientes.

Incrementa a minor quando novas funcionalidades forem acrescentadas. Não tem quebra de funcionalidade para os clientes.

Incrementa a major quando grandes mudanças forem feitas e o código do cliente pode quebrar.

Essas regras podem ser encontradas no site da Semantic Versioning.

No package.json o número de versão de dependências começa com um caractere:

  • ~: Atualiza para versões mais novas que tenham trocado apenas o patch number.
  • ^: Atualiza para versões mais novas que tenham trocado ou o patch number ou minor number.

Pacotes interessantes

  • moment: facilita o trabalho e a formatação de datetime.
  • nodemon: monitor para o node verificando se algum dos arquivos do seu projeto mudou. Se mudou, ele reinicia o node. Então, ao invés de rodar o projeto com node app.js, roda com nodemon app.js. Pra isso instala esse cara no escopo global. Isso foi implementado no próprio node com o comando node --watch app.js.

Criação de componentes em TS

Crie um novo diretório de projeto para escrever o código do componente.

Entre nesse diretório e inicialize o projeto com

npm init -y

Gera o arquivo tsconfig.json com o comando tsc --init.

No tsconfig.json coloca a propriedade "declaration" : true. Isso vai gerar o arquivo de tipos .d.ts.

Escreve o componente, de preferência no arquivo index.ts.

Transpila o componente com tsc. O resultado vai aparecer na pasta ./dist.

Completa o arquivo package.json com as entradas:

"main": "dist/index.js",
"types" : "dist/index.d.ts"

Coloque seu namespace no nome do componente: @alandrade21/<nome-do-componente>.

Compartilhando o código do componente em desenv

Para evitar publicar um componente antes do término do seu desenvolvimento e ainda assim permitir seu uso em outros projetos, vamos linkar a pasta do componente no projeto.

Vai para a pasta do componente e roda o comando:

sudo npm link

Isso cria um link global para o componente em /usr/lib/node_modules/@alandrade21/<nome-do-componente>. Por isso a permissão de sudo.

Vai para a pasta do projeto que usará o componente e roda o comando:

npm link <nome-do-componente>

Publicar um pacote

Coloca a entrada files no package.json, da seguinte forma:

“files”: [“dist/**/*”]

Isso garante que apenas os arquivos necessários serão utilizados no empacotamento.

Loga no repositório npm com o comando:

npm login

Dentro do diretório do componente a ser publicado, publica o pacote com o comando:

npm publish

Desfazer um link antes do build de produção

Desfaça o link no projeto que consome o compenente.

npm uninstall --no-save <nome-do-componente>

Instale o pacote a partir do npm:

npm install

O componente já está no package.json, é só rodar o comando acima para usar a versão publicada.

Para apagar o link global, vai para o diretório do componente e roda:

npm uninstall

JS no Node

Funções providas pelo node

Podem ser consultadas na página de documentação do node, na seção API DOCS.

First-Class Functions

É a capacidade do javascript de passar funções como parâmetros, de colocá-las em arrays, etc. São usadas como vc usa qualquer outro tipo.

Isso é possível pq em JS funções são tipos especiais de objetos.

Function expression é quando vc atribui a definição de uma função a uma variável:

var greetMe = function(){
  console.log('Hi');
}

Pode ser rodada como greetMe().

Function expressions tb podem ser criadas assim:

(function() {
  ...
}());

Function expressions são executadas imediatamente (IIFE - Immediately Invoked Function Expressions). Isso garante que o que é criado no escopo desse tipo de função permanece nesse escopo.

Objetos

Objetos em js são containers de pares nome:valor separados por vírgla.

var person = {
  firstName: 'John',
  lastname: 'Doe',
  greet: function() {
    console.log('Hello, ' + this.firstName + ' ' + this.lastName);
  }
};

A sintaxe acima é uma das formas de se definir um objeto.

Acesso aos componentes do objeto podem ser feitos de diversas maneiras:

person.greet();

console.log(person['firstName']);

Constructor Functions

É uma função que quando chamada com new retorna um objeto vazio. Todos os objetos criados a partir de uma mesma costructor function compartilham o mesmo objeto prototype.

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

var john = new Person('John', 'Doe');

Alterações no prototype da constructor function são compartilhados por todos os objetos criados a partir dela:

Person.prototype.greet = function() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
}

De forma que john.greet() vai funcionar.

O prototype de um objeto pode ser acessado na propriedade __proto__.

Criando Objetos sem Constructor Function

var person = {
  firstName: '',
  lastName: '',
  greet: function() {
    return this.firstName + ' ' + this.lastName;
  }
}

var john = Object.create(person);
john.firstName = 'John';
john.lastName = 'Doe';

No exemplo acime, o objeto person é o prototype do objeto john.

Herança

É um encadeamento de prototypes.

var util = require('util');

function Person() {
  this.firstName = '';
  this.lastName = '';
}

Person.prototype.greet = function() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
}

function Policeman() {
  Person.call(this);
  this.badgeNumber = '';
}

util.inherits(Policeman, Person);

var officer = new Policeman();
officer.firstName = 'John';
officer.lastName = 'Doe';
officer.badgeNumber = '1234';
officer.greet();

A função util.inherits é provida pela API do Node e fa o cascateamento de prototypes.

No entanto, elementos definidos fora dos prototypes, como os atributos de Pessoa, não ficam visíveis a partir do Policeman. Para que isso aconteça é preciso rodar Person.call(this); na primeira linha da constructor function da "subclasse". Isso é similar a chamar super() em outras linguagens.

Usando ES6 Class Syntax ao invés de Constructor Functions

Na seção de constructor functions fizemos algo assim:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.greet = function() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
}

var john = new Person('John', 'Doe');

Agora em ES6 podemos escrever:

'use strict';

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  greet() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
  }
}

var john = new Person('John', 'Doe');

Ouso de 'use strict' é mandatário ao utilizar ES6.

Mas isso é só syntax suggar. Por baixo dos panos a classe é só uma constructor function, e os métodos da classe então sendo criados no prototype do objeto, e o encadeamento de prototypes para fazer herança continua existindo do mesmo jeito de antes.

Herança em ES6

Para mostrar como fazer herança usando constructor functions fizemos:

var util = require('util');

function Person() {
  this.firstName = '';
  this.lastName = '';
}

Person.prototype.greet = function() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
}

function Policeman() {
  Person.call(this);
  this.badgeNumber = '';
}

util.inherits(Policeman, Person);

var officer = new Policeman();
officer.firstName = 'John';
officer.lastName = 'Doe';
officer.badgeNumber = '1234';
officer.greet();

Em ES6 fica:

'use strict';

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  greet() {
  console.log('Hello, ' + this.firstName + ' ' + this.lastName);
  }
}

class Policeman extends Person {
  constructor(firstName, lastName, badgeNumber) {
    super(firstName, lastName)
    this.badgeNumber = badgeNumber;
  }

  var officer = new Policeman('John', 'Doe', '1234');
}

O super é obrigatório para que o encadeamento de prototypes pegue os elementos definidos nos construtores das super classes.

Módulos

Module é um bloco de código reutilizável cuja existência não impacta, acidentalmente, outros códigos.

Só javascript ES6 tem isso.

CommonsJS Modules

CommonsJS Modules é o padrão sobre como módulos devem ser estruturados no node.

Um módulo precisa expor aquilo que vai ser público.

greet.js:

var greet = function() {
  console.log('Hi!');
};

module.exports = greet;

app.js:

var greet = require('./greet'); // Se os arquivos estiverem no mesmo dir.
greet();

Qdo require roda, ele encapsula todo o arquivo em uma function expression e retorna a propriedade module.exports.

Se require não achar um arquivo .js ele procura por uma pasta com o mesmo nome, e dentro dela por um arquivo index.js.

Esse index.js terá requires e exports, como no exemplo abaixo:

var english = require('./english');
var spanish = require('./spanish');

module.exports = {
  english: english,
  spanish: spanish
};

No exemplo acima, existem dois arquivos english.js e spanish.js no mesmo diretório do index.js mostrado acima.

Se esses arquivos estiverem dentro de uma pasta greet, no mesmo nível do arquivo js que quiser consumir esse módulo, o require fica:

var greet = require('./greet');

greet.english();
greet.spanish();

Requires tb pode apontar para um json. Nesse caso um novo objeto é criado com base no json.

Outras formas de trabalhar com módulos:

greet.js:

module.exports.greet = function() {...};

app.js:

var greet = require('./greet');
greet.greet();

ou

var greet = require('./greet').greet;
greet();

Vários requires apontando para um mesmo arquivo retornam o mesmo objeto (qdo um objeto é retornado). Como se fosse um singleton.

Funções nativas podem ser requeridas sem um caminho, usando apenas o nome do arquivo que contém a api desejada, por exemplo var util = require('util');.

No package.json essa opção é identificada pelo "type": "commonsjs" ou usa a extensão .cjs. Não misturar estilos.

ECMAScript Modules

É o padrão de módulos criado pelo js.

Os conceitos são os mesmos, muda a sintaxe.

greet.js:

export function greet() {
  console.log('Hello!');
}

app.js:

import * as greetr from './greet';
greetr.greet();

No package.json essa opção é identificada pelo "type": "module" ou usa a extensão .mjs. Não misturar estilos.

Assincronia

Promise

É um padrão para lidar com eventos assíncronos e callbacks.

Promise é um valor futuro que não está disponível agora.

Assync e Await

Sugar syntax para Promises.

async function fazAlgumaCoisaAssincrona() {
  try {
    let retorno =  await servicoAssincrono(...);
    console.log(retorno);
  } catch (err) {
    console.error('Error: ', err);
  }
}

async informa que essa função será pausada esperando por um retorno assíncrono. O ponto exato de pausa é identificado por await. Durante a pausa o fluxo de execução do js continua. Após o retorno assíncrono, a função pausada é retomada.

No caso de precisar esperar por várias funções assíncronas serem concluídas:

await Promise.all([
  fun1(),
  func2(),
  ...
  funcn()
]);

Error: ENOSPC: System limit for number of file watchers reached

Aumentar o limite de user watches no sistema operacional.

Verificar o limite atual:

cat /proc/sys/fs/inotify/max_user_watches

Ajustar o novo limite no arquivo /etc/sysctl.conf:

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p

O comando acima acrescenta a linha fs.inotify.max_user_watches=524288 ao arquivo /etc/sysctl.conf e em seguida pede ao kernel para recarregar as novas configurações.

Verificar o novo limite:

cat /proc/sys/fs/inotify/max_user_watches
⚠️ **GitHub.com Fallback** ⚠️