ESLint

ES6 and JSX are awesome, but because they are relatively new, there is little documentation on how to set them up with your environment. After spending time tracking down all the pieces and configuring for use with emacs, I created this article to help those that will follow. It enables realtime linting of JS and JSX files in emacs using eslint and babel.

Meta Information

  • Author: Jeff Barczewski
  • Published: April 2nd, 2015
  • Updated: January 14, 2017
  • Tags: reactjs, javascript,emacs
  • Versions: GNU Emacs 24.4.51.2, Aquamacs 3.2, ESLint v0.17.1, Babel-eslint 2.0.2

Summary

My preferred weapon of choice for editing code for the last 11 years has been Emacs. Normally it already has support for all the types of files I edit, however since React.js introduced JSX, emacs needed some additional setup to get everything to play nicely. The config below should work well with any modern version of Emacs 24.4+ or Aquamacs 3.2+.

I will be using the following for js, and jsx editing

Once you complete the setup, you will have automatic realtime syntax checking right in Emacs for js and jsx files.

Emacs configuration for ESLint and JSX

Install eslint and babel

You will need node.js (or io.js), eslint, and babel-eslint installed on your system.

Installing eslint and babel-eslint just use npm

npm install -g eslint babel-eslint eslint-plugin-react
eslint -v # view version and confirm that its in your path

Note: These examples are for Babel 5. Babel version 6 changed its configuration considerably, so if you are using that make sure it is compatible with your version of babel-eslint and adjust your configuration according to the Babel website. You will need to include some preset plugins.

Configure eslint

Create a default ~/.eslintrc and/or a local .eslintrc to customize your eslint configuration. It is a json file, so edit it in emacs and create something like this.

The key thing to add is the parser which is set to babel-eslint and also ecmaFeatures should have the jsx set true. Also if you would like to use the eslint-plugin-react features, add "plugins": ["react"] as well. eslint-plugin-react helps you tune eslint to your needs while using jsx.

There are additional jsx specific rules the eslint-plugin-react enables. See the full list at the eslint-plugin-react README.

Here is my current .eslintrc (including all my rules and feature customizations). Adjust for your own use.

{
  "parser": "babel-eslint",
  "plugins": [ "react" ],
  "env": {
    "browser": true,
    "es6": true,
    "node": true
  },
  "ecmaFeatures": {
    "arrowFunctions": true,
    "blockBindings": true,
    "classes": true,
    "defaultParams": true,
    "destructuring": true,
    "forOf": true,
    "generators": true,
    "modules": true,
    "spread": true,
    "templateStrings": true,
    "jsx": true
  },
  "rules": {
    "consistent-return": [0],
    "key-spacing": [0],
    "quotes": [0],
    "new-cap": [0],
    "no-multi-spaces": [0],
    "no-shadow": [0],
    "no-unused-vars": [1],
    "no-use-before-define": [2, "nofunc"],
    "react/jsx-no-undef": 1,
    "react/jsx-uses-react": 1,
    "react/jsx-uses-vars": 1
  }
}

Install emacs packages

Emacs 24 has a nice package management system bundled in it, so I would recommend using that to install. This system makes it easy to find new packages and also to upgrade them later. It also takes care of getting all the dependencies you need.

Xah Lee has a great tutorial about the emacs package system and its use, so check it out if you need information on that.

To list the available packages

M-x list-packages

Scroll/search down to find the package you want to install, type i to mark for installation and x to execute the installation of all marked packages. Note that the package list is sorted by status initially, so new or updated packages are listed as available, then installed packages are next with status installed.

I am using these packages for my web app development with javascript, and JSX.

Emacs packages to install

  • flycheck - modern lint runner which has support for 30 programming languages with more than 60 different syntax checking tools. Replaces flymake. This will run our eslint command in background for us.
  • web-mode - mode which can handle mixed js and html like jsx
  • js2-mode - nice js editing mode which recognizes modern ES6+ features
  • json-mode - upgraded json mode (optional)
  • exec-path-from-shell - updates emacs exec path from your shell (optional) On OS X, when I was having issues getting emacs exec path setup correctly I installed this and it fixed my issues. I did not need it on other OS’s so I only run it conditionally. See below.

Once you have installed them you should see something like this in your *Packages* buffer.

Package Version Status Description
flycheck 20150401…. installed Modern on-the-fly syntax checking for GNU Emacs
js2-mode 20150202 installed Improved JavaScript editing mode
json-mode 20141105.138 installed Major mode for editing JSON files
web-mode 20150401.252 installed major mode for editing web templates
exec-path-from-shell 20141212.846 installed Get environment variables such as $PATH from the shell

