<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Gustavo Goulart Baptista - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Aprenda a codificar - de graça. Tutoriais de programação em Python, JavaScript, Linux e muito mais. ]]>
        </description>
        <link>https://www.freecodecamp.org/portuguese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Gustavo Goulart Baptista - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/portuguese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 17 May 2026 04:15:28 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/portuguese/news/author/gustavo/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Introdução a estruturas de dados: árvore binária de busca ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por: Kevin Turney Como combinar a eficiência de inserção de uma lista vinculada e a busca rápida de um array ordenado.  O que é uma árvore binária de busca? Vamos começar com a terminologia básica para que possamos falar a mesma língua e investigar conceitos relativos. Primeiramente, quais ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/introducao-a-estruturas-de-dados-arvore-binaria-de-busca/</link>
                <guid isPermaLink="false">6511866586ff8703fbd87d53</guid>
                
                    <category>
                        <![CDATA[ Programação ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Tue, 11 Jun 2024 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2024/05/0_gYtXwdbgInK7hI-u.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/data-structures-101-binary-search-tree-398267b6bff0/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Data Structures 101: Binary Search Tree</a>
      </p><p>Escrito por: Kevin Turney</p><h4 id="como-combinar-a-efici-ncia-de-inser-o-de-uma-lista-vinculada-e-a-busca-r-pida-de-um-array-ordenado-"><strong>Como combinar a eficiência de inserção de uma lista vinculada e a busca rápida de um array ordenado. </strong></h4><h3 id="o-que-uma-rvore-bin-ria-de-busca"><strong><strong>O que é uma </strong>á<strong>rvore </strong>b<strong>inária de </strong>b<strong>usca<strong>?</strong></strong></strong></h3><p>Vamos começar com a terminologia básica para que possamos falar a mesma língua e investigar conceitos relativos. Primeiramente, quais princípios definem a árvore binária de busca (em inglês, <em>Binary Search Tree ou BST)</em>?</p><blockquote>Observação: a partir daqui, usarei somente "BST" para facilitar.</blockquote><p>Uma BST é considerada uma estrutura de dados feita a partir de <strong>nós </strong>(em inglês <em>nodes</em>), assim como as listas encadeadas (em inglês, <em>linked lists –</em><strong> </strong><a href="https://medium.freecodecamp.org/data-structures-101-linked-lists-254c82cf5883">leia mais</a> no artigo em inglês). Esses nós podem ser nulos ou ter referências (encadear) para outros nós. Esses "outros" nós são nós filhos, chamados de nó esquerdo e nó direito. Nós tem <strong>valores </strong>e esses valores determinam quando eles serão alocados dentro da BST.</p><p>Da mesma maneira que ocorre com uma lista vinculada, cada nó é referenciado <strong>somente </strong>por um outro nó, seu próprio pai, exceto o nó originário ou raiz. Podemos dizer, então, que cada nó em uma BST é uma BST própria, pois ao seguirmos a árvore, logo abaixo, nós alcançamos outro nó que tem outros dois filhos, um esquerdo e um direito. Independentemente de onde formos, esse nó vai ter um nó direito e um esquerdo como filhos e assim por diante.</p><p>1. O nó esquerdo é sempre menor que seu pai.</p><p>2. O nó direito é sempre maior que seu pai.</p><p>3. Uma BST é considerada balanceada se cada nível da árvore está completamente preenchido com exceção do último nível. Nele, a árvore é preenchida da esquerda pra direita.</p><p>4. Uma BST perfeita é aquela que é tanto cheia como completa (todos os nós filhos estão no mesmo nível e cada nó tem um nó esquerdo e um direito)</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2024/05/2rTqYlcrnWtICedt131tDft0CmkzZaViExJX.jpg" class="kg-image" alt="2rTqYlcrnWtICedt131tDft0CmkzZaViExJX" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2024/05/2rTqYlcrnWtICedt131tDft0CmkzZaViExJX.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2024/05/2rTqYlcrnWtICedt131tDft0CmkzZaViExJX.jpg 800w" sizes="(min-width: 720px) 720px" width="800" height="970" loading="lazy"></figure><h3 id="aplica-es-teis-da-bst"><strong>Aplicações úteis da BST</strong></h3><p>Quais são os exemplos reais das BSTs? Árvores são comumente usadas em buscas, lógicas de jogos, tarefas de preenchimento automático e gráficos.</p><p>Velocidade. Como mencionado anteriormente, a BST é uma estrutura ordenada de dados. Após a inserção, os nós são colocados de maneira ordenada. Essa ordem herdada faz as buscas serem mais rápidas. Similarmente a busca binária (com um <em>array </em>que é ordenado), cortamos pela metade o número de dados para ordenar a cada iteração. Por exemplo, suponha que estamos buscando por um nó com um pequeno valor. A cada iteração, continuamos movendo à esquerda em direção ao nó buscado. Isso elimina a metade dos valores maiores automaticamente! </p><p>Assim como um <em>array</em>, o dado é guardado por referência. Ao adicionar para a estrutura de dados, criamos um grupo na memória e o conectamos a ele. Fazer isso é mais rápido que criar um <em>array </em>com mais espaços e só então inserir o dado do menor <em>array</em> para o novo e maior.</p><p>Para resumir, inserção, exclusão e busca são habilidades de destaque para as BSTs.</p><p>Agora que entendemos os princípios, os benefícios e os componentes básicos de uma BST, vamos implementar uma em javascript.</p><p>A API para BST contém o seguinte: <strong><strong>Insert, Contains, Get Min, Get Max, Remove Node, Check if Full, Is Balanced</strong></strong> e os tipos de Busca — <strong><strong>Depth First (preOrder, inOrder, postOrder), Breadth First Search</strong></strong> e, por fim, <strong><strong>Get Height</strong></strong>. Essa é uma API bem grande, vamos olhar uma seção de cada vez. </p><h3 id="implementa-o"><strong><strong><strong>Implementa</strong></strong>ção</strong></h3><p><strong>O <strong>construtor</strong></strong></p><p>A BST é feita de nós – e cada nó tem um valor.</p><pre><code>function Node(value){  
	this.value = value;
    this.left = null;
    this.right = null;
}</code></pre><p>O construtor da BST é criado a partir de um nó raiz.</p><pre><code>function BinarySearchTree() {
	this.root = null;
}</code></pre><pre><code>let bst = new BST();
let node = new Node();</code></pre><pre><code>console.log(node, bst); // Node { value: undefined, left: null, right: null } BST { root: null }</code></pre><p>Até agora, tudo vai bem.</p><h3 id="inser-o-insert-"><strong>Inserção (Insert)</strong></h3><pre><code>BinarySearchTree.prototype.insert = function(value){
	let node = new Node(value);
	if(!this.root) this.root = node;
	else{    
    	let current = this.root;
        while(!!current){
        	if(node.value &lt; current.value){
           		if(!current.left){
               		current.left = node;
                   	break;
               	}
        	   	current = current.left;
            }
            else if(node.value &gt; current.value){
            	if(!current.right){
                	current.right = node;
                   	break;
               	}
                current = current.right;
            } 
            else {
            	break;
            }
     	}
     }
     return this;
};</code></pre><pre><code>let bst = new BST();
bst.insert(25); // BST { root: Node { value: 25, left: null, right: null } }</code></pre><p>Vamos adicionar mais valores. </p><pre><code>bst.insert(40).insert(20).insert(9).insert(32).insert(15).insert(8).insert(27);</code></pre><pre><code>BST { root:  Node { value: 25, left: Node { value: 20, left: [Object], right: null }, right: Node { value: 40, left: [Object], right: null } } }</code></pre><p>Para uma visualização melhor, basta <a href="http://btv.melezinek.cz/binary-search-tree.html" rel="noopener">só clicar aqui</a>!</p><p>Vamos decifrar isso.</p><ol><li>Primeiramente, passamos um valor e criamos um nó</li><li>Observar se tem um nó raiz. Senão, configurar esse nó recém-criado para apontar para o nó raiz</li><li>Se existir um nó raiz, criamos uma variável declarada como "current" e configuramos esse valor para o nó raiz</li><li>Se o nó for recém-criado, node.value, for menor que o nó raiz, movemos o nó para a esquerda</li><li>Continuamos comparando esse node.value para os valores da esquerda.</li><li>Se o valor for bem menor e alcançarmos um ponto onde não existirem mais nós esquerdos, colocaremos esse item aqui.</li><li>Se o node.value é maior, repetiremos os mesmos passos acima exceto que em direção à direita.</li><li>Precisamos inserir um <em>break, </em>porque não há um contador de passos para terminar o laço.</li></ol><h3 id="cont-m-contains-"><strong>Contém (contains)</strong></h3><p>Essa abordagem é bem direta.</p><pre><code>BinarySearchTree.prototype.contains = function(value){
	let current = this.root;
    while(current){
    	if(value === current.value) return true;
        if(value &lt; current.value) current = current.left;
        if(value &gt; current.value) current = current.right;
    }
    return false;
};</code></pre><h3 id="obter-o-m-nimo-e-o-m-ximo-get-min-e-get-max-"><strong>Obter o mínimo e o máximo (Get Min e Get Max).</strong></h3><p>Siga para a esquerda para achar o menor valor e para a direita para o maior valor.</p><pre><code>BinarySearchTree.prototype.getMin = function(node){
	if(!node) node = this.root;
    while(node.left) {
    	node = node.left;
    }
    return node.value
};</code></pre><pre><code>BinarySearchTree.prototype.getMax = function(node){
	if(!node) node = this.root;
    while(node.right) {
    	node = node.right;
    }
    return node.value;
};</code></pre><h3 id="remo-o-removal-"><strong>Remoção (removal)</strong></h3><p>Remover um nó é uma operação mais complicada, pois os nós tem que ser reordenados para que as propriedades da BST sejam mantidas. Existe um caso onde um nó tem apenas um filho e um caso onde o nó tem tanto o filho da direita como da esquerda. Usaremos a uma função para nos ajudar a fazer o trabalho pesado. </p><pre><code>BinarySearchTree.prototype.removeNode = function(node, value){
	if(!node){
    	return null;
    }
    if(value === node.value){ // sem filhos
    if(!node.left &amp;&amp; !node.right) return null; // um filho e é o da direita
    if(!node.left) node.right;// um filho e é o da esquerda
    if(!node.right) node.left;  // dois filhos
    const temp = this.getMin(node.right);
    node.value = temp;
    node.right = this.removeNode(node.right, temp);
    return node;
    } else if(value &lt; node.value) {
    	node.left = this.removeNode(node.left, value);
        return node;
   } else  {
      	node.right = this.removeNode(node.right, value);
        return node;
   }
};</code></pre><pre><code>BinarySearchTree.prototype.remove = function(value){
	this.root = this.removeNode(this.root, value);
};</code></pre><p>Funciona assim…</p><p>Diferente de <em>deleteMin </em>e <em>deleteMax</em>, onde apenas cruzamos todo o caminho para a direita ou esquerda e pegamos o último valor, aqui, temos que retirar o nó e substituí-lo por algo. Essa solução foi desenvolvida em 1962 por T. Hibbard. Contamos com o caso onde podemos excluir um nó filho ou nenhum, esse é o caso mais fácil. Sem filhos, sem problemas. Se o filho estiver presente é só movê-lo para cima.</p><p>Entretanto, quando temos um nó para ser removido que tem dois filhos, qual deles fará a substituição? Certamente, não podemos mover o filho maior para baixo, então o que fazemos é trocá-lo pelo seu sucessor. Temos que buscar o menor filho à direita que é maior que o filho esquerdo.</p><ol><li>Crie uma variável temporária e guarde o menor nó na sua direita. Isso cumprirá o requisito de que a propriedade que tem o valor da esquerda, ainda assim, é menor e os valores da esquerda que ainda são maiores;</li><li>Reinicialize o valor do nó essa variável temporária; </li><li>Exclua o nó direito;</li><li>Compare os valores na esquerda e na direita e determine qual será o valor atribuído.</li></ol><p>Acho que uma imagem ilustrará isso melhor:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2024/05/cEcyXZpZvRln6p7jzJq08lOJsORH6yA7Rd0T.jpg" class="kg-image" alt="cEcyXZpZvRln6p7jzJq08lOJsORH6yA7Rd0T" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2024/05/cEcyXZpZvRln6p7jzJq08lOJsORH6yA7Rd0T.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2024/05/cEcyXZpZvRln6p7jzJq08lOJsORH6yA7Rd0T.jpg 800w" sizes="(min-width: 720px) 720px" width="800" height="883" loading="lazy"></figure><h3 id="busca-search-"><strong>Busca (search)</strong></h3><p>Existem dois tipo de busca: <em>Depth First</em> e <em>Breadth First</em>. <em>Breadth First</em> (busca por largura, em português) é simplesmente quando você para a cada nível ao descer pela árvore. Seria algo parecido com isso: começamos na raiz, então filho esquerdo e depois filho direito. Passamos para o próximo nível, filhos esquerdos e filhos direitos. Pense nisso como se estivesse se movendo horizontalmente. Empregamos, até diria que simulamos, uma fila para ajudar a ordenar o processo. Passamos a função, porque, muitas vezes, queremos operar em um valor.</p><pre><code>BinarySearchTree.prototype.traverseBreadthFirst = function(fn) {
	let queue = [];
    queue.push(this.root);
    while(!!queue.length) {
    	let node = queue.shift();
        fn(node);
        node.left &amp;&amp; queue.push(node.left);
        node.right &amp;&amp; queue.push(node.right);
    }
}</code></pre><p>A busca<em> Depth First</em> (busca por profundidade, em português) envolve seguir para baixo da BST de uma maneira específica, ou seja: preOrder, inOrder, ou postOrder (pré-ordem, na ordem ou pós-ordem, respectivamente). Explicarei as diferenças em breve. </p><p>Em termos de código, temos uma função básica, traverseDepthFirst, e passamos uma função e um método. Outra vez, a função implica que queremos fazer algo com o valor no percurso, enquanto o método é o tipo de busca que estamos tentando realizar. Na traverseDepthFirst, temos uma alternativa, a busca preOrder, pré-selecionada.</p><p>Agora, qual a diferença entre elas? Primeiro, enviamos a função inOrder. Deveria ser autoexplicativo, mas não é. Estamos querendo dizer em ordem de inserção, em ordem do maior para o menor, ou do menor para o maior? Eu quero apenas que você tenha isso em mente de antemão. Nesse caso, sim, quer dizer do menor para o maior. </p><p><strong><strong>preOrder</strong></strong> pode ser pensado como <strong><strong>Pa</strong>i<strong>, </strong>Filho Esquerdo, Filho Direito. </strong></p><p><strong><strong>postOrder</strong></strong>, por outro lado, seria como <strong>Filho Esquerdo, Filho Direito, Pai.</strong></p><pre><code>BinarySearchTree.prototype.traverseDFS = function(fn, method){
	let current = this.root;
    if(!!method) this[method](current, fn);
    else this._preOrder(current, fn);
};</code></pre><pre><code>BinarySearchTree.prototype._inOrder = function(node, fn){
	if(!!node){
    	this._inOrder(node.left, fn);
	    if(!!fn) fn(node);
    	this._inOrder(node.right, fn);
    }
};</code></pre><pre><code>BinarySearchTree.prototype._preOrder = function(node, fn){
	if(node){ 
    	if(fn) fn(node);
    	this._preOrder(node.left, fn);
    	this._preOrder(node.right, fn);
    }
};</code></pre><pre><code>BinarySearchTree.prototype._postOrder = function(node, fn){
	if(!!node){ 
    		this._postOrder(node.left, fn);
    		this._postOrder(node.right, fn);
        	if(!!fn) fn(node);
    }
};</code></pre><h3 id="checar-se-a-bst-est-cheia"><strong>Checar se a <strong><strong>BST </strong></strong>está cheia</strong></h3><p>Lembre-se do que comentamos antes: uma BST está cheia se cada nó existente na árvore tiver ou Zero ou dois filhos.</p><pre><code>// uma BST está cheia se cada nó tiver ou zero ou dois filhos (nenhum nó pode ter apenas um filho)</code></pre><pre><code>BinarySearchTree.prototype.checkIfFull = function(fn){
	let result = true;
    this.traverseBFS = (node) =&gt; {
    	if(!node.left &amp;&amp; !node.right) result = false;
        else if(node.left &amp;&amp; !node.right) result = false;
    }
    return result;
};</code></pre><h3 id="obter-a-altura-da-bst"><strong>Obter a altura da BST</strong></h3><p>O que significa obter a altura de uma árvore? Por que é importante? Aqui é onde a <strong>Complexidade de Tempo</strong> (também conhecida como Big O) aparece. Operações básicas são proporcionais ao tamanho da árvore. Então, se lembrarmos do que foi dito antes, ao buscar por um valor específico, o número de operações que temos a fazer é reduzido a metade a cada etapa.</p><p>Isso significa que temos um pão e vamos cortá-lo a metade, depois dividir essa metade em outros dois pedaços e continuamos fazendo isso até termos exatamente o pedaço de pão que queremos.</p><p>Em ciência da computação, chamamos isso de O(log n). Começamos com algum tipo de tamanho de entrada e, com o tempo, o tamanho fica melhor (mais achatado). Uma busca linear direta é denotada como O(n). Conforme o tamanho da entrada aumenta, também aumenta o tempo para executar as operações. O(n), conceitualmente, é uma linha de 45° começando da origem zero em um gráfico se movendo para a direita. A escala horizontal representa o tamanho de uma entrada e a escala vertical representa o tempo levado para completá-la.</p><p>Tempo constante é O(1). Não importa o tamanho da entrada, seja grande ou pequena, a operação leva exatamente o mesmo tempo. Por exemplo, push() e pop() de um array possuem tempo constante, assim como obter o valor em uma HashTable.</p><p>Eu vou explicar mais sobre isso em um artigo futuro, mas já quis adiantar um pouco as coisas agora.</p><p><strong>De volta à altura.</strong></p><p>Temos uma função recursiva. Nosso caso base é: <strong>"se não temos um nó, então começamos em <strong>this.root</strong>"<strong><em><em>.</em></em></strong></strong> Isso implica que podemos começar em valores menores na árvore e obter árvores com subalturas.</p><p>Então se passamos em this.root para começar, recursivamente movemos para baixo e adicionamos funções que chamarão a pilha de execução (em inglês <em>execution stack</em>). Quando alcançamos o fundo, a pilha é preenchida e, então, a chamada é executada e comparamos as alturas da esquerda e da direita e incrementamos um.</p><pre><code>BinarySearchTree.prototype._getHeights = function(node){
	if(!node) return -1;
    let left = this._getHeights(node.left);
    let right = this._getHeights(node.right);
    return Math.max(left, right) + 1;
};</code></pre><pre><code>BinarySearchTree.prototype.getHeight = function(node){
	if(!node) node = this.root;
    return this._getHeights(node);
};</code></pre><h3 id="por-ltimo-is-balanced-est-balanceada-"><strong>Por último, Is Balanced (está balanceada)</strong></h3><p>O que estamos fazendo aqui é observar se a árvore está preenchida em cada nível, e se &nbsp;no último nível ela está preenchida da esquerda para a direita.</p><pre><code>BinarySearchTree.prototype._isBalanced = function(node){
	if(!node) return true;
    let heightLeft = this._getHeights(node.left);
    let heightRight = this._getHeights(node.right);
    let diff = Math.abs(heightLeft — heightRight);
    if(diff &gt; 1) return false;
    else return this._isBalanced(node.left) &amp;&amp;    this._isBalanced(node.right);
};</code></pre><pre><code>BinarySearchTree.prototype.isBalanced = function(node){
	if(!node) node = this.root;
    return this._isBalanced(node);
};</code></pre><h3 id="imprimir-a-rvore-print-"><strong>Imprimir a árvore (print)</strong></h3><p>Use isso para visualizar todos os métodos que você vê, especialmente travessias <em>depth first</em> e <em>breadth first</em>.</p><pre><code>BinarySearchTree.prototype.print = function() {
	if(!this.root) {
    	return console.log('Nó raiz não encontrado');
    }
    let newline = new Node('|');
    let queue = [this.root, newline];
    let string = ''; 
    while(queue.length) {
    	let node = queue.shift();
        string += node.value.toString() + ' ';
        if(node === newline &amp;&amp; queue.length) queue.push(newline);
        if(node.left) queue.push(node.left);
        if(node.right) queue.push(node.right);
     }
     console.log(string.slice(0, -2).trim());
};</code></pre><p><strong>Nosso amigo, o c<strong>onsole.log! </strong>Sinta-se livre para brincar com ele e experimentar. </strong></p><pre><code>const binarySearchTree = new BinarySearchTree();
binarySearchTree.insert(5);
binarySearchTree.insert(3);</code></pre><pre><code>binarySearchTree.insert(7);
binarySearchTree.insert(2);
binarySearchTree.insert(4);
binarySearchTree.insert(4);
binarySearchTree.insert(6);
binarySearchTree.insert(8);
binarySearchTree.print(); // =&gt; 5 | 3 7 | 2 4 6 8</code></pre><pre><code>binarySearchTree.contains(4);</code></pre><pre><code>//binarySearchTree.printByLevel(); // =&gt; 5 \n 3 7 \n 2 4 6 8
console.log('--- DFS inOrder');</code></pre><pre><code>binarySearchTree.traverseDFS(function(node) {
	console.log(node.value);
}, '_inOrder'); // =&gt; 2 3 4 5 6 7 8</code></pre><pre><code>console.log('--- DFS preOrder');</code></pre><pre><code>binarySearchTree.traverseDFS(function(node) {
	console.log(node.value);
}, '_preOrder'); // =&gt; 5 3 2 4 7 6 8</code></pre><pre><code>console.log('--- DFS postOrder');</code></pre><pre><code>binarySearchTree.traverseDFS(function(node) {
	console.log(node.value);
}, '_postOrder'); // =&gt; 2 4 3 6 8 7 5</code></pre><pre><code>console.log('--- BFS');</code></pre><pre><code>binarySearchTree.traverseBFS(function(node) {
	console.log(node.value);
}); // =&gt; 5 3 7 2 4 6 8</code></pre><pre><code>console.log('min é 2:', binarySearchTree.getMin()); // =&gt; 2</code></pre><pre><code>console.log('máx é 8:', binarySearchTree.getMax()); // =&gt; 8</code></pre><pre><code>console.log('árvore contém 3 é true:', binarySearchTree.contains(3)); // =&gt; true</code></pre><pre><code>console.log('árvore contém 9 é false:', binarySearchTree.contains(9)); // =&gt; false</code></pre><pre><code>// console.log('altura da árvore é 2:', binarySearchTree.getHeight()); // =&gt; 2</code></pre><pre><code>console.log('árvore balanceada é true:', binarySearchTree.isBalanced(),'line 220'); // =&gt; true</code></pre><pre><code>binarySearchTree. remove(11); // remove o nó inexistente</code></pre><pre><code>binarySearchTree.print(); // =&gt; 5 | 3 7 | 2 4 6 8</code></pre><pre><code>binarySearchTree.remove(5); // remove 5 e 6 sobe</code></pre><pre><code>binarySearchTree.print(); // =&gt; 6 | 3 7 | 2 4 8</code></pre><pre><code>console.log(binarySearchTree.checkIfFull(), 'deve ser verdadeiro');</code></pre><pre><code>var fullBSTree = new BinarySearchTree(10);</code></pre><pre><code>fullBSTree.insert(5).insert(20).insert(15).insert(21).insert(16).insert(13);</code></pre><pre><code>console.log(fullBSTree.checkIfFull(), 'should be true');</code></pre><pre><code>binarySearchTree.remove(7); // remove 7 e 8 sobe</code></pre><pre><code>binarySearchTree.print(); // =&gt; 6 | 3 8 | 2 4</code></pre><pre><code>binarySearchTree.remove(8); // remove 8 e a árvore fica desbalanceada</code></pre><pre><code>binarySearchTree.print(); // =&gt; 6 | 3 | 2 4</code></pre><pre><code>console.log('árvore balanceada é false:', binarySearchTree.isBalanced()); // =&gt; true</code></pre><pre><code>console.log(binarySearchTree.getHeight(),'altura é 2')</code></pre><pre><code>binarySearchTree.remove(4);</code></pre><pre><code>binarySearchTree.remove(2);</code></pre><pre><code>binarySearchTree.remove(3);</code></pre><pre><code>binarySearchTree.remove(6);</code></pre><pre><code>binarySearchTree.print(); // =&gt; 'Não foi encontrado o nó raiz'</code></pre><pre><code>//binarySearchTree.printByLevel(); // =&gt; 'Não foi encontrado o nó raiz'</code></pre><pre><code>console.log('altura da árvore é -1:', binarySearchTree.getHeight()); // =&gt; -1</code></pre><pre><code>console.log('árvore balanceada é true:', binarySearchTree.isBalanced()); // =&gt; true</code></pre><pre><code>console.log('---');</code></pre><pre><code>binarySearchTree.insert(10);</code></pre><pre><code>console.log('altura da árvore é 0:', binarySearchTree.getHeight()); // =&gt; 0</code></pre><pre><code>console.log('ávroe balanceada é true:', binarySearchTree.isBalanced()); // =&gt; true</code></pre><pre><code>binarySearchTree.insert(6);</code></pre><pre><code>binarySearchTree.insert(14);</code></pre><pre><code>binarySearchTree.insert(4);</code></pre><pre><code>binarySearchTree.insert(8);</code></pre><pre><code>binarySearchTree.insert(12);</code></pre><pre><code>binarySearchTree.insert(16);</code></pre><pre><code>binarySearchTree.insert(3);</code></pre><pre><code>binarySearchTree.insert(5);</code></pre><pre><code>binarySearchTree.insert(7);</code></pre><pre><code>binarySearchTree.insert(9);</code></pre><pre><code>binarySearchTree.insert(11);</code></pre><pre><code>binarySearchTree.insert(13);</code></pre><pre><code>binarySearchTree.insert(15);</code></pre><pre><code>binarySearchTree.insert(17);</code></pre><pre><code>binarySearchTree.print(); // =&gt; 10 | 6 14 | 4 8 12 16 | 3 5 7 9 11 13 15 17</code></pre><pre><code>binarySearchTree.remove(10); // remove 10 e 11 sobe</code></pre><pre><code>binarySearchTree.print(); // =&gt; 11 | 6 14 | 4 8 12 16 | 3 5 7 9 x 13 15 17</code></pre><pre><code>binarySearchTree.remove(12); // remove 12 e 13 sobe</code></pre><pre><code>binarySearchTree.print(); // =&gt; 11 | 6 14 | 4 8 13 16 | 3 5 7 9 x x 15 17</code></pre><pre><code>console.log('árvore balanceada é true:', binarySearchTree.isBalanced()); // =&gt; true</code></pre><pre><code>//console.log('árvore balanceada otimizada é true:', binarySearchTree.isBalancedOptimized()); // =&gt; true</code></pre><pre><code>binarySearchTree.remove(13); // remove 13 e 13 não tem filhos - nada acontece</code></pre><pre><code>binarySearchTree.print(); // =&gt; 11 | 6 14 | 4 8 x 16 | 3 5 7 9 x x 15 17</code></pre><pre><code>console.log('árvore balanceada é false:', binarySearchTree.isBalanced()); // =&gt; false</code></pre><h3 id="complexidade-de-tempo"><strong>Complexidade de tempo</strong></h3><p>1. Inserção O(log n)<br>2. Remoção O(log n)<br>3. Busca O(log n)</p><p>Uau, isso foi realmente muita informação. Espero que as explicações tenham sido claras e introdutórias o máximo possível. Outra vez, escrever me ajuda a solidificar conceitos e, como dizia Richard Feynman: "Quando uma pessoa ensina, duas pessoas aprendem."</p><h3 id="recursos"><strong>Recursos</strong></h3><p>Estes são recursos ótimos para visualização (em inglês):</p><ul><li><a href="https://www.cs.usfca.edu/~galles/visualization/Algorithms.html" rel="noopener"><strong><strong>Data Structure Visualization</strong></strong></a></li><li><a href="http://btv.melezinek.cz/binary-search-tree.html" rel="noopener"><strong><strong>BinaryTreeVisualiser - Binary Search Tree</strong></strong></a></li><li><a href="https://visualgo.net/en/bst?slide=1" rel="noopener"><strong><strong>VisuAlgo - Binary Search Tree, AVL Tree</strong></strong></a></li><li><a href="http://www.bigocheatsheet.com/" rel="noopener"><strong><strong>Big-O Algorithm Complexity Cheat Sheet (Know Thy Complexities!) </strong></strong></a></li><li><a href="https://algs4.cs.princeton.edu/home/" rel="noopener"><strong><strong>Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne</strong></strong></a></li><li><a href="https://en.wikipedia.org/wiki/Binary_search_tree" rel="noopener"><strong><strong>Binary search tree - Wikipedia</strong></strong></a></li></ul><p>Versão da Wikipédia em português: <a href="https://pt.wikipedia.org/wiki/%C3%81rvore_bin%C3%A1ria_de_busca">Árvore binária de busca</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Recursão não é difícil: um passo a passo dessa técnica de programação muito útil ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por: Kevin Turney Já vou dizendo isso de cara: você sabe os eventos que acontecem quando uma função é invocada? Não? Então, começaremos daqui. Chamando uma função Quando nós chamamos uma função, um contexto de execução ocorre na pilha de execução. Vamos entrar em detalhes mais específicos abaixo. Primeiro, ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/recursao-nao-e-dificil-um-passo-a-passo-dessa-tecnica-de-programacao-muito-util/</link>
                <guid isPermaLink="false">64b7f49fb04bf0067ce246e7</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Fri, 15 Sep 2023 11:14:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/08/1_VtrFuLn-4TnmFVDD8G3pxQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/recursion-is-not-hard-858a48830d83/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Recursion is not hard: a step-by-step walkthrough of this useful programming technique</a>
      </p><p>Escrito por: Kevin Turney</p><p>Já vou dizendo isso de cara: você sabe os eventos que acontecem quando uma função é invocada? Não? Então, começaremos daqui.</p><h4 id="chamando-uma-fun-o"><strong>Chamando uma função</strong></h4><p>Quando nós chamamos uma função, um contexto de execução ocorre na pilha de execução. Vamos entrar em detalhes mais específicos abaixo.</p><p>Primeiro, o que é uma pilha? </p><p>Uma pilha é uma estrutura de dados que opera com base em "o último a entrar é o primeiro a sair". Um item é "colocado" (em inglês, <em>pushed</em>) em uma pilha quando o estamos adicionando e é "retirado" (em inglês, <em>popped</em>) da pilha quando queremos removê-lo.</p><p>Usar uma pilha é um método de ordenar um determinado número de operações para a execução.</p><p>Agora, o que seria um contexto de execução? Um contexto de execução é criado quando temos uma chamada de função. Esse contexto ocorre em uma pilha de execução, uma ordem de operações. O item que é sempre o primeiro nessa pilha é o contexto de execução global. Logo após, estão os contextos criados pelas funções.</p><p>Esses contextos de execução tem propriedades, um <strong>objeto de ativação</strong> e um "this" vinculado. O "this" vinculado é uma referência de volta ao contexto de execução. O objeto de ativação inclui: parâmetros passados, declaração de variáveis e declaração de funções.</p><p>Então, toda vez que colocamos um novo contexto na pilha, normalmente, temos tudo de que precisamos para executar o código.</p><p>Por que eu disse <em>normalmente</em>?</p><p>Com recursão, estamos esperando retornar valores vindo de outros contextos de execução. Esses outros contextos de execução estão em pontos mais altos na pilha. Quando o último item na pilha finaliza a execução, esse contexto gerado retorna um valor. Esse valor retornado é passado como um valor retornado pelo caso recursivo para o próximo item. O contexto de execução, então, é retirado da pilha.</p><h4 id="recurs-o"><strong><strong>Recurs</strong>ão</strong></h4><p>Então, o que é recursão?</p><p>Uma função recursiva é uma função que chama a si mesma até que a "condição base" seja verdadeira e a execução pare.</p><p>Enquanto for <em>falsa</em>, continuaremos colocando o contexto de execução em cima da pilha. Isso pode acontecer até que tenhamos um sobrefluxo da pilha – ou, em inglês, "<em>stack overflow</em>". Um <em>stack overflow </em>ocorre quando ficamos sem memória para manter os itens na pilha.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/08/yEbQk71bUba6KLDWGXuamXQlntQU4mkFaoO4.jpeg" class="kg-image" alt="yEbQk71bUba6KLDWGXuamXQlntQU4mkFaoO4" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/08/yEbQk71bUba6KLDWGXuamXQlntQU4mkFaoO4.jpeg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/08/yEbQk71bUba6KLDWGXuamXQlntQU4mkFaoO4.jpeg 800w" sizes="(min-width: 720px) 720px" width="800" height="800" loading="lazy"></figure><p>Em geral, uma função recursiva tem ao menos duas partes: uma condição base e, ao menos, um caso recursivo.</p><p>Vamos olhar um exemplo clássico.</p><h4 id="fatorial"><strong><strong>Fatorial</strong></strong></h4><pre><code>const fatorial = function(num) {
	debugger;
    if (num === 0 || num === 1) {
    	return 1  
    } else {
    	return num * fatorial(num - 1)
    }
}</code></pre><pre><code>fatorial(5)</code></pre><p>Aqui, estamos tentando encontrar 5! (o fatorial de cinco). A <a href="http://mathworld.wolfram.com/Factorial.html" rel="noopener">função fatorial</a> (texto em inglês) é definida como o produto de todos os números positivos menores ou iguais ao seu argumento.</p><p>A primeira condição é: "se o parâmetro passado for igual a 0 ou 1, terminaremos a chamada e retornaremos 1".</p><p>Para os casos recursivos, faremos o seguinte:</p><p>"Se o parâmetro não for 0 ou 1, passaremos o valor de <code>num</code> vezes o valor de retorno da função chamada outra vez com o argumento de <code>num-1</code>".</p><p>Então, se chamarmos <code>fatorial(0)</code>, a função retornará 1 e nunca atingirá o caso recursivo.</p><p>A mesma situação ocorre com <code>fatorial(1)</code>.</p><p>Podemos ver o que está acontecendo se inserirmos uma instrução <code>debugger</code> dentro do código e se usarmos as ferramentas do desenvolvedor para segui-lo e observar a chamada da pilha (em inglês, <em>stack</em>).</p><ol><li>A <em>stack </em>de execução coloca o 5 como argumento dentro da função <code>fatorial()</code>. O caso base é falso, entrando na condição recursiva.</li><li>A <em>stack </em>de execução então chama a função <code>fatorial()</code> uma segunda vez com o argumento <code>num-1</code> (4) como argumento. O caso base é falso entrando, assim, na condição recursiva.</li><li>A <em>stack </em>de execução chamará <code>fatorial()</code> uma terceira vez com o argumento <code>num-1</code> (3). O caso base é falso. Portanto, outra vez, entramos na condição recursiva.</li><li>A <em>stack </em>de execução chama <code>fatorial()</code> uma quarta vez com <code>num-1</code> (2) como seu argumento. O caso base é falso. Entramos na condição recursiva.</li><li>A <em>stack </em>executa <code>fatorial()</code> uma quinta vez com &nbsp;<code>num-1</code> (1) como seu argumento. Aqui o caso base é verdadeiro. Então, retornamos 1.</li></ol><p>Nesse ponto, diminuímos o argumento passado por 1 em cada chamada de função, até alcançarmos a condição de retorno 1.</p><p>6. A partir daí, o último contexto de execução completa <code>num === 1</code>. Então, a função retorna 1.</p><p>7. A seguir, temos <code>num === 2</code>. O valor retornado é 2. (1×2).</p><p>8. Depois, temos <code>num === 3</code>. Retornamos 6. (2×3). Até agora, temos 1×2×3.</p><p>9. Posteriormente, temos <code>num === 4</code>, (4×6). 24 é o valor retornado no próximo contexto.</p><p>10. Finalmente, <code>num === 5</code>, (5×24) e, aqui, teremos 120 como valor final.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/08/KioR-yl8aB2lxriDCulsNMTivQ1J5xlmEyrg.jpeg" class="kg-image" alt="KioR-yl8aB2lxriDCulsNMTivQ1J5xlmEyrg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/08/KioR-yl8aB2lxriDCulsNMTivQ1J5xlmEyrg.jpeg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/08/KioR-yl8aB2lxriDCulsNMTivQ1J5xlmEyrg.jpeg 800w" sizes="(min-width: 720px) 720px" width="800" height="569" loading="lazy"></figure><p>Recursão é bem elegante, certo?</p><p>Poderíamos ter feito a mesma coisa com um laço <em>for </em>ou <em>while, </em>mas, usando recursão, temos uma alternativa mais apresentável e bonita. </p><p>É por isso que usamos soluções recursivas. </p><p>Muitas vezes, um problema dissecado em partes menores é mais eficiente e ajuda a superá-lo mais facilmente. Desse modo, recursão é uma abordagem do tipo <em>dividir-e-conquistar</em> para resolver obstáculos. </p><ul><li>Subproblemas são mais fáceis de solucionar que o problema original;</li><li>Soluções para subproblemas são combinadas para resolver o problema original.</li></ul><p>"Dividir-e-conquistar" é bastante utilizado para atravessar ou buscar em estrutura de dados como em árvores de busca binária, gráficos e pilhas. Isso também funciona com muitos algoritmos de ordenação, como <a href="https://www.khanacademy.org/computing/computer-science/algorithms/quick-sort/a/overview-of-quicksort" rel="noopener">quicksort</a> e <a href="https://www.geeksforgeeks.org/heap-sort/" rel="noopener">heapsort</a> (links em inglês).</p><p>Vamos trabalhar com alguns exemplos. Usaremos as ferramentas do desenvolvedor para ter um entendimento conceitual do que está acontecendo, onde e quando. Vamos lembrar de usar a ferramenta <code>debugger</code> e seguir passo a passo cada parte do processo.</p><h4 id="fibonacci"><strong><strong>Fibonacci</strong></strong></h4><pre><code>const fibonacci = function(num) {
    if (num &lt;= 1) {
    	return num
    } else {
        return fibonacci(num - 1) + fibonacci(num - 2)
	}
}

