fzfでpackage.jsonのscriptsを選んで実行
npm run foo
やpnpm run bar
を毎回タイピングして実行するのは面倒ですし、実行される内容まで細かく覚えていられません。そこで、fzfを使って実行します。
以前Zennに書いた記事ではpnpm
の方に対応していませんでしたが、今回は対応させました!
動作例
次のように、コマンド名 = 実行内容
のリストをfzfで選んで実行できます。
fzfを使っているため当然絞り込みできます。

必要なもの
今回はzshを使った例です。zsh以外の人はよしなに書き換えてください。
実行方法
Node.jsのプロジェクトにあるpackage.json
にて、Ctrl+x,nを入力すると実行できます。
ホットキーは必要に応じて書き換えましょう。
コード
いったんコード全体を載せます。次の項目から分けて解説します。
function fzf_npm_scripts() { local prefix="npm" if [ -e pnpm-lock.yaml ]; then prefix="pnpm" fi if [ ! -e package.json ]; then echo 'fzf_npm_scripts' echo 'There is no package.json' zle send-break return 1 fi if ! type jq > /dev/null; then echo 'fzf_npm_scripts' echo 'jq command is required' zle send-break return 1 fi
local scripts=`jq -r '.scripts | to_entries | .[] | .key + " = " + .value' package.json 2>/dev/null || echo ''` if [[ -z $scripts ]]; then echo 'fzf_npm_scripts' echo 'There is no scripts in package.json' zle send-break return 1 fi local selected=`echo $scripts | FZF_DEFAULT_OPTS='' fzf --height=50% --reverse --exit-0 | awk -F ' = ' '{ print $1}'`
zle reset-prompt if [[ -z $selected ]]; then return 0 fi
# 再実行したくなるときのために履歴に残して実行 BUFFER="$prefix run $selected" zle accept-line}zle -N fzf_npm_scriptsbindkey "^Xn" fzf_npm_scripts
pnpmだった場合に切り替える
まずpnpmを使っている場合は書き換えます。
local prefix="npm"if [ -e pnpm-lock.yaml ]; then prefix="pnpm"fi
必要なファイル・コマンドが無い場合
package.json
とjq
が無い場合はエラーにします。
if [ ! -e package.json ]; then echo 'fzf_npm_scripts' echo 'There is no package.json' zle send-break return 1fiif ! type jq > /dev/null; then echo 'fzf_npm_scripts' echo 'jq command is required' zle send-break return 1fi
send-break
を使わないと、エラーになったときに次のプロンプトが表示されません。Enterを押せばよいのですが、せっかくなので気持ちを新たにプロンプトも切り替えたいところです。

send-break
を使えば、エラーになったときに次のプロンプトに切り替えてくれます。

scripts
を取得
jqを使ってpackage.json
のscripts
の中身を取っています。
local scripts=`jq -r '.scripts | to_entries | .[] | .key + " = " + .value' package.json 2>/dev/null || echo ''`
取得した中身はつぎのようにコマンド名 = 実行内容
のリストになります。
dev = astro devstart = astro devbuild = astro check && astro buildpreview = astro previewastro = astroformat = prettier -w .
jqの書き方を詳しく知りたい方は公式ドキュメントやとほほのjq入門あたりを読みましょう。
package.json
にscripts
が無かったらエラーにします。
if [[ -z $scripts ]]; then echo 'fzf_npm_scripts' echo 'There is no scripts in package.json' zle send-break return 1fi
fzfにリストを渡す
ここまできたらあとはいよいよfzfにリストを渡します。FZF_DEFAULT_OPTS
の設定に左右されないよう、デフォルトオプションを空にしています。
local selected=`echo $scripts | FZF_DEFAULT_OPTS='' fzf --height=50% --reverse --exit-0 | awk -F ' = ' '{ print $1}'`
awk
を使って=
の前、つまりコマンド名を抜き出して変数に入れます。
何も選ばれなかったらそこで終了します。プロンプトのテーマによってはreset-prompt
を使わないとカーソルの位置がずれます。
zle reset-promptif [[ -z $selected ]]; then return 0fi
ZLE経由で実行
あとは現在のプロンプトの入力をpnpm run dev
のように書き換えて実行です。BUFFER=
で入力を書き換えることで、コマンドの履歴として残すことができます。
BUFFER="$prefix run $selected"zle accept-line
今回はZLEを使ったため、実行にはZLEのウィジェットとしての登録とキーバインドの設定が必要です。
zle -N fzf_npm_scriptsbindkey "^Xn" fzf_npm_scripts
キーバインドの割り当て状況を知りたいならbindkey
コマンドを実行しましょう。一覧がずらっと出てきます。
bindkey
"^@" set-mark-command"^A" beginning-of-line"^B" backward-char"^C" send-break"^D" delete-char-or-list"^E" end-of-line"^F" forward-char"^G" send-break"^H" backward-delete-char"^I" fzf-completion"^J" accept-line"^K" my_fzf_completion省略
npm run ◯◯
、pnpm run ◯◯
をfzfで実行できるようなスクリプトの紹介でした。fzfとZLEって本当に便利です。