blogworktalksabout
Blog

Run yarn/npm scripts with fzf

July 2020

In a Node enviroment or a project with a package.json, It's very common to have a field "scripts" inside the package.json which stores and lists the scripts available to build, test and run the project.

In order to see which scripts are available in the project without opening the package.json with an editor, yarn and npm have a command that lists them: yarn run or npm run.

Which is a prompt that allows you to write which command you want to run or list them, depend if you are using npm or yarn.

This is the output in a recently created expo app:

yarn run v1.22.4
info Project commands
   - android
      expo start --android
   - eject
      expo eject
   - ios
      expo start --ios
   - start
      expo start
   - web
      expo start --web
question Which command would you like to run?:
yarn run v1.22.4
info Project commands
   - android
      expo start --android
   - eject
      expo eject
   - ios
      expo start --ios
   - start
      expo start
   - web
      expo start --web
question Which command would you like to run?:

It's very handy and allows you to not spend energy trying to remember those and have a fast lookup, but it's very slow (around 250ms!) since needs to start a node process, parse the json file and output something to stdin and the UX can be better since it forces you to type the command again.

I was a little frustrated by it and I created a small bash script that solves it, it's aliased to run in my local enviroment, take a look how it works:

yarn-run.gif

It uses fzf to filter the commands available and allows a fast search for them. If you don't know what's fzf, I can't recommend enough tacking a look at it. fzf stands for fuzzy finder and it's used by when hit CTRL+R to navigate the bash history and It's commonly used to navigate between files in your editor.

fzf.gif

What's the script about

if cat package.json > /dev/null 2>&1; then
    scripts=$(cat package.json | jq .scripts | sed '1d;$d' | fzf --height 40%)
 
    if [[ -n $scripts ]]; then
        script_name=$(echo $scripts | awk -F ': ' '{gsub(/"/, "", $1); print $1}')
        print -s "yarn run "$script_name;
        yarn run $script_name
    else
        echo "Exit: You haven't selected any script"
    fi
else
    echo "Error: There's no package.json"
fi
if cat package.json > /dev/null 2>&1; then
    scripts=$(cat package.json | jq .scripts | sed '1d;$d' | fzf --height 40%)
 
    if [[ -n $scripts ]]; then
        script_name=$(echo $scripts | awk -F ': ' '{gsub(/"/, "", $1); print $1}')
        print -s "yarn run "$script_name;
        yarn run $script_name
    else
        echo "Exit: You haven't selected any script"
    fi
else
    echo "Error: There's no package.json"
fi

This can be intimidating at first so in order to understand it better, let's break it down to explain a little what each command does.

The first interesting bit is to preview the content of package.json and make it appear on a fzf menu.

cat package.json | jq .scripts | sed '1d;$d'

cat package.json prints the content of the package.json

jq .scripts parses the json and selects only "scripts"

sed '1d;$d' removes the first and the last line

"predeploy": "yarn build",
"deploy": "gh-pages -d public",
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write src/**/*.{js,jsx}",
"start": "npm run develop",
"serve": "gatsby serve",
"predeploy": "yarn build",
"deploy": "gh-pages -d public",
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write src/**/*.{js,jsx}",
"start": "npm run develop",
"serve": "gatsby serve",

This output gets passed to fzf, which creates the floating menu, and once you select one of the rows, the $script value is set to "start": "npm run develop" and finally you are able to get the command name "start", saves that command to the history and finaly runs "yarn run start".

I appended yarn run instead of yarn start which is also valid, to avoid shadowing any binary.

Custom

This script lives under my dotfiles repo, which is an easy way to install and setup your enviroment each time you change laptops.

Feel free to can change this script to use npm or do any sort of change, I found playing with my terminal very rewarding, getting to learn deeper about the tools that I used everyday, allowed me to do stuff that I wasn't capable before and having those small performance boosts made my life a little easier. I will keep sharing more as they pop in my head.

Resources

  • For an introduction to the fzf, see this blog post
  • My personal setup davesnx/setup using zsh
  • If you aren't familiar with the dotfiles this would give a little clarity
  • For the spanish speakers I found this course amazing

Thanks for reaching the end. Let me know if you have any feedback, corrections or questions. Always happy to chat.

If you like it enough, consider to

share it on Twitter