fibonacci(5);</code></pre><h4 id="arrays-recursivos"><strong>Arrays recursivos</strong></h4><pre><code>function aplainar(arr) {
	var result = []  arr.forEach(function(element) {
    	if (!Array.isArray(element)) {
        	result.push(element)
        } else {
        	result = result.concat(aplainar(element))
		}
	})
    return result
}

aplainar([1, [2], [3, [[4]]]]);</code></pre><h4 id="invertendo-uma-string"><strong>Invertendo uma string</strong></h4><pre><code>function inverter(str) {
	if (str.length === 0) return ''
    return str[str.length - 1] + inverter(str.substr(0, str.length - 1))
}

inverter('abcdefg');</code></pre><h4 id="quicksort"><strong><strong>Quicksort</strong></strong></h4><pre><code>function quickSort(arr, lo, hi) {
	if (lo === undefined) lo = 0
    if (hi === undefined) hi = arr.length - 1
    if (lo &lt; hi) {    // fazer o particionamento do array
    	var p = partition(arr, lo, hi)
        console.log('partição de, ' + lo + ' até ' + hi + '=&gt; partition: ' + p)    // ordenar os subarrays
        quickSort(arr, lo, p - 1)
        quickSort(arr, p + 1, hi)  
    }  // para a chamada inicial, retornar um array ordenado
    if (hi - lo === arr.length - 1) return arr
}

function partition(arr, lo, hi) {  
	// seleciona o último elemento como pivot
	var pivot = arr[hi]  
    // rastreia o index onde colocar o pivot  
    var pivotLocation = lo  
    // percorre o subarray e se element &lt;= pivot, coloca element antes do pivot  
    for (var i = lo; i &lt; hi; i++) {
    	if (arr[i] &lt;= pivot) {
        	swap(arr, pivotLocation, i)
            pivotLocation++
        }
    }
    swap(arr, pivotLocation, hi)
    return pivotLocation
}

function swap(arr, index1, index2) {
	if (index1 === index2) return
    var temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
    console.log('trocados' + arr[index1], arr[index2], +' em ', arr)  
    return arr
}

