25

A lot of projects I work on install eslint as a dev dependency, with a custom set of eslint plugins. Right now, flycheck uses the globally installed version of eslint rather than the version of eslint installed with each project.

I would like to make flycheck point to node_modules/eslint/bin/eslint.js instead. The full path should depend on the path of the current buffer's file so I can be working on multiple projects at the same time with each buffer using a potentially different eslint executable.

Is this possible? Any suggestions for how I would go about doing this?

6 Answers 6

35

You can programmatically change flycheck-javascript-eslint-executable, e.g.

(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/.bin/eslint"
                                 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)

This code looks for a node_modules directory in any parent of the buffer's directory and configures Flycheck to use an eslint executable from that directory if any exists.

5
  • I wish my emacs-fu were this good. Thanks for the answer!
    – pcardune
    Commented Apr 4, 2016 at 16:35
  • 2
    If you have global-flycheck-mode set, then I recommend (and eslint (file-executable-p eslint)) I was encountering errors otherwise trying to, i.e. edit my .emacs file Commented Jun 14, 2016 at 15:31
  • Here's what you can do if you need an inline hook (and avoid adding custom functions) : emacs-fu.blogspot.in/2008/12/hooks.html Commented Dec 6, 2017 at 16:12
  • 1
    security note: only open files in trusted folders/projects; you will auto-execute any malicions eslint binary
    – maxy
    Commented May 14, 2018 at 9:21
  • 3
    I would use the path "node_modules/.bin/eslint", just in case eslint ever changes their directory structure. Commented Jun 25, 2018 at 3:01
4

I don't have enough reputation to comment on the accepted solution. But I made a variation of it, that works well with nested packages (for example using lerna). This is the exact same code, however it looks up the parent node_modules folder recursively until it finds an eslint binary, and uses it. This way, you can have a lerna project have only one eslint configuration, shared between all sub-packages.

(defun my/use-eslint-from-node-modules ()
  (let ((root (locate-dominating-file
               (or (buffer-file-name) default-directory)
               (lambda (dir)
                 (let ((eslint (expand-file-name "node_modules/eslint/bin/eslint.js" dir)))
                  (and eslint (file-executable-p eslint)))))))
    (when root
      (let ((eslint (expand-file-name "node_modules/eslint/bin/eslint.js" root)))
        (setq-local flycheck-javascript-eslint-executable eslint)))))
(add-hook 'flycheck-mode-hook #'my/use-eslint-from-node-modules)
0

I'm on windows and this wasn't working for me, I think the file-executable-p or perhaps the flycheck internals were failing to locate the correct file. As when its working it is pointing to the .cmd file and not the .js file.

I found a comment in flycheck-eslint does not use version of eslint installed in JavaScript project and their blog post to use directory variables instead.

In the root directory of the project create a file .dir-locals.el

((nil . ((eval . (progn
                   (add-to-list 'exec-path (concat (locate-dominating-file default-directory ".dir-locals.el") "node_modules/.bin/")))))))

Now when I visit the javascript file flycheck-verify-setup correctly finds the file <dir>/node_modules/.bin/eslint.cmd

0

My previous solution was a pain to get working across different projects.

I've modified the accepted answer to hard code windows stupidities.

(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/.bin/eslint.cmd"
                                        root))))
    (when (and eslint (file-exists-p eslint))
      (setq-local flycheck-javascript-eslint-executable eslint))))
0

Here's a solution that combines Bae's and lunaryorn's answers using a dir-locals.el file in the root directory of the project (or, more specifically, in the directory that contains the node_modules/ folder where the eslint binary lives). Create said dir-locals.el file with the following content.

((js2-mode
   (flycheck-checker . javascript-eslint)
   (eval . (setq-local flycheck-javascript-eslint-executable
             (concat (locate-dominating-file default-directory
                       ".dir-locals.el") "node_modules/.bin/eslint")))))

Here I'm assuming you are using the js2-mode emacs major mode to edit javascript files. If that's not the case, change that line accordingly. The second line of the file makes the javascript-eslint syntax checker the only one to be used within this project. The third line sets the variable flycheck-javascript-eslint-executable to <root-dir>/node_modules/.bin/eslint. In this way we don't modify emacs' exec-path.

0

I've tried a few different variations of this and never found exactly what I wanted. I've finally leveled up enough in Emacs lisp to modify it to my liking. What my version does is look for a local eslint and if one is not found it falls back to a globally installed one:

(defun configure-web-mode-flycheck-checkers ()
    ;; See if there is a node_modules directory
    (let* ((root (locate-dominating-file
                  (or (buffer-file-name) default-directory)
                  "node_modules"))
           (eslint (or (and root
                            ;; Try the locally installed eslint
                            (expand-file-name "node_modules/eslint/bin/eslint.js" root))

                       ;; Try the global installed eslint
                       (concat (string-trim (shell-command-to-string "npm config get prefix")) "/bin/eslint"))))

      (when (and eslint (file-executable-p eslint))
        (setq-local flycheck-javascript-eslint-executable eslint)))

    (if eslint
        (flycheck-select-checker 'javascript-eslint)))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.