Qualidade de vida ao desenvolver JavaScript: ESLint + Atom + ES6 + JSX

Nesse post veremos como configurar o linter ESLint (inclusive no Atom) para ganhar alguns anos de vida a mais ao diminuir o stress no desenvolvimento JavaScript. O post segue o formato “receita-de-bolo” e é destinado ao “eu-do-futuro-que-esqueceu-isso”. Mas pode ser útil para terceiros que sabem o que é um linter :D.

Ah, o setup considera minha realidade de trabalho (ES6, JSX). Se você ainda usa ES5 … não sabe a diferença … melhorou muito :D

Há varios linters por aí, como os famosos JSlint, JSHint, JSCS, ESLint.
O mais recente desses 4 linters populares é o ESLint e o mesmo tem chamado a atenção por causa de seus recursos.

O ESLint possui features interessantes como:

  • Suporta configuração
  • Suporta plugins
  • Melhor suporte à ES6 e JSX
  • Possui rules que além de estilo ajudam a detectar bugs

Devido a essas características, optamos pelo ESLint. Ver mais aqui.

Para deixá-lo ainda mais versátil utilizamos conjutamente com:

  • parser babel-eslint que permite lintar todo o código suportado pelo babel.
  • plugin ESLint-plugin-React que oferece um conjunto de rules específicas para React (e inclusive corrige problemas como a rule "no-unused-vars" não detectar o uso de vars na sintaxe JSX)

Vejamos o setup do mesmo tanto no âmbito do sistema operacional (global) quanto de um determinado projeto (local):

Global

Instalação

  1. Instale globalmente o eslint >= 2: $ sudo npm install --global eslint
  2. Instale globalmente o babel-eslint p/ o eslint >=2: $ npm install --global babel-eslint@next
  3. Instale globalmente o eslint-plugin-react: $ sudo npm install --global eslint-plugin-react

Configuração

  1. Crie uma configuração inicial com: $ eslint --init (será criado no dir atual)
  2. Mova o arquivo criado para ~/
  3. Edite o arquivo em ~/ de acordo com suas preferências (ver site oficial por documentação ou exemplo comentado abaixo).

Notas:

  • O formato do arquivo de configuração pode ser JSON, JS, YAML (o eslint --init permite escolher). Optei por JS por permitir comentários e etc (com o devido highlight de IDEs).
  • Se houver um arquivo de configuração no dir onde é invocado eslint, esse será utilizado ao invés do arquivo em ~/.

Desintalação

  1. Desinstale os módulos globais: $ sudo npm uninstall --global eslint babel-eslint eslint-plugin-react
  2. (OPCIONAL) Remova o arquivo de configuração: $ rm ~/.eslintrc*

Utilização