Configuring Emacs

We need to:

  1. register *.jsx files with web-mode
  2. require flycheck so it will be started
  3. turn on flycheck globally
  4. disable jshint checker, so eslint will be used
  5. register eslint with flycheck web-mode
  6. disable json checking (just my preference to not use it)
  7. make sure emacs exec path finds eslint (optionally install and run exec-path-from-shell)

For steps 4 and 6, you can optionally use Emacs customize variable to add javascript-jshint and json-jsonlist to the flycheck-disabled-checkers list. I just use code to customize those variables so that I can keep all my config in one place.

Step 7 only became an issue for me on OS X. Emacs needs to be able to find eslint on its exec-path which it should get from the shell. You can see your exec path by executing this command in emacs.

M-x describe
exec-path

If eslint -v works from your shell but emacs isn’t finding it (the exec-path is probably wrong), then installing exec-path-from-shell and enabling it should fix the issue for you. I only need it for OS X so I have it conditionally run, see below.

In your .emacs file, add the following

;; use web-mode for .jsx files
(add-to-list 'auto-mode-alist '("\\.jsx$" . web-mode))

;; http://www.flycheck.org/manual/latest/index.html
(require 'flycheck)

;; turn on flychecking globally
(add-hook 'after-init-hook #'global-flycheck-mode)

;; disable jshint since we prefer eslint checking
(setq-default flycheck-disabled-checkers
  (append flycheck-disabled-checkers
    '(javascript-jshint)))

;; use eslint with web-mode for jsx files
(flycheck-add-mode 'javascript-eslint 'web-mode)

;; customize flycheck temp file prefix
(setq-default flycheck-temp-prefix ".flycheck")

;; disable json-jsonlist checking for json files
(setq-default flycheck-disabled-checkers
  (append flycheck-disabled-checkers
    '(json-jsonlist)))

;; https://github.com/purcell/exec-path-from-shell
;; only need exec-path-from-shell on OSX
;; this hopefully sets up path and other vars better
(when (memq window-system '(mac ns))
  (exec-path-from-shell-initialize))

Customizing flycheck to use local node_modules eslint if exists

If you want flycheck to prefer a local eslint in your node_modules directory rather than the default, you can add this hook. The main reason you might watn this is if you have different versions of eslint, eslint-plugins, and eslint config then you would want to run the version that your local project uses rather than the default you use globally otherwise you could get lots of errors for incompatible versions.

I got this solution from stack overflow here.

;; use local eslint from node_modules before global
;; http://emacs.stackexchange.com/questions/21205/flycheck-with-file-relative-eslint-executable
(defun my/use-eslint-from-node-modules ()
  (let* ((root (locate-dominating-file
                (or (buffer-file-name) default-directory)
                "node_modules"))
         (eslint (and root
                      (expand-file-name "node_modules/eslint/bin/eslint.js"
                                        root))))
    (when (and eslint (file-executable-p eslint))
      (setq-local flycheck-javascript-eslint-executable eslint))))
(add-hook 'flycheck-mode-hook #'my/use-eslint-from-node-modules)

Customizing indent

To adjust the indent in web-mode to my preference of two spaces I also added the following code.

;; adjust indents for web-mode to 2 spaces
(defun my-web-mode-hook ()
  "Hooks for Web mode. Adjust indents"
  ;;; http://web-mode.org/
  (setq web-mode-markup-indent-offset 2)
  (setq web-mode-css-indent-offset 2)
  (setq web-mode-code-indent-offset 2))
(add-hook 'web-mode-hook  'my-web-mode-hook)

Improving the JSX syntax-hightlighting in web-mode

Patrick @halbtuerke suggested this JSX syntax highlighting adjustment.

;; for better jsx syntax-highlighting in web-mode
;; - courtesy of Patrick @halbtuerke
(defadvice web-mode-highlight-part (around tweak-jsx activate)
  (if (equal web-mode-content-type "jsx")
    (let ((web-mode-enable-part-face nil))
      ad-do-it)
    ad-do-it))

Flycheck usage

Read the comprehensive flycheck manual for information on how to use and configure.

Flycheck is running by default so you will get feedback as you edit.

To use flycheck features, you start by entering C-c, then !, followed by the command key. You may also map these to your own keys as you see fit.

  • C-c ! l - see full list of errors in a buffer. Details
  • C-c ! n - next error
  • C-c ! p - previous error

Resources