A custom "dev" command with auto-completion
- Published at
- Updated at
- Reading time
- 3min
I went into the rabbit hole of writing shell scripting today. ๐ The goal: saving keystrokes and manually ran commands when starting to work on a project.
Here's the result. ๐
The video above shows a custom dev
command that:
- navigates into a project
- runs
npm run dev
- opens VS Code
The exciting part is that it supports auto-completion to pass paths to projects with a working npm run dev
command quickly.
To define a custom shell function that runs various commands add the following lines to your
or
.
function dev() {
echo "Running 'npm run dev' in $1 ..."
cd ~/Projects/"$1" || return
code .
npm run dev
}
Run source ~/
/source ~/
or open a new terminal session to load the new configuration. Tada!!! You now have a new dev
command available.
First step; done! Let's come to the tricky part โ the auto-completion...
Disclaimer: if you follow the instructions in this article, keep in mind that my setup heavily relies on ohmyzsh. There is a chance that the described steps don't work in your environment.
If you start reading about auto-completion functionality, you'll learn that the recommended way is to add "completion files" to your system. These files should follow the convention of starting with an underscore. As an example, the file _dev
acts as the auto-completion for the dev
command. Additionally, the files have to start with the line #compdef
.
Alternatively, you can also use the compdev
command, but I didn't explore it and I actually like to have a separate file for every completion.
Let's have a look at my _dev
completion file.
#compdef dev
# Cache the parsing of the projects directory for repeated use
# The first auto-completion for `dev` will be slow
# because all projects have to be scanned.
# After the first run it's speedy
__DIRS_WITH_DEV_COMMAND=()
function _dev() {
# only iterate over projects if cached results are empty
if [ ${#__DIRS_WITH_DEV_COMMAND[@]} -eq 0 ]; then
# iterate over directory including all coding projects
for i in ~/Projects/*; do
if [ -f "$i/package.json" ]; then
# parse the package.json and see if `npm run dev` is available
DEV_COMMAND=$(jq ".scripts.dev" < "$i/package.json")
if [ "$DEV_COMMAND" != "null" ]; then
__DIRS_WITH_DEV_COMMAND+=("$(basename "$i")")
fi
fi
done
fi
# set auto completion result
_describe 'command' __DIRS_WITH_DEV_COMMAND
}
The script iterates over my ~/Projects
directory and checks if the included sub-directories include a package
file that then defines a dev
command. To define the completion result _describe 'command'
is ran with an array of completion options.
This basic functionality worked great but was fairly slow because every completion attempt read and parsed all project directories. To speed things up, I defined "a cache variable" outside of the dev
function __DIRS_WITH_DEV_COMMAND
. I'm sure there's a better way to cache completion results, but it does the trick for me.
But where should you place the _dev
file then? After reading the following guide, I learned that the $fpath
variable defines where completion files are located.
I went with adding my completion files to ~/
. And voilร โ I could now "tab around" to quickly start working on a project on my machine! ๐
Let's sum up what it took me to add a custom dev
function with auto-completion.
- define a function in your
/.zshrc .bashrc - create a completion file in a directory that's included in
$fpath
- wrestle with the art of shell scripting and define your command completion results
And that's pretty much it (it still took me ages to figure out, though ๐). If you have comments on improving these scripts, I'd love to hear them.
And lastly, if you like these quick tricks, I send a weekly newsletter all around web development and love to have you read along!
Join 5.5k readers and learn something new every week with Web Weekly.