動機
FlycheckでESLintを使っていると、グローバルインストールされた eslint
コマンドではなく、各プロジェクトにインストールされた node_modules/.bin/eslint
を使いたくなるときがあります。
実現
npm bin
コマンドの出力を exec-path
に追加することで実現します(npm bin
コマンドは普通、そのプロジェクトの node_modules/.bin
の絶対パスを出力します)。
下記のEmacs Lispコードを init.el
に追加してください。
(defun my/use-node-modules-bin ()
(let* ((local-path (replace-regexp-in-string
"[\r\n]+$" "" (shell-command-to-string "npm bin"))))
(setq-local exec-path (cons local-path exec-path))))
(add-hook 'flycheck-mode-hook #'my/use-node-modules-bin)
別解(2017-10-25 追記)
add-node-modules-path という素晴らしいパッケージを見つけましたので、これを使いましょう。
exec-path
をバッファローカルに変更してくれます。
これでもう flycheck-executable-find
をカスタマイズする必要はありません。
別解(2017-07-21 追記)
-
npm bin
は Node.js プロセスを立ち上げるので遅い ⇒node_modules
固定で Emacs Lisp で実装する -
exec-path
を更新するのではなく、Flycheck が提供するflycheck-executable-find
変数を使う
(defun my/find-npm-command (command)
(let* ((dirname "node_modules")
(root (locate-dominating-file default-directory dirname)))
(if root (concat
(file-name-as-directory root)
(file-name-as-directory dirname)
(file-name-as-directory ".bin")
command))))
(defun my/executable-find (command)
(let* ((file-path (my/find-npm-command command)))
(if (and file-path (file-executable-p file-path))
file-path (executable-find command))))
(setq flycheck-executable-find #'my/executable-find)
最終解(2017-07-22 追記) ⇒最終解ではないです
これが一番シンプルな解(かも)。
このQiitaの記事にあるように、あらかじめ PATH
を設定しておいて、
export PATH="./node_modules/.bin:${PATH}"
# Or
pathmunge ./node_modules/.bin
exec-path-from-shell
を使って PATH
を Emacs に読み込ませるようにすればよいです。
こうすることで、上記にあるような Flycheck への設定はすべて不要になります。
セキュリティ上問題あり(2017-07-24 追記)
…と書きましたが、
export PATH="./node_modules/.bin:${PATH}"
は、
export PATH="${PATH}:./node_modules/.bin"
とするのが、セキュリティ上正しいです。
理由は、仮に ./node_modules/.bin/touch
というファイルが存在した場合、OSコマンドの touch
をオーバーライドして悪意のあるコマンドを実行することができるからです。
(詳しくは https://github.com/npm/npm/issues/957#issuecomment-237064313 )
ただ PATH
の一番最後に ./node_modules/.bin
を置いてしまうと、グローバルインストールした npm パッケージの方がローカルインストールしたパッケージより優先されてしまうという点が悩ましいです。
やはり Flycheck に設定した方が、影響範囲が限定されるので安全かもしれません。