quickSort([1, 4, 3, 56, 9, 8, 7, 5])</code></pre><p>Praticar técnicas de recursão é importante. Para estruturas de dados aninhadas como árvores, gráficos e pilhas, a recursão é inestimável.</p><p>Em um artigo futuro, discutirei otimização das chamada da cauda (em inglês, <em>tail-call</em>), técnicas de memoização e como elas se relacionam à recursão. </p><p>Agradeço por ter me acompanhado até aqui!</p><h4 id="recursos-complementares"><strong>Recursos complementares</strong></h4><p><a href="https://pt.wikipedia.org/wiki/Recursividade">Wikipédia</a></p><p><a href="https://softwareengineering.stackexchange.com/questions/25052/in-plain-english-what-is-recursion" rel="noopener">Software Engineering</a> (em inglês)</p><p><a href="https://www.topcoder.com/community/data-science/data-science-tutorials/an-introduction-to-recursion-part-2/" rel="noopener">Outro artigo interessante</a> (em inglês)</p><p><a href="http://web.mit.edu/6.005/www/fa15/classes/10-recursion/" rel="noopener">Material de curso aberto (gratuito) da M.I.T.</a> (em inglês)</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Uma introdução à complexidade temporal dos algoritmos ]]>
                </title>
                <description>
                    <![CDATA[ Na ciência da computação, análise de algoritmos é uma parte fundamental. É importante encontrar o algoritmo mais eficiente para a resolução de um problema. É possível que muitos algoritmos sejam capazes de resolver um problema, mas o desafio, aqui, é escolher o mais eficiente.  O ponto agora é: como ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/uma-introducao-a-complexidade-temporal-dos-algoritmos/</link>
                <guid isPermaLink="false">64808ade3aab28058e381429</guid>
                
                    <category>
                        <![CDATA[ Estruturas de dados ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Tue, 25 Jul 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/aron-visuals-322314-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/time-complexity-of-algorithms/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">An Introduction to the Time Complexity of Algorithms</a>
      </p><p>Na ciência da computação, análise de algoritmos é uma parte fundamental. É importante encontrar o algoritmo mais eficiente para a resolução de um problema. É possível que muitos algoritmos sejam capazes de resolver um problema, mas o desafio, aqui, é escolher o mais eficiente. </p><p>O ponto agora é: como vamos reconhecer o mais eficiente se temos um grupo com diversos algoritmos? Aqui, o conceito de complexidade de tempo e de espaço dos algoritmos ganha vida. A complexidade de tempo e a de espaço agem como uma medida escalonável para algoritmos. Comparamos os algoritmos com base na sua complexidade de espaço (total de memória) e de tempo (número de operações).</p><p>O valor total de memória do computador usada por um algoritmo quando executado é a complexidade de espaço daquele algoritmo. Para deixar este artigo um pouco menor, não discutiremos esse tipo de complexidade aqui. </p><h2 id="complexidade-de-tempo"><strong>Complexidade de tempo</strong></h2><p>Complexidade de tempo é o numero de operações que um algoritmo necessita para completar seu objetivo (considerando que cada operação leva o mesmo tanto de tempo). O algoritmo que executa a tarefa no menor número de operações possível é considerado o mais eficiente em termos de complexidade de tempo. Entretanto, as complexidades de tempo e de espaço também são afetadas por fatores como seu sistema operacional e hardware, mas não incluiremos isso nessa discussão.</p><p>Agora, para entender a complexidade de tempo, veremos um exemplo em que comparamos dois algoritmos diferentes que foram usados para resolver um problema específico. </p><p>O problema é a busca. Temos que buscar por um elemento em um array (nesse problema, assumiremos que o array está ordenado em ordem ascendente). Para resolver esse problema, temos dois algoritmos:</p><p>1. <a href="https://www.hackerearth.com/practice/algorithms/searching/linear-search/tutorial/">a busca linear (em inglês, </a><a href="https://www.hackerearth.com/practice/algorithms/searching/linear-search/tutorial/" rel="nofollow noopener"><em>linear search</em>)</a></p><p>2. <a href="https://www.hackerearth.com/practice/algorithms/searching/binary-search/tutorial/">a busca binária (em inglês, <em>b</em></a><em><a href="https://www.hackerearth.com/practice/algorithms/searching/binary-search/tutorial/" rel="nofollow noopener">inary search</a></em><a href="https://www.hackerearth.com/practice/algorithms/searching/binary-search/tutorial/" rel="nofollow noopener">)</a></p><p>Vamos assumir que o array contém dez elementos e temos que encontrar o número <code>10</code> no array.</p><pre><code class="language-javascript">const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const buscar_elemento = 10;</code></pre><p>O algoritmo de busca linear compara cada elemento no array com a variável <strong>buscar_elemento</strong>. Quando o encontra dentro do array, retorna <strong><strong><strong><strong>true</strong></strong></strong></strong>.</p><p>Agora, vamos contar o número de operações necessárias. Aqui, a resposta é 10 (visto que compara cada elemento do array). Então, a busca linear usa dez operações para encontrar o elemento desejado (este é o número máximo de operações para esse array; no caso da busca linear, também é considerado o <a href="https://www.geeksforgeeks.org/analysis-of-algorithms-set-2-asymptotic-analysis/" rel="nofollow noopener">pior caso</a> para um algoritmo).</p><p>Em geral, a busca linear necessitará de um número <strong><strong><strong><strong>n</strong></strong></strong> </strong>de operações no seu pior caso (onde n é o tamanho do array).</p><p>Vamos examinar o algoritmo de <strong>busca binária</strong> para esse caso.</p><p>A busca binária pode ser facilmente entendida nesse exemplo:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/07/binarySearch.png" class="kg-image" alt="binarySearch" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/07/binarySearch.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/07/binarySearch.png 816w" sizes="(min-width: 720px) 720px" width="816" height="283" loading="lazy"><figcaption>Fonte: <a href="https://www.learneroo.com/modules/71/nodes/399">Learneroo</a></figcaption></figure><p>Se estivermos tentando aplicar essa lógica no nosso problema, primeiro comparamos <strong>buscar_elemento</strong> com o elemento do meio do array, que é 5. Agora visto que 5 é menor que 10, começaremos a procurar por <strong>buscar_elemento </strong>no array de elementos maiores do que 5, e continuaremos assim até encontrar o desejado elemento – que, neste caso, é 10.</p><p>Agora, tente contar o número de operações de busca binária necessários para encontrar o elemento desejado. Levou, aproximadamente, quatro operações. Esse foi o pior caso para a busca binária. Isso mostra que existe uma relação <a href="https://www.khanacademy.org/math/algebra2/exponential-and-logarithmic-functions/introduction-to-logarithms/a/intro-to-logarithms" rel="nofollow noopener">logarítmica</a> entre o número de operações necessárias e o tamanho total do array.</p><p>número de operações = log(10) = 4 na base 2 (aproximadamente)</p><p>Podemos generalizar esse resultado para a busca binária:</p><blockquote>Para um array de tamanho <strong><strong><strong><strong>n</strong></strong></strong></strong>, o número de operações executadas pela busca binária é <strong><strong><strong><strong>log(n)</strong></strong></strong></strong></blockquote><h2 id="nota-o-big-o"><strong>Notação <strong>Big O</strong></strong></h2><p>Nas declarações acima, vimos que, para um array de tamanho <strong><strong><strong><strong>n</strong></strong></strong></strong>, a busca linear executará <strong><strong><strong><strong>n</strong></strong></strong></strong> operações para completar a busca. Em contrapartida, a busca binária executou <strong><strong><strong><strong>log(n)</strong></strong></strong></strong> operações. Para isso, consideramos os respectivos piores casos. Podemos representar isso através de um gráfico (onde o <strong>eixo x</strong> é o número de elementos do array e o<strong><strong><strong><strong> </strong></strong></strong>eixo <strong><strong><strong>y</strong></strong></strong></strong> é o número de operações).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/07/linearSearch-vs-binary-search-diagram_0.jpg" class="kg-image" alt="linearSearch-vs-binary-search-diagram_0" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/07/linearSearch-vs-binary-search-diagram_0.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/07/linearSearch-vs-binary-search-diagram_0.jpg 758w" sizes="(min-width: 720px) 720px" width="758" height="461" loading="lazy"><figcaption>Fonte: <a href="https://www.techtud.com/computer-science-and-information-technology/algorithms/searching/binary-search">Techtud</a></figcaption></figure><p>É bem claro pela figura que a taxa na qual a complexidade aumenta para a busca linear é muito mais alta que para a busca binária.</p><p>Quando analisamos um algoritmo, usamos uma notação para representar sua complexidade de tempo – essa notação se chama Big O.</p><p>Por exemplo: a complexidade de tempo para a busca linear pode ser representada como <strong><strong><strong><strong>O(n)</strong></strong></strong></strong>.<strong> </strong>Para a busca binária, temos <strong><strong><strong><strong>O(log n)</strong></strong></strong></strong>. Consideraremos que <strong><strong><strong><strong>n</strong></strong></strong></strong> e <strong><strong><strong><strong>log(n)</strong></strong></strong></strong> são o número de operações para cada tipo de busca.</p><p>A complexidade de tempo (ou notação Big O) para alguns dos algoritmos populares está listada abaixo:</p><ol><li>Busca binária: O(log n)</li><li>Busca linear: O(n)</li><li>Ordenamento rápido (em inglês, <em>Quick Sort</em>): O(n * log n)</li><li>Ordenação por seleção (em inglês, <em>Selection Sort</em>): O(n * n)</li><li>Algoritmo do caixeiro viajante (em inglês, <em>Travelling salesperson</em>): O(n!)</li></ol><h2 id="conclus-o"><strong><strong>Conclus</strong>ão</strong></h2><p>Parabéns se você chegou até este ponto do artigo. Agora, você pode estar se perguntando: porque a complexidade de tempo é tão importante de entender?</p><p>Sabemos que, para um número pequeno de elementos (digamos que 10), a diferença entre o número de operações realizadas pela busca binária e pela busca linear não é tão grande, No mundo real, porém, na maioria das vezes, lidamos com problemas que tem um grande volume de dados. </p><p>Por exemplo, se tivermos 4 bilhões de elementos usando a busca linear, então, no pior dos casos faremos 4 bilhões de operações para completar a tarefa. A busca binária completará a tarefa em apenas 32 operações. É uma diferença enorme! Agora, vamos assumir que, se uma operação leva 1 ms para completar, a busca binária levará apenas 32 ms enquanto a busca linear levará 4 bilhões de milissegundos (aproximadamente, 46 dias). É uma diferença realmente significativa.</p><p>Esse é o motivo pelo qual o estudo da complexidade de tempo se torna importante quando falamos de um grande volume de dados.</p><h2 id="recursos-em-ingl-s-"><strong><strong>Re</strong>cursos (em inglês)</strong></h2><p><a href="https://www.amazon.com.br/Grokking-Algorithms-illustrated-programmers-curious/dp/1617292230/">Grokking Algorithms – escrito por Aditya Y Bhargava</a></p><p><a href="https://youtu.be/D6xkbGLQesk">Introduction to Big O notation and Time Complexity – vídeo do CS Dojo</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como eu solucionei e debuguei meu problema no Webpack através de tentativa, erro e um pouco de ajuda externa ]]>
                </title>
                <description>
                    <![CDATA[ Escrito por: Margarita Obraztsova Eu diria que essa foi uma bela de uma jornada. Eu sabia que o Webpack não era fácil de configurar: tem muitas partes com muitas opções, tem o npm hell, e isso tudo muda com as novas atualizações. Não é surpresa que possa se tornar, facilmente, ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-eu-solucionei-e-debuguei-meu-problema-no-webpack-atraves-de-tentativa-erro-e-um-pouco-de-ajuda-externa/</link>
                <guid isPermaLink="false">6436aadb84d2fb0595fd5f0e</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jun 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/1_foxbYY6DryL2han-19rLEA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-solve-webpack-problems-the-practical-case-79fb676417f4/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How I solved and debugged my Webpack issue through trial, error, and a little outside help.</a>
      </p><p>Escrito por: Margarita Obraztsova</p><p>Eu diria que essa foi uma bela de uma jornada. Eu sabia que o Webpack não era fácil de configurar: tem muitas partes com muitas opções, tem o npm hell, e isso tudo muda com as novas atualizações. Não é surpresa que possa se tornar, facilmente, <strong>uma tarefa problemática de se resolver depois que alguma coisa não saiu como você esperava </strong>(ou seja, não do modo como está na documentação).</p><h3 id="tentando-resolver"><strong>Tentando resolver</strong></h3><p>Minha jornada de <em>debug </em>começou com a seguinte configuração: </p><p><em><em>webpack.config.js</em></em></p><pre><code>// webpack v4.6.0</code></pre><pre><code>const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const WebpackMd5Hash = require('webpack-md5-hash');const CleanWebpackPlugin = require('clean-webpack-plugin');const webpack = require('webpack');</code></pre><pre><code>module.exports = {  entry: { main: './src/index.js' },  output: {    path: path.resolve(__dirname, 'dist'),    filename: '[name].[chunkhash].js'  },  devServer: {    contentBase: './dist',    hot: true,    open: true  },  module: {    rules: [      {         test: /\.js$/,        exclude: /node_modules/,        use: [          { loader: 'babel-loader' },          {            loader: 'eslint-loader',            options: {               formatter: require('eslint/lib/formatters/stylish')             }           }         ]       }     ]  },  plugins: [    new CleanWebpackPlugin('dist', {}),    new HtmlWebpackPlugin({      inject: false,      hash: true,      template: './src/index.html',      filename: 'index.html'    }),    new WebpackMd5Hash()  ]</code></pre><pre><code>};</code></pre><p><em><em>package.json</em></em></p><pre><code>{  "name": "post",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "build": "webpack --mode production",    "dev": "webpack-dev-server"   },  "author": "",  "license": "ISC",  "devDependencies": {    "babel-cli": "^6.26.0",    "babel-core": "^6.26.0",    "babel-loader": "^7.1.4",    "babel-preset-env": "^1.6.1",    "babel-preset-react": "^6.24.1",    "babel-runtime": "^6.26.0",    "clean-webpack-plugin": "^0.1.19",    "eslint": "^4.19.1",    "eslint-config-prettier": "^2.9.0",    "eslint-loader": "^2.0.0",    "eslint-plugin-prettier": "^2.6.0",    "eslint-plugin-react": "^7.7.0",    "html-webpack-plugin": "^3.2.0",    "prettier": "^1.12.1",    "react": "^16.3.2",    "react-dom": "^16.3.2",    "webpack": "^4.6.0",    "webpack-cli": "^2.0.13",    "webpack-dev-server": "^3.1.3",    "webpack-md5-hash": "0.0.6"  }}</code></pre><p><em><em>.babelrc</em></em></p><pre><code>{  "presets": ["env", "react"]}</code></pre><p><em><em>.eslintrc.js</em></em></p><pre><code>module.exports = {  env: {    browser: true,    commonjs: true,    es6: true  },  extends: [    'eslint:recommended',    'plugin:react/recommended',    'prettier',    'plugin:prettier/recommended'  ],  parserOptions: {    ecmaFeatures: {      experimentalObjectRestSpread: true,      jsx: true    },    sourceType: 'module'  },  plugins: ['react', 'prettier'],  rules: {    indent: ['error', 2],    'linebreak-style': ['error', 'unix'],    quotes: ['warn', 'single'],    semi: ['error', 'always'],    'no-unused-vars': [      'warn',      { vars: 'all', args: 'none', ignoreRestSiblings: false }    ],    'prettier/prettier': 'error'   }};</code></pre><p><em><em>prettier.config.js</em></em></p><pre><code>// .prettierrc.js</code></pre><pre><code>module.exports = {  printWidth: 80,  tabWidth: 2,  semi: true,  singleQuote: true,  bracketSpacing: true};</code></pre><p>e, dentro da pasta <code>src/</code>:</p><p><em><em>index.html</em></em></p><pre><code>&lt;html&gt; &lt;head&gt;&lt;/head&gt; &lt;body&gt;    &lt;div id="app"&gt;&lt;/div&gt;    &lt;script src="&lt;%= htmlWebpackPlugin.files.chunks.main.entry %&gt;"&gt;&lt;/script&gt; &lt;/body&gt;&lt;/html&gt;</code></pre><p><em><em>index.js</em></em></p><pre><code>import React from 'react';import { render } from 'react-dom';import Hello from './Hello';</code></pre><pre><code>class App extends React.Component {  render() {    return (      &lt;div&gt;        &lt;Hello hello={'Hello, world! And the people of the world!'} /&gt;     &lt;/div&gt;    );  }}render(&lt;App /&gt;, document.getElementById('app'));</code></pre><p><em><em>Hello.js</em></em></p><pre><code>import React from 'react';import PropTypes from 'prop-types';</code></pre><pre><code>class Hello extends React.Component {  render() {    return &lt;div&gt;{this.props.hello}&lt;/div&gt;;  }}</code></pre><pre><code>Hello.propTypes = {  hello: PropTypes.string};</code></pre><pre><code>export default Hello;</code></pre><p>Este era o panorama geral da estrutura do projeto:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/IA4bRe-OgTy6uExvsJwfk8WTW8uc-atToltw.png" class="kg-image" alt="IA4bRe-OgTy6uExvsJwfk8WTW8uc-atToltw" width="464" height="530" loading="lazy"></figure><h3 id="ent-o-qual-era-o-problema">Então, qual era o problema?</h3><p>Como você pode perceber, eu configurei o ambiente, o ESLinting, e assim por diante. Criei tudo para que eu pudesse começar a escrever meus códigos e adicionar meus novos componentes à minha nova e brilhante biblioteca.</p><p>Se eu tivesse um erro, porém, o que eu faria? Vamos bagunçar as coisas um pouco.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/5SfyW4tlIrnZSnRqM7h1z-5X4Kx218yWsVeN.png" class="kg-image" alt="5SfyW4tlIrnZSnRqM7h1z-5X4Kx218yWsVeN" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/5SfyW4tlIrnZSnRqM7h1z-5X4Kx218yWsVeN.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/5SfyW4tlIrnZSnRqM7h1z-5X4Kx218yWsVeN.png 800w" sizes="(min-width: 720px) 720px" width="800" height="257" loading="lazy"></figure><p><strong>Se tentarmos rastrear a origem do erro direto do console do nosso navegador do<strong> Google Chrome, </strong>seria muito difícil.<strong> </strong></strong>Nos depararíamos com algo assim:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/IAUMuN5vGBRK02XiutfpKGAV-qnuQlhqjVwx.png" class="kg-image" alt="IAUMuN5vGBRK02XiutfpKGAV-qnuQlhqjVwx" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/IAUMuN5vGBRK02XiutfpKGAV-qnuQlhqjVwx.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/IAUMuN5vGBRK02XiutfpKGAV-qnuQlhqjVwx.png 800w" sizes="(min-width: 720px) 720px" width="800" height="178" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/PiUlbODsL34GCkasgETv-gePRv-KYohgEBsU.png" class="kg-image" alt="PiUlbODsL34GCkasgETv-gePRv-KYohgEBsU" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/PiUlbODsL34GCkasgETv-gePRv-KYohgEBsU.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/PiUlbODsL34GCkasgETv-gePRv-KYohgEBsU.png 800w" sizes="(min-width: 720px) 720px" width="800" height="628" loading="lazy"></figure><p>Os mapas de origem não estão configurados!</p><p>Eu quero apontar para um arquivo <code>Hello.js</code>, não para o arquivo minificado.</p><h3 id="isso-provavelmente-uma-coisa-pequena"><strong>Isso, provavelmente, é uma coisa pequena</strong></h3><p>OU, ao menos, era o que eu pensava. Então, tentei configurar os mapas de origem como era <a href="https://webpack.js.org/guides/development/#using-webpack-dev-server" rel="noopener">descrito na documentação</a>, adicionando a <a href="https://webpack.js.org/configuration/devtool/" rel="noopener"><strong><strong>devtool</strong></strong></a>.</p><blockquote>Quando o webpack coloca seu código-fonte em um bundle, pode se tornar difícil de rastrear erros e avisos de volta ao local original. Por exemplo, se você colocar em um bundle três (chamado <code>bundle.js</code>) arquivos-fonte, <code>a.js</code>, <code>b.js</code> e <code>c.js</code>, e se um dos arquivos-fonte contiver um erro, o stack trace simplesmente apontará para <code>bundle.js</code>. Isso nem sempre é útil, pois você provavelmente vá querer saber em qual arquivo-fonte está o erro. <br><br>Para facilitar o rastreamento de erros e avisos, o JavaScript oferece <a href="http://blog.teamtreehouse.com/introduction-source-maps" rel="noopener">mapas de origem</a> (texto em inglês), que mapeiam seu código compilado de volta ao código-fonte original. Se um erro vier de <code>b.js</code>, o mapa de origem informará isso com exatidão. (<a href="https://webpack.js.org/guides/development/" rel="noopener">fonte</a>)</blockquote><p>Então, eu, inocentemente, assumi que isso funcionaria no meu arquivo <em><em>webpack.config.js</em></em>:</p><pre><code>...</code></pre><pre><code>module.exports = {  entry: { main: './src/index.js' },  output: {    path: path.resolve(__dirname, 'dist'),    filename: '[name].[chunkhash].js'  },  devtool: 'inline-source-map',  devServer: {    contentBase: './dist',    hot: true,    open: true  },  ...</code></pre><p><em>e no <em>package.json</em></em></p><pre><code>..."scripts": {  "build": "webpack --mode production",  "dev": "webpack-dev-server --mode development"}...</code></pre><p>Você tem que adicionar uma <em>flag</em> de desenvolvimento quando estiver trabalhando, ou, então, não vai funcionar como a documentação está dizendo. Ainda assim, mesmo com o valor sugerido, o mapa de origem não estava se comportando como eu esperei que fosse.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/NN9n-KUqtxlW6sfANLqiIJHAB6Nka6XfiSkr.png" class="kg-image" alt="NN9n-KUqtxlW6sfANLqiIJHAB6Nka6XfiSkr" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/NN9n-KUqtxlW6sfANLqiIJHAB6Nka6XfiSkr.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/NN9n-KUqtxlW6sfANLqiIJHAB6Nka6XfiSkr.png 800w" sizes="(min-width: 720px) 720px" width="800" height="530" loading="lazy"></figure><p>Se você ler este <a href="https://survivejs.com/webpack/building/source-maps/#-sourcemapdevtoolplugin-and-evalsourcemapdevtoolplugin-" rel="noopener">guia</a> d0 SurviveJS – e você deveria fazê-lo – verá com seus próprios olhos.</p><p>Depois de esgotar todas as opções, eu fui ao Github tentar achar alguma solução possível.</p><h3 id="buscando-solu-es-no-github"><strong>Buscando soluções no <strong>GitHub</strong></strong></h3><p>Tentei todas as sugestões nessa busca e nenhuma delas me ajudou. </p><p>Em certo ponto, cheguei a me questionar se havia algo errado com o meu webpack-dev-server. Acabei descobrindo que o webpack-dev-server já estava em manutenção há alguns meses.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/5bNNZcWIaA8PlUQgFKo9KprA3gkMyCY1i6Fx.png" class="kg-image" alt="5bNNZcWIaA8PlUQgFKo9KprA3gkMyCY1i6Fx" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/5bNNZcWIaA8PlUQgFKo9KprA3gkMyCY1i6Fx.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/5bNNZcWIaA8PlUQgFKo9KprA3gkMyCY1i6Fx.png 633w" width="633" height="230" loading="lazy"></figure><p>Descobri, depois de encontrar por acaso esta <a href="https://github.com/webpack/webpack-dev-server/issues/1161" rel="noopener"><em>issue</em></a>, onde eles <strong><strong>recom</strong>endam usar<strong> webpack-serve </strong>ao invés de <strong>webpack-dev-server.</strong></strong></p><p>Honestamente, foi a primeira vez que eu escutei sobre essa alternativa chamada <strong><strong>webpack-serve</strong></strong>. As documentações não pareciam promissoras também, mas eu, ainda assim, decidi dar uma oportunidade.</p><pre><code>npm install webpack-serve --save-dev</code></pre><p>Eu criei <em><em>serve.config.js</em></em></p><pre><code>const serve = require('webpack-serve');</code></pre><pre><code>const config = require('./webpack.config.js');</code></pre><pre><code>serve({ config });</code></pre><p>Eu substituí <strong><strong>webpack-dev-server </strong>por <strong>webpack serve</strong></strong> no meu arquivo package.json.</p><p>Entretanto, o webpack-serve não me ajudou tampouco.</p><p>Nesse momento, eu me sentia como se tivesse esgotado <strong>cada sugestão que encontrei no<strong> GitHub:</strong></strong></p><ul><li><a href="https://stackoverflow.com/questions/48986641/webpack-4-sourcemaps" rel="noopener">Webpack 4 — Sourcemaps</a> : essa <em>issue</em> sugere que <code>devtool: 'source-map'</code> deveria funcionar, mas esse não foi o caso para mim;</li><li><a href="https://stackoverflow.com/questions/34185748/how-to-make-webpack-sourcemap-to-original-files" rel="noopener">Como fazer o mapa de origem do webpack para os arquivos originais</a>: adicionando <code>devtoolModuleFilenameTemplate: info =&gt;'file://' + path.resolve(info.absoluteResourcePath).replace(/\\/g, '</code>/'). A minha configuração de saída não me ajudou, mas, ao invés de me mostrar o arquivo client.js, me mostrou o index.js;</li><li><a href="https://github.com/webpack/webpack/issues/6400" rel="noopener">https://github.com/webpack/webpack/issues/6400</a>: essa aqui não é uma descrição muito precisa do meu problema, então tentar executá-la não parecia que me ajudaria;</li><li>Eu tentei usar <code>webpack.SourceMapDevToolPlugin</code>, mas não funcionou com a minha configuração, mesmo quando eu excluí devtools ou o configurei como false;</li><li>Eu não usei o plug-in UglifyJS aqui, então configurar opções para isso não seria uma solução;</li><li>Eu sei que o webpack-dev-server está em manutenção por agora. Então, eu tentei webpack-serve, mas parece que os arquivos de configuração não funcionaram também;</li><li>Eu tentei o pacote source-map-support também, mas sem sorte. Eu vi uma situação similar <a href="https://github.com/webpack/webpack/issues/3165" rel="noopener">aqui</a>.</li></ul><h3 id="o-aben-oado-stackoverflow"><strong>O abençoado<strong> StackOverflow</strong></strong></h3><p>Eu estava perdendo um tempão para configurar os mapas de origem. Então, criei uma <a href="https://stackoverflow.com/questions/50105741/webpack-4-devtool-option-does-not-work-with-webpack-dev-server" rel="noopener"><em>issue</em></a> no StackOverflow.</p><p>Fazer o <em>debugging </em>da configuração do webpack normalmente é uma tarefa pesada: a melhor maneira a seguir é criar uma configuração do zero. Se alguma coisa nas documentações não funcionar como o esperado, talvez seja uma boa ideia encontrar uma <em>issue </em>parecida em uma <em>branch</em>, ou criar sua própria <em>issue</em>. Era o que eu pensava.</p><p>O primeiro rapaz que me respondeu estava realmente tentando me ajudar. Ele compartilhou sua própria configuração de trabalho. Ele até me ajudou criando um <a href="https://github.com/marharyta/webpack-fast-development/pull/1/files" rel="noopener"><em>pull request</em></a>.</p><p>Porém, tínhamos um problema aqui: <strong>por que sua configuração funciona?</strong> Olha, isso provavelmente não é magia, e temos alguma incompatibilidade de módulo aqui. Infelizmente, eu não pude entender por que minhas configurações não estavam funcionando: </p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/WqPfJ5e4-WAP3mCeLTFwwA2c-0qXMBAmzwKV.png" class="kg-image" alt="WqPfJ5e4-WAP3mCeLTFwwA2c-0qXMBAmzwKV" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/WqPfJ5e4-WAP3mCeLTFwwA2c-0qXMBAmzwKV.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/WqPfJ5e4-WAP3mCeLTFwwA2c-0qXMBAmzwKV.png 800w" sizes="(min-width: 720px) 720px" width="800" height="292" loading="lazy"></figure><p>O que passou é que ele fez, com a melhor das intenções, uma reestruturação do projeto do seu próprio jeito. </p><p>Isso significa que eu teria mais configurações ainda no projeto e teria também que mudar bastante coisa. Talvez, isso seria ok se eu tivesse fazendo uma configuração de teste, mas <strong>eu decidi fazer mudanças graduais nos arquivos para ver o que estava quebrando.</strong></p><h4 id="dissecando-a-issue"><strong><strong>Disse</strong>cando a<strong> <em>issue</em></strong></strong></h4><p>Vamos dar uma olhada nas diferenças entre os arquivos Webpack e <em><em>package.json</em> </em>dele e os meus. Apenas para deixar registrado, eu usei um repositório diferente na questão. Então, aqui está meu <a href="https://github.com/marharyta/webpack-fast-development" rel="noopener">link para o repositório</a> e para minha configuração. </p><pre><code>// webpack v4.6.0</code></pre><pre><code>const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const WebpackMd5Hash = require('webpack-md5-hash');const CleanWebpackPlugin = require('clean-webpack-plugin');const stylish = require('eslint/lib/formatters/stylish');const webpack = require('webpack');</code></pre><pre><code>module.exports = {  entry: { main: './src/index.js' },  output: {   devtoolModuleFilenameTemplate: info =&gt; 'file://' + path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),</code></pre><pre><code>   path: path.resolve(__dirname, 'dist'),   filename: '[name].[hash].js'  },  mode: 'development',  devtool: 'eval-cheap-module-source-map',  devServer: {    contentBase: './dist',    hot: true  },  module: {    rules: [      {        test: /\.js$/,        exclude: /node_modules/,        loader: 'babel-loader'      },      {        test: /\.js$/,        exclude: /node_modules/,        loader: 'eslint-loader',        options: {          formatter: stylish         }       }     ]   },   plugins: [    // new webpack.SourceMapDevToolPlugin({    //   filename: '[file].map',    //   moduleFilenameTemplate: undefined,    //   fallbackModuleFilenameTemplate: undefined,    //   append: null,    //   module: true,    //   columns: true,    //   lineToLine: false,    //   noSources: false,    //   namespace: ''    // }),    new CleanWebpackPlugin('dist', {}),    new HtmlWebpackPlugin({      inject: false,      hash: true,      template: './src/index.html',      filename: 'index.html'    }),    new WebpackMd5Hash(),    // new webpack.NamedModulesPlugin(),    new webpack.HotModuleReplacementPlugin()  ]</code></pre><pre><code>};</code></pre><p><em><em>package.json</em></em></p><pre><code>{</code></pre><pre><code>"name": "post","version": "1.0.0","description": "","main": "index.js","scripts": {  "storybook": "start-storybook -p 9001 -c .storybook",  "dev": "webpack-dev-server --mode development --open",  "build": "webpack --mode production"},"author": "","license": "ISC","devDependencies": {  "@storybook/addon-actions": "^3.4.3",  "@storybook/react": "v4.0.0-alpha.4",  "babel-cli": "^6.26.0",  "babel-core": "^6.26.0",  "babel-loader": "^7.1.4",  "babel-preset-env": "^1.6.1",  "babel-preset-react": "^6.24.1",  "babel-runtime": "^6.26.0",  "clean-webpack-plugin": "^0.1.19",  "eslint": "^4.19.1",  "eslint-config-prettier": "^2.9.0",  "eslint-loader": "^2.0.0",  "eslint-plugin-prettier": "^2.6.0",  "eslint-plugin-react": "^7.7.0",  "html-webpack-plugin": "^3.2.0",  "prettier": "^1.12.1",  "react": "^16.3.2",  "react-dom": "^16.3.2",  "webpack": "v4.6.0",  "webpack-cli": "^2.0.13",  "webpack-dev-server": "v3.1.3",  "webpack-md5-hash": "0.0.6",  "webpack-serve": "^0.3.1"},"dependencies": {  "source-map-support": "^0.5.5"}</code></pre><pre><code>}</code></pre><p>Eu deixei eles interagirem propositalmente para que vocês pudessem ver minhas tentativas de <em>debug.</em> <strong>Tudo funcionou exceto os mapas de origem.</strong> Abaixo, eu vou compartilhar o que ele mudou na minha configuração - mas, obviamente, é melhor checar o <em>pull request </em>completo <a href="https://github.com/marharyta/webpack-fast-development/pull/1/files?diff=unified" rel="noopener">aqui</a>.</p><pre><code> // webpack v4.6.0 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); // deleting this module from the config-const WebpackMd5Hash = require('webpack-md5-hash'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const stylish = require('eslint/lib/formatters/stylish'); const webpack = require('webpack');  module.exports = {  // adding the mode setting here instead of a flag+  mode: 'development',   entry: { main: './src/index.js' },   output: {  // deleting the path and the template from the output-    devtoolModuleFilenameTemplate: info =&gt;-      'file://' + path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),-    path: path.resolve(__dirname, 'dist'),     filename: '[name].[hash].js'   },  // adding resolve option here+  resolve: {+    extensions: ['.js', '.jsx']+  },   //changing the devtool option   devtool: 'eval-cheap-module-source-map',  // changing the devServer settings   devServer: {-    contentBase: './dist',-    hot: true+    hot: true,+    open: true   },   module: {     rules: [    // putting my two checks into one (later he asked me in the chat to delete eslint option completely)       {-        test: /\.js$/,-        exclude: /node_modules/,-        loader: 'babel-loader'-      },-      {-        test: /\.js$/,+        test: /\.jsx?$/,         exclude: /node_modules/,-        loader: 'eslint-loader',-        options: {-          formatter: stylish-        }+        use: [+          { loader: 'babel-loader' },+          { loader: 'eslint-loader', options: { formatter: stylish } }+        ]       }     ]   },   plugins: [    //cleaned some options-    new CleanWebpackPlugin('dist', {}),+    new CleanWebpackPlugin('dist'),    //deleted some settings from the HTMLWebpackPlugin     new HtmlWebpackPlugin({-      inject: false,-      hash: true,-      template: './src/index.html',-      filename: 'index.html'+      template: './src/index.html'     }),  //completely removed the hashing plugin and added a hot module replacement one</code></pre><pre><code>-    new WebpackMd5Hash(),-    new webpack.NamedModulesPlugin(),+    new webpack.HotModuleReplacementPlugin()   ] };</code></pre><p><em><em>package.json</em></em></p><pre><code>"main": "index.js",   "scripts": {     "storybook": "start-storybook -p 9001 -c .storybook",  //added different flags for webpack-dev-server-    "dev": "webpack-dev-server --mode development --open",+    "dev": "webpack-dev-server --config ./webpack.config.js",     "build": "webpack --mode production"   },   "author": "",@@ -31,11 +31,6 @@     "react-dom": "^16.3.2",     "webpack": "v4.6.0",     "webpack-cli": "^2.0.13",-    "webpack-dev-server": "v3.1.3",-    "webpack-md5-hash": "0.0.6",-    "webpack-serve": "^0.3.1"-  },-  "dependencies": {//moved dev server to dependencies</code></pre><pre><code>-    "source-map-support": "^0.5.5"+    "webpack-dev-server": "v3.1.3"   } }</code></pre><p>Como você pôde perceber, ele deletou um montão de módulos e adicionou <code>mode</code> dentro das configurações. Dando uma olhada no <em>pull request</em>, você pode ver que ele também adicionou alguns <em>hot-module replacement</em> específicos orientados ao React.</p><p>Isso ajudou os mapas de origem a funcionarem através do sacrifício de vários plug-ins, mas não tem uma explicação muito concreta para ele fazer o que ele fez. Como uma pessoa que costuma ler a documentação, isso não era muito satisfatório para mim.</p><p>Mais tarde, peguei meu webpack.config.js inicial e comecei adicionar as mudanças passo a passo para ver quando os mapas de origem finalmente começariam a funcionar.</p><p><strong>Mudança <strong>1:</strong></strong></p><pre><code>-    new CleanWebpackPlugin('dist', {}),+    new CleanWebpackPlugin('dist'),</code></pre><p><strong>Mudança <strong>2:</strong></strong></p><p>Adicionei o webpack-dev-server às dependências, não às devDependencies:</p><pre><code>...</code></pre><pre><code>},</code></pre><pre><code>"dependencies": {</code></pre><pre><code>  "webpack-dev-server": "^3.1.3"</code></pre><pre><code>}</code></pre><pre><code>}</code></pre><pre><code>...</code></pre><p><strong>Mudança <strong>3:</strong></strong></p><p>Em seguida, removi algumas das configurações de devServer:</p><pre><code>devServer: {-    contentBase: './dist',+    hot: true,+    open: true   },</code></pre><p><strong>Mudança <strong>4:</strong></strong></p><p>Remover o estilo:</p><pre><code>...</code></pre><pre><code>},</code></pre><pre><code>devtool: 'inline-source-map',  devServer: {    hot: true,    open: true  },</code></pre><pre><code>...</code></pre><pre><code>use: [  { loader: 'babel-loader' },  {    loader: 'eslint-loader'  }</code></pre><pre><code>]</code></pre><pre><code>....</code></pre><p><strong>Mudança <strong>5:</strong></strong></p><p>Vamos tentar remover o <em>plug-in</em> de <em>hashing </em>WebpackMd5Hash agora:</p><pre><code>...</code></pre><pre><code>module.exports = {mode: 'development',entry: { main: './src/index.js' },output: {  path: path.resolve(__dirname, 'dist'),  filename: '[name].js'  },devtool: 'inline-source-map',...</code></pre><pre><code>plugins: [  new CleanWebpackPlugin('dist'),  new HtmlWebpackPlugin({    inject: false,    hash: true,    template: './src/index.html',    filename: 'index.html'  })-    new WebpackMd5Hash(), ]</code></pre><pre><code>};</code></pre><p><strong>Mudança <strong>6:</strong></strong></p><p>Agora, vamos tentar remover algumas configurações do HtmlWebpackPlugin:</p><pre><code>...</code></pre><pre><code>plugins: [  new CleanWebpackPlugin('dist'),  new HtmlWebpackPlugin({    template: './src/index.html'  })]};</code></pre><pre><code>...</code></pre><p><strong>Mudança <strong>7:</strong></strong></p><p>Como podemos perceber nesse código, ele adicionou algumas opções aqui. Eu, pessoalmente, não entendo por que precisamos delas aqui. Eu não encontrei a informação nas documentações tampouco.</p><pre><code>...</code></pre><pre><code>resolve: {  extensions: ['.js', '.jsx']},module: {</code></pre><pre><code>...</code></pre><p><strong>Mudança <strong>8:</strong></strong></p><p>Deletando o caminho de saída:</p><pre><code>...</code></pre><pre><code>entry: { main: './src/index.js' },output: {  filename: '[name].js'},devtool: 'inline-source-map',</code></pre><pre><code>...</code></pre><p><strong>Mudança <strong>9:</strong></strong></p><p>Adicionando o plug-in <em>hot-module replacement</em>:</p><pre><code>...</code></pre><pre><code>const HtmlWebpackPlugin = require('html-webpack-plugin');const CleanWebpackPlugin = require('clean-webpack-plugin');const webpack = require('webpack');</code></pre><pre><code>...</code></pre><pre><code>plugins: [  new CleanWebpackPlugin('dist'),  new HtmlWebpackPlugin({    template: './src/index.html'  }),  new webpack.HotModuleReplacementPlugin()]};</code></pre><pre><code>...</code></pre><p><strong>Mudança <strong>10:</strong></strong></p><p>Adicionando — <code>config</code> ao <code>package.json</code>:</p><pre><code>-    "dev": "webpack-dev-server --mode development --open",+    "dev": "webpack-dev-server --config ./webpack.config.js",</code></pre><p><strong>Vamos deixar as coisas claras: nesse ponto, eu já tinha quase reescrito a configuração.</strong></p><p>Isso é um grande problema, visto que eu não posso reescrever tudo toda vez que algo não está funcionando!</p><h3 id="create-react-app-na-melhor-fonte-para-aprender-sobre-webpack"><strong><strong>Create-react-app </strong>na melhor fonte para aprender sobre<strong> webpack</strong></strong></h3><p>Como último recurso, eu fui verificar como o <code>create-react-app</code> implementa o mapa de origem. Essa foi a melhor decisão depois de tudo. Essa é a configuração do <a href="https://github.com/facebook/create-react-app/blob/next/packages/react-scripts/config/webpack.config.dev.js" rel="noopener">webpack na versão dev</a>.</p><p>Se conferirmos como o <strong><strong>devtool</strong></strong> é implementado aqui, veremos que:</p><blockquote>// Você pode querer 'avaliar' se você prefere ver a saída compilada no DevTools.</blockquote><blockquote><a href="https://github.com/facebook/create-react-app/issues/343." rel="noopener">https://github.com/facebook/create-react-app/issues/343.</a></blockquote><blockquote>devtool: ‘cheap-module-source-map’</blockquote><p>Então, isso me apontou uma outra <em>issue</em>, encontrada <a href="https://github.com/facebook/create-react-app/issues/343" rel="noopener">aqui</a>.</p><pre><code>// webpack v4</code></pre><pre><code>const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const WebpackMd5Hash = require('webpack-md5-hash');const CleanWebpackPlugin = require('clean-webpack-plugin');</code></pre><pre><code>module.exports = {</code></pre><pre><code>mode: "development",entry: { main: './src/index.js' },output: {  path: path.resolve(__dirname, 'dist'),  filename: '[name].[hash].js'},devtool: 'cheap-module-source-map',devServer: {  contentBase: './dist',  hot: true,  open: true},module: {</code></pre><p>Mudar as linhas ainda não funcionou - ainda! Eu aprendi que mapa de origem é uma questão que demanda tempo.</p><h3 id="perguntando-para-a-pessoa-certa"><strong>Perguntando para a pessoa certa</strong></h3><p>Todo projeto <em>open source</em> tem pessoas para mantê-lo. Nesse caso, era definitivamente uma boa alternativa perguntar aos meninos diretamente.</p><p>Embora meu método de tentativa e erro não tenha funcionado exatamente como eu esperava, fui surpreendida positivamente em como o time de manutenção estava disponível para responder.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/dcdxfW7u7weQbhJK1zspAgQblsQxbH9ObSzj.png" class="kg-image" alt="dcdxfW7u7weQbhJK1zspAgQblsQxbH9ObSzj" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/dcdxfW7u7weQbhJK1zspAgQblsQxbH9ObSzj.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/dcdxfW7u7weQbhJK1zspAgQblsQxbH9ObSzj.png 750w" sizes="(min-width: 720px) 720px" width="750" height="1334" loading="lazy"><figcaption>-&gt; <strong>"Oi! Parece que uma opção de devtool não funciona com o servidor de Dev do webpack. Ao menos, eu não vejo o comportamento esperado. Devo criar uma issue?" </strong>-&gt; "Hmm. Estranho. É, com certeza, vá no repositório webpack-dev-server. Veja se já não tem uma issue para ela. Qual é a opção?" -&gt; <strong>"Eu tentei todas elas. Todas as opções de devtool."</strong></figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/GP1mi8Ahmtigeeq-bzIxmVzcbs6LDUsf66zP.png" class="kg-image" alt="GP1mi8Ahmtigeeq-bzIxmVzcbs6LDUsf66zP" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/GP1mi8Ahmtigeeq-bzIxmVzcbs6LDUsf66zP.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/GP1mi8Ahmtigeeq-bzIxmVzcbs6LDUsf66zP.png 750w" sizes="(min-width: 720px) 720px" width="750" height="1334" loading="lazy"><figcaption>&gt; <strong>"Eu usei plugins, adicionei a bandeira -d. Os mapas de origem não funcionam. Atualmente, suspeito que o webpack não está gerando os mapas de origem por alguma razão" </strong>-&gt; "Não por padrão, você pode especificar isso na configuração do devtool, eu não sei se isso é permitido a nível de CLI no último lançamento"</figcaption></figure><p>Então, criei um repositório com a configuração que você encontra <a href="https://github.com/marharyta/webpack-4.6.0-test" rel="noopener">aqui</a>. Dê uma olhada no segundo commit lá.</p><p>Para torná-lo mais fácil para você, aqui está meu projeto webpack.js, onde eu voltei para a minha configuração inicial, mais limpa: </p><pre><code>// webpack v4</code></pre><pre><code>const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const WebpackMd5Hash = require('webpack-md5-hash');const CleanWebpackPlugin = require('clean-webpack-plugin');</code></pre><pre><code>module.exports = {  mode: 'development',  entry: { main: './src/index.js' },  output: {    path: path.resolve(__dirname, 'dist'),    filename: '[name].[hash].js'  },  devtool: 'inline-source-map',  devServer: {    contentBase: './dist',    hot: true,    open: true  },  module: {    rules: [      {        test: /\.js$/,        exclude: /node_modules/,        use: [          { loader: 'babel-loader' },          {            loader: 'eslint-loader',            options: {               formatter: require('eslint/lib/formatters/stylish')             }          }         ]        }      ]  },  plugins: [    new CleanWebpackPlugin('dist'),    new HtmlWebpackPlugin({      inject: false,      hash: true,      template: './src/index.html',      filename: 'index.html'    }),    new WebpackMd5Hash()  ]};</code></pre><p>Depois de verificar meu código, o responsável pela manutenção criou uma <a href="https://github.com/marharyta/webpack-4.6.0-test/issues/1" rel="noopener"><em>issue</em></a>.</p><p>Vamos recapitular o que ele incluiu aqui:</p><blockquote>Configuração da opção <code>mode</code></blockquote><blockquote>O primeiro problema que encontrei dizia respeito a como &nbsp;a opção <code>mode</code> foi configurada. Nos scripts do npm, <code>mode</code> estava definido como:</blockquote><blockquote><strong><strong>webpack --mode production</strong></strong></blockquote><blockquote>A maneira correta seria:</blockquote><blockquote><strong><strong>webpack --mode=production</strong></strong></blockquote><blockquote>Os scripts do npm aparecem mais ou menos assim para mim no final:</blockquote><blockquote><strong><strong>"scripts": {</strong></strong><br><strong><strong>"build": "webpack --mode=production",</strong></strong><br><strong><strong>"start": "webpack-dev-server --mode=development --hot"</strong></strong><br><strong><strong>}</strong></strong></blockquote><blockquote>Também alterei <code>dev</code> para <code>start</code>, já que é mais padrão e esperado de outros desenvolvedores como comando. Você pode, de fato, usar <code>npm start</code>, sem o comando <code>run</code>?</blockquote><pre><code>...</code></pre><pre><code>"scripts": {  "build": "webpack --mode production",  "dev": "webpack-dev-server --mode=development --hot"},</code></pre><pre><code>...</code></pre><p>A seguir, ele sugeriu o seguinte:</p><blockquote>devtool para mapas de origem</blockquote><blockquote>Sempre recomendo a opção <code>inline-source-map</code>. É a mais direta e está incluída no próprio bundle como um comentário ao final.</blockquote><blockquote><strong><strong>module.exports = {</strong></strong><br><strong><strong>+ devtool: 'inline-source-map',</strong></strong><br><strong><strong>// rest of your config</strong></strong><br><strong><strong>}</strong></strong></blockquote><blockquote>Eu também recomendo criar um objeto separado e populá-lo somente em desenvolvimento:</blockquote><blockquote>comando</blockquote><blockquote><strong><strong>webpack-dev-server --mode=development NODE_ENV=development</strong></strong></blockquote><blockquote>webpack.config.js</blockquote><blockquote><strong><strong>const webpackConfig = {}</strong></strong></blockquote><blockquote><strong><strong>if (process.env.NODE_ENV === 'development') {</strong></strong><br><strong><strong>webpackConfig.devtool = 'inline-source-map'</strong></strong><br><strong><strong>}</strong></strong></blockquote><blockquote>Dessa maneira, você tem certeza de que o bundle em produção não é afetado por isso.</blockquote><p>Então, ele sugeriu remover o ESLint da configuração de carregamento (em inglês, <em>loaders</em>):</p><blockquote>Linting e experiência do desenvolvedor</blockquote><blockquote><strong><strong>Honest</strong>amente<strong>, </strong>eu deletaria <strong><code>eslint</code> </strong>como um<strong> loader</strong>. É superinvasivo e atrapalha o fluxo de desenvolvimento<strong>. </strong>Eu prefiro adicionar um githook de<strong> precommit </strong>para verificar isso<strong>.</strong></strong></blockquote><blockquote>Isso é superfácil e somente precisa adicionar um script como esse:</blockquote><blockquote><strong><strong>"scripts": {</strong></strong><br><strong><strong>+ "lint": "eslint ./src/**/*.js"</strong></strong><br><strong><strong>"build": "webpack --mode=production",</strong></strong><br><strong><strong>"start": "webpack-dev-server --mode=development --hot"</strong></strong><br><strong><strong>}</strong></strong></blockquote><blockquote>Então, combiná-lo com husky</blockquote><p>e adicioná-lo aos scripts:</p><pre><code>...</code></pre><pre><code>"scripts": {</code></pre><pre><code>"lint": "eslint ./src/**/*.js",</code></pre><pre><code>"build": "webpack --mode production",</code></pre><pre><code>"dev": "webpack-dev-server --mode=development --hot"</code></pre><pre><code>},</code></pre><pre><code>...</code></pre><p>Eu cometi um erro em <code>src/<em><em>Hello.js</em></em></code><em><em> </em></em><strong>de propósito</strong><em><em> </em></em>para ver como os mapas de origem funcionariam dessa vez.</p><pre><code>import React from 'react';import PropTypes from 'prop-types';</code></pre><pre><code>class Hello extends React.Component {  console.log(hello.world);  render() {    return &lt;div&gt;{this.props.hello}&lt;/div&gt;;  }}Hello.propTypes = {  hello: PropTypes.string};export default Hello;</code></pre><h3 id="como-eu-consertei-o-problema"><strong>Como eu consertei o problema</strong></h3><p>O problema era o ESLint. Depois de especificar os modos corretamente e de remover o eslint-loader, os mapas de origem funcionaram corretamente!</p><p>Seguindo o exemplo que o mantenedor me deu, eu atualizei meu Webpack para:</p><pre><code>// webpack v4</code></pre><pre><code>const path = require('path');const HtmlWebpackPlugin = require('html-webpack-plugin');const WebpackMd5Hash = require('webpack-md5-hash');const CleanWebpackPlugin = require('clean-webpack-plugin');module.exports = {  entry: { main: './src/index.js' },  output: {    path: path.resolve(__dirname, 'dist'),    filename: '[name].[hash].js'  },  devtool: 'inline-source-map',  devServer: {    contentBase: './dist',    hot: true,    open: true  },  module: {    rules: [     {      test: /\.js$/,      exclude: /node_modules/,      use: [{ loader: 'babel-loader' }]     }    ]  },  plugins: [    new CleanWebpackPlugin('dist'),    new HtmlWebpackPlugin({      inject: false,      hash: true,      template: './src/index.html',      filename: 'index.html'    }),    new WebpackMd5Hash()  ]};</code></pre><p>e meu package JSON para:</p><pre><code>{</code></pre><pre><code>"name": "post","version": "1.0.0","description": "","main": "index.js","scripts": {  "build": "webpack --mode=production",  "start": "NODE_ENV=development webpack-dev-server --mode=development --hot"},"author": "","license": "ISC","devDependencies": {  "babel-cli": "^6.26.0",  "babel-core": "^6.26.0",  "babel-loader": "^7.1.4",  "babel-preset-env": "^1.6.1",  "babel-preset-react": "^6.24.1",  "babel-runtime": "^6.26.0",  "clean-webpack-plugin": "^0.1.19",  "eslint": "^4.19.1",  "eslint-config-prettier": "^2.9.0",  "eslint-loader": "^2.0.0",  "eslint-plugin-prettier": "^2.6.0",  "eslint-plugin-react": "^7.7.0",  "html-webpack-plugin": "^3.2.0",  "prettier": "^1.12.1",  "react": "^16.3.2",  "react-dom": "^16.3.2",  "webpack": "^4.6.0",  "webpack-cli": "^2.0.13",  "webpack-md5-hash": "0.0.6"},"dependencies": {  "webpack-dev-server": "^3.1.3"}</code></pre><pre><code>}</code></pre><p><strong>Finalmente, meus mapas de origem funcionaram<strong>!</strong></strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/57d0d324-44dd-4d12-ac55-edee632906eb.png" class="kg-image" alt="57d0d324-44dd-4d12-ac55-edee632906eb" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/57d0d324-44dd-4d12-ac55-edee632906eb.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/57d0d324-44dd-4d12-ac55-edee632906eb.png 800w" sizes="(min-width: 720px) 720px" width="800" height="556" loading="lazy"></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/06/cb4fa68e-9532-48ca-bae5-427c80de5977.png" class="kg-image" alt="cb4fa68e-9532-48ca-bae5-427c80de5977" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/06/cb4fa68e-9532-48ca-bae5-427c80de5977.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/06/cb4fa68e-9532-48ca-bae5-427c80de5977.png 750w" sizes="(min-width: 720px) 720px" width="750" height="1334" loading="lazy"><figcaption>-&gt; "O erro vem do hmr e eslint que honestamente é uma bagunça, dessa maneira eu sugiro remover o eslint como plugin de carregamento. Outro pacote provavelmente vai reportar o erro corretamente" -&gt; <strong>"Ok! Muito obrigada" Eu tentarei isso"</strong> -&gt;"👍" -&gt; <strong>"Oba! funcionou!"</strong></figcaption></figure><h3 id="conclus-es-"><strong>Conclusões<strong><strong><strong>:</strong></strong></strong></strong></h3><p>Mapas de origem tem pautado varias discussões de bugs desde 2016, como você pode ver <a href="https://github.com/webpack/webpack/issues/3165" rel="noopener">aqui</a>.</p><p><strong>O <strong>Webpack </strong>precisa de ajuda com auditoria<strong>!</strong></strong></p><p>Depois de encontrar esse bug, eu submeti uma <em><a href="https://github.com/webpack-contrib/eslint-loader/issues/227" rel="noopener">issue</a> </em>para o pacote ESLint loader.</p><p>Quando estamos falando de verificar a qualidade dos mapas de origem, nós podemos usar <a href="http://sokra.github.io/source-map-visualization/" rel="noopener">esta ferramenta</a>.</p><h3 id="o-que-voc-pode-fazer-se-tiver-um-problema-com-o-webpack"><strong>O que você pode fazer se tiver um problema com o <strong>webpack?</strong></strong></h3><p>Caso você se veja preso em um problema com Webpack, não se desespere! Siga os passos abaixo:</p><ul><li>Procure nas <em>issues </em>do GitHub situações similares às suas.</li><li>Tente conferir os arquivos iniciais e ver como o recurso foi implementado lá, com o create-react-app, por exemplo.</li><li>Faça perguntas no StackOverflow — não tenha medo! Tenha certeza de que você tentou todas as soluções possíveis ao seu alcance.</li><li>Não hesite em fazer um tweet sobre o assunto e perguntar diretamente aos mantenedores.</li><li>Crie issues no Github quando as encontrar. Isso vai ajudar muito os contribuidores!</li></ul><p>Neste artigo, eu deixo o meu arquivo de configuração e o processo usado para configurá-lo passo a passo. </p><p>Nota: considerando que muitas das dependências do npm podem ter mudado quando você estiver lendo isso, essa mesma configuração pode não funcionar para você!</p><p><strong>Se puder, compartilhe este artigo. Obrigada! </strong></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Você nunca deveria executar o Node.js diretamente em produção... será? ]]>
                </title>
                <description>
                    <![CDATA[ Às vezes, fico me perguntando se eu sei muito sobre poucas coisas.  Há algumas semanas, eu conversava com um amigo que mencionou despretensiosamente que "você nunca deveria rodar uma aplicação do Node diretamente em produção". Eu balancei minha cabeça vigorosamente em sinal de afirmação para demonstrar que eu também ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/voce-nunca-deveria-executar-o-node-js-diretamente-em-producao-sera/</link>
                <guid isPermaLink="false">63c6991291baea05fef70b0f</guid>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Thu, 04 May 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_Lh8JaRfiqPj9bTrd8a3xgQ.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/you-should-never-ever-run-directly-against-node-js-in-production-maybe-7fdfaed51ec6/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">You should never ever run directly against Node.js in production. Maybe.</a>
      </p><p>Às vezes, fico me perguntando se eu sei muito sobre poucas coisas. </p><p>Há algumas semanas, eu conversava com um amigo que mencionou despretensiosamente que "você nunca deveria rodar uma aplicação do Node diretamente em produção".</p><p>Eu balancei minha cabeça vigorosamente em sinal de afirmação para demonstrar que eu <em>também </em>nunca rodaria Node em produção porque... hahaha... todo mundo sabe disso. Só que eu não sabia disso! &nbsp;Eu deveria saber disso? EU AINDA TENHO PERMISSÃO PARA PROGRAMAR?</p><p>Se eu fosse desenhar um Diagrama de Venn do que eu sei <em>em comparação </em>com o que eu acho que os outros sabem, seria algo assim...</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_ThJbM2JMjrnTuHczo0tLqw.png" class="kg-image" alt="1_ThJbM2JMjrnTuHczo0tLqw" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_ThJbM2JMjrnTuHczo0tLqw.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_ThJbM2JMjrnTuHczo0tLqw.png 800w" sizes="(min-width: 720px) 720px" width="800" height="801" loading="lazy"><figcaption>O que eu sei x o que eu acho que os outros sabem</figcaption></figure><p>A propósito, aquele pontinho bem pequeno ali fica cada vez menor conforme eu fico velho.</p><p>Existe um diagrama melhor criado por <a href="https://medium.com/counter-intuition/overcoming-impostor-syndrome-bdae04e46ec5" rel="noopener">Alicia Liu</a> que, de certo modo, mudou minha vida. Ela diz que, na verdade, seria mais parecido com isso...</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_N7vv39ri9yC0l6krsSFlQA.png" class="kg-image" alt="1_N7vv39ri9yC0l6krsSFlQA" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_N7vv39ri9yC0l6krsSFlQA.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_N7vv39ri9yC0l6krsSFlQA.png 800w" sizes="(min-width: 720px) 720px" width="800" height="300" loading="lazy"></figure><p>Eu amo esse diagrama porque eu quero muito que ele seja verdade. Eu não quero passar o resto da minha vida como um pontinho azul pequeno – e cada vez menor – e insignificante.</p><p>MUITO DRAMÁTICO. Ao menos, não estamos partindo da <a href="https://open.spotify.com/playlist/1aab5jZgKu90bQIpzbWYKv?si=13121037c7ee46bd">estakazero</a>. Eu não estou controlando qual a próxima música que vai tocar enquanto estou escrevendo, mas escrever artigos com <a href="https://open.spotify.com/track/3XA3NYOoFxiLQe0OLtQlNw?si=593f031b37204d1e">confidências</a> é viciante como uma droga</p><p>Bem, assumindo que o diagrama de Alicia seja verdade, eu gostaria de compartilhar com você o que eu sei <em>agora </em>sobre rodar aplicações do Node em produção. Talvez nossos Diagramas de Venn relativos não se sobreponham nesse tema.</p><p>Primeiro, vamos analisar a declaração "nunca rodar aplicações do Node diretamente em produção".</p><h4 id="nunca-rodar-o-node-diretamente-em-produ-o"><strong>Nunca rodar o Node diretamente em produção</strong></h4><p>Talvez, sim; talvez, não. Vamos falar sobre o raciocínio por trás dessa declaração. Primeiro, vamos dar uma olhada nos motivos contra fazermos isso.</p><p>Suponha que temos um simples servidor do Express. O servidor do Express mais simples que eu posso pensar...</p><pre><code class="language-js">const express = require("express");
const app = express();
const port = process.env.PORT || 3000;

// Visível em http://localhost:3000
app.get("/", function(req, res) {
  res.send("Again I Go Unnoticed"); //"De novo, ninguém me nota", em português 
});

app.listen(port, () =&gt; console.log(`Example app listening on port ${port}!`)); //"App de exemplo escutando na porta... ", em português </code></pre><p>Rodaríamos isso usando o script <code>start</code> no arquivo <code>package.json</code>.</p><pre><code>"scripts": {
  "dev": "npx supervisor index.js",
  "start": "node index.js"
}</code></pre><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_VceC98Qk5zKqzBmiu1szDQ.png" class="kg-image" alt="1_VceC98Qk5zKqzBmiu1szDQ" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_VceC98Qk5zKqzBmiu1szDQ.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_VceC98Qk5zKqzBmiu1szDQ.png 800w" sizes="(min-width: 720px) 720px" width="800" height="478" loading="lazy"></figure><p>Existem dois problemas aqui. O primeiro é um problema de desenvolvimento. O segundo é um problema de produção.</p><p>O problema de desenvolvimento: ao mudarmos o código, teremos que parar e começar a aplicação para termos nossas mudanças captadas.</p><p>Para solucionar isso, normalmente usamos algo como um gerenciador de processos do Node, como o <code>supervisor</code> ou o <code>nodemon</code>. Esses pacotes observarão nosso projeto e reiniciarão nosso servidor sempre que fizermos mudanças. Eu normalmente faço algo assim:</p><pre><code>"scripts": {  "dev": "npx supervisor index.js",  "start": "node index.js"}</code></pre><p>Então, eu rodo <code>npm run dev</code>. Note que eu estou rodando <code>npx supervisor</code> aqui, o que me permite usar o pacote <code>supervisor</code> sem ter de instalá-lo. Eu amo a modernidade (na maior parte do tempo).</p><p>Nosso outro problema é que ainda estamos rodando diretamente o Node e já dissemos que era algo ruim. Agora, estamos prestes a descobrir o porquê.</p><p>Adiciono outro roteador aqui, que tenta ler um arquivo do disco que não existe. Esse é um erro que poderia facilmente aparecer em qualquer aplicação do mundo real. </p><pre><code class="language-js">const express = require("express");
const app = express();
const fs = require("fs");
const port = process.env.PORT || 3000;

// viewed at http://localhost:3000
app.get("/", function(req, res) {
  res.send("Again I Go Unnoticed");
});

app.get("/read", function(req, res) {
  // Isso aqui não existe 
  fs.createReadStream("minha-autoestima.txt");
});

app.listen(port, () =&gt; console.log(`Example app listening on port ${port}!`));</code></pre><p>Se rodarmos diretamente o Node com o <code>npm start</code> e navegarmos até o <em>endpoint</em> <code>read</code>, vamos obter um erro, pois o arquivo não existe.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_pAUoJV-LRJwxs7MRegnuoA.png" class="kg-image" alt="1_pAUoJV-LRJwxs7MRegnuoA" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_pAUoJV-LRJwxs7MRegnuoA.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_pAUoJV-LRJwxs7MRegnuoA.png 800w" sizes="(min-width: 720px) 720px" width="800" height="478" loading="lazy"></figure><p>Não é um grande problema, certo? É um erro. Acontece.</p><p>É um grande problema, sim. Se você voltar ao seu terminal, verá que sua aplicação quebrou completamente.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_69LuEt53W2isIXP34vUlYA.png" class="kg-image" alt="1_69LuEt53W2isIXP34vUlYA" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_69LuEt53W2isIXP34vUlYA.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_69LuEt53W2isIXP34vUlYA.png 800w" sizes="(min-width: 720px) 720px" width="800" height="450" loading="lazy"></figure><p>Isso significa que, se você voltar ao navegador e se tentar acessar o URL raiz do site, terá a mesma mensagem de erro. Um erro em apenas um método quebrou sua aplicação para <strong>todos os usuários</strong>. </p><p>Isso é péssimo – muito ruim, mesmo. Essa é uma das razões principais porque as pessoas dizem <strong>"nunca rode o Node diretamente em produção"</strong>. </p><p>OK. Então, se nós não podemos rodar o Node diretamente em produção, qual seria a maneira certa de fazê-lo?</p><h4 id="op-es-para-o-node-em-produ-o">Opções para o Node em produção</h4><p>Temos algumas opções.</p><p>Uma delas seria simplesmente usar algo como o <code>supervisor</code> ou o <code>nodemon</code> em produção da mesma maneira que o usamos em ambiente de desenvolvimento. Isso poderia funcionar, mas essas ferramentas são muito pouco para um controle adequado. Uma opção melhor é chamada de pm2.</p><h4 id="resolva-com-o-pm2">Resolva com o pm2</h4><p>O pm2 é um gerenciador de processos do Node que tem muitos alertas. Assim como todo o resto em "JavaScript", você o instala (globalmente) através do <code>npm</code> — ou pode simplesmente usar o <code>npx</code> outra vez. Faça como você quiser.</p><p>Existem muitas maneiras de rodar sua aplicação com o pm2. A maneira mais simples possível seria apenas chamar <code>pm2 start</code> no seu ponto de entrada.</p><pre><code>"scripts": {
  "start": "pm2 start index.js",
  "dev": "npx supervisor index.js"
},</code></pre><p>Você verá algo mais ou menos assim no seu terminal…,</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_uvsnmQahRBHtnh0X7tt_xA.png" class="kg-image" alt="1_uvsnmQahRBHtnh0X7tt_xA" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_uvsnmQahRBHtnh0X7tt_xA.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_uvsnmQahRBHtnh0X7tt_xA.png 800w" sizes="(min-width: 720px) 720px" width="800" height="450" loading="lazy"></figure><p>Esse é o nosso processo rodando, monitorado pelo pm2 em segundo plano. Se você visitar o endpoint <code>read</code> e se a aplicação quebrar, o pm2 vai reinicializá-la automaticamente. Você não verá nada disso no terminal, pois está rodando em segundo plano. Se você quiser observar o pm2 fazendo o trabalho, precisará rodar <code>pm2 log 0</code>, sendo <code>0</code> o id do processo cujos relatórios desejamos ver.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_AbR1eyySpr2IllYtA4wE-Q.png" class="kg-image" alt="1_AbR1eyySpr2IllYtA4wE-Q" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_AbR1eyySpr2IllYtA4wE-Q.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_AbR1eyySpr2IllYtA4wE-Q.png 800w" sizes="(min-width: 720px) 720px" width="800" height="450" loading="lazy"></figure><p>Pronto, tudo certo! Você pode ver a reinicialização da aplicação quando ela cair devido ao nosso erro não tratado. </p><p>Podemos também colocar nosso comando <code>dev</code> e fazer com que o pm2 observe os arquivos para nós e os reinicialize em quaisquer mudanças.</p><pre><code>"scripts": {
  "start": "pm2 start index.js --watch",
  "dev": "npx supervisor index.js"
},</code></pre><p>Note que, em função de o pm2 rodar tudo em segundo plano, você não pode simplesmente apertar <code>ctrl+c</code> para terminar o processo do pm2. Você tem que pará-lo usando o ID ou o nome.</p><p><code>pm2 stop 0</code></p><p><code>pm2 stop index</code></p><p>Observe, também, que o pm2 retém uma referência para o processo para que você possa recomeçá-lo. </p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_Z4yLru6TmwUVQv84DkZDAQ.png" class="kg-image" alt="1_Z4yLru6TmwUVQv84DkZDAQ" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_Z4yLru6TmwUVQv84DkZDAQ.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_Z4yLru6TmwUVQv84DkZDAQ.png 800w" sizes="(min-width: 720px) 720px" width="800" height="450" loading="lazy"></figure><p>Se você quiser excluir a referência do processo, precisa executar <code>pm2 delete</code>. Você pode parar e excluir um processo em um comando com <code>delete</code>.</p><p><code>pm2 delete index</code></p><p>Também podemos usar o pm2 para rodar múltiplos processos da nossa aplicação. O pm2 automaticamente balanceará o carregamento através dessas instâncias.</p><h4 id="m-ltiplos-processos-com-pm2-no-modo-fork"><strong><strong>M</strong>ú<strong>ltipl</strong>os<strong> process</strong>o<strong>s</strong> com <strong>pm2 </strong>no modo <strong>fork</strong></strong></h4><p>O pm2 tem diversas opções de configuração e elas estão contidas em um arquivo de "ecossistema". Para criar um, execute <code>pm2 init</code>. Você vai obter um resultado parecido com esse...</p><pre><code class="language-js">module.exports = {
  apps: [
    {
      name: "Express App",
      script: "index.js",
      instances: 4,
      autorestart: true,
      watch: true,
      max_memory_restart: "1G",
      env: {
        NODE_ENV: "development"
      },
      env_production: {
        NODE_ENV: "production"
      }
    }
  ]
};</code></pre><p>Eu vou ignorar a seção "deploy" nesse artigo, pois não tenho a menor ideia do que ela faz. </p><p>A seção "apps" é onde você define as aplicações que você deseja que o pm2 rode e monitore. Você pode rodar mais de uma. Muitas dessas configurações do arquivo são, provavelmente, autoexplicativas. Eu quero me concentrar naquela configuração chamada <em>instances</em> (em português, <strong>instâncias</strong>).</p><p>O pm2 pode rodar múltiplas instâncias da sua aplicação. Você pode informar um número de <em>instances</em> que você deseja rodar e o pm2 fará o trabalho. Então, se queremos rodar 4 <em>instances</em>, podemos rodar o seguinte arquivo de configuração: </p><pre><code class="language-js">module.exports = {
  apps: [
    {
      name: "Express App",
      script: "index.js",
      instances: 4,
      autorestart: true,
      watch: true,
      max_memory_restart: "1G",
      env: {
        NODE_ENV: "development"
      },
      env_production: {
        NODE_ENV: "production"
      }
    }
  ]
};</code></pre><p>Então, tudo que precisamos é digitar <code>pm2 start</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1__rYp7NMQTCMmOfBV0w0RTw.png" class="kg-image" alt="1__rYp7NMQTCMmOfBV0w0RTw" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1__rYp7NMQTCMmOfBV0w0RTw.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1__rYp7NMQTCMmOfBV0w0RTw.png 800w" sizes="(min-width: 720px) 720px" width="800" height="450" loading="lazy"></figure><p>O pm2 está agora rodando em um modo <em>cluster</em> (em português, "agrupamento"). Cada um desses processos está rodando em uma CPU diferente na minha máquina, dependendo de quantos núcleos de processamento (em inglês, <em>cores</em>)<em> </em>eu tiver. Se rodarmos um processo para cada <em>core, </em>sem saber quantos <em>cores</em> temos, precisamos apenas passar o parâmetro <code>max</code> para o valor <code>instances</code>.</p><pre><code>{
   ...
   instances: "max",
   ...
}</code></pre><p>Vamos descobrir quantos <em>cores </em>eu tenho nessa máquina.</p><figure class="kg-card kg-image-card"><img src="https://cdn-media-1.freecodecamp.org/images/1*nhjuG0xFsMgkYB382_xfyw.png" class="kg-image" alt="1*nhjuG0xFsMgkYB382_xfyw" width="800" height="450" loading="lazy"></figure><p>8 CORES! Nossa!!! Eu vou instalar <em>Subnautica</em> na minha máquina da Microsoft. Não diga a eles que eu disse isso.</p><p>A parte coisa sobre rodar processos em CPUs separadas é que, se você tiver um processo que perca o controle e tome 100% da CPU, os outros ainda funcionarão. Se você passar mais instâncias do que <em>cores</em>, o pm2 dobrará o número de processos em uma CPUs conforme for necessário.</p><p>Você pode fazer MUITO mais com o pm2, incluindo monitoramento e o tratamento daquelas <a href="https://medium.freecodecamp.org/">variáveis de ambiente</a> irritantes.</p><p>Um outro item a se observar: se, por alguma razão, você quiser executar seu script <code>npm start</code>, você pode fazer isso através do npm como processo e passar a bandeira <code>-- start</code>. O espaço antes de <em>start</em> (em português, "início") é super importante aqui.</p><pre><code>pm2 start npm -- start</code></pre><p>No <a href="https://docs.microsoft.com/en-us/azure/app-service/?WT.mc_id=medium-blog-buhollan" rel="noopener">Azure AppService</a>, incluímos o pm2 por padrão em segundo plano. Se você quiser usar o pm2 no Azure, não precisa incluí-lo em seu arquivo <code>package.json</code>. Você somente precisa adicionar um arquivo de ecossistema e está pronto para continuar.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_TYOm2q57lXad3te35tGwqg.png" class="kg-image" alt="1_TYOm2q57lXad3te35tGwqg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_TYOm2q57lXad3te35tGwqg.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_TYOm2q57lXad3te35tGwqg.png 800w" sizes="(min-width: 720px) 720px" width="800" height="495" loading="lazy"></figure><p>Certo! Agora que aprendemos tudo sobre o pm2, vamos falar um pouco mais sobre o porquê você pode querer não usá-lo. Também veremos que pode, de fato, não haver problema em rodar o Node diretamente. </p><h4 id="rodando-o-node-diretamente-em-produ-o"><strong>Rodando o Node diretamente em produção</strong></h4><p>Eu tinha algumas dúvidas sobre isso. Então, falei com <a href="https://twitter.com/bitandbang" rel="noopener">Tierney Cyren</a>, que é uma parte enorme do círculo laranja de conhecimento, especialmente quando o assunto é o Node.</p><p>Tierney me apontou algumas desvantagens de se usar o Node baseado em gerenciadores de processos, como o pm2.</p><p>A principal delas é que você não deveria usar Node para monitorar a si mesmo. Você não quer usar a coisa que você está monitorando para monitorar a si mesma. Seria como pedir ao meu filho adolescente para tomar conta de si mesmo em uma sexta-feira à noite: isso acabaria mal? Pode ser que sim, pode ser que não, mas você não quer descobrir da pior maneira.</p><p>Tierney recomenda que você não tenha um gerenciador de processos do Node rodando em sua aplicação nunca. Ao invés disso, tenha algo em um nível mais alto que observe em conjunto as instâncias separadas de sua aplicação. Por exemplo, uma configuração ideal seria se você tivesse um agrupamento de Kubernetes com sua aplicação rodando em contêineres separados. O Kubernetes podem, então, monitorar aqueles contêineres e, se algum deles cair, pode trazê-lo de volta e registrar sua condição de integridade. </p><p>Nesse caso, você <strong>pode </strong>rodar o Node diretamente porque você o está monitorando um nível acima.</p><p>Acontece que o Azure já está fazendo isso. Se não subirmos um arquivo de ecossistema do pm2 para o Azure, a aplicação iniciará com nosso script <code>start</code> do nosso arquivo <code>package.json</code> e podemos, então, rodar diretamente o Node. </p><pre><code>"scripts": {
  "start": "node index.js"
}</code></pre><p>Nesse caso, estamos rodando diretamente o Node e não há problema. Se a aplicação quebrar, você notará que ela reinicializará. Isso acontece porque, no Azure, sua aplicação roda em um contêiner. O Azure está orquestrando o contêiner que sua aplicação está rodando e saberá quando ela tiver problemas.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_YSvtZOR4DIt1McSdDChVew.png" class="kg-image" alt="1_YSvtZOR4DIt1McSdDChVew" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/04/1_YSvtZOR4DIt1McSdDChVew.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/04/1_YSvtZOR4DIt1McSdDChVew.png 800w" sizes="(min-width: 720px) 720px" width="800" height="495" loading="lazy"></figure><p>Porém, você ainda tem apenas uma instância aqui. É necessário, ao contêiner, algum tempo para voltar a ficar on-line depois de quebrar, o que significa que, nesse tempo, sua aplicação estaria fora do ar para seus usuários.</p><p>Idealmente, você vai querer ter mais de um contêiner rodando. A solução para isso seria subir múltiplas <em>instances </em>de sua aplicação para múltiplos sites do <em>Azure AppService</em> e, então, usar o <em>Azure Front Door</em> para balancear a carga das aplicações por detrás de um único endereço de IP. O <em>Front Door</em> saberá quando um contêiner cair e roteará o tráfego para outras instâncias funcionais de sua aplicação.</p><p><strong>Saiba mais sobre o serviço Azure Front Door <a href="https://azure.microsoft.com/pt-br/products/frontdoor/?WT.mc_id=medium-blog-buhollan">aqui</a>.</strong></p><h4 id="systemd"><strong><strong>systemd</strong></strong></h4><p>Outra sugestão que o Tierney nos dá é rodar o Node com <code>systemd</code>. Eu não entendo muito (na verdade, não entendo nada) sobre <code>systemd</code> e já estraguei tudo parafraseando ele uma vez. Então, vamos deixar Tierney isso dizer com suas próprias palavras…</p><blockquote>Essa opção só é possível se você tiver acesso ao Linux na sua implementação (em inglês, <strong>deployment</strong>), e se você controlar o modo como o Node é inicializado em nível de serviço. Se você estiver rodando seu processo do Node.js em uma máquina virtual (em inglês, <strong>VM</strong>), Linux de longa duração, como as VMs do Azure, poderá rodar o Node.js com systemd sem problemas. Se você só estiver fazendo o deploy de seus arquivos para um serviço como o Azure AppService ou o Heroku ou rodando o Node dentro de um ambiente conteinerizado, como o Azure Container Instances, você provavelmente deverá evitar essa opção.</blockquote><p><strong><a href="https://nodesource.com/blog/running-your-node-js-app-with-systemd-part-1/"><strong>R</strong>odando seu App<strong> Node.js </strong>com<strong> Systemd - Part</strong>e<strong> 1</strong></a></strong> (texto em inglês sobre o assunto)</p><h4 id="worker-threads-do-node-js"><strong><strong>Worker Threads</strong> do <strong>Node.js</strong></strong></h4><p>Tierney também gostaria de informar que as Worker Threads estão chegando ao Node. Isso permitirá a você começar sua aplicação em múltiplos "trabalhadores" (em inglês, <em>workers</em>), evitando, desse modo, a necessidade de algo como o pm2. Talvez. Eu não sei. De fato, não li esse artigo. </p><p><strong><a href="https://nodejs.org/api/worker_threads.html#worker-threads">Documentação d0 <strong>Node.js </strong>sobre Worker Threads</a></strong></p><h4 id="seja-um-adulto">Seja um adulto</h4><p>A última sugestão de Tierney era apenas para lidar com o erro e escrever alguns testes (como um adulto faria), mas quem tem tempo para isso, não é mesmo?</p><h4 id="o-pequeno-c-rculo-permanece">O pequeno círculo permanece</h4><p>Agora você sabe a maior parte do que está no pequeno círculo azul. O restante tem a ver apenas com fatos inúteis sobre bandas e cerveja.</p><p>Para mais informações sobre o pm2, Node e Azure, confira os recursos a seguir:</p><ul><li><a href="https://nodejs.org/api/worker_threads.html#worker-threads"></a><a href="https://pm2.keymetrics.io/">Site do PM2</a></li><li><a href="https://code.visualstudio.com/docs/nodejs/nodejs-deployment">Implantação do Node.js com o VS Code</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como gerar sua própria chave privada de Bitcoin ]]>
                </title>
                <description>
                    <![CDATA[ Em criptomoedas, uma chave privada permite aos usuários acessar suas carteiras. A pessoa que possui a chave privada controla completamente as moedas naquela carteira. Por essa razão, você deve mantê-la em segredo. Se você realmente quiser gerar sua própria chave, faz sentido que ela seja gerada de uma maneira segura. ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-gerar-sua-propria-chave-privada-de-bitcoin/</link>
                <guid isPermaLink="false">6377ee6de2dc4305dbdcf1cc</guid>
                
                    <category>
                        <![CDATA[ Bitcoin ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Wed, 15 Mar 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/03/1_6pWGbFF9kCmlGSra6Kmh-w.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-generate-your-very-own-bitcoin-private-key-7ad0f4936e6c/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to generate your very own Bitcoin private key</a>
      </p><p>Em criptomoedas, uma chave privada permite aos usuários acessar suas carteiras. A pessoa que possui a chave privada controla completamente as moedas naquela carteira. Por essa razão, você deve mantê-la em segredo. Se você realmente quiser gerar sua própria chave, faz sentido que ela seja gerada de uma maneira segura. </p><p>Aqui, vou introduzir o assunto das chaves privadas e mostrar como você pode gerar sua própria chave usando várias funções criptográficas. Vou fornecer a descrição de um algoritmo e seu código em Python.</p><h3 id="eu-preciso-gerar-uma-chave-privada"><strong>Eu preciso gerar uma chave privada? </strong></h3><p>Na maioria das vezes, não. Por exemplo, se você usar uma carteira web como a Coinbase ou Blockchain.info, elas criam e administram a chave privada para você. A mesma coisa acontece com as transações.</p><p>Carteiras de celulares e desktops também geram normalmente uma chave privada para você, embora elas também possam dar a opção de gerar sua própria chave.</p><p>Então, porque gerá-la mesmo assim? Aqui, eu cito alguns motivos meus:</p><ul><li>Você quer ter certeza de que ninguém sabe qual é a chave</li><li>Você quer apenas aprender mais sobre criptografia e geração de números aleatórios (do inglês, <em>random number generation, ou RNG</em>)</li></ul><h3 id="o-que-exatamente-uma-chave-privada"><strong>O que exatamente é uma chave privada?</strong></h3><p>Formalmente, uma chave privada para Bitcoin (e muitas outras criptomoedas) é uma série de 32 bytes. Hoje em dia, existem muitas maneiras de gravar esses bytes. Eles podem ser gravados como uma sequência de caracteres (em inglês, <em>string</em>)<em> </em>de 256 zeros e uns (32 * 8 = 256) ou 100 jogadas de dados. Pode ser uma <em>string </em>binária, uma string de base 64, uma <a href="https://en.bitcoin.it/wiki/Wallet_import_format">chave WIF</a> (texto em inglês), uma <a href="https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki" rel="noopener">frase mnemônica</a> (texto em inglês) ou ainda uma <em>string </em>hexadecimal. Neste artigo, usaremos uma<em> string hexadecimal</em> de 64 caracteres.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/03/lyrhBKkIKdFsCCrBdSXnaRYJrwj67NUaMXNy.png" class="kg-image" alt="lyrhBKkIKdFsCCrBdSXnaRYJrwj67NUaMXNy" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/03/lyrhBKkIKdFsCCrBdSXnaRYJrwj67NUaMXNy.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/03/lyrhBKkIKdFsCCrBdSXnaRYJrwj67NUaMXNy.png 800w" sizes="(min-width: 720px) 720px" width="800" height="320" loading="lazy"><figcaption>A mesma chave privada, escrita em formatos diferentes.</figcaption></figure><p>Por que exatamente 32 bytes? Ótima pergunta! Para criar uma chave pública a partir de uma privada, Bitcoin usa o <strong><strong>ECDSA</strong></strong>, ou Algoritmo de Assinatura Digital de Curva Elíptica (em inglês, <em>Elliptic Curve Digital Signature Algorithm</em>). Mais especificamente, usa uma curva particular chamada <strong><strong>secp256k1</strong></strong>.</p><p>Essa curva tem um arranjo de 256 bits, tem uma entrada de 256 bits e uma saída contendo números de 256 bits. 256 bits são exatamente 32 bytes. Isso quer dizer que precisamos de 32 bytes de dados para alimentar esse algoritmo de curva.</p><p>Existe um requisito adicional para uma chave privada. Por usarmos ECDSA, a chave deve ser positiva e inferior ao arranjo da curva. O arranjo de secp256k1 é <code>FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141</code>, o que é relativamente grande: quase todo número de 32 bytes será menor que ele.</p><h3 id="modo-iniciante"><strong>Modo iniciante</strong></h3><p>Então, como geramos um número de 32 bytes? A primeira coisa que nos vem à mente é usar uma biblioteca RNG na linguagem de sua escolha. Python até oferece uma maneira bonita de gerar esses números com apenas alguns bits necessários:</p><pre><code class="language-python">import random
bits = random.getrandbits(256)
# 30848827712021293731208415302456569301499384654877289245795786476741155372082
bits_hex = hex(bits)
# 0x4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32
private_key = bits_hex[2:]
# 4433d156e8c53bf5b50af07aa95a29436f29a94e0ccc5d58df8e57bdc8583c32</code></pre><p>Parece bom, mas na verdade não é. Bibliotecas RNG comuns não foram criadas para criptografia. Sendo assim, elas não são muito seguras. Elas geram números baseados em uma semente (em inglês <em>seed</em>), que por padrão, é a hora atual. Sendo assim, se você souber aproximadamente quando eu gerei os bits acima, tudo o que você precisa é tentar um ataque de força bruta com algumas variantes.</p><p>Quando você gera uma chave privada, deseja estar extremamente seguro. Lembre-se de que, se alguém souber a chave privada, poderá facilmente roubar todas as moedas da carteira correspondente. Você não terá chance nenhuma de recuperá-las de volta.</p><p>Então, vamos tentar uma maneira mais segura.</p><h3 id="rng-criptograficamente-forte"><strong><strong>RNG</strong> criptograficamente forte</strong></h3><p>Junto com o método básico de RNG, linguagens de programação normalmente fornecem um RNG desenhado especificamente para operações criptográficas. Esse método é usualmente muito mais seguro, pois extrai entropia diretamente do sistema operacional. O resultado desse RNG é muito mais difícil de reproduzir. Você não pode obtê-lo apenas sabendo a hora que foi gerado ou sua <em>seed, </em>pois não há nenhuma <em>seed. </em>Bom, ao menos o usuário não insere nenhuma <em>seed. </em>Ela é criada pelo programa.</p><p>Em Python, um RNG criptograficamente forte é implementado pelo módulo <code>secrets</code>. Vamos modificar o código acima para tornar a geração da chave privada mais segura! </p><pre><code class="language-python">import secrets
bits = secrets.randbits(256)
# 46518555179467323509970270980993648640987722172281263586388328188640792550961
bits_hex = hex(bits)
# 0x66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31
private_key = bits_hex[2:]
# 66d891b5ed7f51e5044be6a7ebe4e2eae32b960f5aa0883f7cc0ce4fd6921e31</code></pre><p>Isso é incrível. Eu aposto que você não seria capaz de reproduzi-lo, mesmo com acesso ao meu PC. Podemos, no entanto, ir mais longe.</p><h3 id="sites-especializados"><strong>Sites especializados</strong></h3><p>Existem sites que geram esses números aleatórios para você. Vamos analisar apenas dois aqui. Um é o <a href="https://random.org/" rel="noopener">random.org</a>, um gerador de números bem conhecido. Outro é o <a href="https://bitaddress.org/" rel="noopener">bitaddress.org</a>, que é desenhado especificamente para geração de chaves privadas de Bitcoin.</p><p>O <a href="https://random.org/" rel="noopener">random.org</a> poderianos ajudar a gerar uma chave? Com certeza, já que eles têm um <a href="https://www.random.org/bytes" rel="noopener">serviço</a> para geração de bytes aleatórios. Porém, dois problemas emergem aqui. O <a href="https://random.org/" rel="noopener">random.org</a> afirma ser um gerador verdadeiramente aleatório, mas será que você pode confiar? Você consegue ter certeza que é, de fato, aleatório? Como ter certeza de que o proprietário não grava todos os resultados gerados, especialmente os que se parecem com chaves privadas? Essa resposta fica a seu critério. Ah, você também não pode executá-lo localmente, o que gera um problema adicional. Esse método, definitivamente, não é 100% seguro.</p><p>Já com o <a href="https://bitaddress.org/" rel="noopener">bitaddress.org</a>, a história é completamente diferente. Ele é de código aberto (em inglês <em>open source</em>). Então, você pode checar o que há debaixo dos panos. É <em>client-side</em>, ou seja você pode fazer o download e executá-lo localmente, até mesmo sem conexão a internet.</p><p>Então, como funciona? Ele usa você – sim, você – como uma fonte de entropia. Ele pede que você mova seu mouse ou pressione teclas aleatórias. Você realiza esses passos até que seja inviável reproduzir os resultados.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/03/y5eDywvm3A2NMywdEk2u6pQYUORSP42gtWr2.gif" class="kg-image" alt="y5eDywvm3A2NMywdEk2u6pQYUORSP42gtWr2" width="862" height="540" loading="lazy"><figcaption>O processo de gerar uma entropia ao mover o mouse aleatoriamente. Os inúmeros símbolos mostram o agrupamento.</figcaption></figure><p>Tem interesse em descobrir como o <a href="https://bitaddress.org/" rel="noopener">bitaddress.org</a> funciona? Para fins educacionais, vamos dar uma olhada no seu código e tentar reproduzi-lo em Python.</p><blockquote><em>Nota rápida<em>: </em>o <em>bitaddress.org </em>fornece a chave privada em formato de <em>WIF </em>comprimido<em>, </em>que é parecido ao <a href="https://en.bitcoin.it/wiki/Wallet_import_format">formato <em>WIF</em></a><em> </em>(texto em inglês) que discutimos previamente. Para nossos propósitos, faremos o algoritmo retornar uma string hexadecimal. Dessa maneira, poderemos usá-la mais tarde na geração de uma chave pública.</em></blockquote><h3 id="bitaddress-as-especificidades"><strong><strong>Bitaddress: </strong>as especificidades</strong></h3><p>O Bitaddress cria uma entropia em duas maneiras: pelo movimento do mouse e pelo pressionamento das teclas. Vamos falar sobre ambos, mas vamos focar no pressionamento das teclas, já que é difícil de implementar o rastreamento do mouse na biblioteca do Python. Esperaremos o usuário apertar teclas suficientes até que tenhamos entropia suficiente. Então, geraremos uma chave.</p><p>O Bitaddress faz três coisas. Ele inicializa um <em>array </em>de bytes, tentando obter o máximo de entropia possível de seu computador, preenche o <em>array </em>com inserções do usuário (em inglês, <em>inputs) </em>e, então, gera uma chave privada.</p><p>O Bitaddress usa o <em>array </em>de 256 bytes para guardar a entropia. Esse <em>array</em> é reescrito em ciclos. Então, quando o array é preenchido pela primeira vez, o ponteiro vai para zero e o processo de preenchimento começa de novo.</p><p>O programa inicia um <em>array </em>com 256 bytes a partir de <a href="https://developer.mozilla.org/pt-BR/docs/Web/API/crypto_property">window.crypto</a>. Então, ele escreve um número representativo de um momento específico (nesse caso, a hora atual – em inglês, <em>timestamp</em>)<em> </em>para obter 4 bytes adicionais de entropia. Por fim, ele obtém dados como o tamanho da tela, seu fuso horário, informações sobre os plugins do seu navegador, sua localização e mais. Isso resulta em outros 6 bytes.</p><p>Depois da inicialização, o programa continuamente espera os <em>inputs </em>do usuário para reescrever os bytes iniciais. Quando o usuário move o cursor, o programa escreve a posição do cursor. Quando o usuário pressiona teclas, o programa escreve o código da tecla pressionada.</p><p>Finalmente, o bitaddress usa a entropia acumulada para gerar uma chave privada. É preciso gerar 32 bytes. Para essa tarefa, o bitaddress usa um algoritmo RNG chamado ARC4. O programa inicializa o ARC4 com a hora atual e a entropia coletada e, então, coleta os bytes um por um 32 vezes.</p><p>Isso tudo é uma simplificação de como o programa funciona, mas eu espero que tenha dado uma ideia de como ele é. Você pode checar o algoritmo completo e seus detalhes no <a href="https://github.com/pointbiz/bitaddress.org" rel="noopener">Github</a>.</p><h3 id="fa-a-voc-mesmo"><strong>Faça você mesmo</strong></h3><p>Para nossos propósitos, construiremos uma versão mais simples do bitaddress. Não vamos coletar dados sobre a localização e a máquina do usuário, somente inseriremos a entropia via texto, visto que é bem desafiador receber continuamente a posição do mouse em um script do Python (dê uma olhada em <a href="https://github.com/asweigart/pyautogui" rel="noopener">PyAutoGUI</a> se tiver interesse em fazer isso).</p><p>Isso nos traz para especificação formal de nossa biblioteca geradora. Primeiro, vamos inicializar um <em>array </em>de byte com RNG criptográfico. Depois, preencheremos com a <em>timestamp</em> e, finalmente, preencheremos com os caracteres inseridos pelo usuário. Depois que o agrupamento de <em>seeds</em> for preenchido, a biblioteca deixará o desenvolvedor criar a chave. Na verdade, o desenvolvedor será capaz de criar quantas chaves privadas ele quiser – tudo seguro de acordo com a entropia coletada.</p><h4 id="inicializando-o-agrupamento"><strong><strong>Ini</strong>c<strong>ializ</strong>ando o agrupamento</strong></h4><p>Aqui, colocamos alguns bytes de RNG criptográfico e uma <em>timestamp</em>. <code>__seed_int</code> e<code>__seed_byte</code> são dois métodos auxiliares que inserem entropia ao nosso array agrupador. Note que usamos a biblioteca <code>secrets</code>.</p><pre><code class="language-python">def __init_pool(self):
    for i in range(self.POOL_SIZE):
        random_byte = secrets.randbits(8)
        self.__seed_byte(random_byte)
    time_int = int(time.time())
    self.__seed_int(time_int)
def __seed_int(self, n):
    self.__seed_byte(n)
    self.__seed_byte(n &gt;&gt; 8)
    self.__seed_byte(n &gt;&gt; 16)
    self.__seed_byte(n &gt;&gt; 24)
def __seed_byte(self, n):
    self.pool[self.pool_pointer] ^= n &amp; 255
    self.pool_pointer += 1
    if self.pool_pointer &gt;= self.POOL_SIZE:
        self.pool_pointer = 0</code></pre><h4 id="alimentando-com-input"><strong>Alimentando com <strong><em>input</em></strong></strong></h4><p>Aqui, primeiro colocamos uma <em>timestamp </em>e, então, inserimos os caracteres, um a um.</p><pre><code>def seed_input(self, str_input):
    time_int = int(time.time())
    self.__seed_int(time_int)
    for char in str_input:
        char_code = ord(char)
        self.__seed_byte(char_code)</code></pre><h4 id="gerando-a-chave-privada"><strong><strong>Gera</strong>ndo a chave privada</strong></h4><p>Essa parte pode parecer difícil, mas, na verdade, é bem simples.</p><p>Primeiro, precisamos gerar um número de 32 bytes usando nosso agrupador. Infelizmente, não podemos apenas criar nosso próprio objeto <code>random</code> e usá-lo somente para a geração da chave. Ao invés disso, existe um objeto compartilhado que é usado por qualquer código que está sendo executado em um único script.</p><p>O que isso significa para nós? Significa que a cada momento, em qualquer lugar do código, um simples <code>random.seed(0)</code> pode destruir toda nossa entropia coletada. Não queremos isso. Por sorte, o Python nos dá os métodos <code>getstate</code> e <code>setstate</code>. Dessa maneira, para salvar nossa entropia cada vez que geramos uma chave, nós lembramos o estado onde paramos e o configuramos na próxima vez que necessitamos fazer uma chave.</p><p>Em segundo lugar, queremos ter certeza de que nossa chave está em uma variação específica (1, <code>CURVE_ORDER</code>). Isso é um requisito para todas chaves privadas ECDSA. O <code>CURVE_ORDER</code> é o arranjo da curva secp256k1, que é <code>FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141</code>.</p><p>Por fim, por conveniência, convertemos para hexadecimal e cortamos a parte '0x'.</p><pre><code class="language-python">def generate_key(self):
    big_int = self.__generate_big_int()
    big_int = big_int % (self.CURVE_ORDER — 1) # key &lt; curve order
    big_int = big_int + 1 # key &gt; 0
    key = hex(big_int)[2:]
    return key
def __generate_big_int(self):
    if self.prng_state is None:
    seed = int.from_bytes(self.pool, byteorder=’big’, signed=False)
    random.seed(seed)
    self.prng_state = random.getstate()
    random.setstate(self.prng_state)
    big_int = random.getrandbits(self.KEY_BYTES * 8)
    self.prng_state = random.getstate()
    return big_int</code></pre><h4 id="em-a-o"><strong>Em ação</strong></h4><p>Vamos tentar usar a biblioteca. Na verdade, é bem simples: você pode gerar uma chave privada em três linhas de código! </p><pre><code>kg = KeyGenerator()
kg.seed_input(‘Uma frase completamente aleatória. Eu joguei um dado e obtive 4.’)
kg.generate_key()
# 60cf347dbc59d31c1358c8e5cf5e45b822ab85b79cb32a9f3d98184779a9efc2</code></pre><p>Veja você mesmo. A chave é aleatória e totalmente válida. Além disso, cada vez que você rodar o código, obterá um resultado diferente.</p><h3 id="conclus-o"><strong><strong>Conclus</strong>ão</strong></h3><p>Como pudemos ver, existem muitas maneiras de se gerar uma chave privada. Elas diferem em simplicidade e segurança.</p><p>Gerar uma chave privada é apenas o primeiro passo. O próximo passo é extrair uma chave pública e uma carteira que será usada para receber seus pagamentos. O processo de gerar uma carteira difere um pouco para Bitcoin e Ethereum. Eu planejo escrever outros dois artigos explorando mais esse tópico.</p><p>Se você quiser brincar com o código, ele está nesse <a href="https://github.com/Destiner/blocksmith">repositório do Github</a>.</p><p><em>O autor também trabalhou em um curso de criptomoedas<em>. </em>A <a href="https://medium.com/longcaller/blockchain-explained-2b26b28657ca">primeira parte</a> (texto em inglês) é uma descrição detalhada de blockchain.</em></p><p><em>Ele também publica pensamentos aleatórios sobre criptografia e criptomoedas no <em><a href="https://twitter.com/DestinerX" rel="noopener">Twitter</a></em>.<em> </em>Talvez você queira conferir.</em></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como testar componentes do React: o guia completo ]]>
                </title>
                <description>
                    <![CDATA[ Quando eu comecei aprender a testar minhas aplicações antigamente, fiquei muito frustrado com os diferentes tipos, estilos e tecnologias usadas para fazer testes, junto com os inúmeros posts espalhados pelos blogs, tutoriais e artigos. Eu penso que isso é verdade, também, para testagem em React. Então, eu decidi escrever um ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-testar-componentes-do-react-o-guia-completo/</link>
                <guid isPermaLink="false">6273d011e15e0b050ea33ffa</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Mon, 02 Jan 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/photo-1562679817-2aac4f5581ec.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/testing-react-hooks/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Test React Components: the Complete Guide</a>
      </p><p>Quando eu comecei aprender a testar minhas aplicações antigamente, fiquei muito frustrado com os diferentes tipos, estilos e tecnologias usadas para fazer testes, junto com os inúmeros posts espalhados pelos blogs, tutoriais e artigos. Eu penso que isso é verdade, também, para testagem em <em>React</em>.</p><p>Então, eu decidi escrever um guia completo de testagem em <em>React </em>em um só artigo.</p><p>Você poderia me perguntar: "Guia completo? Então, você vai cobrir todas as possibilidades e cenários de teste?" A resposta é: claro, que não! Porém, será um guia completo de fundamentos para testar e será o suficiente para construir a maioria para os outros casos extremos.</p><p>Também selecionei uma grande coleção de publicações em blogs, artigos e tutoriais para uma seção de leitura complementar ao final que deve dar a você conhecimento suficiente para entrar no top 10% de desenvolvedores em termos de testagem.</p><p>Você pode encontrar o projeto completo aqui:</p><p><a href="https://github.com/iqbal125/react-hooks-testing-complete">https://github.com/iqbal125/react-hooks-testing-complete</a><br></p><h2 id="tabela-de-conte-dos"><strong><strong>Tab</strong>e<strong>l</strong>a<strong> </strong>de conteúdos</strong></h2><p><strong><strong>T</strong>eoria</strong></p><ul><li>O que é a testagem?</li><li>Por que testar?</li><li>O que testar?</li><li>O que não testar?</li><li>Como testar</li><li>Testes com <em>shallow</em> x testes com <em>mount</em></li><li>Testes unitários x testes de integração x testes de ponta a ponta</li></ul><p><strong>Informações preliminares</strong></p><ul><li>Algumas informações extras</li></ul><p><strong><strong>Enzyme</strong></strong></p><ul><li>Configuração do Enzyme</li><li><em>react-test-renderer</em></li><li>Testes de <em>snapshot</em></li><li>Implementação dos detalhes da testagem</li></ul><p><strong>Biblioteca de testagem no <strong>React</strong></strong></p><ul><li>useState e props</li><li>useReducer()</li><li>useContext()</li><li>Componentes de formulário controlados</li><li>useEffect() e requisições para a API do Axios</li></ul><p><strong><strong>Cypress</strong></strong></p><ul><li>Uma testagem completa de ponta a ponta</li></ul><p><strong>Integração contínua</strong></p><ul><li>Travis.yml</li><li>Cobertura de código com coveralls</li></ul><h2 id="teoria"><strong>Teoria</strong></h2><h3 id="o-que-testagem"><strong>O que é testagem?</strong></h3><p>Vamos começar pelo início e discutir o que é testagem. Testagem é um processo de três passos que se parece assim:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-12.png" class="kg-image" alt="image-12" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-12.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/11/image-12.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-12.png 1211w" sizes="(min-width: 720px) 720px" width="1211" height="354" loading="lazy"><figcaption>Em português: organização -&gt; ação -&gt; declaração</figcaption></figure><p>Organização é o estado original em que se encontra sua aplicação. Ação é alguma coisa acontece (como um evento de clique, entrada de usuário e assim por diante). Então, ocorre a declaração, ou é criada uma hipótese, sobre o novo estado da aplicação. Os testes passarão se a hipótese estiver correta e falharão se estiver errada.</p><p>Ao contrário dos componentes do React, os testes não são executados no navegador. Jest é o executor de testes e, também, o framework utilizado pelo React. O Jest é o ambiente onde todos os seus testes serão executados. É por isso que você não precisa importar <code>expect</code> e <code>describe</code> dentro desse arquivo. Essas funções já estão disponíveis globalmente no ambiente do Jest.</p><p>A sintaxe dos testes terá a seguinte aparência:</p><pre><code class="language-javascript">describe('Teste de soma', () =&gt; {
    function sum(a, b) {
       return a + b;
    }

    it('deve ser igual a 4',()=&gt;{
       expect(sum(2,2)).toBe(4);
      })

    test('também deve ser igual a 4', () =&gt; {
        expect(sum(2,2)).toBe(4);
      }) 
});</code></pre><p><code>describe</code> envolve nossos blocos <code>it</code> ou <code>test</code>. É uma maneira de agrupá-los. Tanto <code>it</code> quanto <code>test</code> são palavras-chaves que podem ser usadas intercambiadas. O texto entre aspas<em> </em>descreverá algo que deveria acontecer com os testes e que será impressa no console. <code>toBe()</code><strong><strong> </strong></strong>é um combinador que funciona com expectativas que permitem fazer declarações. Existem mais combinadores e variáveis globais oferecidas pelo Jest. Clique nos links abaixo para ver uma lista completa.</p><p><a href="https://jestjs.io/pt-BR/docs/using-matchers">https://jestjs.io/pt-BR/docs/using-matchers</a></p><p><a href="https://jestjs.io/pt-BR/docs/API">https://jestjs.io/pt-BR/docs/api</a></p><h2 id="por-que-testar">Por que testar?</h2><p>A testagem é feita para garantir que sua aplicação está funcionando como foi planejada para seus usuários finais. Ter uma testagem fará com que sua aplicação fique mais robusta e menos propensa a erros. É uma maneira de verificar que seu código está fazendo o que os desenvolvedores projetaram.</p><p>Potenciais desvantagens:</p><ul><li>Escrever testes consome muito tempo e é difícil</li><li>Em certos cenários, executar testes em Integração Contínua (em inglês, <em>Continuous Integration, </em>ou<em> CI</em>)<em> </em>pode ter um custo em dinheiro.</li><li>Se feito incorretamente, pode gerar falsos positivos. Os testes passarão, mas a aplicação não funcionará como se espera.</li><li>Pode gerar falsos negativos. Os testes falharão, mas a aplicação funcionará como pretendida.</li></ul><h3 id="o-que-testar"><strong>O que testar?</strong></h3><p>Ao construí-los cobrindo o último estado da aplicação, os testes devem testar a funcionalidade da aplicação, simulando como ele será utilizado pelos usuários finais. Isso dará a você a confiança de que a aplicação funcionará como projetado em ambiente de produção. Naturalmente, entraremos em muito mais detalhes durante este artigo, mas essa é a essência básica.</p><h3 id="o-que-n-o-testar"><strong>O que não testar?</strong></h3><p>Eu gosto da filosofia de Kent C. Dodds, que diz que você não deve testar detalhes da implementação.</p><p>Detalhes da implementação representam testar coisas que não são funcionalidades do usuário final. Veremos um exemplo disso na seção do Enzyme mais adiante.</p><p>Parece que você está testando funcionalidades, mas, na verdade, não está. Você está testando o nome da função. Você pode alterar o nome da função e seus testes falharão, mas sua aplicação continuará funcionando, dando a você um falso negativo.</p><p>Preocupar-se constantemente com nomes de funções e variáveis é uma dor de cabeça. Ter que reescrever testes toda vez que você muda os nomes é tedioso. Então, mostrarei aqui uma abordagem melhor.</p><p><strong>Variáveis c<strong>onst:</strong></strong> essas variáveis são imutáveis. Você não tem a necessidade de testá-las.</p><p><strong>Bibliotecas externas<strong>:</strong></strong> não é seu trabalho testá-las. Quem precisa fazer isso são os criadores delas. Se você não tem certeza se a biblioteca foi testada, você não deve usá-la ou deve ler o código-fonte para ver se o autor incluiu testes. Você também pode fazer o download do código-fonte e rodar os testes você mesmo. Uma alternativa é perguntar ao autor se a biblioteca está pronta para produção ou não. </p><h3 id="minha-abordagem-pessoal-para-a-testagem">Minha abordagem pessoal para a testagem</h3><p>Muito de minha abordagem pessoal de testagem é baseada nos ensinamentos de Kent C. Dodds. Então, você perceberá um pouco das falas de Kent ecoadas aqui, juntamente com algumas ideias minhas.</p><p>Muitos testes de integração, nenhum teste de <em>snapshot, </em>poucos testes unitários e poucos testes de ponta a ponta.</p><p>Testes unitários são um passo acima do <em>teste de snapshot, </em>mas não são ideais. Eles são, no entanto, muito mais fáceis de compreender e de se fazer sua manutenção do que os <em>testes de snapshot</em>.</p><p>Escreva, em sua maioria, testes de integração. Testes unitários são bons, mas eles não necessariamente se assemelham à maneira como o seu usuário final interage com a aplicação. É muito fácil testar detalhes de implementação com testes unitários, especialmente com a<em> </em>renderização superficial (em inglês, <em>shallow render</em>).</p><p>Testes de integração devem usar o mínimo de <em>mocks </em>(uma espécie de "dublê de teste"), se possível.</p><p>Não teste detalhes de implementação, como nomes de variáveis e funções.</p><p>Por exemplo, se você estiver testando um botão e muda o nome da função no método <em>onClick</em> de increment() para handleClick(), os testes quebrarão, mas o componente permanecerá funcionando. Essa não é uma boa prática, pois estamos basicamente testando o nome da função, que é um detalhe da implementação com o qual nosso usuário final não se importa.</p><h3 id="testes-com-shallow-x-testes-com-mount"><strong>Testes com s<strong>hallow </strong>x<strong> </strong>testes com <strong>mount</strong></strong></h3><p>Testes com mount(), de fato, executam o código html, css e js como um navegador faria, mas de maneira simulada. Eles não renderizam ou imprimem nada na interface, mas agem como um navegador da web simulado e executam o código em segundo plano. </p><p>Não perder tempo imprimindo nada na interface torna os testes muito mais rápidos. No entanto, testes com <em>mount() </em>ainda são bem mais lentos que testes com <em>shallow().</em></p><p>É por isso que você desmonta ou faz a limpeza do componente depois de cada teste. É quase como uma aplicação em funcionamento. Um teste acabará afetando o outro.</p><p>Testes com <em>mount()/render()</em> são tipicamente usados para testes de integração, enquanto testes com <em>shallow() </em>são usados para testes unitários.</p><p>Os testes com shallow() apenas imprimem um único componente, aquele que estamos testando. Por não renderizar componentes filhos, conseguimos testar nossos componentes em isolamento.</p><p>Por exemplo, considere esse componente filho e o componente pai.</p><pre><code class="language-javascript">import React from 'react';

const App = () =&gt; {
  return (
    &lt;div&gt; 
      &lt;ComponenteFilho /&gt; 
    &lt;/div&gt; 
  )
}

const ComponenteFilho = () =&gt; {
  return (
    &lt;div&gt;
     &lt;p&gt; Componentes filhos &lt;/p&gt;
    &lt;/div&gt;
  )
}</code></pre><p>Se usarmos o teste com shallow() de <code>App.js</code>, teremos algo como o que vemos abaixo. Observe que nenhum dos nós do DOM para o componente filho está presente. Por isso, usamos o termo <em>shallow render </em>(em português, renderização superficial).</p><pre><code>&lt;App&gt;
  &lt;div&gt; 
    &lt;ComponenteFilho /&gt; 
  &lt;/div&gt;
&lt;/App&gt; </code></pre><p>Agora, podemos comparar isso com a montagem do componente:</p><pre><code>&lt;App&gt;
  &lt;div&gt; 
    &lt;ComponenteFilho&gt; 
      &lt;div&gt;
       &lt;p&gt; Componentes filhos &lt;/p&gt;
      &lt;/div&gt;
    &lt;/ComponenteFilho&gt;
   &lt;/div&gt;
&lt;/App&gt; </code></pre><p>O que temos acima é muito mais próximo do que veremos na aplicação no navegador. Por isso, dizemos que os testes com mount()/render() são superiores.</p><h3 id="testes-unit-rios-x-testes-de-integra-o-x-testes-de-ponta-a-ponta">Testes unitários x testes de integração x testes de ponta a ponta</h3><p><strong>Teste unitário</strong>: teste de uma parte isolada da aplicação, normalmente feita em combinação com o teste com shallow(). Exemplo: um componente renderizado com <em>props </em>(propriedades) padrão.</p><p><strong>Teste de integração<strong>:</strong></strong> testa se as partes distintas funcionam ou se integram corretamente entre si. Normalmente, é feito na criação ou na renderização do componente. Exemplo: teste se um componente filho pode atualizar o <em>state </em>contextual no componente pai.</p><p><strong>Teste de ponta a ponta</strong>: normalmente, é uma combinação de várias etapas, combinando testes unitários e testes de integração dentro de uma etapa maior, geralmente com pouco <em>mock </em>(simulação). Os testes são feitos em um navegador simulado e podem ocorrer ou não enquanto o teste de interface está rodando. Exemplo: testagem de um fluxo inteiro de autenticação.</p><h2 id="informa-es-preliminares"><strong>Informações preliminares</strong></h2><p><strong><strong>react-testing-library:</strong> </strong>eu, pessoalmente, gosto de usar a biblioteca react-testing-library, mas, normalmente, usa-se a Enzyme. Mostrarei a você um exemplo com a Enzyme, pois é importante estar ciente do básico de Enzyme. Os outros exemplos serão com react-testing-library.</p><p><strong>Perfil dos exemplos:</strong> nossos exemplos seguem um padrão. Eu, primeiramente, mostrarei o componente do React e, então, os testes para ele, com explicações detalhadas de cada um. Você também pode acompanhar através do repositório cujo link eu deixei no início deste artigo.</p><p><strong><strong>Configura</strong>ção<strong>:</strong></strong> eu também assumirei que você está usando o create-react-app com a configuração de teste padrão, com jest. Assim, eu vou pular as configurações manuais.</p><p><strong><strong>Sinon, mocha, chai:</strong></strong> Muitas das configurações oferecidas pelo Sinon estão disponíveis por padrão com o Jest. Assim, você não precisa do Sinon. Mocha e Chai são uma substituição para o Jest. O Jest vem pré-configurado de início para trabalhar com sua aplicação. Desse modo, não faz sentido usar o Mocha ou o Chai.</p><p><strong>Esquema de nomeação de componentes<strong>:</strong></strong> meu esquema de nomeação para o componente é <code>&lt;TesteAlgo /&gt;</code>, mas não significa que eles sejam componentes falsos. Eles são componentes do React normais. Esse é apenas o esquema de nomeação.</p><p><strong><strong>npm test </strong>e o modo <strong>jest watch</strong></strong>: <code>yarn test</code> funcionou para mim. <code>npm test</code> não funcionou corretamente com o modo <code>jest watch</code>.</p><p><strong>T<strong>est</strong>e com um único arquivo<strong>:</strong></strong> <code>yarn test</code> nome_do_arquivo</p><p><strong><strong>Hooks </strong>x<strong> </strong>c<strong>lasses</strong> no React<strong>:</strong></strong> eu uso componentes com hooks do React para a maioria dos exemplos, mas, devido ao poder da react-testing-library, todos esses testes funcionarão com componentes de classes também.</p><p>Com essas informações preliminares, podemos partir para a escrita do código.</p><h2 id="enzyme"><strong><strong>Enzyme</strong></strong></h2><h3 id="configurando-o-enzyme"><strong>Configurando o <strong>Enzyme</strong></strong></h3><p>Nossas bibliotecas externas</p><p><code>npm install enzyme enzyme-to-json &nbsp;enzyme-adapter-react-16</code></p><p>Primeiro, vamos começar fazendo as importações</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import Basic from '../basic_test';

import Enzyme, { shallow, render, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() })</code></pre><p>Vamos começar nossas importações básicas. As três primeiras são para o React e para o nosso componente.</p><p>Depois, importamos o Enzyme. Em seguida, importamos a função toJson da biblioteca 'enzyme-to-json'. Vamos precisar converter nosso componente para teste com shallow() em JSON, que poderá ser salvo no arquivo do <em>snapshot</em>. </p><p>Finalmente, importamos nosso Adapter para fazer o Enzyme funcionar com o React 16 e o inicializamos como é mostrado abaixo.</p><p><strong>react-test-renderer</strong></p><p>O React, de fato, vem com seu próprio renderizador de testes. Você pode usá-lo ao invés do Enzyme se quiser. Sua sintaxe se parece com a do exemplo abaixo:</p><pre><code class="language-javascript">// import TestRenderer from 'react-test-renderer';
// import ShallowRenderer from 'react-test-renderer/shallow';


// Teste básico com react-test-renderer
// it('renderiza corretamente o react-test-renderer', () =&gt; {
//   const renderer = new ShallowRenderer();
//   renderer.render(&lt;Basic /&gt;);
//   const result = renderer.getRenderOutput();
//
//   expect(result).toMatchSnapshot();
// });</code></pre><p>Porém, até mesmo a documentação do próprio react-test-render sugere usar o Enzyme devido à sua sintaxe um pouco melhor e por ele fazer a mesma coisa. É bom ter isso em mente.</p><h3 id="teste-de-snapshot"><strong>Teste de s<strong>nap</strong>s<strong>hot</strong></strong></h3><p>Agora o nosso primeiro teste, um <em>teste de snapshot</em>.</p><pre><code class="language-javascript">it('renderiza corretamente o Enzyme', () =&gt; {
  const wrapper = shallow(&lt;Basic /&gt;)

  expect(toJson(wrapper)).toMatchSnapshot();
});</code></pre><p>Se você nunca rodou esse comando antes, um diretório __snapshots__ &nbsp;e um arquivo test.js.snap serão criados para você automaticamente. Em cada teste subsequente, o novo arquivo de <em>snapshot</em> será comparado com o arquivo de <em>snapshot</em> já existente. O teste passará se o <em>snapshot</em> não tiver alterações e falhará se elas existirem.</p><p>Então, essencialmente, o teste de <em>snapshot</em> permite você ver como seu componente se alterou desde o último teste, linha por linha. As linhas de código que sofreram mudanças são conhecidas como <em>diff, </em>abreviação em inglês para diferenças.</p><p>Aqui está o componente básico em que realizamos o teste:</p><pre><code>import React from 'react';


const Basic = () =&gt; {
  return (
    &lt;div &gt;
      &lt;h1&gt; Basic Test &lt;/h1&gt;
         &lt;p&gt; This is a basic Test Component &lt;/p&gt;
    &lt;/div&gt;
  );
}

export default Basic;</code></pre><p><br>Rodar o teste acima gerará um arquivo que contém um código parecido com o que é mostrado abaixo. Essa é, essencialmente, nossa árvore de nós do React DOM.</p><pre><code>// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renderiza corretamente o enzyme 1`] = `
&lt;div&gt;
  &lt;h1&gt;
     Basic Test
  &lt;/h1&gt;
  &lt;p&gt;
     This is a basic Test Component
  &lt;/p&gt;
&lt;/div&gt;
`;</code></pre><p>Isso produzirá uma estrutura de diretórios como a mostrada abaixo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-6.png" class="kg-image" alt="image-6" width="325" height="256" loading="lazy"></figure><p>Seu terminal gerará um resultado<em> </em>parecido com este:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-7.png" class="kg-image" alt="image-7" width="511" height="304" loading="lazy"></figure><p>Entretanto, o que acontece se mudarmos nosso componente básico para isso:</p><pre><code>import React from 'react';


const Basic = () =&gt; {
  return (
    &lt;div &gt;
      &lt;h1&gt; Basic Test &lt;/h1&gt;

    &lt;/div&gt;
  );
}

export default Basic;</code></pre><p>Nosso snapshot falhará.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-8.png" class="kg-image" alt="image-8" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-8.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-8.png 646w" width="646" height="233" loading="lazy"></figure><p>Ele também nos mostrará a diferença.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-9.png" class="kg-image" alt="image-9" width="557" height="300" loading="lazy"></figure><p>Assim como no git, o sinal " - " antes de cada linha significa que ela foi removida.</p><p>Nós precisamos apenas apertar "w" para ativar o modo de observação (do inglês, <em>watch</em>) e, então, pressionar a tecla "u" (do inglês, <em>update</em>) para atualizar o <em>snapshot</em>.</p><p>Nosso arquivo de <em>snapshot</em> será atualizado automaticamente com o novo <em>snapshot</em> e passará nos testes.</p><pre><code>// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renderiza corretamente o Enzyme 1`] = `
&lt;div&gt;
  &lt;h1&gt;
     Basic Test
  &lt;/h1&gt;
&lt;/div&gt;
`;</code></pre><p>Isso é tudo que temos para os testes de <em>snapshot, </em>mas, se você ler a seção onde exponho minhas opiniões, perceberá que eu não faço o teste de <em>snapshot</em>. Eu o incluí aqui porque, assim como o Enzyme, eles são bem comuns e são algo que você deveria conhecer, mas explicarei abaixo por que eu não o uso.</p><p>Vamos olhar novamente o que é o teste de <em>snapshot</em>. Ele essencialmente permitirá que você veja como o seu componente mudou desde o último teste. Os benefícios disso são: </p><ul><li>É bem rápido e fácil de implementar e, algumas vezes, requer somente algumas linhas de código.</li><li>Você pode ver se seu componente está renderizando corretamente e ver os nós do DOM claramente com a função .debug().</li></ul><p><strong>Desvantagens e<strong> </strong>a<strong>rgument</strong>o<strong>s</strong> contra os testes de<strong> <em>snapshot</em>:</strong></strong></p><ul><li>A única coisa que o teste de <em>snapshot</em> faz é dizer a você onde a sintaxe do seu código sofreu alterações desde o último teste.</li><li>Então, isso é realmente um teste? Alguns diriam que não muito.</li><li>Renderizar o app corretamente é um trabalho do React. Desse modo, você estaria indo um pouco na direção de testar uma biblioteca de terceiros. </li><li>Comparar diffs é algo que pode ser feito com o controle de versões do git. Isso não deveria ser um trabalho do teste de <em>snapshot</em>.</li><li>Um teste que falhou, aqui, não significa que sua aplicação não está funcionando como pretendido. Significa apenas que o seu código sofreu mudanças desde a última vez em que você rodou o teste. Isso poderia levar à vários falsos negativos e gerar uma falha na confiança na testagem. Isso também poderia levar pessoas a apenas atualizar os testes sem observá-los atentamente.</li><li>Testes de <em>snapshot </em>também dizem se sua sintaxe do JSX está correta. Porém, novamente, isso pode ser feito facilmente em um ambiente de desenvolvimento. Rodar um teste de <em>snapshot </em>apenas para verificar erros de sintaxe não faz nenhum sentido.</li><li>Pode ser bem difícil de se entender o que está acontecendo em um teste de <em>snapshot</em>, visto que a maioria das pessoas o usam com o teste com shallow(), que não renderiza componentes filhos. Então, ela não dá ao desenvolvedor nenhuma percepção do que está acontecendo realmente.</li></ul><p>Veja a seção de leitura complementar para mais informações.</p><h3 id="detalhes-de-implementa-o-de-testes-com-enzyme"><strong>Detalhes de implementação de testes com <strong>Enzyme</strong></strong></h3><p>Aqui eu vou dar a você um exemplo do motivo de não testar detalhes de implementação. Vamos dizer que temos um simples componente contador, como o que mostramos abaixo:</p><pre><code class="language-javascript">import React, { Component } from 'react';


class Counter extends Component {
  constructor(props) {
    super(props)

    this.state = {
      count: 0
    }
  }

  increment = () =&gt; {
    this.setState({count: this.state.count + 1})
  }

  // Este código incorreto ainda fará com que os testes passem
  // &lt;button onClick={this.incremen}&gt;
  //   Clicked: {this.state.count}
  // &lt;/button&gt;

  render() {
    return (
      &lt;div&gt;
        &lt;button className="counter-button" onClick={this.incremen}&gt;
          Clicked: {this.state.count}
        &lt;/button&gt;
      &lt;/div&gt;
  )}
}

export default Counter;</code></pre><p>Você notará que eu fiz um comentário sugerindo que um aplicativo não funcional, ainda assim, passaria nos testes. Como exemplo, podemos escrever incorretamente o nome da função no evento onClick.</p><p>Vamos ver abaixo nos testes por que isso acontece.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import Counter from '../counter';

import Enzyme, { shallow, render, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() })

// A atribuição de função incorreta no método onClick 
// ainda fará os testes passarem.

test('o método increment incrementa a contagem', () =&gt; {
  const wrapper = mount(&lt;Counter /&gt;)

  expect(wrapper.instance().state.count).toBe(0)

  // wrapper.find('button.counter-button').simulate('click')
  // wrapper.setState({count: 1})
  wrapper.instance().increment()
  expect(wrapper.instance().state.count).toBe(1)
})
</code></pre><p>Rodar o código acima passará nos testes, assim como usar 0 <code>wrapper.setState()</code>. Então, temos testes passando em uma aplicação não funcional. Eu não sei você, mas isso não me transmite a confiança de que nossa aplicação funcionará como pretendido para os nossos usuários finais.</p><p>Simular a ação de clique no botão não passará nos testes, mas isso pode gerar um problema oposto: um falso negativo. Vamos dizer que nós queremos aplicar mudanças no estilo do botão, declarando uma nova classe CSS nele – uma situação bem comum. Nossos testes falharão, pois não conseguiremos mais achar o botão. Nossa aplicação, no entanto, continuará funcionando, o que nos dará um falso negativo. Isso também acontece sempre que mudarmos os nomes de nossas funções ou o estado das variáveis.</p><p>Sempre que nós quisermos mudar nossas funções e nomes das classes do CSS, teremos que reescrever nossos testes, um processo bem ineficiente e tedioso.</p><p>Então, o que podemos fazer para resolver?</p><h2 id="react-testing-library"><strong>r<strong>eact-testing-library</strong></strong></h2><h3 id="usestate"><strong><strong>useState</strong></strong></h3><p>Observando a documentação da biblioteca react-testing-library, podemos perceber que o princípio orientador mais importante é:</p><blockquote>Quanto mais seus testes se assemelharem à maneira como seu software é usado, mais confiança você terá (de que sua aplicação funciona adequadamente).</blockquote><p>Vamos manter esse princípio orientador em mente conforme seguimos com nossos testes.</p><p>Vamos começar com um componente básico de hooks do React e testar seu <em>state </em>e suas propriedades (do inglês, <em>props</em>).</p><pre><code class="language-javascript">import React, { useState } from 'react';


const TestHook = (props) =&gt; {
  const [state, setState] = useState("Initial State")

  const changeState = () =&gt; {
    setState("Initial State Changed")
  }

  const changeNameToSteve = () =&gt; {
    props.changeName()
  }

  return (
  &lt;div&gt;
    &lt;button onClick={changeState}&gt;
      State Change Button
    &lt;/button&gt;
    &lt;p&gt;{state}&lt;/p&gt;
    &lt;button onClick={changeNameToSteve}&gt;
       Change Name
    &lt;/button&gt;
    &lt;p&gt;{props.name}&lt;/p&gt;
  &lt;/div&gt;
  )
}


export default TestHook;</code></pre><p>Nossas <em>props </em>estão vindo do componente pai</p><pre><code class="language-javascript">  const App = () =&gt; {
      const [state, setState] = useState("Some Text")
      const [name, setName] = useState("Moe")
  ...
      const changeName = () =&gt; {
        setName("Steve")
      }

      return (
        &lt;div className="App"&gt;
         &lt;Basic /&gt;
        &lt;h1&gt; Counter &lt;/h1&gt;
         &lt;Counter /&gt;
        &lt;h1&gt; Basic Hook useState &lt;/h1&gt;
         &lt;TestHook name={name} changeName={changeName}/&gt;
    ...     </code></pre><p>Então, mantendo nosso princípio orientador em mente, como os nossos testes ficarão?</p><p>A maneira que nosso usuário final usará essa aplicação será: ver algum texto na UI, ver o texto no botão, clicar e finalmente ver um outro texto na UI. </p><p>É assim que nós escrevemos nossos testes usando a biblioteca de testes do React.</p><p>Use este comando para instalar a biblioteca react-testing-library:</p><p><code>npm install @testing-library/react</code></p><p><strong>Não use este comando:</strong></p><p><code>npm install react-testing-library</code></p><p>Agora, vamos aos testes.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHook from '../test_hook.js';
import {render, fireEvent, cleanup} from '@testing-library/react';
import App from '../../../App'

afterEach(cleanup)

it('Texto no state se altera quando o botão é clicado', () =&gt; {
    const { getByText } = render(&lt;TestHook /&gt;);

    expect(getByText(/inicial/i).textContent).toBe("State inicial")

    fireEvent.click(getByText("Botão de mudança do state"))

    expect(getByText(/inicial/i).textContent).toBe("State inicial alterado")
 })


it('clique do botão altera as props', () =&gt; {
  const { getByText } = render(&lt;App&gt;
                                &lt;TestHook /&gt;
                               &lt;/App&gt;)

  expect(getByText(/Moe/i).textContent).toBe("Moe")

  fireEvent.click(getByText("Mudar nome"))

  expect(getByText(/Steve/i).textContent).toBe("Steve")
})</code></pre><p>Começamos com nossas importações usuais.</p><p>Depois, temos a função <code>afterEach(cleanup)</code>. Como não estamos usando o <em>teste com shallow()</em>, temos que fazer a desmontagem (em inglês, <em>unmount</em>),<em> </em>ou a limpeza depois de cada teste. É exatamente esse o papel dessa função.</p><p><code>getByText</code> é o método da query que obtivemos desestruturando o valor da função de renderização. Existem muitos mais métodos de query, mas esse você usará a maior parte do tempo.</p><p>Para testar nosso <em>state</em>, perceba que nós não estamos nenhum nome de função ou de variável. Estamos mantendo nosso princípio orientador ao não testar detalhes de implementação. Como o usuário final verá o texto no botão da interface, é assim que buscaremos os nós do DOM. Nós também buscaremos o botão dessa maneira e clicaremos nele. Finalmente, buscaremos o estado final, também com base no texto.</p><p><code>(/Inicial/i)</code> é uma expressão regular (do inglês, <em>regex expression</em>)<em> </em>que retorna o primeiro nó que contenha pelo menos o texto "inicial".</p><p>Também podemos fazer exatamente a mesma coisa com as props. Se as <strong><strong>props</strong></strong> forem mudadas em <code>App.js</code>, precisaremos renderizá-las junto com nosso componente. Assim como no exemplo anterior, não estamos testando nomes de função ou variáveis, mas fazemos isso do mesmo jeito que nosso usuário faria ao usar nossa aplicação, ou seja, através do texto que ele vê na interface.</p><p>Espero que isso dê a você uma boa ideia de como testar com <code>react-testing-library</code> e seu princípio orientador. Você, geralmente, vai querer usar <code>getByText</code> na maior parte do tempo. Existem poucas exceções e nós as veremos conforme seguimos com a explicação.</p><h3 id="usereducer"><strong><strong>useReducer</strong></strong></h3><p>Agora, nós podemos testar um componente com o <em>hook </em><code>useReducer</code>. Neste caso, precisaremos de ações e <em>reducers </em>para trabalhar com nosso componente. Então, vamos configurá-los:</p><p>Nosso <em>reducer</em></p><pre><code class="language-javascript">import * as ACTIONS from './actions'

export const initialState = {
    stateprop1: false,
}

export const Reducer1 = (state = initialState, action) =&gt; {
  switch(action.type) {
    case "SUCCESS":
      return {
        ...state,
        stateprop1: true,
      }
    case "FAILURE":
      return {
        ...state,
        stateprop1: false,
      }
    default:
      return state
  }
}</code></pre><p>E as ações:</p><pre><code>


export const SUCCESS = {
  type: 'SUCCESS'
}

export const FAILURE = {
  type: 'FAILURE'
}

</code></pre><p>Manteremos as coisas simples e usaremos ações ao invés de criadores de ação.</p><p>Por fim, o componente usará essas ações e <em>reducers</em>:</p><pre><code class="language-javascript">import React, { useReducer } from 'react';
import * as ACTIONS from '../store/actions'
import * as Reducer from '../store/reducer'


const TestHookReducer = () =&gt; {
  const [reducerState, dispatch] = useReducer(Reducer.Reducer1, Reducer.initialState)

  const dispatchActionSuccess = () =&gt; {
    dispatch(ACTIONS.SUCCESS)
  }

  const dispatchActionFailure = () =&gt; {
    dispatch(ACTIONS.FAILURE)
  }


  return (
    &lt;div&gt;
       &lt;div&gt;
        {reducerState.stateprop1
           ? &lt;p&gt;stateprop1 is true&lt;/p&gt;
           : &lt;p&gt;stateprop1 is false&lt;/p&gt;}
       &lt;/div&gt;
       &lt;button onClick={dispatchActionSuccess}&gt;
         Dispatch Success
       &lt;/button&gt;
    &lt;/div&gt;
  )
}


export default TestHookReducer;
</code></pre><p>Esse é um simples componente que mudará o <code>stateprop1</code> de falso para verdadeiro ao disparar uma ação de <code>SUCCESS</code>.</p><p>Agora, vamos ao nosso teste.</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHookReducer from '../test_hook_reducer.js';
import {render, fireEvent, cleanup} from '@testing-library/react';
import * as Reducer from '../../store/reducer';
import * as ACTIONS from '../../store/actions';


afterEach(cleanup)

describe('testar o reducer e as ações', () =&gt; {
  it('deve retornar ao state inicial', () =&gt; {
    expect(Reducer.initialState).toEqual({ stateprop1: false })
  })

  it('deve alterar stateprop1 de false para true', () =&gt; {
    expect(Reducer.Reducer1(Reducer.initialState, ACTIONS.SUCCESS ))
      .toEqual({ stateprop1: true  })
  })
})

it('O reducer altera stateprop1 de false para true', () =&gt; {
   const { container, getByText } = render(&lt;TestHookReducer /&gt;);

   expect(getByText(/stateprop1 is/i).textContent).toBe("stateprop1 is false")

   fireEvent.click(getByText("Dispatch Success"))

   expect(getByText(/stateprop1 is/i).textContent).toBe("stateprop1 is true")
})</code></pre><p>Primeiro, começamos testando nosso <em>reducer</em> e dividimos os testes para o <em>reducer</em> no bloco <code>describe</code>. Esses são testes bem básicos. Estamos usando esses testes para ter certeza de que o <strong>state inicial </strong>é o que queremos e que as ações produzem o resultado que queremos.</p><p>Você pode argumentar que a testagem do <em>reducer </em>é um teste de detalhe de implementação, mas o que eu encontrei na prática foi que a testagem das ações e dos <em>reducers </em>são testes unitários que são sempre necessários.</p><p>Esse é um exemplo simples. Então, ele não parece um grande desafio agora, mas, em grandes projetos e aplicações, não fazer a testagem de <em>reducers </em>e ações pode ter consequências desastrosas. Então, ações e <em>reducers </em>seriam uma exceção para a regra sobre não testar detalhes de implementação.</p><p>Agora, temos nossos testes para o componente em si. Observe, novamente, que, aqui, não estamos testando detalhes de implementação. Estamos usando o mesmo padrão do exemplo anterior <em>useState</em>, obtendo nossos nós do DOM por texto, além de encontrando e clicando no botão com o texto.</p><h3 id="usecontext"><strong><strong>useContext</strong></strong></h3><p>Agora vamos seguir em frente e testar se um componente filho pode atualizar o <em>state</em> do contexto em um componente pai. Isso pode parecer complexo, mas é bem simples e direto. </p><p>Primeiro, precisamos do nosso objeto de contexto, onde inicializaremos nosso próprio arquivo.</p><pre><code class="language-javascript">import React from 'react';

const Context = React.createContext()

export default Context
</code></pre><p>Também precisamos atualizar nosso componente <em>app</em> pai, que manterá o provedor (do inglês, <em>provider</em>) de <code>Context</code>. O valor passado para o <code>Provider</code> será o valor do <em>state</em>, além da função <code>setState</code> do componente <code>App.js</code>.</p><pre><code class="language-javascript">import React, { useState } from 'react';
import TestHookContext from './components/react-testing-lib/test_hook_context';


import Context from './components/store/context';


const App = () =&gt; {
  const [state, setState] = useState("Um texto")
  

  const changeText = () =&gt; {
    setState("Outro texto")
  }


  return (
    &lt;div className="App"&gt;
    &lt;h1&gt; Basic Hook useContext&lt;/h1&gt;
     &lt;Context.Provider value={{changeTextProp: changeText,
                               stateProp: state
                                 }} &gt;
        &lt;TestHookContext /&gt;
     &lt;/Context.Provider&gt;
    &lt;/div&gt;
  );
}

export default App;</code></pre><p>E para o nosso componente</p><pre><code class="language-javascript">import React, { useContext } from 'react';

import Context from '../store/context';

const TestHookContext = () =&gt; {
  const context = useContext(Context)

  return (
    &lt;div&gt;
    &lt;button onClick={context.changeTextProp}&gt;
        Alterar texto
    &lt;/button&gt;
      &lt;p&gt;{context.stateProp}&lt;/p&gt;
    &lt;/div&gt;
  )
}


export default TestHookContext;</code></pre><p>Temos um componente simples, que mostra o texto inicializado em <code>App.js</code> e passamos a função <code>setState</code> para o método <code>onClick</code>.</p><p><strong>Observação<strong>:</strong></strong> o <em>state</em> mudou, inicializado e contido em nosso componente <code>App.js</code>. Simplesmente passamos o valor do <em>state</em> e a função <code>setState</code> para nosso componente filho através do contexto, mas o <em>state</em> foi controlado no componente <code>App.js</code>. Isso será importante para entender nosso teste.</p><p>Aqui está o nosso teste:</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import TestHookContext from '../test_hook_context.js';
import {act, render, fireEvent, cleanup} from '@testing-library/react';
import App from '../../../App'

import Context from '../../store/context';

afterEach(cleanup)

it('Context value is updated by child component', () =&gt; {

   const { container, getByText } = render(&lt;App&gt;
                                            &lt;Context.Provider&gt;
                                             &lt;TestHookContext /&gt;
                                            &lt;/Context.Provider&gt;
                                           &lt;/App&gt;);

   expect(getByText(/Some/i).textContent).toBe("Um texto")

   fireEvent.click(getByText("Alterar texto"))

   expect(getByText(/Some/i).textContent).toBe("Outro texto")
})
</code></pre><p>Até para o nosso contexto, você pode perceber que não quebramos o nosso padrão de testes. Ainda encontramos e simulamos nossos eventos com os testes.</p><p>Fiz a inclusão do <code>&lt;Context.Provider/&gt;</code> e do componente <code>&lt;TestHookContext /&gt;</code> na função de renderização, pois faz com que o código fique mais legível, mas não precisamos deles necessariamente. Nosso teste funcionará se passarmos apenas o componente <code>&lt;App /&gt;</code> para a função de renderização.</p><pre><code>const { container, getByText } = render(&lt;App/&gt;) </code></pre><p>Por que esse é o caso?</p><p>Vamos pensar de novo no que sabemos sobre o contexto. Todo o <em>state</em> do contexto é controlado em <code>App.js</code>. Por essa razão, é o nosso componente principal que estamos testando realmente, mesmo que pareça que estamos testando o componente filho que usa o <em>hook</em> <strong><strong>useContext</strong></strong>. Esse código também funciona por causa da criação/montagem do componente (em inglês, <strong><strong>mount/render</strong></strong>). Como sabemos, no teste com shallow(), os componentes filhos <strong>não são renderizados</strong>, somente na criação. Sabendo que <code>&lt;Context.Provider /&gt;</code> e <code>&lt;TestHookContext /&gt;</code> são ambos componentes filhos de <code>&lt;App /&gt;</code>, eles serão renderizados automaticamente.</p><h3 id="componentes-de-formul-rios-controlados"><strong>Componentes de formulários controlados</strong></h3><p>Um componente de formulário controlado basicamente significa que o formulário trabalhará através do <em>state</em> do React ao invés do formulário manter seu próprio <em>state</em>. Desse modo, o método <code>onChange</code> salvará o texto do input no React em cada toque no teclado.</p><p>Testar o formulário será um pouco diferente do que temos visto até agora, mas vamos tentar manter os nossos princípios guias em mente.</p><pre><code class="language-javascript">import React, { useState } from 'react';

const HooksForm1 = () =&gt; {
  const [valueChange, setValueChange] = useState('')
  const [valueSubmit, setValueSubmit] = useState('')

  const handleChange = (event) =&gt; (
    setValueChange(event.target.value)
  );

  const handleSubmit = (event) =&gt; {
    event.preventDefault();
    setValueSubmit(event.target.text1.value)
  };

    return (
      &lt;div&gt;
       &lt;h1&gt; Formulário de hooks do React &lt;/h1&gt;
        &lt;form data-testid="form" onSubmit={handleSubmit}&gt;
          &lt;label htmlFor="text1"&gt;Texto de entrada:&lt;/label&gt;
          &lt;input id="text1" onChange={handleChange} type="text" /&gt;
          &lt;button type="submit"&gt;Enviar&lt;/button&gt;
        &lt;/form&gt;
        &lt;h3&gt;React State:&lt;/h3&gt;
          &lt;p&gt;Alterar: {valueChange}&lt;/p&gt;
          &lt;p&gt;Valor de envio: {valueSubmit}&lt;/p&gt;
        &lt;br /&gt;
      &lt;/div&gt;
    )
}


export default HooksForm1;</code></pre><p>Temos aqui um formulário básico. Vamos mostrar o valor da mudança e enviar o valor em nosso JSX. Temos o atributo <code>data-testid="form"</code>, que usaremos no nosso teste da <em>query </em>para o formulário.</p><p>Nossos testes:</p><pre><code class="language-javascript">import React from 'react';
import ReactDOM from 'react-dom';
import HooksForm1 from '../test_hook_form.js';
import {render, fireEvent, cleanup} from '@testing-library/react';

afterEach(cleanup)

//Teste de um formulário de componente controlado.
it('Inserir texto atualiza o state', () =&gt; {
    const { getByText, getByLabelText } = render(&lt;HooksForm1 /&gt;);

    expect(getByText(/Change/i).textContent).toBe("Alterar: ")

    fireEvent.change(getByLabelText("Texto de entrada:"), {target: {value: 'Text' } } )

    expect(getByText(/Change/i).textContent).not.toBe("Alterar: ")
 })


 it('O envio do formulário funciona corretamente', () =&gt; {
     const { getByTestId, getByText } = render(&lt;HooksForm1 /&gt;);

     expect(getByText(/Submit Value/i).textContent).toBe("Valor de envio: ")

     fireEvent.submit(getByTestId("form"), {target: {text1: {value: 'Texto' } } })

     expect(getByText(/Submit Value/i).textContent).not.toBe("Valor de envio: ")
  })</code></pre><p>Considerando que um elemento de input vazio não possui texto, usaremos a função <code>getByLabelText()</code> para obter o nó do input. Ainda manteremos nosso princípio guia, visto que o texto da <em>label </em>é o que o usuário lerá antes de digitar o texto.</p><p>Note que disparará o evento <code>.change()</code> ao invés do evento usual, <code>.click()</code>. Também passaremos um dado teste como exemplificado abaixo:</p><p><code>{ target: { value: "Texto" } }</code></p><p>Considerando que o valor do formulário será acessado na forma de <code>event.target.value</code>, é assim que simularemos o evento.</p><p>Geralmente, não sabemos qual texto o usuário enviará, então podemos simplesmente usar a palavra-chave <code>.not</code> para ter certeza de que o texto mudou ou renderizou o método.</p><p>Podemos testar o envio do formulário de modo similar. A única diferença é que usamos o evento <code>.submit()</code> e passamos dados de teste dessa maneira:</p><p><code>{ target: { text1: { value: 'Texto' } } }</code></p><p>É assim que acessamos dados do formulário através do evento sintético quando um usuário o submeter, onde <code>text1</code> é o id de nosso elemento input. Temos que fugir um pouco do nosso padrão aqui e usar o atributo <code>data-testid="form"</code> para buscar o formulário, visto que não há outro modo de fazê-lo.</p><p>É isso que temos para o formulário. Não é tão diferente de outros exemplos já feitos por nós anteriormente. Se você achar que já entendeu tudo, podemos seguir para algo um pouco mais complexo.</p><h3 id="useeffect-e-requisi-es-a-apis-com-o-axios"><strong><strong>useEffect </strong>e requisições a <strong>API</strong>s<strong> </strong>com<strong> </strong>o <strong>axios</strong></strong></h3><p>Agora, vamos ver como testaríamos o <em>hook </em><strong><strong>useEffect </strong></strong>e as requisições a APIs. Isso será um pouco diferente do que temos visto até agora.</p><p>Digamos que temos um url passado de um componente pai para um filho.</p><pre><code class="language-javascript">
...

     &lt;TestAxios url='https://jsonplaceholder.typicode.com/posts/1' /&gt;
     
 ... </code></pre><p>O componente seria algo assim:</p><pre><code class="language-javascript">import React, { useState, useEffect } from 'react';
import axios from 'axios';


const TestAxios = (props) =&gt; {
  const [state, setState] = useState()

  useEffect(() =&gt; {
    axios.get(props.url)
      .then(res =&gt; setState(res.data))
  }, [])


  return (
    &lt;div&gt;
    &lt;h1&gt; Teste do axios &lt;/h1&gt;
        {state
          ? &lt;p data-testid="title"&gt;{state.title}&lt;/p&gt;
          : &lt;p&gt;...Carregando&lt;/p&gt;}
    &lt;/div&gt;
  )
}


export default TestAxios;</code></pre><p>Simplesmente fazemos uma requisição a uma API e salvamos os resultados em um <em>state</em> local. Também usamos uma expressão ternária no nosso método de renderização para esperar até que a requisição esteja completa. Assim, mostrarmos o título do json como <em>placeholder</em>.</p><p>Você notará que, novamente, por necessidade, teremos que usar o atributo <code>data-testid</code>. Novamente, é um detalhe de implementação, já que o usuário não verá nem vai interagir com esse atributo de nenhuma maneira, mas, aqui, é mais realista, uma vez que, geralmente, não saberemos o texto vindo da requisição de antemão. </p><p>Usaremos dados <em>mockados</em> (de simulação)<em> </em>nesse teste.</p><p>Um <strong><strong>mock</strong></strong> é uma maneira de simular um comportamento que não queremos que aconteça em nossos testes. Por exemplo, fazemos o <em>mock</em> das requisições à API porque não queremos fazer requisições reais em nossos testes. </p><p>Não queremos requisições reais em nossos testes por diversas razões: farão com que nossos testes fiquem mais lentos, podem nos dar um falso negativo, as requisições à API podem nos custar dinheiro ou podemos alterar nosso banco de dados com os testes.</p><pre><code>import React from 'react';
import ReactDOM from 'react-dom';
import TestAxios from '../test_axios.js';
import {act, render, fireEvent, cleanup, waitForElement} from '@testing-library/react';

import axiosMock from "axios";

</code></pre><p>Temos nossas importações usuais, mas você notará algo específico. Estamos importando <code>axiosMock</code> da biblioteca <code>axios</code>. Não estamos importando um objeto axios da biblioteca <code>axios</code>. Na verdade, estamos <em>mockando</em> (simulando)<em> </em>a biblioteca <code>axios</code> em si.</p><p>Como?</p><p>Usando a funcionalidade de <em>mocking </em>oferecida pelo Jest.</p><p>Primeiro, vamos fazer um diretório <code>__mocks__</code> adjacente para testar nosso diretório teste. Seria algo assim:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-13.png" class="kg-image" alt="image-13" width="262" height="165" loading="lazy"></figure><p>Dentro do diretório de <em>mocks</em>,<em> </em>temos um arquivo <code>axios.js</code>. Essa é a nossa biblioteca <strong><strong>axios </strong></strong>falsa. Dentro de nossa biblioteca <strong><strong>axios</strong></strong> falsa, temos nossa <strong>função de mock do <strong>jest</strong></strong>.</p><p>Funções de <em>mock</em> nos permitem usá-las em nosso ambiente do Jest sem ter que realmente implementar sua lógica.</p><p>Então, basicamente, não vamos implementar a lógica através de uma requisição do axios. Usaremos a função mock em vez disso.</p><pre><code class="language-javascript">export default {
  get: jest.fn(() =&gt; Promise.resolve({ data: {} }) )
};
</code></pre><p>Aqui, temos nossa função falsa <code>get</code>. Ela é uma função simples que, na verdade, é um objeto em JS. <code>get</code> é nossa chave e o valor é a <strong>função <strong>mock</strong></strong>. Como em uma requisição a APIs do <strong><strong>axios</strong></strong>, resolvemos uma <em>promise </em>(em português, promessa). Não passaremos nenhum dado aqui. Faremos isso em nossa configuração dos testes.</p><p>Segue aqui a nossa configuração de testes:</p><pre><code class="language-javascript">//importações
...

afterEach(cleanup)

it('A solicitação assíncrona do axios funciona', async () =&gt; {
  axiosMock.get.mockResolvedValue({data: { title: 'some title' } })

  const url = 'https://jsonplaceholder.typicode.com/posts/1'
  const { getByText, getByTestId, rerender } = render(&lt;TestAxios url={url} /&gt;);

  expect(getByText(/...Loading/i).textContent).toBe("...Carregando")

  const resolvedEl = await waitForElement(() =&gt; getByTestId("title"));

  expect((resolvedEl).textContent).toBe("some title")

  expect(axiosMock.get).toHaveBeenCalledTimes(1);
  expect(axiosMock.get).toHaveBeenCalledWith(url);
 })</code></pre><p>A primeira coisa que fazemos em nosso teste é chamar nossa <strong>requisição get do <strong>axios</strong></strong> e fazer o <em>mock </em>do valor resolvido com a função, ironicamente chamada de <code>mockResolvedValue</code>, provida pelo Jest. Essa função faz exatamente o que seu nome diz, ela resolve uma <em>promise</em> com o dado que passamos nela, simulando o que o axios faz.</p><p>Essa função tem que ser chamada antes da nossa função <code>render()</code>. Caso contrário, nosso teste não funcionará. Lembre-se de que estamos <em>mockando </em>a biblioteca<strong><strong> axios </strong></strong>em si. Quando nosso componente rodar o comando <code>import axios from 'axios';</code> estará <strong><strong>import</strong>ando nossa biblioteca falsa<strong> </strong>do <strong>axios </strong></strong>ao invés da verdadeira e essa biblioteca falsa será substituída em nosso componente toda vez que usarmos o axios.</p><p>A seguir, temos o nosso nó de texto "...Carregando", visto que ele é mostrado antes de a <em>promise </em>ser resolvida. Depois disso, temos uma função que ainda não vimos até agora, a função <code>waitForElement()</code>, que esperará até que a <em>promise </em>seja resolvida antes de seguir para a próxima afirmação.</p><p>Também note que as palavras-chave <strong><strong>await</strong></strong> e <strong><strong>async</strong></strong> são usadas do mesmo jeito que são usadas fora do ambiente de teste.</p><p>Uma vez resolvida, o nó do DOM terá o texto "some title", que é o dado passado para nossa biblioteca falsa do axios.</p><p>A seguir, temos a certeza de que a requisição foi chamada apenas uma vez e com o url certo. Mesmo que estejamos testando o url, não fazemos uma requisição à API com esse url.</p><p>Ficamos por aqui com nossas requisições à API com axios. Na próxima sessão, exploraremos um pouco de testes com o Cypress.</p><h2 id="cypress"><strong><strong>Cypress</strong></strong></h2><p>Agora, vamos explorar um pouco o Cypress, que eu acredito ser o melhor framework para rodar testes de ponta a ponta. Nos afastaremos um pouco do Jest agora. Vamos trabalhar unicamente com o Cypress, que possui seu próprio ambiente e sintaxe de testes. </p><p>O Cypress é incrível e poderoso. De fato, ele é tão incrível e poderoso que podemos rodar todos os testes que já vimos até agora em apenas um bloco de código de teste e observar o Cypress rodar esses testes em tempo real em um simulador de navegador.</p><p>Muito legal, não é?</p><p>Eu penso que sim. De qualquer forma, antes de fazermos tudo isso, precisamos configurar o Cypress. Surpreendentemente, o Cypress pode ser instalado como um módulo normal do npm.</p><p><code>npm install cypress</code></p><p>Para ter acesso ao Cypress, você precisa rodar este comando no seu terminal.</p><p><code>node_modules/.bin/cypress open</code></p><p>Se escrever esse comando toda vez parecer muito incômodo, você pode adicioná-lo no seu arquivo <code>package.json</code>.</p><pre><code class="language-javascript">...

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "cypress": "node_modules/.bin/cypress open", 
    
   ...</code></pre><p>Isso permite, então, que você abra o Cypress apenas usando o seguinte comando: <code>npm run cypress</code>.</p><p>Ao abrir o Cypress, você encontrará uma interface gráfica (em inglês, <em>GUI</em>, ou<em> Graphic User Interface</em>),<em> </em>que pode ter a aparência seguinte:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-14.png" class="kg-image" alt="image-14" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-14.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/11/image-14.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-14.png 1012w" sizes="(min-width: 720px) 720px" width="1012" height="599" loading="lazy"></figure><p>Para rodar os testes no Cypress, sua aplicação deverá estar rodando ao mesmo tempo, o que veremos em um segundo.</p><p>Rodar o comando <code>cypress open</code> dará a você uma configuração básica do Cypress e criará alguns arquivos e diretórios para você automaticamente. Um diretório será criado na raiz do seu projeto. Escreveremos nosso código no diretório <em>integration</em>.</p><p>Começaremos excluindo o diretório <em>examples</em>. Diferentemente do Jest, arquivos do Cypress tem como extensão <code>.spec.js</code>. &nbsp;Por ser um teste de ponta a ponta, rodaremos no nosso arquivo <code>App.js</code>. Sua estrutura de diretórios, agora, deve se parecer com a da imagem abaixo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-21.png" class="kg-image" alt="image-21" width="316" height="405" loading="lazy"></figure><p>Também configuramos um url base no arquivo <code>cypress.json</code>, como no exemplo abaixo:</p><p><code>{ "baseUrl": "<a href="http://localhost:3000/">http://localhost:3000</a>" }</code></p><p>Agora, vamos ao nosso longo teste monolítico</p><pre><code class="language-javascript">import React from 'react';

describe ('Teste completo de ponta a ponta', () =&gt; {
  it('teste completo de ponta a ponta', () =&gt; {
    cy.visit('/')
    //teste de contador
    cy.contains("Clicked: 0")
      .click()
    cy.contains("Clicked: 1")
    // teste básico de hooks
    cy.contains("State inicial")
    cy.contains("Botão de alteração do state")
      .click()
    cy.contains("State inicial alterado")
    cy.contains("Moe")
    cy.contains("Alterar nome")
      .click()
    cy.contains("Steve")
    //teste de useReducer
    cy.contains('stateprop1 is false')
    cy.contains('Dispatch Success')
      .click()
    cy.contains('stateprop1 is true')
    //teste de useContext
    cy.contains("Um texto")
    cy.contains('Alterar texto')
      .click()
    cy.contains("Outro texto")
    //teste do formulário
    cy.get('#text1')
      .type('Novo texto {enter}')
    cy.contains("Alterara: Novo texto")
    cy.contains("Enviar valor: Novo texto")
    //teste de axios
    cy.request('https://jsonplaceholder.typicode.com/posts/1')
      .should(res =&gt; {
          expect(res.body).not.to.be.null
          cy.contains(res.body.title)
        })
  });
});</code></pre><p>Como mencionado antes, estamos rodando todos os testes que já passamos em apenas um bloco de código. Separei cada sessão com um comentário para que fique mais fácil a visualização.</p><p>Nosso teste pode ser um pouco intimidador a princípio, mas a maioria dos testes individuais seguirão o mesmo padrão arranjo-ação-declaração.</p><pre><code class="language-javascript">
cy.contains(Algum texto de innerHTML do nó do DOM)

cy.contains (texto do botão)
.click()

cy.contains(Texto atualizado de innerHTML do nó do DOM)
</code></pre><p>Considerando que esse é um teste de ponta a ponta, você não encontrará qualquer tipo de <em>mock</em>. Nossa aplicação será rodada na sua versão de desenvolvimento completa em um simulador de navegador com uma interface. Essa é a maneira de testagem mais próxima da realidade que podemos ter.</p><p>Ao contrário dos testes unitários e de integração, não precisamos explicitamente declarar algumas coisas. Isso acontece porque o Cypress tem alguns comandos embutidos em algumas declarações por padrão. <strong><strong>De</strong>clarações padrão </strong>são exatamente como o nome as descreve. Elas são declaradas por padrão. Então, não há a necessidade de adicionar um combinador.</p><p><a href="https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Default-Assertions">Declarações padrão do Cypress</a> (documentação do Cypress, em inglês)</p><p>Os comandos são encadeados uns nos outros. Então, ordená-los é bem importante. Cada comando esperará até que o comando anterior esteja completo antes de rodar. </p><p>Mesmo nas testagens com Cypress, ainda manteremos nossa filosofia de não testar detalhes de implementação. Na prática, isso quer dizer que não usaremos classes de html/css, ids ou propriedades como seletores se pudermos. A única vez em que precisaremos usar ids é para obter acesso ao elemento de input. </p><p>Faremos uso do comando <code>cy.contains()</code>, que retornará um nó do DOM com um texto correspondente. O que nosso usuário final fará será ver o texto na interface e interagir com ele. Então, testar dessa maneira será nossa linha com nossos princípios-guia. </p><p>Como não estamos <em>mockando </em>nada, você perceberá que nossos testes serão bem simples. Isso é bom. Já que essa é uma aplicação rodando, nossos testes não terão nenhum valor artificial.</p><p>Em nossos testes com axios, faremos uma requisição de http real para nosso <em>endpoint</em>. Fazer uma requisição em um teste de ponta a ponta é bem comum. Então, verificaremos se o valor não é nulo. Depois disso, nos certificaremos de que nosso dado aparece na interface.</p><p>Se for feito corretamente, você verá que o cypress rodou com sucesso os testes no Chromium. </p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-22.png" class="kg-image" alt="image-22" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-22.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/11/image-22.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-22.png 1229w" sizes="(min-width: 720px) 720px" width="1229" height="941" loading="lazy"></figure><h2 id="integra-o-cont-nua"><strong>Integração contínua</strong></h2><p>Rastrear e rodar todos esses testes manualmente pode se tornar uma tarefa um pouco tediosa. Para resolver isso, temos a integração contínua (em inglês, <em>Continuous Integration</em>, ou<em> CI</em>), que é uma maneira de automatizar nossos testes continuamente.</p><h3 id="travis-ci"><strong><strong>Travis CI</strong></strong></h3><p>Para deixar as coisas mais simples, usaremos o Travis CI para nossa integração contínua. Você deve ter em mente, porém, que existem configurações muito mais complexas para a integração contínua usando o Docker e o Jenkins.</p><p>Você precisará estar conectado a uma conta do Travis e a uma do Github. Por sorte, ambas são gratuitas.</p><p>Eu sugiro que você selecione a opção "Entrar com Github" no Travis CI.</p><p>Ao sincronizar sua conta, você pode ir até o ícone que leva até o seu perfil e clicar no botão de ativar/desativar, correspondente ao repositório no qual você quer ativar a CI.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-15-1.png" class="kg-image" alt="image-15-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-15-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-15-1.png 905w" sizes="(min-width: 720px) 720px" width="905" height="58" loading="lazy"></figure><p>Aqui, o Travis CI sabe do que você precisa para configurar um arquivo <code>.travis.yml</code> na raiz do seu projeto.</p><pre><code class="language-yaml">language: node_js

node_js: 
  - stable
  
  
install:
  - npm install

script:
  - npm run test
  - npm run coveralls</code></pre><p>Isso, essencialmente, diz ao Travis que estamos usando o node_js. Faça o download de uma versão estável, instale as dependências e rode os comandos <code>npm run test</code> e <code>npm run coveralls</code>.</p><p>É isso. Você pode ir ao painel de controle e começar a <em>build</em>. O Travis rodará os testes automaticamente e entregará a você um resultado como o que vemos abaixo. Se seus testes passarem, você pode seguir em frente. Se falharem, sua <em>build</em> falhará e você precisará consertar o seu código e reiniciar a <em>build.</em></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-16.png" class="kg-image" alt="image-16" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-16.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/11/image-16.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-16.png 1423w" sizes="(min-width: 720px) 720px" width="1423" height="815" loading="lazy"></figure><h3 id="testes-de-cobertura"><strong>Testes de cobertura</strong></h3><p>Os testes de cobertura nos dão um relatório que, basicamente, nos diz o quanto de nosso código está sendo testado.</p><p>Você precisará se registrar e sincronizar com sua conta do GitHub. Do mesmo modo que fizemos com o Travis CI, é necessário apenas que você vá para a aba <em>Add Repo</em> (Adicionar repositórios, em português) e ative o repositório que você também ativou no Travis CI.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-17.png" class="kg-image" alt="image-17" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-17.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-17.png 940w" sizes="(min-width: 720px) 720px" width="940" height="590" loading="lazy"></figure><p>Agora, vá em seu arquivo <code>package.json</code> e adicione a linha de código de <code>coveralls</code>:</p><pre><code>  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --coverage",
    "eject": "react-scripts eject",
    "cypress": "node_modules/.bin/cypress open", 
    "coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls"
  },</code></pre><p>Certifique-se de que a flag <code>--coverage</code> esteja adicionada ao comando <code>react-scripts test</code>. Isso é o que vai gerar os dados necessários de que o teste de cobertura precisará para gerar o relatório final.</p><p>Você pode, de fato, ver esses dados de cobertura no Travis CI depois que seus testes terminarem de rodar.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-18.png" class="kg-image" alt="image-18" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-18.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/11/image-18.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-18.png 1043w" sizes="(min-width: 720px) 720px" width="1043" height="796" loading="lazy"></figure><p>Considerando que não estamos lidando com um repositório privado nem com o Travis CI Pro, não precisamos nos preocupar com nenhum tipo de chave.</p><p>Uma vez concluído, você poderá adicionar uma insígnia (em inglês, <em>badge</em>) ao seu README, copiando o link gerado no dashboard.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-19.png" class="kg-image" alt="image-19" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/11/image-19.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-19.png 702w" width="702" height="394" loading="lazy"></figure><p>O resultado será mais ou menos assim:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/11/image-20.png" class="kg-image" alt="image-20" width="515" height="246" loading="lazy"></figure><h2 id="conclus-o"><strong><strong>Conclus</strong>ão</strong></h2><p>Você pode se considerar nos melhores 20% em termos de desenvolvedores com relação à testagem em React caso tenha conseguido acompanhar o tutorial até aqui. </p><p>Obrigado pela leitura, pessoal. </p><p>Siga o autor no <a href="https://twitter.com/iqbal125sf?lang=en">Twitter</a> para ver mais tutoriais escritos por ele.</p><h3 id="leitura-complementar">Leitura complementar<br></h3><p><strong>Publicações em blogs (em inglês)</strong></p><p><a href="https://djangostars.com/blog/what-and-how-to-test-with-enzyme-and-jest-full-instruction-on-react-component-testing/#utm_source=medium&amp;utm_medium=blog.bitsrc.io&amp;utm_campaign=react%20components%20testing&amp;utm_content=continue%20reading%20the%20original%20article%20on%20our%C2%A0blog">https://djangostars.com/blog/what-and-how-to-test-with-enzyme-and-jest-full-instruction-on-react-component-testing/</a></p><p><a href="https://engineering.ezcater.com/the-case-against-react-snapshot-testing">https://engineering.ezcater.com/the-case-against-react-snapshot-testing</a></p><p><a href="https://medium.com/@tomgold_48918/why-i-stopped-using-snapshot-testing-with-jest-3279fe41ffb2">https://medium.com/@tomgold_48918/why-i-stopped-using-snapshot-testing-with-jest-3279fe41ffb2</a></p><p><a href="https://circleci.com/blog/continuously-testing-react-applications-with-jest-and-enzyme/">https://circleci.com/blog/continuously-testing-react-applications-with-jest-and-enzyme/</a></p><p><a href="https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html">https://testing.googleblog.com/2015/04/just-say-no-to-more-end-to-end-tests.html</a></p><p><a href="https://willowtreeapps.com/ideas/best-practices-for-unit-testing-with-a-react-redux-approach">https://willowtreeapps.com/ideas/best-practices-for-unit-testing-with-a-react-redux-approach</a></p><p><a href="https://blog.pragmatists.com/genuine-guide-to-testing-react-redux-applications-6f3265c11f63">https://blog.pragmatists.com/genuine-guide-to-testing-react-redux-applications-6f3265c11f63</a></p><p><a href="https://hacks.mozilla.org/2018/04/testing-strategies-for-react-and-redux/">https://hacks.mozilla.org/2018/04/testing-strategies-for-react-and-redux/</a></p><p><a href="https://codeburst.io/deliberate-practice-what-i-learned-from-reading-redux-mock-store-8d2d79a4b24d">https://codeburst.io/deliberate-practice-what-i-learned-from-reading-redux-mock-store-8d2d79a4b24d</a></p><p><a href="https://www.robinwieruch.de/react-testing-tutorial/">https://www.robinwieruch.de/react-testing-tutorial/</a></p><p><a href="https://medium.com/@ryandrewjohnson/unit-testing-components-using-reacts-new-context-api-4a5219f4b3fe">https://medium.com/@ryandrewjohnson/unit-testing-components-using-reacts-new-context-api-4a5219f4b3fe</a></p><p><strong>Textos de <strong>Kent C</strong>.<strong> </strong>D<strong>odds </strong>sobre testes (em inglês)</strong></p><p><a href="https://kentcdodds.com/blog/introducing-the-react-testing-library">https://kentcdodds.com/blog/introducing-the-react-testing-library</a></p><p><a href="https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests">https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests</a></p><p><a href="https://kentcdodds.com/blog/why-i-never-use-shallow-rendering">https://kentcdodds.com/blog/why-i-never-use-shallow-rendering</a></p><p><a href="https://kentcdodds.com/blog/demystifying-testing">https://kentcdodds.com/blog/demystifying-testing</a></p><p><a href="https://kentcdodds.com/blog/effective-snapshot-testing">https://kentcdodds.com/blog/effective-snapshot-testing</a></p><p><a href="https://kentcdodds.com/blog/testing-implementation-details">https://kentcdodds.com/blog/testing-implementation-details</a></p><p><a href="https://kentcdodds.com/blog/common-testing-mistakes">https://kentcdodds.com/blog/common-testing-mistakes</a></p><p><a href="https://kentcdodds.com/blog/ui-testing-myths">https://kentcdodds.com/blog/ui-testing-myths</a></p><p><a href="https://kentcdodds.com/blog/why-youve-been-bad-about-testing">https://kentcdodds.com/blog/why-youve-been-bad-about-testing</a></p><p><a href="https://kentcdodds.com/blog/the-merits-of-mocking">https://kentcdodds.com/blog/the-merits-of-mocking</a></p><p><a href="https://kentcdodds.com/blog/how-to-know-what-to-test">https://kentcdodds.com/blog/how-to-know-what-to-test</a></p><p><a href="https://kentcdodds.com/blog/avoid-the-test-user">https://kentcdodds.com/blog/avoid-the-test-user</a></p><p><strong>Atalhos úteis<strong>/threads</strong> do GitHub (em inglês)</strong></p><p><a href="https://devhints.io/enzyme">https://devhints.io/enzyme</a></p><p><a href="https://devhints.io/enzyme">https://devhints.io</a>/jest</p><p><a href="https://github.com/ReactTraining/react-router/tree/master/packages/react-router/modules/__tests__">https://github.com/ReactTraining/react-router/tree/master/packages/react-router/modules/__tests__</a></p><p><a href="https://github.com/airbnb/enzyme/issues/1938">https://github.com/airbnb/enzyme/issues/1938</a></p><p><a href="https://gist.github.com/fokusferit/e4558d384e4e9cab95d04e5f35d4f913">https://gist.github.com/fokusferit/e4558d384e4e9cab95d04e5f35d4f913</a></p><p><a href="https://airbnb.io/enzyme/docs/api/selector.html">https://airbnb.io/enzyme/docs/api/selector.html</a></p><p><strong><strong>Doc</strong>umentação</strong></p><p><a href="https://docs.cypress.io/">https://docs.cypress.io</a></p><p><a href="https://airbnb.io/enzyme/">https://airbnb.io/enzyme/</a></p><p><a href="https://github.com/dmitry-zaets/redux-mock-store">https://github.com/dmitry-zaets/redux-mock-store</a></p><p><a href="https://jestjs.io/docs/en">https://jestjs.io/docs/en</a></p><p><a href="https://testing-library.com/docs/learning">https://testing-library.com/docs/learning</a></p><p><a href="https://sinonjs.org/releases/v7.3.2/">https://sinonjs.org/releases/v7.3.2/</a></p><p><a href="https://redux.js.org/recipes/writing-tests">https://redux.js.org/recipes/writing-tests</a></p><p><a href="https://jestjs.io/docs/en/using-matchers">https://jestjs.io/docs/en/using-matchers</a></p><p><a href="https://jestjs.io/docs/en/api">https://jestjs.io/docs/en/api</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Hash tables em JavaScript – array associativo de hashing em JS ]]>
                </title>
                <description>
                    <![CDATA[ Hash tables são estruturas de dados que vão permitir que você crie uma lista de valores pareados. Você pode, então, recuperar determinado valor usando a respectiva chave para aquele valor, que você coloca na tabela de antemão. Uma hash table transforma uma chave em um número inteiro, que serve como ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/hash-tables-em-javascript-array-associativo-de-hashing-em-js/</link>
                <guid isPermaLink="false">6242fce86a6ca90519ad0a1d</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Tue, 26 Apr 2022 13:02:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/04/JavaScript-Hash-Table.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/javascript-hash-table-associative-array-hashing-in-js/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">JavaScript Hash Table – Associative Array Hashing in JS</a>
      </p><p><em>Hash tables</em> são estruturas de dados que vão permitir que você crie uma lista de valores pareados. Você pode, então, recuperar determinado valor usando a respectiva chave para aquele valor, que você coloca na tabela de antemão.</p><p>Uma <em>hash table </em>transforma uma chave em um número inteiro, que serve como índice, usando uma função <em>hash</em>. Esse índice decidirá onde armazenar na memória o par chave/valor:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/04/g983.jpg" class="kg-image" alt="g983" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/04/g983.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/04/g983.jpg 1000w" sizes="(min-width: 720px) 720px" width="1000" height="695" loading="lazy"><figcaption>Hash table para armazenar números de telefone (extraído da <a href="https://en.wikipedia.org/wiki/Hash_table">Wikipédia</a>)</figcaption></figure><p>Você normalmente usará uma <em>hash table </em>por suas rápidas operações de busca, inserção e remoção:</p><!--kg-card-begin: html--><table style="box-sizing: inherit; margin: 0.5em 0px 2.5em; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, Roboto, Oxygen, Ubuntu, Cantarell, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, sans-serif; font-size: 1.6rem; vertical-align: top; border-spacing: 0px; border-collapse: collapse; display: inline-block; overflow-x: auto; max-width: 100%; width: auto; white-space: nowrap; background: radial-gradient(at left center, rgba(0, 0, 0, 0.2) 0px, rgba(0, 0, 0, 0) 75%) 0px center / 10px 100% no-repeat scroll, radial-gradient(at right center, rgba(0, 0, 0, 0.2) 0px, rgba(0, 0, 0, 0) 75%) 100% center / 10px 100% scroll;"><thead style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><th style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 1.2rem; vertical-align: baseline; color: var(--gray85); letter-spacing: 0.2px; text-align: center; text-transform: uppercase; background-color: var(--gray10);"></th><th style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 1.2rem; vertical-align: baseline; color: var(--gray85); letter-spacing: 0.2px; text-align: center; text-transform: uppercase; background-color: var(--gray10);">COMPLEXIDADE DE TEMPO DA HASH TABLE NA NOTAÇÃO BIG O</th><th style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: 700; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 1.2rem; vertical-align: baseline; color: var(--gray85); letter-spacing: 0.2px; text-align: center; text-transform: uppercase; background-color: var(--gray10);"></th></tr></thead><tbody style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to right, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Algoritmo</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; text-align: center;">Média</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-position: 100% 0px; background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Pior caso</td></tr><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to right, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Espaço</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; text-align: center;">O(n)</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-position: 100% 0px; background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">O(n)</td></tr><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to right, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Pesquisa</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; text-align: center;">O(1)</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-position: 100% 0px; background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">O(n)</td></tr><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to right, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Inserção</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; text-align: center;">O(1)</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-position: 100% 0px; background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">O(n)</td></tr><tr style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline;"><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to right, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">Remoção</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; text-align: center;">O(1)</td><td style="box-sizing: inherit; margin: 0px; padding: 6px 12px; border: var(--gray10) 1px solid; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 16px; vertical-align: baseline; background-image: linear-gradient(to left, rgb(255, 255, 255) 50%, rgba(255, 255, 255, 0) 100%); background-position: 100% 0px; background-size: 20px 100%; background-repeat: no-repeat; text-align: center;">O(n)</td></tr></tbody></table><!--kg-card-end: html--><p>Retirado da <a href="https://en.wikipedia.org/wiki/Hash_table">Wikipédia</a></p><p>Esse tutorial ajudará você a entender implementações de <em>hash tables</em> no JavaScript e também mostrará como você pode construir suas próprias classes de <em>hash table</em>.</p><p>Primeiramente, vamos dar uma olhada nas classes do JavaScript <code>Object</code> e <code>Map</code>.</p><h2 id="como-usar-hash-tables-com-as-classes-object-e-map-em-javascript"><strong>Como usar <em>hash tables</em> com as classes <em>Object</em> e <em>Map</em> em <strong>JavaScript</strong></strong></h2><p>O exemplo mais comum de uma <em>hash table</em> em JavaScript é a estrutura de dados <code>Object</code>, onde você pode parear duas propriedades de chave e valor.</p><p>No exemplo a seguir, a chave <code>Nathan</code> está pareada com o numero de telefone de valor <code>"555-0182"</code> e a chave <code>Jane</code> está pareada com o valor <code>"315-0322"</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">let obj = {
  Nathan: "555-0182",
  Jane: "315-0322"
}</code></pre><figcaption><em>Object</em>, em JavaScript, é um exemplo de implementação de uma <em>hash table</em></figcaption></figure><p>Porém, a estrutura de dados do tipo <code>Object</code> em JavaScript é um tipo especial de implementação de uma <em>hash table</em> por duas razões:</p><ul><li>Ela tem propriedades adicionadas pela classe <code>Object</code>. Chaves que você insere podem entrar em conflito e sobrescrever propriedades padrão já herdadas da classe.</li><li>O tamanho de uma <em>hash table</em> não é monitorado. Você precisa contar manualmente quantas propriedades são definidas pelo programador ao invés de herdar do <em>prototype</em>.</li></ul><p>Por exemplo, o <em>prototype </em>de <code>Object</code> &nbsp;tem o método <code>hasOwnProperty()</code>, que sempre permitirá a você checar se uma propriedade não é herdada:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const obj = {};
obj.name = "Nathan";

console.log(obj.hasOwnProperty("name")); // true</code></pre><figcaption>Exemplo de chamada de um método herdado de <em>object</em> em JavaScript</figcaption></figure><p>O JavaScript não impede você de tentar sobrescrever o método <code>hasOwnProperty()</code>, o que pode causar um erro deste tipo:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const obj = {};
obj.name = "Nathan";
obj.hasOwnProperty = true;

console.log(obj.hasOwnProperty("name")); 
// Error: obj.hasOwnProperty is not a function</code></pre><figcaption>A propriedade JavaScript herdada de <em>object </em>foi sobrescrita</figcaption></figure><p>Para lidar com essas deficiências, o JavaScript criou uma outra implementação de estrutura de dados <em>hash table</em> chamada <code>Map</code></p><p>Assim como <code>Object</code>, <code>Map</code> permite a você guardar pares chave/valor dentro de uma estrutura de dados. Aqui está um exemplo de <code>Map</code> em ação:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const collection = new Map();

collection.set("Nathan", "555-0182");
collection.set("Jane", "555-0182");

console.log(collection.get("Nathan")); // 555-0182
console.log(collection.size); // 2</code></pre><figcaption>A classe <em>Map</em> em JavaScript é outra implementação de uma <em>hash table</em></figcaption></figure><p>Ao contrário do tipo <code>Object</code>, <code>Map</code> requer que você use os métodos <code>set()</code> e <code>get()</code> para definir e buscar quaisquer valores de chave/valor que desejar adicionar à estrutura de dados.</p><p>Você também não consegue sobrescrever, em &nbsp;<code>Map</code>, propriedades herdadas. Por exemplo, o código a seguir tentou sobrescrever o valor da propriedade <code>size</code> para &nbsp;<code>false</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const collection = new Map();

collection.set("Nathan", "555-0182");
collection["size"] = false;

console.log(collection.get("size")); // undefined
console.log(collection.size); // 1</code></pre><figcaption>Propriedades em estruturas do tipo Map não podem ser sobrescritas</figcaption></figure><p>Como você pode ver no código acima, você não pode adicionar uma nova entrada para o objeto <code>Map</code> sem usar o método <code>set()</code>.</p><p>A estrutura de dados <code>Map</code> é iterável também, o que significa que você pode fazer um <em>loop</em> para percorrer os dados como no código a seguir: </p><figure class="kg-card kg-code-card"><pre><code class="language-js">const myMap = new Map();

myMap.set("Nathan", "555-0182");
myMap.set("Jane", "315-0322");

for (let [key, value] of myMap) {
  console.log(`${key} = ${value}`);
}</code></pre><figcaption>Iterando sobre um objeto Map</figcaption></figure><p>Agora que você aprendeu como JavaScript implementa <em>hash tables</em> nas estruturas de dados <code>Object</code> e <code>Map</code>, vamos ver como você pode criar sua própria implementação de uma <em>hash table</em> a seguir.</p><h2 id="como-implementar-uma-estrutura-de-dados-de-hash-table-em-javascript"><strong>Como implementar uma estrutura de dados de </strong><em>hash table</em><strong><strong> </strong>em<strong> JavaScript</strong></strong></h2><p>Embora o JavaScript já tenha duas implementações de <em>hash table</em>, escrever sua própria implementação é uma dos exercícios mais comuns de entrevistas de &nbsp;emprego que envolvem JavaScript.</p><p>Você pode implementar uma <em>hash table</em> em JavaScript em três passos:</p><ul><li>Criar uma classe <code>HashTable</code> com <code>table</code> e <code>size</code> como propriedades iniciais</li><li>Adicionar uma função <code>hash()</code> para transformar chaves em índices, <em>index </em>em inglês</li><li>Adicionar métodos <code>set()</code> e <code>get()</code> para inserir e obter pares de chave/valor da tabela.</li></ul><p>Dito isso, começaremos &nbsp;criando uma classe <code>HashTable</code>. O código abaixo criará uma <code>table</code> com <code>127</code> espaços para armazenamento:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">class HashTable {
  constructor() {
    this.table = new Array(127);
    this.size = 0;
  }
}</code></pre><figcaption>Propriedades iniciais da classe HashTable</figcaption></figure><p>Todos seus pares de chave/valor serão armazenados dentro da propriedade <code>table</code>.</p><h3 id="como-escrever-o-m-todo-hash-"><strong>Como escrever o método <strong>hash()</strong></strong></h3><p>A seguir, você precisa criar o método hash() que aceitará um valor <code>key</code> e transformá-lo em um <em>index</em>.</p><p>Um jeito simples de criar a <em>hash </em>seria somar o código ASCII dos caracteres na chave usando o método <code>charCodeAt()</code> como é feito abaixo. Note que o método é nomeado usando <code>_</code> &nbsp;para indicar que essa é uma classe privada:</p><pre><code class="language-js">_hash(key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash;
}</code></pre><p>Considerando, no entanto, que a classe <code>HashTable</code> tem apenas 127 espaços, o método <code>_hash()</code> deve retornar um número entre <code>0 e 127</code>.</p><p>Para assegurar que o valor do <em>hash </em>não excederá o tamanho permitido, você precisa usar o operador módulo como é mostrado a seguir:</p><pre><code class="language-js">_hash(key) {
  let hash = 0;
  for (let i = 0; i &lt; key.length; i++) {
    hash += key.charCodeAt(i);
  }
  return hash % this.table.length;
}</code></pre><p>Agora que você tem o método <code>_hash()</code> completo, é hora de escrever os métodos <code>set()</code> e <code>get()</code>.</p><h3 id="como-criar-o-m-todo-set-"><strong>Como criar o método<strong> set()</strong></strong></h3><p>Para inserir os pares de chave/valor na sua <em>hash table</em>, você precisa escrever um método <code>set()</code> que aceita como parâmetros <code>(chave, valor)</code>:</p><ul><li>O método <code>set()</code> chamará o método <code>_hash()</code> para obter o valor do <code>index</code></li><li>O par <code>[chave, valor]</code> &nbsp;será atribuído na <code>tabela</code> no<code>index</code> especificado</li><li>Então, a propriedade <code>size</code> será incrementada uma vez</li></ul><pre><code class="language-js">set(key, value) {
  const index = this._hash(key);
  this.table[index] = [key, value];
  this.size++;
}</code></pre><p>Agora que o método <code>set()</code> está completo, vamos escrever o método <code>get()</code> para obter o valor por sua chave.</p><h3 id="como-escrever-o-m-todo-get-"><strong>Como escrever o método<strong> get()</strong></strong></h3><p>Para obter um certo valor da <em>hash table</em>, você precisa escrever um método <code>get()</code> que aceita um valor <code>key</code> como parâmetro:</p><ul><li>O método chamará o método <code>_hash()</code> novamente para obter na tabela o <code>index</code></li><li>Retorne o valor guardado em <code>table[index]</code></li></ul><pre><code class="language-js">get(key) {
  const index = this._hash(key);
  return this.table[index];
}</code></pre><p>Desse modo, o método <code>get()</code> retornará o par chave/valor ou <code>undefined</code> quando não tiver nenhum par de chave/valor guardado no <code>index</code> especificado.</p><p>Tudo certo por enquanto. Vamos, a seguir, adicionar outro método para excluir um par de chave/valor da <em>hash table</em>.</p><h3 id="como-escrever-o-m-todo-remove-"><strong>Como escrever o método<strong> remove()</strong></strong></h3><p>Para excluir um par de chave/valor da <em>hash table</em>, você precisa escrever um método <code>remove()</code>, que aceita um valor <code>key</code> como parâmetro:</p><ul><li>Obtenha o <code>index</code> certo usando o método <code>_hash()</code></li><li>Confira se <code>table[index]</code> tem um valor verdadeiro (<em>truthy,</em> em inglês<em>) </em>e se a propriedade <code>length</code> é maior que zero. Atribua o valor <code>undefined</code> para <code>index</code> e decremente a propriedade <code>size</code> se possível.</li><li>Caso contrário, simplesmente retorne <code>false</code></li></ul><pre><code class="language-js">remove(key) {
  const index = this._hash(key);

  if (this.table[index] &amp;&amp; this.table[index].length) {
    this.table[index] = undefined;
    this.size--;
    return true;
  } else {
    return false;
  }
}</code></pre><p>Com isso, você agora tem um método <code>remove()</code> funcionando. Vamos ver se a classe <code>HashTable</code> funciona corretamente.</p><h2 id="como-testar-a-implementa-o-da-hash-table"><strong>Como testar a implementação da<strong> </strong></strong><em>hash table</em></h2><p>É hora de testar a implementação da <em>hash table</em>. Aqui está, novamente, o código completo para a implementação da <em>hash table</em>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">class HashTable {
  constructor() {
    this.table = new Array(127);
    this.size = 0;
  }

  _hash(key) {
    let hash = 0;
    for (let i = 0; i &lt; key.length; i++) {
      hash += key.charCodeAt(i);
    }
    return hash % this.table.length;
  }

  set(key, value) {
    const index = this._hash(key);
    this.table[index] = [key, value];
    this.size++;
  }

  get(key) {
    const target = this._hash(key);
    return this.table[target];
  }

  remove(key) {
    const index = this._hash(key);

    if (this.table[index] &amp;&amp; this.table[index].length) {
      this.table[index] = [];
      this.size--;
      return true;
    } else {
      return false;
    }
  }
}</code></pre><figcaption>Implementação da <em>hash table</em> em JavaScript</figcaption></figure><p>Para testar a classe <code>HashTable</code>, eu vou criar uma nova instância da classe e configurar alguns pares de chave e valor como mostrado abaixo. Os pares de chave/valor abaixo contém apenas valores arbitrários combinados sem nenhum significado especial:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const ht = new HashTable();
ht.set("Canada", 300);
ht.set("France", 100);
ht.set("Spain", 110);</code></pre><figcaption>Testando o método set() da <em>HashTable</em></figcaption></figure><p>Então, vamos tentar obtê-los usando o método <code>get()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">console.log(ht.get("Canada")); // [ 'Canada', 300 ]
console.log(ht.get("France")); // [ 'France', 100 ]
console.log(ht.get("Spain")); // [ 'Spain', 110 ]</code></pre><figcaption>Testando o método get() da <em>HashTable</em></figcaption></figure><p>Finalmente, vamos tentar excluir um desses valores com o método <code>remove()</code>:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">console.log(ht.remove("Spain")); // true
console.log(ht.get("Spain")); // undefined</code></pre><figcaption>Testando o método remove() da <em>HashTable</em></figcaption></figure><p>Tudo bem, todos os métodos estão funcionando como esperado. Vamos tentar outra inserção com uma nova instância de <code>HashTable</code> e obter esses valores:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const ht = new HashTable();

