Desenvolvimento

17 abr, 2015

Detectando problemas automaticamente no JavaScript com ESLint

Publicidade

Eu perco muito tempo corrigindo erros básicos quando estou escrevendo JavaScript. Costumo renomear uma variável, testar meu aplicativo e depois ver que não renomeei a variável em um ponto. Renomeio as funções e acontece a mesma coisa. Digito coisas erradas e novamente perco tempo clicando ao redor do navegador.

O sentimento é sempre o mesmo – por que eu cometo esse erro de novo? Eu programo há mais de 15 anos e ainda continuo fazendo isso.

É por isso que eu amo o ESLint. É como programar em conjunto com outra pessoa que presta mais atenção a isso do que eu. “Ei, você se esqueceu de renomear aquilo”. “Ei, você não deve fazer isso”.

O ESLint é uma ferramenta que analisa o código e aponta quaisquer problemas que encontrar. Ele pode encontrar bugs, áreas potencialmente problemáticas, estilos de codificação ruins e questões de estilo. O melhor de tudo é que ele é altamente configurável, por isso, se não concordar com ele em alguma coisa, você pode dizer a ele para se calar sobre isso.

Deixa eu te mostrar um exemplo real de como usar ESLint vai te beneficiar.

Instalação e configuração

Antes de prosseguir, é preciso instalar ESLint. Assim como acontece com a maioria das ferramentas de JS hoje, você precisa do nodejs para fazê-lo. Após ter configurado, execute…

npm install -g eslint

Isto tornará o programa eslint disponível a partir de linha de comando.

Um exemplo da vida real

Para explicar os benefícios do ESLint, vou te mostrar um exemplo da vida real de uma base de código em que trabalhei. Vamos percorrer o código e podemos ver o que ESLint faz com ele para facilitar nossa vida.

O arquivo de exemplo JavaScript é mostrado abaixo. Não se preocupe com o uso de AngularJS nele – você vai poder utilizar essas técnicas com qualquer biblioteca ou framework.

var module = angular.module('uploader', []);
/**
 * XMLHttpRequest wrapper that supports file upload progress since $http doesn't
 *
 * Usage similar to $http, returns a promise from the send method
 */
module.service('uploader', ['$q', function($q) {
  function readyStateChange(deferred, xhr) {
    if(xhr.readyState == 4) {
      if(xhr.status == 200) {
        deferred.resolve(JSON.parse(xhr.responseText));
      }
      else {
        deferred.reject('HTTP status ' + xhr.status);
      }
    }
  }
 
  function onProgress(deferred, xhr, ev) {
    if(ev.lengthComputable) {
      deferred.notify({ loaded: ev.loaded, total: ev.total });
    }
  }
 
  return {
    send: function(url, data) {
      var fd = new FormData();
      for(var k in data) {
        fd.append(k, data[k]);
      }
 
      var d = $q.defer();
 
      var xhr = new XMLHttpRequest();
 
      xhr.open('POST', url, true);
      xhr.onreadystatechange = readyStateChange.bind({}, d, xhr);
      xhr.upload.onprogress = onProgress.bind({}, d, xhr);
      xhr.send(fd);
 
      return d.promise;
    }
  };
}]);

 

Este é um componente básico para upload de arquivos. O código é funcional, mas vamos ver o que acontece quando o passamos pelo ESLint.

Um ponto de partida típico com ESLint é primeiramente analisar o seu código com ele e olhar para a saída. Abaixo está a saída ESLint para o módulo de exemplo.

eslint-1

Neste ponto, a saída contém erros que não deveriam estar lá, tais como Angular e XMLHttpRequest não estarem definidos.

Por que o ESLint está reclamando do XMLHttpRequest? Certamente não deveria fazer isso, pois o XMLHttpRequest é padrão. Bem, XMLHttpRequest é o único padrão no navegador. Outros ambientes, como NodeJS, podem não tê-lo. Portanto, o primeiro passo é informar ao ESLint que o nosso código vai rodar no browser.

Para isso, vamos criar um arquivo de configuração chamado .eslintrc, o qual podemos usar para dizer ao ESLint o que fazer. Você vai ver aqui embaixo a nossa primeira versão do arquivo .eslintrc.

{
  "env": {
    "browser": 1
  }
}

O ESLint pode ser configurado usando JSON. Aqui, nós estamos dizendo a ele que o ambiente é o navegador. O ambiente browser impede que o ESLint dê erros sobre coisas como XMLHttpRequest ou window. Se quiser rodar isso em NodeJS, você deve incluir “node”: 1, que faz o mesmo, mas para não Node-builtins.

