Electron Dev Tool

3 min read Original article ↗

I used this project as an opportunity to check out Electron. Electron is an open source project that runs a local Node.js server inside Chromium (the open source part of Google Chrome) along with a Web UI. It sounds kind of janky, but it works pretty well. The Atom code editor (open source and supported by Github) is built on Electron, and Facebook’s Nuclide code editor is built on Atom. So I’m not too worried about this being a dead-end.

Electron comes with a basic inter-process-communication (IPC) API, so I made a simple wrapper to create remote procedure calls (RPC) by attaching a unique id to each request. Then I created some nice higher-order React components for building the UI with associated shell commands. The result is surprisingly easy way to build a simple dashboard for running shell commands!

Here’s the UI I build for Shindig in about 5-10 minutes.

Building your own dev tool app couldn’t be easier. You need to have a Mac, and I’ve tailored the commands to work with iTerm2 (e.g. opening up a new Tab). To install iTerm2 if you haven’t already:

brew cask install iterm2

Then you just need to clone the repo, install some dependencies, and you’re good to go!

git clone https://github.com/ccorcos/dev-tool
cd dev-tool
npm install

I also use trash for safely deleting files in some of the shell commands, so go ahead and grab that as well.

brew install trash

To start the app:

./start.sh

And to build a distributable standalone build:

./build.sh

And there you have it! The demo app gives examples for how to use each of the higher-order components I’ve built, and I’ll explain them to you now.

There are currently 6 core components that compose really well to handle all of the typical things you might want from a dev-tool.

The RunBtn will run a command in a new iTerm2 tab.

RunBtn("Node REPL", "cd ~; node;")

The ExecBtn will run a command silently.

ExecBtn("say hi", "say hello `whoami`")

Exec will run a command and call back with the results to render with.

Exec("whoami", (name) => {
return <span>Welcome {name}</div>
})

There’s a simple Text input component:

Text({placeholder: 'shell command'}, (cmd) => {
return RunBtn('Run', cmd)
})

And a Path component with autocompletion:

Path({placeholder: '~/path/to/file'}, (path) => {
return ExecBtn('open', `open ${path}`)
})

And a Select component to select options:

Select(['Desktop', 'Documents'], (selection) => {
return ExecBtn('Open Folder', `open ~/${selection}`)
})

The beauty of this design is that these components compose very concisely into pretty much whatever you want. The following will look up files on your Desktop, parse the string, and create a dropdown menu to select which file you want to open.

Exec('ls -1 ~/Desktop/', (result) => {
const files = R.pipe(
R.trim,
R.split('\n'),
R.map(R.trim)
)(result)
return Select(files, (selected) => {
return ExecBtn("Open", `open ~/Desktop/${selected}`)
})
})