O linter pode ser facilmente utilizado através da invocação de linhas como:

  • $ eslint file1.js file2.js
  • $ eslint lib/**

Para mais info ver a documentação oficial.

Local (projeto) - recomendado

Se quisermos utilizar o eslint apenas num projeto, podemos instalar ele localmente no projeto. Entre as vantagens, está o fato de podermos usar uma determinada versão com uma certa configuração e plugins específicos e de levarmos essa configuração (no package.json) junto com o projeto (que já estará corretamente configurado para uso por terceiros).

Instalação

  1. Situe-se na raiz do projeto npm: $ cd ./projeto
  2. Instale localmente o eslint >= 2: $ npm install --save-dev eslint
  3. Instale localmente o babel-eslint p/ o eslint >=2: $ npm install --save-dev babel-eslint@next
  4. Instale localmente o eslint-plugin-react: $ npm install --save-dev eslint-plugin-react

Configuração

  1. Situe-se na raiz do projeto npm: $ cd ./projeto
  2. Crie uma configuração inicial com: $ eslint --init (será criado no dir atual)
  3. Edite o arquivo de acordo com suas preferências (ver site oficial por documentação ou exemplo comentado abaixo).

Notas:

  • Mesmo a instalação sendo local ao projeto, caso o arquivo de configuração não seja encontrado no dir do projeto, o linter tentará utilizar o arquivo de configuração localizado nos dirs acima ou em ~/ (se houver).

Desinstalação

  1. Desinstale os módulos do projeto: $ npm uninstall --save-dev eslint babel-eslint eslint-plugin-react
  2. (OPCIONAL) Remova o arquivo de configuração do projeto: $ rm ./projeto/.eslintrc*

Utilização

A única diferença para a utilização do eslint instalado globalmente é que devemos apontar para seu executável dentro de seu dir em node_modules. Exemplo: ./node_modules/eslint/bin/eslint.js index.js


Notas

  • A prioridade na busca por arquivos de configuração é:
    1. Dir local
    2. Dirs acima no path
    3. ~/
  • É recomendado possuir um arquivo de configuração básico em ~/, com rules que sejam interessantes independente das particularidades de um determinado projeto.
  • O arquivo de configuração é simples. E podemos escolher se uma rule estará desligada ("off" ou 0), emitindo aviso ("warn" ou 1) ou emitindo erro ("error" ou 2). Exemplo: "indent": [2, 4] (ou "indent": ["error", 4]) emite error quando identação for != de 4 espaços.
  • Quando invocamos o eslint, as mensagens de aviso/erro contém as regras que as dispararam. Assim fica fácil localizar e modificar regras que não estejam adequadas.
    Exemplo:
    1
    7:9  error  Expected '===' and instead saw '=='                              eqeqeq
    Na linha 7, coluna 9 do arquivo analisado foi encontrado um == quando era esperado ===. A regra que emitiu esse erro foi eqeqeq.
  • O babel-eslint possui algumas limitações descritas em sua página que é bom considerar.
  • Há um bug no momento em que escrevo que o babel-eslint mais recente (5.0.0) não funciona com o eslint mais recente (2.3.0) e exige que instalemos o eslint 2.2.0.

Arquivo de configuração sample

Arquivo de configuração funcional comentado (.eslintrc.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
module.exports = {
parser: 'babel-eslint', // parser que permite lintar todo o código suportado pelo babel
parserOptions: {
ecmaVersion: 7,
sourceType: 'module', // habilita suporte a ECMAScript modules
ecmaFeatures: {
impliedStrict: true, // todo código é considerado implicitamente 'strict' (mesmo sem 'use strict').
jsx: true, // suporte a JSX
experimentalObjectRestSpread: true,
},
},
env: {
browser: true, // permite vars globais de browsers
commonjs: true, // permite vars globais de commonjs (p/ browser code que usa browserify/webpack)
embertest: true, // permite vars globais de embertest
es6: true, // permite recursos do ES6
node: true, // permite vars globais do node e seu escopo
},
plugins: ['react'], // habilita plugin `eslint-plugin-react` (deve ser instalado)
rules: { // all rules are disabled by default (veja "eslint:recommended" abaixo)
'block-scoped-var': 2, // proíbe vars de serem usadas fora do bloco onde foram declaradas (emula estilo do C)
'comma-dangle': [2, 'always-multiline'], // exige ',' em {}s e []s multi line e proíbe em single line
'dot-location': [2, 'property'], // exige que newlines sejam antes do ponto numa *member expression* (ponto fica junto com propriedade).
eqeqeq: [2, 'smart'], // exige comparação estrita (===) salvo em alguns casos (http://eslint.org/docs/rules/eqeqeq)
indent: [2, 2], // exige indentação de 2 espaços
'linebreak-style': [2, 'unix'], // proíbe quebras de linha do Windows
'no-else-return': 2, // proíbe 'else' desnecessario quando 'if' já tem 'return' (pq pode ficar fora do bloco)
'no-unused-vars': [2, { 'vars': 'all', 'args': 'none' }], // exige que todas as vars sejam usadas, ignora (ñ) uso de argumentos
quotes: [2, 'single', 'avoid-escape'], // exige '' e não reclama de "quotes 'assim'"
semi: [2, 'always'], // exige ';'
strict: [1, 'global'], // warn pedindo que todo código esteja no strict mode (declaração no escopo global, reclama de 'use strict's redundantes)
'vars-on-top': 2, // exige que vars estejam declaradas no topo do escopo
},
extends: [ // extende conf. com a de outros arquivos (últimos tem primazia)
'eslint:recommended', // habilita rules recomendadas (ver essas em <http://eslint.org/docs/rules/>)
'plugin:react/recommended', // habilita rules recomendadas pelo plugin p/ boas práticas de React (inclusive 'jsx-uses-vars' que evita vars JSX de serem incorretamente marcadas como ñ usadas)
],
};

Com Atom

Para utilizar o ESLint em conjunto com o editor Atom, o pacote mais famoso é o linter-eslint.
Basta instalar o pacote linter-eslint desde o Atom.
O linter-eslint depende do pacote linter e irá instalar esse automaticamente caso necessário.

Caso você utilize o Nuclide sobre o Atom, o mesmo utiliza uma solução alternativa ao pacote linter, chamada nuclide-diagnostics. Qualquer pacote (incluindo o linter-eslint) que funcione com o linter funciona com o nuclide-diagnostics, de modo que apenas um dos dois deve estar habilitado. Caso os dois estejam habilitados ao mesmo tempo, veremos uma UI duplicada para exibir dados do linter durante a utilização.

O linter-eslint, por padrão, tentará utilizar uma versão do eslint local ao seu projeto (vimos como instalar localmente acima). A versão local é buscada em projeto/node_modules/eslint. Se a versão local não for encontrada, será utilizada uma que vem com o pacote. Caso desejado, podemos via configuração espeficicar para o linter-eslint utilizar um eslint instalado globalmente.

Para utilizar plugins do ESLint, basta que estes estejam instalados localmente no seu projeto conjuntamente com o ESLint (também visto acima). A outra opção é instalar tanto o ESLint quanto seus plugins globalmente (também visto acima) e habilitar nas configurações do pacote linter-eslint (via interface) a opção Use Global Eslint para utilizar o eslint instalado globalmente.

A localização do arquivo de configuração do ESLint utilizado pelo linter-eslint segue as mesmas regras utilizadas normalmente pelo eslint (ver acima).

Obs: Versões utilizadas na confecção do post:

  • eslint: 2.2.0
  • babel-eslint: 5.0.0
  • eslint-plugin-react: 4.2.1
  • atom: 1.5.3
  • linter-eslint: 7.0.0