ht.set("Spain", 110);
ht.set("ǻ", 192);

console.log(ht.get("Spain")); // [ 'ǻ', 192 ]
console.log(ht.get("ǻ")); // [ 'ǻ', 192 ]</code></pre><figcaption>Conflitos de index na <em>HashTable</em></figcaption></figure><p>Opa! Parece que nós temos um problema aqui. 😨</p><h2 id="como-resolver-os-conflitos-de-index"><strong>Como resolver os conflitos de <em>index</em></strong></h2><p>Algumas vezes, a função hash em uma <em>hash table</em> pode retornar o mesmo <code>index</code>. No caso de teste acima, a string <code>"Spain"</code> e <code>"ǻ"</code> <strong>retornam o mesmo valor de <strong><code>hash</code></strong>,<strong> </strong></strong>pois o número <code>507</code> é a soma de ambos os códigos ASCII.</p><p>O mesmo valor <code>hash</code> fará com que os índices entrem em <em>conflito</em>, sobrescrevendo o anterior com o novo valor configurado.</p><p>Até agora, o dado guardado em nossa implementação de <em>hash table</em> tem essa aparência:</p><pre><code class="language-js">[
    [ "Spain", 110],
    [ "France", 100]
]</code></pre><p>Para resolver o conflito de <code>index</code>, você precisa guardar o par de chave/valor em um array secundário de modo que o resultado final seja algo assim:</p><pre><code class="language-js">[
    [
        [ "Spain", 110 ],
        [ "ǻ", 192 ]
    ],
    [
        ["France", 100]
    ],
]</code></pre><p>Para criar o segundo array, você precisa atualizar o método <code>set()</code> para que ele:</p><ul><li>Procure o <code>table[index]</code> e itere sobre seus valores.</li><li>Se a chave em um dos arrays for igual ao parâmetro <code>key</code> passado ao método, repita o valor no index <code>1</code> e pare quaisquer execuções futuras declarando um <code>return</code>.</li><li>Se não encontrar nenhuma <code>key</code> igual, crie um novo array de chave e valor para o segundo array.</li><li>Então, inicialize um novo array e coloque o par de chave/valor no <code>index</code> especificado.</li><li>Sempre que um método <code>push()</code> for chamado, incremente a propriedade <code>size</code> em uma unidade.</li></ul><p>O código do método <code>set()</code> completo está abaixo:</p><pre><code class="language-js">set(key, value) {
  const index = this._hash(key);
  if (this.table[index]) {
    for (let i = 0; i &lt; this.table[index].length; i++) {
      //Encontre o par chave/valor na cadeia
      if (this.table[index][i][0] === key) {
        this.table[index][i][1] = value;
        return;
      }
    }
    //Se não for encontrado, crie um novo par de chave/valor
    this.table[index].push([key, value]);
  } else {
    this.table[index] = [];
    this.table[index].push([key, value]);
  }
  this.size++;
}</code></pre><p>A seguir, atualize o método <code>get()</code> de maneira que ele também confira o array de segundo nível com um laço <code>for</code> e retorne o par correto de chave/valor:</p><pre><code class="language-js">get(key) {
  const target = this._hash(key);
  if (this.table[target]) {
    for (let i = 0; i &lt; this.table.length; i++) {
      if (this.table[target][i][0] === key) {
        return this.table[target][i][1];
      }
    }
  }
  return undefined;
}</code></pre><p>Finalmente, você precisa atualizar o método <code>remove()</code> para que ele faça um <em>laço </em>para iterar sobre o array de segundo nível e remova o array com o valor da <code>key</code> correta usando o método <code>splice()</code>:</p><pre><code class="language-js">remove(key) {
  const index = this._hash(key);

  if (this.table[index] &amp;&amp; this.table[index].length) {
    for (let i = 0; i &lt; this.table.length; i++) {
      if (this.table[index][i][0] === key) {
        this.table[index].splice(i, 1);
        this.size--;
        return true;
      }
    }
  } else {
    return false;
  }
}</code></pre><p>Com isso, sua classe <code>HashTable</code> será capaz de evitar quaisquer conflitos de <em>index </em>eventuais e guardará o par de chave/valor dentro do array de segundo nível.</p><p>Como bônus, vamos adicionar um método <code>display()</code> que mostrará todos os pares de chave/valor guardados na <em>Hash Table</em>. Você só precisa usar o método <code>forEach()</code> para iterar sobre a tabela e usar <code>map()</code> para mapear os valores para uma <em>string </em>assim como no exemplo a seguir:</p><pre><code class="language-js">display() {
  this.table.forEach((values, index) =&gt; {
    const chainedValues = values.map(
      ([key, value]) =&gt; `[ ${key}: ${value} ]`
    );
    console.log(`${index}: ${chainedValues}`);
  });
}</code></pre><p>Aqui está o código da classe <code>HashTable</code> completo novamente com a aplicação correta para a resolução do conflito de índices, para sua referência:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">class HashTable {
  constructor() {
    this.table = new Array(127);
    this.size = 0;
  }

  _hash(key) {
    let hash = 0;
    for (let i = 0; i &lt; key.length; i++) {
      hash += key.charCodeAt(i);
    }
    return hash % this.table.length;
  }

  set(key, value) {
    const index = this._hash(key);
    if (this.table[index]) {
      for (let i = 0; i &lt; this.table[index].length; i++) {
        if (this.table[index][i][0] === key) {
          this.table[index][i][1] = value;
          return;
        }
      }
      this.table[index].push([key, value]);
    } else {
      this.table[index] = [];
      this.table[index].push([key, value]);
    }
    this.size++;
  }

  get(key) {
    const index = this._hash(key);
    if (this.table[index]) {
      for (let i = 0; i &lt; this.table.length; i++) {
        if (this.table[index][i][0] === key) {
          return this.table[index][i][1];
        }
      }
    }
    return undefined;
  }

  remove(key) {
    const index = this._hash(key);

    if (this.table[index] &amp;&amp; this.table[index].length) {
      for (let i = 0; i &lt; this.table.length; i++) {
        if (this.table[index][i][0] === key) {
          this.table[index].splice(i, 1);
          this.size--;
          return true;
        }
      }
    } else {
      return false;
    }
  }

  display() {
    this.table.forEach((values, index) =&gt; {
      const chainedValues = values.map(
        ([key, value]) =&gt; `[ ${key}: ${value} ]`
      );
      console.log(`${index}: ${chainedValues}`);
    });
  }
}</code></pre><figcaption>Implementação completa da classe HashTable</figcaption></figure><p>Você pode testar a implementação criando uma nova instância de <code>HashTable</code> e fazer algumas inserções e remoções:</p><figure class="kg-card kg-code-card"><pre><code class="language-js">const ht = new HashTable();

