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:
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.
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 ofyarn start
which is also valid, to avoid shadowing any binary.
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.
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