fzfを使いnpm run 〇〇を一覧から選んで実行

2022/06/19に公開

どうも。 えーたん(@eetann092)です。

npm run startnpm run devなど、npm run 〇〇はプロジェクトによって違います。
忘れるたびにpackage.jsonscriptsを見るのが面倒になってきたため、fzfで一覧から選択したものを実行できるようにしました。
fzfを使ってnpm scriptsを実行

実装

全体

今回は.zshrcに書き、Ctrl + xを押してからnを入力して実行できるようにしました。引数のあるものは想定していません。

function fzf_npm_scripts() {
  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="npm run $selected"
  zle accept-line
}
zle -N fzf_npm_scripts
bindkey "^Xn" fzf_npm_scripts

解説

存在チェック

最初にpackage.jsonがあるか確認し、無ければストップします。

if [ ! -e package.json ]; then
  echo 'fzf_npm_scripts'
  echo 'There is no package.json'
  zle send-break
  return 1
fi

今回はpackage.jsonscriptsに書いてある内容を引き出すためにjqを使います。
そのため、jqがない場合もストップします。

if ! type jq > /dev/null; then
  echo 'fzf_npm_scripts'
  echo 'jq command is required'
  zle send-break
  return 1
fi

scriptsの取得

jqを使い、package.jsonscriptsを取得して加工します。

local scripts=`jq -r '.scripts | to_entries | .[] | .key + " = " + .value' package.json 2>/dev/null || echo ''`

今回はfzfpreviewオプションは使わず、以下のようなスクリプト名 = スクリプト内容形式で表示しました。

start = react-scripts start
build = react-scripts build
test = react-scripts test
eject = react-scripts eject

fzfpreviewオプションを使わなかったのは、以下の画像のようにウィンドウ幅が広い場合、左右の視線の移動が増えるためです。

fzfでpreviewオプションを使った例

fzfのpreviewオプションを使う場合のjq
jq -r '.scripts | keys | .[]' package.json | fzf --preview "jq -r '.scripts | .{}' package.json"

2>/dev/null || echo ''や次の行のif文は、
万が一scriptsが書かれていなかった場合にストップするための処理です。

fzfでの選択

いよいよfzfでの選択です。
変数selectedには、npm run 〇〇〇〇の部分が入ります(startdevなど)。

local selected=`echo $scripts| FZF_DEFAULT_OPTS='' fzf --height=50% --reverse --exit-0 | awk -F ' = ' '{ print $1}'`

まず、FZF_DEFAULT_OPTSの設定に左右されないように、デフォルトオプションを空にしてから実行しています。

echo $scripts | FZF_DEFAULT_OPTS='' fzf --height=50% --reverse --exit-0

続いて、awkを使ってスクリプト名のみを取得します。

awk -F ' = ' '{ print $1}'`

npm run 〇〇 の実行

zleとBUFFER変数を使い、どのスクリプトを選んだのか分かるように表示してから実行します。
また、何も選ばずに終了した場合は何も実行しないif文を書きました。

zle reset-prompt
if [[ -z $selected ]]; then
  return 0
fi
BUFFER="npm run $selected"
zle accept-line

キーバーインドの設定

今回はzleを使っているため、キーバインドを設定しています。

zle -N fzf_npm_scripts
bindkey "^Xn" fzf_npm_scripts

実行例

↓最後にもう一度デモのGIFを貼ります。

fzfを使ってnpm scriptsを実行

余談:fzf-tmuxは使わないの?

https://zenn.dev/eetann/articles/2022-03-19-fzf-tmux-popup

以前書いた↑の記事では、fzfの代わりにfzf-tmuxコマンドを使うことで、表示領域を広くしました。
今回fzf-tmuxを使わなかったのはfzfpreviewオプションを使わなかったのと同じ理由で、ウィンドウ幅が広い場合に左右の視線の移動が増えるのを防ぐためです。

リンク集

参考にしたリンクは以下です。

Discussion