ht.set("France", 111);
ht.set("Spain", 150);
ht.set("ǻ", 192);

ht.display();
// 83: [ France: 111 ]
// 126: [ Spain: 150 ],[ ǻ: 192 ]

console.log(ht.size); // 3
ht.remove("Spain");
ht.display();
// 83: [ France: 111 ]
// 126: [ ǻ: 192 ]</code></pre><figcaption>Outro teste com HashTable</figcaption></figure><p>Aqui, não houve conflitos dentro da instância da <code>HashTable</code>. Ótimo trabalho!</p><h2 id="conclus-o"><strong><strong>Conclus</strong>ão</strong></h2><p>Nesse tutorial, você aprendeu o que é uma <em>hash table</em> e como o JavaScript a usa para criar as estruturas de dados <code>Object</code> e <code>Map</code>.</p><p>Você também aprendeu como implementar sua própria classe <code>HashTable</code> e como evitar o conflito de índices na <em>hash table</em> usando a técnica de encadeamento.</p><p>Ao usar a estrutura de dados <em>hash table</em>, você será capaz de criar um array associativo com operações rápidas de busca, inserção e remoção. 😉</p><h2 id="obrigado-por-ler-esse-tutorial"><strong>Obrigado por ler esse<strong><strong><strong><strong><strong><strong><strong><strong><strong><strong><strong><strong><strong><strong><strong> tutorial</strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></strong></h2><p>Se você quiser ler mais sobre JavaScript, pode dar uma olhada no meu site pessoal, sebhastian.com, onde eu tenho publicados <a href="https://sebhastian.com/javascript-tutorials/">mais de 100 tutoriais sobre programação em JavaScript</a>, todos eles usando explicações bem fáceis de entender e exemplos de código.</p><p>Os tutoriais incluem manipulação de strings, manipulação de datas, métodos de arrays e de objetos, soluções de algoritmos em JavaScript e muito mais.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como adicionar hooks de commit ao Git com Husky para automatizar tarefas ]]>
                </title>
                <description>
                    <![CDATA[ Existem muitas ferramentas para automatizar nossos processos de codificação. Nós podemos procurar por erros de sintaxe com ESLint e formatar nosso código com Prettier. Nem todo mundo na sua equipe se lembrará de rodar todos os comandos toda vez que for feito um commit. Como nós podemos usar o Husky ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-adicionar-hooks-de-commit-ao-git-com-husky-para-automatizar-tarefas/</link>
                <guid isPermaLink="false">620cf21d06c4bd05012ff542</guid>
                
                    <category>
                        <![CDATA[ Automação ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Mon, 21 Mar 2022 13:33:24 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/husky.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Add Commit Hooks to Git with Husky to Automate Code Tasks</a>
      </p><p>Existem muitas ferramentas para automatizar nossos processos de codificação. Nós podemos procurar por erros de sintaxe com ESLint e formatar nosso código com Prettier.</p><p>Nem todo mundo na sua equipe se lembrará de rodar todos os comandos toda vez que for feito um <em>commit</em>. Como nós podemos usar o Husky para adicionar hooks do Git e fazer com que eles executem esses comandos para nós?</p><ul><li>O que são os hooks do Git?</li><li>O que é Husky?</li><li>O que nós vamos construir?</li><li>Passo 0: como configurar um novo projeto</li><li>Passo 1: como instalar o Husky em um projeto</li><li>Passo 2: como configurar o Husky para rodar os hooks do Git</li><li>Passo 3: como usar o Husky para formar o código com Prettier</li></ul><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.17977528089888%;" class="fluid-width-video-wrapper">
            <iframe width="356" height="200" src="https://www.youtube.com/embed/tuzys2b1J70?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen="" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><h2 id="o-que-s-o-os-hooks-do-git"><strong>O que são os hooks do Git?</strong></h2><p>Os <a href="https://git-scm.com/docs/githooks">Git hooks</a> (ou hooks do Git) são scripts que você pode configurar para <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">rodar em certos eventos</a> no ciclo de vida do Git. Esses eventos incluem diferentes estágios do <em>commit</em>, como antes de um <em>commit </em>(<em>pré-commit</em>) e depois de um <em>commit </em>(<em>pós-commit</em>).</p><p>Eles são muito úteis, pois permitem que desenvolvedores rodem processos de código automatizados ou até forcem padrões ao automatizar outros scripts para rodar esses processos.</p><h2 id="o-que-o-husky"><strong>O que é o Husky?</strong></h2><p>O <a href="https://github.com/typicode/husky">Husky</a> é uma ferramenta que nos permite facilmente configurar hooks do Git e executar scripts que queremos em certos estágios.</p><p>Ele inclui um objeto dentro do nosso arquivo <code>package.json</code> &nbsp;que rodará o Husky nos scripts que nós especificarmos. Depois disso, o Husky gerencia os pontos do ciclo de vida do Git em que nossos scripts rodarão.</p><h2 id="o-que-n-s-vamos-construir"><strong>O que nós vamos construir?</strong></h2><p>Vamos configurar um projeto simples onde poderemos testar os hooks do Git.</p><p>Você pode acompanhar o artigo com qualquer projeto em que você estiver trabalhando. Usarei o <a href="https://nextjs.org/">Next.js</a> como ponto inicial, simplesmente pelo fato de que precisamos rodar apenas um comando para ter o projeto iniciado.</p><p>Uma consideração é que, ao acompanhar com esse projeto, saiba que estaremos usando o <a href="https://prettier.io/">Prettier</a> como um exemplo do que você pode fazer com hooks do Git.</p><p>O Prettier é uma ferramenta que automaticamente formatará nosso código sem que tenhamos que fazê-lo, o que, se você não estiver se preparando para fazer, pode causar muita tensão. Acompanhar comigo usando o projeto com Next.js vai permitir que você faça testes sem mudanças não intencionais.</p><p>Quanto ao teste dos hooks do Git, começaremos adicionando uma instrução simples na linha de comando para ver o Husky funcionando e nós também testaremos a adição do Prettier, que formatará o nosso código automaticamente.</p><p>Por fim, enquanto escrevo esse artigo, soube que o Husky lançou uma versão <a href="https://typicode.github.io/husky/#/">Alpha v7</a>. Considerando que é apenas uma versão Alpha, seguiremos com a <a href="https://github.com/typicode/husky/tree/v4.3.0">v4</a>, que nos permite instalar facilmente o Husky com o npm.</p><h2 id="passo-0-como-configurar-um-novo-projeto"><strong>Passo 0: como configurar um novo projeto</strong></h2><p>Como eu mencionei, você pode seguir os mesmos passos com qualquer projeto gerenciado com um arquivo <code>package.json</code>.</p><p>O Next.js é absolutamente algo além do necessário para um passo a passo básico. Porém, o objetivo é minimizar os passos de configuração para focar no trabalho com o Husky.</p><p>Para começar com o Next.js, vá até o diretório em que você deseja começar seu projeto e rode o seguinte comando:</p><pre><code>yarn create-next-app meu-projeto-com-husky
# ou
npx create-next-app meu-projeto-com-husky
</code></pre><p><em><em>Not</em>a<em>: </em>sinta-se livre para substituir <code>meu-projeto-com-husky</code><em> </em>pelo nome que você quiser que o seu diretório tenha<em>.</em></em></p><p>Isso criará uma nova pasta, um novo projeto Next.js, e instalará todas as dependências.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/create-next-app.jpg" class="kg-image" alt="create-next-app" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/create-next-app.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/create-next-app.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/create-next-app.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/create-next-app.jpg 1787w" sizes="(min-width: 1200px) 1200px" width="1787" height="694" loading="lazy"></figure><p>Ao terminar, navegue até aquela nova pasta e podemos seguir em frente!</p><p><a href="https://github.com/colbyfayock/my-husky-project/commit/9e0b39c8f34c2755e074a32ef9de8d4047b68f67">Acompanhe aqui com o <em>commit</em></a>.</p><h2 id="passo1-como-instalar-o-husky-em-um-projeto"><strong>Passo1: como instalar o Husky em um projeto</strong></h2><p>Para instalar o Husky, podemos utilizar o yarn ou o npm.</p><pre><code>yarn add husky
# ou
npm install husky
</code></pre><p><em><em>Not</em>a<em>:</em> se, ao instalar o Husky nesse momento, a versão instalada for a <em>v</em>7<em>,</em> isso significa que a<em> v</em>7 foi oficialmente lançada<em>.</em> Confira a <a href="https://typicode.github.io/husky/#/">documentação<em> </em>atualizada do Husky</a> <a href="https://typicode.github.io/husky/#/"> </a>(em inglês) ou instale a última versão<em> </em>da <em>v4 </em>especificando <em>husky@4.3.0 (</em>ou seja qual for a versão mais recente<em>) </em>ao instalar<em>.</em></em></p><p>Quando o pacote terminar de instalar, podemos seguir com o Husky.</p><p><a href="https://github.com/colbyfayock/my-husky-project/commit/720728cd595d41c9197640bd4c48e9133bd7d956">Acompanhe aqui com o <em>commit</em></a>.</p><h2 id="passo-2-como-configurar-o-husky-para-rodar-os-hooks-do-git"><strong>Passo 2: como configurar o Husky para rodar os </strong>hooks do Git</h2><p>Agora, vamos configurar o Husky para conseguir usar nossos hooks do Git.</p><p>Dentro do nosso arquivo <code>package.json</code>, crie uma nova propriedade chamada <code>husky</code> com um objeto vazio.</p><pre><code class="language-json">"husky": {},</code></pre><p>Você pode, de fato, adicionar essa propriedade onde você quiser no arquivo <code>package.json</code>. Eu, no entanto, a adicionarei logo após a propriedade <code>scripts</code> &nbsp;para conseguir gerenciá-las juntas mais facilmente.</p><p>Dentro da propriedade Husky, adicionaremos outra propriedade chamada <code>hooks</code>, que também contém um objeto vazio:</p><pre><code class="language-json">"husky": {
  "hooks": {}
},
</code></pre><p>Aqui é onde adicionaremos nossos hooks do Git. O Husky suporta basicamente todos os <a href="https://git-scm.com/docs/githooks">hooks do Git definidos pelo próprio Git</a>. Então, podemos ser tão flexíveis quanto quisermos dentro do nosso fluxo de eventos do Git.</p><p>Para fins de teste, eu criei uma <a href="https://github.com/colbyfayock/my-husky-project/tree/main+test">nova <em>branch</em></a> onde eu literalmente adicionei todos os hooks do Git daquela página, incluindo um script que simplesmente escreve no terminal <code>[Husky] nome do evento</code>.</p><p><em><em>Not</em>a<em>: </em>você não precisa fazer isso a não ser que esteja curioso<em>. </em>O objetivo é ser capaz de demonstrar a você, com meu exemplo, como funciona<em>.</em></em></p><pre><code>“husky”: {
  “hooks”: {
    “applypatch-msg”: “echo \”[Husky] applypatch-msg\””,
    “pre-applypatch”: “echo \”[Husky] pre-applypatch\””,
    “post-applypatch”: “echo \”[Husky] post-applypatch\””,
    “pre-commit”: “echo \”[Husky] pre-commit\””,
	}
}</code></pre><p>O que isso fará é dizer ao Husky que, em cada etapa na qual temos permissão para nos conectar ao Git, diremos a ele a ação desejada!</p><p>Quando eu fizer os <em>commits d</em>essas mudanças, nós poderemos imediatamente ver o Husky disparar alguns de nossos scripts.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/husky-commit-hooks.jpg" class="kg-image" alt="husky-commit-hooks" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/husky-commit-hooks.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/husky-commit-hooks.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/husky-commit-hooks.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/husky-commit-hooks.jpg 1775w" sizes="(min-width: 1200px) 1200px" width="1775" height="566" loading="lazy"></figure><p>Esses são todos eventos aos quais o Git permite que nos conectemos e que acontecem durante o processo de <em>commit</em>.</p><p>Do mesmo modo, se eu der um <em>push </em>nessas mudanças para o Github, poderei ver que o processo de <em>push </em>roda o <em>hook </em><code>pre-push</code>!</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/husky-push-hooks.jpg" class="kg-image" alt="husky-push-hooks" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/husky-push-hooks.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/husky-push-hooks.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/husky-push-hooks.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/husky-push-hooks.jpg 1782w" sizes="(min-width: 1200px) 1200px" width="1782" height="566" loading="lazy"></figure><p>Pode ser que nunca usemos a maioria dos <em>hooks </em>que o Husky e o Git proporcionam (vimos apenas alguns deles).</p><p>Porém, é realmente incrível ver como isso pode ser poderoso, seja rodando códigos que formatarão o nosso código, evitando que se faça o <em>commit</em> de chaves de acesso secretas sejam <em>commitadas</em>, ou qualquer coisa que possa ajudar a automatizar tarefas importantes do nosso fluxo de trabalho.</p><p>Conseguimos ver, agora, que configuramos o Husky ao especificar a configuração e os <em>hooks </em>dentro do nosso <code>package.json</code>.</p><p><a href="https://github.com/colbyfayock/my-husky-project/commit/108583a7e96564baf0fac994eafa6cf98d65d03e">Acompanhe aqui com o commit</a>.</p><p><em><em>Not</em>a<em>: </em>se quiser conferir minha branch que inclui todos os testes com hooks do Git, <a href="https://github.com/colbyfayock/my-husky-project/tree/main+test">você pode encontrá-la no Github</a>.</em></p><h2 id="passo-3-como-usar-o-husky-para-formatar-seu-c-digo-com-prettier"><strong>Passo 3: como usar o Husky para formatar seu código com Prettier</strong></h2><p>Finalmente, em um caso mais próximo à realidade, testaremos o Prettier e formataremos nosso código automaticamente.</p><p>O Prettier é uma ferramenta de formatação de código que permite limpar o código facilmente, fazendo parecer como se uma única pessoa o tivesse escrito.</p><p>Por que ferramentas como o Prettier são importantes? Quando estamos programando, especialmente em equipe, é importante manter a consistência para que todo mundo saiba o que esperar. Isso evitará discussões sobre um ponto e vírgula em uma <em>code review</em>, mas também ajudará a identificar erros de sintaxe e a prevenir bugs.</p><p><em>Atenção<em>: </em>rodar o <em>Prettier </em>formatará automaticamente todo o código. Por enquanto, estamos testando isso antes de fazer o commit das mudanças. Assim que você aplicar isso a um Git Hook, ele automatizará esse processo.</em></p><p>Para começar, vamos instalar o Prettier com nosso gerenciador de pacotes:</p><pre><code>yarn add prettier -D
# ou
npm install prettier --save-dev
</code></pre><p><em><em>Not</em>a<em>: </em>estamos instalando o Prettier como uma<em> <code>devDependency</code></em>, já que sua aplicação não precisa disso para rodar<em>.</em></em></p><p>Depois, podemos adicionar um novo script ao nosso <code>package.json</code> que facilitará rodar o Prettier para fazer os testes.</p><p>Dentro da propriedade <code>scripts</code>, adicione:</p><pre><code class="language-json">"lint": "prettier --check ."
</code></pre><p>Para começar, rodaremos o primeiro teste na modalidade "check", o que nos permite ver quais arquivos seriam alterados.</p><p>Execute o seguinte comando:</p><pre><code>yarn lint
# ou 
npm run lint
</code></pre><p>Assim que o fizermos, poderemos perceber que o Prettier nos dirá quais dos arquivos listados serão alterados.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/prettier-check.jpg" class="kg-image" alt="prettier-check" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/prettier-check.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/prettier-check.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/prettier-check.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/prettier-check.jpg 1790w" sizes="(min-width: 1200px) 1200px" width="1790" height="384" loading="lazy"></figure><p>Nesse ponto, nosso código se manterá inalterado. Se quisermos que mudanças reais sejam feitas, primeiros temos que incluir um script adicional:</p><pre><code class="language-json">"format": "prettier --write ."
</code></pre><p>Se rodarmos esse script, ele atualizará todos aqueles arquivos mencionados de acordo com a especificação do Prettier.</p><p><em>Atenção<em>:</em> executar o <em>Prettier</em> para realizar as mudanças fará alterações em seus arquivos<em>. </em>Todas essas alterações são apenas para fins de adequação de estilo e não deveriam impactar o modo como seu código é executado<em>, </em>apenas a aparência dele<em>.</em> Antes de formatar, é prudente salvar todas as suas mudanças através de um <em>commit </em>no<em> Git</em>, de maneira a poder reverter facilmente essas mudanças caso não esteja feliz com elas<em>.</em></em></p><p>Você agora pode rodar o script com:</p><pre><code>yarn format
</code></pre><p>E podemos ver que o Prettier atualizou seus arquivos!</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/prettier-write.jpg" class="kg-image" alt="prettier-write" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/prettier-write.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/prettier-write.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/prettier-write.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/prettier-write.jpg 1792w" sizes="(min-width: 1200px) 1200px" width="1792" height="462" loading="lazy"></figure><p>Agora, a parte que é relevante nessa jornada: podemos adicionar isso como um hook do Git. Dessa maneira, quando alguém tentar realizar um <em>commit </em>em um código, o Prettier executará antes de o código ser salvo. Isso significa que sempre manteremos um código consistente, com o Prettier formatando o estilo.</p><p>Dentro de nossa configuração dos hooks no Husky, vamos adicionar:</p><pre><code class="language-json">"husky": {
  "hooks": {
    "pre-commit": "prettier --write . &amp;&amp; git add -A ."
  }
},
</code></pre><p>Como você pode perceber, no nosso <em>hook pré-commit</em>, também estamos adicionando <code>git add -A .</code>.</p><p>Quando o Husky for executado, ele simplesmente executará o script fornecido. Ao rodar o comando do Prettier, estamos apenas formatando o código, mas ainda faltaria salvar essas mudanças como parte do processo. Por isso, usaremos <code>git add</code> para guardar todas essas mudanças e incluí-las no <em>commit</em>.</p><p>Para fins de teste, eu reverti as mudanças de todos os arquivos formatados antes. Se você estiver acompanhando com o mesmo projeto, você pode executar o seguinte comando:</p><pre><code>git checkout pages
</code></pre><p>Essa ação apagará todas as mudanças em <code>pages</code> para o último <em>commit</em>.</p><p>Agora, vamos testar adicionar todos os nossos arquivos com Git e fazer o <em>commit </em>dessas mudanças.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/git-commit-husky-precommit-prettier.jpg" class="kg-image" alt="git-commit-husky-precommit-prettier" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/git-commit-husky-precommit-prettier.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/git-commit-husky-precommit-prettier.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/03/git-commit-husky-precommit-prettier.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/git-commit-husky-precommit-prettier.jpg 1778w" sizes="(min-width: 1200px) 1200px" width="1778" height="548" loading="lazy"></figure><p>Tendo executado o comando de <em>commit</em>, podemos ver que o hook <em>pré-commit </em>do Husky já fez seu trabalho e formatou nosso código.</p><p><a href="https://github.com/colbyfayock/my-husky-project/commit/315112d062a791f20eda11f9c608c5fa794ba73e">Acompanhe aqui com o <em>commit</em></a>.</p><h2 id="o-que-eu-posso-fazer-depois"><strong>O que eu posso fazer depois?</strong></h2><h3 id="use-lint-staged-para-formatar-somente-os-arquivos-modificados"><strong>Use <em>lint-staged</em> para formatar somente os arquivos modificados</strong></h3><p>Estamos usando o Prettier no nosso hook pré-commit e especificando <code>.</code>, o que significa rodar em todos os arquivos todas as vezes.</p><p>Podemos usar uma ferramenta chamada <a href="https://github.com/okonet/lint-staged">lint-staged</a>, que ainda nos permite rodar nossos hooks do Git com o Husky, executando-o apenas naqueles arquivos que estão em <em>stage </em>no Git.</p><p>Por exemplo, se quiséssemos fazer isso com o Husky e o Prettier, nossa configuração se pareceria com algo do tipo:</p><pre><code>"husky": {
  "hooks": {
    "pre-commit": "lint-staged"
  }
},
"lint-staged": {
  "*": "prettier --write"
},
</code></pre><p>Como parte de como <em>lint-staged</em> é executado, ele anexará as mudanças dos arquivos alterados ao final de nossa instrução para o Prettier automaticamente. </p><p>Você também perceberá que não incluímos <code>git add</code>. O <em>lint-staged</em> também adicionará quaisquer mudanças ao Git instantaneamente para nós.</p><h3 id="personalize-a-configura-o-do-prettier-para-formatar-as-regras"><strong>Personalize a configuração do Prettier para formatar as regras</strong></h3><p>O Prettier é bem opinativo. Há algumas coisas das quais eu, pessoalmente, não gosto e talvez você concorde comigo. </p><p>Felizmente, o Prettier permite a você configurar um arquivo que pode sobrescrever algumas regras para fazer com que seu código seja do jeito que você e seu time queiram.</p><h3 id="diga-ao-prettier-para-ignorar-arquivos-com-prettierignore"><strong>Diga ao Prettier para ignorar arquivos com .prettierignore</strong></h3><p>Você provavelmente não quer o Prettier rodando em "tudo" (bem, talvez sim).</p><p>O Prettier permite que você configure um arquivo <code>.prettierignore</code> &nbsp;dentro da raiz do seu projeto, próximo ao <code>package.json</code>, de modo similar ao <code>.gitignore</code>, que possibilita a você dizer ao Prettier quais arquivos ele não deve monitorar.</p><figure class="kg-card kg-image-card"><img src="https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card" class="kg-image" alt="Follow me for more Javascript, UX, and other interesting things!" width="2000" height="400" loading="lazy"></figure><ul><li><a href="https://twitter.com/colbyfayock">Siga o autor no Twitter</a></li><li><a href="https://youtube.com/colbyfayock">Inscreva-se no canal do YouTube do autor</a></li><li><a href="https://www.colbyfayock.com/newsletter/">Inscreva-se para receber a newsletter do autor</a></li><li><a href="https://github.com/sponsors/colbyfayock">Patrocine o autor</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Elementos semânticos do HTML5 explicados ]]>
                </title>
                <description>
                    <![CDATA[ Elementos semânticos do HTML5 são aqueles que descrevem claramente seu significado de uma maneira que seja legível tanto para humanos quanto para máquinas. Elementos como <header>, <footer> e <article> (cabeçalho, rodapé e artigo, respectivamente, traduzidos para o português) são considerados semânticos, pois eles descrevem com precisão o propósito do elemento ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/elementos-semanticos-do-html5-explicados/</link>
                <guid isPermaLink="false">61f6cfb653557304fa19d4cb</guid>
                
                    <category>
                        <![CDATA[ HTML5 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gustavo Goulart Baptista ]]>
                </dc:creator>
                <pubDate>Tue, 15 Feb 2022 17:44:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/02/5f9c9ebe740569d1a4ca3ed0.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/semantic-html5-elements/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Semantic HTML5 Elements Explained</a>
      </p><p>Elementos semânticos do HTML5 são aqueles que descrevem claramente seu significado de uma maneira que seja legível tanto para humanos quanto para máquinas.</p><p>Elementos como <code>&lt;header&gt;</code>, <code>&lt;footer&gt;</code> e <code>&lt;article&gt;</code> &nbsp;(cabeçalho, rodapé e artigo, respectivamente, traduzidos para o português) são considerados semânticos, pois eles descrevem com precisão o propósito do elemento e o tipo de conteúdo dentro dele.</p><h3 id="o-que-s-o-elementos-sem-nticos"><strong><strong>O que são elementos semânticos?</strong></strong></h3><p>O HTML foi originalmente criado como uma linguagem de marcação para descrever documentos no início da internet. Conforme a internet cresceu e foi sendo adotada por cada vez mais pessoas, algumas mudanças precisaram acontecer.</p><p>A internet, que foi originalmente pensada para compartilhamento de artigos científicos, agora se tornou um local onde as pessoas querem compartilhar outras coisas também. Rapidamente, elas começaram a querer fazer com que a Web fosse mais atrativa visualmente.</p><p>Devido à web não ser ter sido inicialmente construída pensando em design, os programadores usaram alguns truques diferentes para arrumar tudo de diversas maneiras. Ao invés de usar a tag <code>&lt;table&gt;&lt;/table&gt;</code> (tabela, em inglês) para descrever informações usando uma tabela, programadores a usavam para posicionar outros elementos dentro de uma página.</p><p>Conforme as interfaces progrediram visualmente, os programadores começaram a usar tags genéricas e não semânticas como <code>&lt;div&gt;</code> (divisão, em inglês). Eles costumavam dar a esses elementos atributos de classe (<code>class</code>) ou <code>id</code> que descrevessem seu propósito. Por exemplo, ao invés de <code>&lt;header&gt;</code> era comumente escrito algo como <code>&lt;div class="header"&gt;</code></p><p>Como o HTML5 é algo ainda relativamente novo, esse uso de elementos não semânticos ainda é muito comum em websites atualmente. </p><h3 id="lista-de-elementos-sem-nticos-"><strong>Lista de elementos semânticos:</strong></h3><p>Os elementos semânticos adicionados ao HTML5 foram:</p><ul><li><code>&lt;article&gt;</code></li><li><code>&lt;aside&gt;</code></li><li><code>&lt;details&gt;</code></li><li><code>&lt;figcaption&gt;</code></li><li><code>&lt;figure&gt;</code></li><li><code>&lt;footer&gt;</code></li><li><code>&lt;header&gt;</code></li><li><code>&lt;main&gt;</code></li><li><code>&lt;mark&gt;</code></li><li><code>&lt;nav&gt;</code></li><li><code>&lt;section&gt;</code></li><li><code>&lt;summary&gt;</code></li><li><code>&lt;time&gt;</code></li></ul><p>Elementos como <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, <code>&lt;section&gt;</code>, <code>&lt;article&gt;</code>, <code>&lt;aside&gt;</code> e <code>&lt;footer&gt;</code> funcionam mais ou menos como elementos <code>&lt;div&gt;</code>. Eles agrupam outros elementos juntos em seções de uma página. Uma tag &nbsp;<code>&lt;div&gt;</code> poderia conter qualquer tipo de informação. Contudo, é fácil identificar que tipo de informação seria incluída em uma tag semântica como <code>&lt;header&gt;</code>.</p><p><strong>Um exemplo de uma interface com elementos semânticos da <strong><strong><strong>w3schools</strong></strong></strong></strong></p><h2 id="por-que-usar-elementos-sem-nticos"><strong>Por que usar elementos semânticos?</strong></h2><p>Para ver os benefícios dos elementos semânticos, aqui temos dois trechos de código em HTML. Esse primeiro bloco de código utiliza elementos semânticos:</p><pre><code class="language-text">&lt;header&gt;&lt;/header&gt;
&lt;section&gt;
	&lt;article&gt;
		&lt;figure&gt;
			&lt;img&gt;
			&lt;figcaption&gt;&lt;/figcaption&gt;
		&lt;/figure&gt;
	&lt;/article&gt;
&lt;/section&gt;
&lt;footer&gt;&lt;/footer&gt;</code></pre><p>Enquanto esse segundo bloco de código usa elementos não semânticos:</p><pre><code class="language-text">&lt;div id="header"&gt;&lt;/div&gt;
&lt;div class="section"&gt;
	&lt;div class="article"&gt;
		&lt;div class="figure"&gt;
			&lt;img&gt;
			&lt;div class="figcaption"&gt;&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
&lt;div id="footer"&gt;&lt;/div&gt;</code></pre><p>O primeiro é muito mais <strong>fácil de ler</strong>. Essa, provavelmente, é a primeira coisa que você notará quando for ler o primeiro trecho de código, que está usando elementos semânticos. Esse é um exemplo pequeno. Porém, como programador, você lerá centenas ou milhares de linhas de código. Quanto mais fácil for ler e entender o código, mais fácil será seu trabalho.</p><p>Ele tem <strong>maior acessibilidade</strong>. Você não é o único que acha que elementos semânticos são mais fáceis de entender. Os algoritmos de indexação em sites de busca e as tecnologias assistivas (como leitores de tela para usuários com deficiência visual) também entenderão melhor o contexto e conteúdo do seu website, entregando uma melhor experiência para os usuários.</p><p>Geralmente, elementos semânticos também resultam em um <strong>código mais consistente</strong>. Quando criamos um cabeçalho usando elementos não semânticos, diversos programadores podem escrever algo como <code>&lt;div class="header"&gt;</code>, <code>&lt;div id="header"&gt;</code>, <code>&lt;div class="head"&gt;</code> ou, simplesmente, <code>&lt;div&gt;</code>. Existem várias maneiras de criar blocos de código para representar um elemento de cabeçalho. Todas eles dependem de uma preferência pessoal do programador. Criando um padrão com elementos semânticos, facilitamos o trabalho para todos.</p><p>Em outubro de 2014 o HTML4 foi atualizado para o HTML5, que incluiu alguns novos elementos "semânticos". Até hoje, alguns de nós ainda nos confundimos com relação aos diversos elementos que não parecem demonstrar nenhuma grande mudança.</p><h4 id="section-e-article"><strong><strong><strong><code>&lt;section&gt;</code> </strong></strong>e <strong><strong><code>&lt;article&gt;</code></strong></strong></strong></h4><p>"Qual a diferença?", você pode estar se perguntando. Ambos os elementos são usados para seccionar o conteúdo – e, sim, eles definitivamente podem ser intercambiáveis. É uma questão de analisar cada situação. O HTML4 nos oferecia somente um tipo de elemento container, a <code>&lt;div&gt;</code>. Embora ela ainda seja usada em HTML5, ainda temos a possibilidade de substituí-la em duas maneiras, usando <code>&lt;section&gt;</code> e <code>&lt;article&gt;</code>.</p><p>Os elementos <code>&lt;section&gt;</code> e <code>&lt;article&gt;</code> são conceitualmente similares e intercambiáveis. Para decidir qual deles você deve escolher, observe o que citaremos abaixo:</p><ol><li>Um <code>&lt;article&gt;</code> tem como objetivo ser independentemente distribuível ou reutilizável. </li><li>Uma <code>&lt;section&gt;</code> é um temático agrupamento de conteúdos.</li></ol><pre><code class="language-html">&lt;section&gt;
  &lt;p&gt;Histórias novas&lt;/p&gt;
  &lt;section&gt;
    &lt;p&gt;Notícias&lt;/p&gt;
    &lt;article&gt;História 1&lt;/article&gt;
    &lt;article&gt;História 2&lt;/article&gt;
    &lt;article&gt;História 3&lt;/article&gt;
  &lt;/section&gt;
  &lt;section&gt;
    &lt;p&gt;Esportes&lt;/p&gt;
    &lt;article&gt;História 1&lt;/article&gt;
    &lt;article&gt;História 2&lt;/article&gt;
    &lt;article&gt;História 3&lt;/article&gt;
  &lt;/section&gt;
&lt;/section&gt;</code></pre><h4 id="header-e-hgroup"><strong><strong><strong><code>&lt;header&gt;</code> </strong></strong>e <strong><strong><code>&lt;hgroup&gt;</code></strong></strong></strong></h4><p>O elemento <code>&lt;header&gt;</code> é geralmente encontrado no topo de um documento, uma seção ou um artigo e geralmente contém o cabeçalho principal da página e algumas navegações e barras de busca.</p><pre><code class="language-html">&lt;header&gt;
  &lt;h1&gt;Company A&lt;/h1&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href="/home"&gt;Home&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/about"&gt;Sobre&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/contact"&gt;Contato&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;form target="/search"&gt;
    &lt;input name="q" type="search" /&gt;
    &lt;input type="submit" /&gt;
  &lt;/form&gt;
&lt;/header&gt;</code></pre><p>O elemento <code>&lt;hgroup&gt;</code> deve ser usado quando você quer um cabeçalho principal com um ou mais subcabeçalhos.</p><pre><code class="language-html">&lt;hgroup&gt;
  &lt;h1&gt;Cabeçalho 1&lt;/h1&gt;
  &lt;h2&gt;Subcabeçalho 1&lt;/h2&gt;
  &lt;h2&gt;Subcabeçalho 2&lt;/h2&gt;
&lt;/hgroup&gt;</code></pre><p>Lembre-se de que o elemento <code>&lt;header&gt;</code> pode conter qualquer conteúdo. Porém, o elemento <code>&lt;hgroup&gt;</code> somente contém outros cabeçalhos, ou seja, elementos de <code>&lt;h1&gt;</code> (<em>header1</em>, do inglês) a <code>&lt;h6&gt;</code> (<em>header6</em>, do inglês) e outros <code>&lt;hgroup&gt;</code>.</p><h4 id="aside"><strong><strong><strong><code>&lt;aside&gt;</code></strong></strong></strong></h4><p>O elemento <code>&lt;aside&gt;</code> é destinado a conteúdos que não fazem parte do fluxo do texto, mas que, ainda sim, estão relacionados de alguma forma. Esse <code>&lt;aside&gt;</code> poderia ser utilizado como uma barra lateral (<em>sidebar</em>) para o seu conteúdo principal.</p><pre><code class="language-html">&lt;aside&gt;
  &lt;p&gt;Essa é uma sidebar, por exemplo, uma definição de uma terminologia ou uma descrição curta para uma imagem de uma figura histórica.&lt;/p&gt;
&lt;/aside&gt;</code></pre><p>Antes do HTML5, nosso menus eram criados com as tags <code>&lt;ul&gt;</code> e <code>&lt;li&gt;</code>. Agora, junto com esses elementos, nós podemos separar nosso itens no menu com um elemento &nbsp;<code>&lt;nav&gt;</code> para a navegação entre as páginas. Você pode ter mais de um elemento &nbsp;<code>&lt;nav&gt;</code> na página. É comum, por exemplo, ter uma navegação global percorrendo o topo da página (dentro do elemento <code>&lt;header&gt;</code>) e uma navegação local em uma sidebar (dentro de um elemento <code>&lt;aside&gt;</code>).</p><pre><code class="language-html">&lt;nav&gt;
  &lt;ul&gt;
    &lt;li&gt;&lt;a href="/home"&gt;Home&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/about"&gt;Sobre&lt;/a&gt;&lt;/li&gt;
    &lt;li&gt;&lt;a href="/contact"&gt;Contato&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/nav&gt;</code></pre><h4 id="footer"><strong><strong><strong><code>&lt;footer&gt;</code></strong></strong></strong></h4><p>Se existe um <code>&lt;header&gt;</code> (cabeçalho, em inglês), deve existir um <code>&lt;footer&gt;</code> (rodapé, em inglês). Uma tag <code>&lt;footer&gt;</code> é geralmente encontrada no final de um documento, uma seção ou um artigo. Assim como no <code>&lt;header&gt;</code> o conteúdo é geralmente uma metainformação, por exemplo, com detalhes sobre o autor, informações legais, e/ou links e informações relacionadas. Também é válido incluir elementos <code>&lt;section&gt;</code> dentro de um <em>footer</em>.</p><pre><code class="language-html">&lt;footer&gt;&amp;copy;Empresa A&lt;/footer&gt;</code></pre><h4 id="small"><strong><strong><strong><code>&lt;small&gt;</code></strong></strong></strong></h4><p>O elemento <code>&lt;small&gt;</code> (pequeno, na tradução para o português) sempre aparece dentro de um elemento <code>&lt;footer&gt;</code> ou <code>&lt;aside&gt;</code>, normalmente incluindo informações de direitos autorais ou avisos legais, além de outras "letras miúdas". No entanto, esse elemento não é feito para deixar seu texto menor. É apenas uma descrição de seu conteúdo e não faz nenhuma prescrição visual. </p><pre><code class="language-html">&lt;footer&gt;&lt;small&gt;&amp;copy;Empresa A&lt;/small&gt; Data&lt;/footer&gt;</code></pre><h4 id="time"><strong><strong><strong><code>&lt;time&gt;</code></strong></strong></strong></h4><p>O elemento <code>&lt;time&gt;</code> permite que uma data no formato ISO 8601 não ambígua seja adicionada a uma versão mais amigável para leitura humana daquela data.</p><pre><code class="language-html">&lt;time datetime="2017-10-31T11:21:00+02:00"&gt;Terça, 31 Outubro 2017&lt;/time&gt;</code></pre><p>Por que se preocupar com <code>&lt;time&gt;</code>? Embora humanos possam eliminar ambiguidades e ler o tempo de maneiras normais, os computadores podem ler datas em formato ISO 8601 e ver a data, a hora e o fuso horário.</p><h4 id="figure-e-figcaption"><strong><strong><strong><code>&lt;figure&gt;</code> </strong></strong>e <strong><strong><code>&lt;figcaption&gt;</code></strong></strong></strong></h4><p><code>&lt;figure&gt;</code> serve para inserir conteúdos de imagem, enquanto <code>&lt;figcaption&gt;</code> é destinado às legendas dessa imagem.</p><pre><code class="language-html">&lt;figure&gt;
  &lt;img src="https://en.wikipedia.org/wiki/File:Shadow_of_Mordor_cover_art.jpg" alt="Shadow of Mordor" /&gt;
  &lt;figcaption&gt;Arte de capa de Middle Earth: Shadow of Mordor&lt;/figcaption&gt;
&lt;/figure&gt;</code></pre><h3 id="aprenda-mais-sobre-os-novos-elementos-do-html5-"><strong>Aprenda mais sobre os novos elementos do HTML5<strong><strong>:</strong></strong></strong></h3><ul><li>A <a href="https://www.w3schools.com/html/html5_semantic_elements.asp">w3schools</a> mantém uma descrição simples e clara de muitos dos novos elementos e de como/onde eles devem ser usados.</li><li>A <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element">MDN</a> também é uma grande referência para todos os elementos HTML e vai mais a fundo em cada um deles.</li></ul> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
