flycheck
flycheck copied to clipboard
Enable npx with eslint
This is portion of my ~/.spacemacs
(syntax-checking :variables
flycheck-javascript-eslint-executable "/home/dmitry/code/admin-vue/node_modules/.bin/eslint"
flycheck-select-checker 'javascript-eslint)
And it works properly, however I want to replace it with local npx call, and If I do so, I get javascript-eslint immediately disabled.
(syntax-checking :variables
flycheck-javascript-eslint-executable "npx eslint"
flycheck-select-checker 'javascript-eslint)
emacs version: 25.3.1
flycheck version: 20180311.1014
flycheck-verify-setup
Syntax checkers for buffer metric.js in js2-mode:
javascript-eslint (disabled)
- may enable: Automatically disabled!
- executable: Not found
- config file: missing or incorrect
javascript-jshint
- may enable: yes
- executable: Found at /usr/bin/jshint
- configuration file: Not found
javascript-standard
- may enable: Automatically disabled!
- executable: Not found
Flycheck Mode is enabled. Use M-m u C-c ! x to enable disabled checkers.
--------------------
Flycheck version: 32snapshot (package: 20180311.1014)
Emacs version: 25.3.1
System: x86_64-redhat-linux-gnu
Window system: x
npx eslint --print-config . displays proper output, no errors.
Thanks for the request; sorry for the delay.
The problem here is that flycheck starts looking for a binary called "npx eslint" (with a space in the name). The right way to do it would look like this:
(with-eval-after-load 'flycheck
(push "npx" (flycheck-checker-get 'javascript-eslint 'command)))
(note that in that case you wouldn't want to set flycheck-javascript-eslint-executable separately).
Can you try this and report?
Thanks @cpitclaudel, tried it, got this
Error (use-package): flycheck/:init: Symbol’s function definition is void: \(setf\ flycheck-checker-get\)
Ouch, sorry. It's the usual with-eval-after-load issue. Please try this:
(with-eval-after-load 'flycheck
(eval '(push "npx" (flycheck-checker-get 'javascript-eslint 'command))))
After this change I got no errors, and in flycheck-verify-setup
I got this
javascript-eslint (disabled)
- may enable: Automatically disabled!
- executable: Found at /usr/bin/npx
- config file: missing or incorrect
Ah, I know what the issue is. The config file check in flycheck-eslint-config-exists-p will just call npx --print-config
now, instead of npx eslint --print-config
.
For things to work, I think you'd need to also adjust flycheck-eslint-config-exists-p
to call npx eslint
. Let me know if you need help with that.
This is much easier to do that I described above, thanks to a feature that I had entirely forgotten about:
flycheck-command-wrapper-function is a variable defined in ‘flycheck.el’.
Its value is ‘identity’
This variable may be risky if used as a file-local variable.
Documentation:
Function to modify checker commands before execution.
The value of this option is a function which is given a list
containing the full command of a syntax checker after
substitution through ‘flycheck-substitute-argument’ but before
execution. The function may return a new command for Flycheck to
execute.
The default value is ‘identity’ which does not change the
command. You may provide your own function to run Flycheck
commands through ‘bundle exec’, ‘nix-shell’ or similar wrappers.
Which means that the following is enough:
(defun my-flycheck-npx-wrapper (command)
(cons "npx" command))
(defun my-js-mode-hook ()
(setq-local flycheck-command-wrapper-function #'my-flycheck-npx-wrapper))
(add-hook 'js-mode-hook #'my-js-mode-hook)
Sorry for the extended brainfart.
Hi @cpitclaudel thank you! Will try tomorrow and report, appreciate the help! L)
@cpitclaudel this is what I have got
Syntax checkers for buffer main.js in js2-mode:
javascript-eslint (disabled)
- may enable: Automatically disabled!
- executable: Found at /usr/bin/eslint
- config file: missing or incorrect
(spacemacs|use-package-add-hook js2-mode
:post-init
(setq-local flycheck-command-wrapper-function #'my-flycheck-npx-wrapper)
(setq flycheck-select-checker 'javascript-eslint))
I even tried your code 1:1 (except for js2-mode) and got the same flycheck verify setup output
Some debugging info:
(print flycheck-command-wrapper-function) <ctrl-j>
> identity
(print flycheck-select-checker) <ctrl-j>
> javascript-eslint
I then tried this
(spacemacs|use-package-add-hook js2-mode
:post-init
(setq flycheck-command-wrapper-function
(lambda (command)
(append '("npx") command)))
(setq flycheck-select-checker 'javascript-eslint))
and after that print flycheck-command-wrapper-function gives that lambda command, but output for flycheck verify setup is still the same.
Maybe the wrapper function is not invoked if eslint is found globally?
Any ideas?
Maybe the solution is to look around flycheck-executable-find?
Sorry, I don't know anything about spacemacs :/ Can you confirm that the hook thing that I showed above sets the wrapper properly?
(Btw, that flycheck-select-checker looks suspicious: that variable doesn't exist AFAIK)
I'm also dealing with frustration around this.
The wrapper suggested wraps the command in npx
, but I still need the eslint
executable somewhere in exec-path
to begin with to enable the checker.
If I add node_modules/.bin
to my exec-path
then the checker is enabled, but then I also don't really need the npx
wrapper - the real checker works fine even though flycheck-compile
fails (it's in exec-path
but not $PATH
and flycheck-compile
shells out).
The wrapper suggested wraps the command in npx, but I still need the eslint executable somewhere in exec-path to begin with to enable the checker.
Ah, right, because the :enabled
check doesn't take the npx
wrapping into account. Snap.
The only way we have fore this is to customize flycheck-executable-find
as well.
What does npx report is eslint is missing?
We could also add a Flycheck setting to use npx.
(it's in exec-path but not $PATH and flycheck-compile shells out).
Yuck yuck. Can you open a separate issue about this?
@cpitclaudel
I'd love to help with this. Is there anything specific I can do to help move this forward?
What does npx report is eslint is missing?
$ npx --version
9.7.1
$ npx --no-install eslint ; echo $?
not found: eslint
127
$ npx --no-install --quiet eslint ; echo $?
127
(Without --no-install
, it installs the package for the duration of the command, which is probably never what Flycheck users want.)
Yarn has a similar script wrapper:
$ yarn --version
1.6.0
$ yarn --no-color --offline --silent run eslint ; echo $?
error Command "eslint" not found. Did you mean "tslint"?
1
$ yarn --no-color --offline --silent run asdf ; echo $?
error Command "asdf" not found.
1
npx --no-install --quiet eslint ; echo $?
Thanks, this is useful. Are there other commonly used wrappers besides yarn and npx? If not, the best might be to add support for these two, specifically.
yeah those two are the most commonly used, I think it is enough to add support only for them. Btw has been any progress on this issue?
As it is some variation of what other people are doing I use this and it has served me well for all sorts of npm/yarn based projects.
(defun flycheck-node_modules-executable-find (executable)
(or
(let* ((base (locate-dominating-file buffer-file-name "node_modules"))
(cmd (if base (expand-file-name (concat "node_modules/.bin/" executable) base))))
(if (and cmd (file-exists-p cmd))
cmd))
(flycheck-default-executable-find executable)))
(defun my-node_modules-flycheck-hook ()
(setq-local flycheck-executable-find #'flycheck-node_modules-executable-find))
(add-hook 'js2-mode-hook 'my-node_modules-flycheck-hook)
(add-hook 'js-mode-hook 'my-node_modules-flycheck-hook)
(add-hook 'web-mode-hook 'my-node_modules-flycheck-hook)
...
I guess that at least in theory npx is better because it uses npm's knowledge about where paths are instead of explicitly stating them in elisp, what I wonder is if yarn in some cases might know something that npm doesnt'?
This is after all javascript/node ecosystem where every other god darn tool has it's own idea about how to do the same thing.
What kind of environments do people use for javascript development in Emacs these days ? What variables do the corresponding packages set? If someone wants to write the code, we can definitely add a setting in Flycheck to toggle between npx, yarn, and a plain call to eslint. We'd just have a customizable variable whose setter changes the :command
of the eslint checker.
Thinking more about this, a part of the problem is that we run the command through flycheck-command-wrapper-function after checking whether the executable exists. That variable was introduced by @svenkeidel a long long time ago for the nix case; @svenkeidel , how do things work with nix? Isn't it a problem that we check whether command xyz
exists before invoking the command wrapper?
In the meantime, we can either add separate code that sets exec-path, bypassing npx, or add support for npx in a separate variable.
One solution is to install eslint_d globally and set flycheck-javascript-eslint-executable
to "eslint_d"
instead of "eslint"
. eslint_d is a daemon for ESLint that uses the version of ESLint that's installed in the current working directory's node_modules.
As a workaround, I am simply providing in ~/.local/bin
a wrapper for eslint:
#!/bin/sh
exec yarn --offline --silent run eslint "$@"