├── README.md └── find-local-executable.el /README.md: -------------------------------------------------------------------------------- 1 | # find-local-executable 2 | 3 | Find executables installed locally by npm, yarn, composer, stack, pip, gem... 4 | 5 | ## Motivation 6 | 7 | When we use packages like flycheck, company, lsp we often install the 8 | tools locally in a specific version using a package manager. These 9 | packages often only look for a globally installed executable which 10 | might not be what we want. 11 | 12 | ## Usage 13 | 14 | This package provides a uniform interface for setting up paths to 15 | executables installed locally (meaning not system-wide). 16 | 17 | To find the `flow` binary installed by `npm` or `yarn`, use 18 | 19 | ``` emacs-lisp 20 | (find-local-executable-nodejs "flow") 21 | ``` 22 | 23 | You can substitute `"flow"` with any binray you want. 24 | 25 | The general pattern is: 26 | 27 | ``` emacs-lisp 28 | (defun find-local-executable-PLATFORM (binary &optional use-global-fallback)) 29 | ``` 30 | 31 | On top of that, we support many popular packages out of the box. For 32 | example, to set up paths for packages relying on `"flow"` in nodejs, 33 | use: 34 | 35 | ``` emacs-lisp 36 | (find-local-executable-nodejs-setup-flow) 37 | ``` 38 | 39 | This will configure paths for (among others) 40 | 41 | ``` emacs-lisp 42 | company-flow-executable 43 | flow-minor-default-binary 44 | flycheck-javascript-flow-coverage-executable 45 | flycheck-javascript-flow-executable 46 | lsp-clients-flow-server 47 | ``` 48 | 49 | The general pattern is 50 | 51 | ``` emacs-lisp 52 | (defun find-local-executable-PLATFORM-setup-BINARY ()) 53 | ``` 54 | 55 | Following is an exhaustive list of supported platforms. If you don't 56 | see your favorite platform, please open an issue or even better submit 57 | a pull request! 58 | 59 | ## Supported platforms 60 | 61 | ### nodejs [via npm, yarn] 62 | 63 | Binary: 64 | 65 | ``` emacs-lisp 66 | (find-local-executable-nodejs BINARY) 67 | ``` 68 | 69 | Supported binaries: 70 | 71 | ``` emacs-lisp 72 | (find-local-executable-nodejs-setup-flow) 73 | (find-local-executable-nodejs-setup-eslint) 74 | ``` 75 | 76 | ### typescript [via npm, yarn] 77 | 78 | Binary: 79 | 80 | ``` emacs-lisp 81 | (find-local-executable-typescript BINARY) 82 | ``` 83 | 84 | Supported binaries: 85 | 86 | ``` emacs-lisp 87 | (find-local-executable-typescript-setup-tslint) 88 | (find-local-executable-typescript-setup-prettier) 89 | ``` 90 | 91 | ### php [via composer] 92 | 93 | Binary: 94 | 95 | ``` emacs-lisp 96 | (find-local-executable-php BINARY) 97 | ``` 98 | 99 | Supported binaries: 100 | 101 | ``` emacs-lisp 102 | (find-local-executable-php-setup-phpstan) 103 | (find-local-executable-php-setup-phpcs) 104 | ``` 105 | -------------------------------------------------------------------------------- /find-local-executable.el: -------------------------------------------------------------------------------- 1 | ;;; find-local-executable.el --- find local executables -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2019 Matúš Goljer 4 | 5 | ;; Author: Matúš Goljer 6 | ;; Maintainer: Matúš Goljer 7 | ;; Version: 0.0.1 8 | ;; Created: 19th October 2019 9 | ;; Package-requires: () 10 | ;; Keywords: files, convenience 11 | 12 | ;; This program is free software; you can redistribute it and/or 13 | ;; modify it under the terms of the GNU General Public License 14 | ;; as published by the Free Software Foundation; either version 3 15 | ;; of the License, or (at your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;;; Code: 28 | 29 | (defun find-local-executable--vendored 30 | (vendor-root-dir vendor-bin-dir binary use-global-fallback) 31 | "Find a vendored binary. 32 | 33 | First, detect the project root by looking up the file hierarchy until 34 | VENDOR-ROOT-DIR is found. Then relative to the root, find 35 | VENDOR-BIN-DIR. 36 | 37 | Look up BINARY in this directory. If it does not exist and 38 | USE-GLOBAL-FALLBACK is non-nil, look for a globally installed 39 | executable with `executable-find'. 40 | 41 | Return full path to the executable or nil if not found." 42 | (let ((local 43 | (when-let 44 | ((root (locate-dominating-file default-directory vendor-root-dir)) 45 | (executable (expand-file-name (concat root vendor-bin-dir binary)))) 46 | (and (file-exists-p executable) executable)))) 47 | (if local 48 | local 49 | (when use-global-fallback 50 | (executable-find binary))))) 51 | 52 | 53 | ;;; nodejs 54 | 55 | (defun find-local-executable-nodejs (binary &optional use-global-fallback) 56 | "Find locally installed BINARY. 57 | 58 | Return full path to the BINARY or nil if not found. 59 | 60 | If USE-GLOBAL-FALLBACK is non-nil, try to locate globally installed 61 | binary of the same name." 62 | (find-local-executable--vendored 63 | "node_modules" "node_modules/.bin/" binary use-global-fallback)) 64 | 65 | (eval-when-compile 66 | (defvar company-flow-executable) 67 | (defvar flow-minor-default-binary) 68 | (defvar flycheck-javascript-flow-coverage-executable) 69 | (defvar flycheck-javascript-flow-executable) 70 | (defvar lsp-clients-flow-server)) 71 | 72 | ;;;###autoload 73 | (defun find-local-executable-nodejs-setup-flow () 74 | "Setup paths to flow executable for current buffer" 75 | (interactive) 76 | (let ((executable (find-local-executable-nodejs "flow"))) 77 | (setq-local company-flow-executable executable) 78 | (setq-local flow-minor-default-binary executable) 79 | (setq-local flycheck-javascript-flow-coverage-executable executable) 80 | (setq-local flycheck-javascript-flow-executable executable) 81 | (setq-local lsp-clients-flow-server executable))) 82 | 83 | (eval-when-compile 84 | (defvar flycheck-javascript-eslint-executable)) 85 | 86 | ;;;###autoload 87 | (defun find-local-executable-nodejs-setup-eslint () 88 | "Setup paths to eslint executable for current buffer" 89 | (interactive) 90 | (let ((executable (find-local-executable-nodejs "eslint"))) 91 | (setq-local flycheck-javascript-eslint-executable executable))) 92 | 93 | 94 | ;;; typescript 95 | 96 | (defalias 'find-local-executable-typescript 'find-local-executable-nodejs) 97 | 98 | (eval-when-compile 99 | (defvar flycheck-typescript-tslint-executable)) 100 | 101 | ;;;###autoload 102 | (defun find-local-executable-typescript-setup-tslint () 103 | "Setup paths to tslint executable for current buffer" 104 | (interactive) 105 | (let ((executable (find-local-executable-typescript "tslint"))) 106 | (setq-local flycheck-typescript-tslint-executable executable))) 107 | 108 | ;;;###autoload 109 | (defun find-local-executable-typescript-setup-prettier () 110 | "Setup paths to prettier executable for current buffer" 111 | (interactive) 112 | (let ((executable (find-local-executable-typescript "prettier"))) 113 | (setq-local prettier-js-command executable))) 114 | 115 | 116 | ;;; php 117 | 118 | (defun find-local-executable-php (binary &optional use-global-fallback) 119 | "Find locally installed BINARY. 120 | 121 | Return full path to the BINARY or nil if not found. 122 | 123 | If USE-GLOBAL-FALLBACK is non-nil, try to locate globally installed 124 | binary of the same name." 125 | (find-local-executable--vendored 126 | "vendor" "vendor/bin/" binary use-global-fallback)) 127 | 128 | (eval-when-compile 129 | (defvar flycheck-php-phpstan-executable)) 130 | 131 | ;;;###autoload 132 | (defun find-local-executable-php-setup-phpstan () 133 | "Setup paths to phpstan executable for current buffer" 134 | (interactive) 135 | (if-let ((executable (find-local-executable-php "phpstan"))) 136 | (setq-local flycheck-php-phpstan-executable executable) 137 | (when-let ((executable (find-local-executable-php "phpstan.phar"))) 138 | (setq-local flycheck-php-phpstan-executable executable)))) 139 | 140 | (eval-when-compile 141 | (defvar flycheck-php-phpcs-executable)) 142 | 143 | ;;;###autoload 144 | (defun find-local-executable-php-setup-phpcs () 145 | "Setup paths to phpcs executable for current buffer" 146 | (interactive) 147 | (when-let ((executable (find-local-executable-php "phpcs"))) 148 | (setq-local flycheck-php-phpcs-executable executable))) 149 | 150 | (provide 'find-local-executable) 151 | ;;; find-local-executable.el ends here 152 | --------------------------------------------------------------------------------