Vamos executar novamente o programa eslint e descobrir o que ele diz agora.

eslint-2

Os erros que dizem respeito ao ambiente do navegador já se foram, mas tem outro que não queremos: ‘angular’ não está definido. Em um aplicativo típico, nós incluiríamos bibliotecas como Angular como tags de script, o que as torna disponíveis globalmente. Para falar com o ESLint sobre isso, precisamos de algumas opções adicionais no arquivo de configuração:

{
  "env": {
    "browser": 1
  },
 
  "globals": {
    "angular": 1
  }
}

O campo globals configura variáveis globais. Neste caso, definimos angular, mas, se você estiver usando jQuery, Backbone, Underscore ou qualquer outra coisa, você pode adicionar $ ou Backbone ou _ da mesma forma.

Execute novamente o eslint, e o erro vai desaparecer.

eslint-3

Ainda existem algumas coisas que quero mudar. Eu prefiro usar aspas simples para strings, então vou adicionar uma regra para corrigir isso.

{
  "env": {
    "browser": 1
  },
 
  "globals": {
    "angular": 1
  },
 
  "rules": {
    "quotes": [2, "single"]
  }
}

A propriedade rules configura as regras do ESLint. A regra quotes define se o ESLint fornece um erro para o estilo quote e que o estilo é permitido. O número 2 o torna um erro. Defini-lo para 1 o tornaria um aviso, que aparece de forma diferente na saída. “single” diz ao ESLint que eu quero permitir apenas aspas simples.

O código desse exemplo não utiliza o strict mode, nem requer iguais triplos, então vou adicionar essas regras também.

Para saber qual regra configurar, você pode observar a saída.

eslint-4

A partir disso, podemos ver que para “use strict” a regra é “strict”, e para === a regra é “eqeqeq”. Podemos acrescentar esses dois na configuração:

{
  "env": {
    "browser": 1
  },
 
  "globals": {
    "angular": 1
  },
 
  "rules": {
    "quotes": [2, "single"],
    "eqeqeq": 0,
    "strict": 0
  }
}

eslint-5

Definir uma regra para 0 faz o ESLint ignorá-la.

Os demais erros são fáceis de corrigir. Vamos remover os espaços à direita da linha 35 e remover a linha em branco a partir do final do arquivo.

Capturando erros

O código de exemplo agora transmite o ESLint sem erros. Vamos introduzir algumas mudanças para tornar as coisas mais interessantes.

Lembre-se de que eu disse que muitas vezes renomeei uma variável e, em seguida, esqueci de renomeá-lo em todos os lugares? Vamos ver como ESLint lida com isso. Eu vou renomear xhr em request

var request = new XMLHttpRequest();
 
request.open('POST', url, true);
request.onreadystatechange = readyStateChange.bind({}, d, xhr);
request.upload.onprogress = onProgress.bind({}, d, xhr);
request.send(fd);

Você notou um bug de relance? Deixei em dois casos de xhr. Vamos ver o que acontece quando executamos ESLint.

eslint-6

O ESLint aponta duas variáveis indefinidas, que neste caso são causadas pela mudança de nome. Agora podemos ver isso facilmente sem perder tempo clicando ao redor no navegador.

Podemos também jogar um erro de sintaxe só por diversão:

eslint-7

Estes são apenas dois exemplos do que o ESLint pode capturar. A lista de regras internas é muito longa e você ainda pode escrever regras personalizadas ou instalar plugins.

Recomendações

O ESLint pode ser uma ferramenta muito valiosa, mas, como qualquer ferramenta, você precisa usá-la para obter os benefícios.

Minhas cinco recomendações para que você possa tirar o máximo de proveito do ESLint são:

  1. Verifique a documentação para obter mais informações.
  2. Execute-o contra o seu projeto e configure-o de acordo com seu estilo de codificação.
  3. Instale plugins adicionais para as bibliotecas que você utiliza para tornar o ESLint ainda mais útil.
  4. Automatize o ESLint, assim você nunca vai esquecer de executá-lo.
  5. Obtenha feedback instantâneo, integrando-o ao seu editor ou IDE.

Para facilitar as coisas para você, eu criei um guia de 5 passos para essas etapas.

O ESLint nos dá uma rede de segurança básica. Ele vai pegar muitas coisas fáceis de se tornarem erros, e é muito útil quando se trabalha em equipe para impor um estilo de codificação. Mas, para uma rede de segurança mais rigorosa, você deve investir em testes unitários. Isso, no entanto, é um assunto para outra ocasião.

***

Jani Hartikainen faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: http://davidwalsh.name/eslint