Making bsqr
Sometimes I have the thought "Damn, someone should make this into an npx utility" ... well, a few months ago I actually listened to that voice in my head! In the rest of this post I'll go over what this little utility is & the thought process that went into creating it.
Table of Contents:
First, bsqr
:
It's a little script that combines BrowserSync with
qrcode-terminal
— I'm definitely
standing on the shoulders of
giants here,
not reinventing the wheel. Still, I think it's a nifty utility because it solves
one nagging issue I have with BrowserSync: the external URL should be
represented a QR code!
I almost always want to use the external URL on my phone. Universal
Clipboard is nice when I'm working on
my personal machines but that won't cut it on my work machine where I'm not signed
into iCloud.
So, with that thought in mind, I decided to make a utility that would:
- Get the same external URL BrowserSync is using
- Display the URL as a QR code
- Call BrowserSync like normal
Here's a GIF of bsqr
doing exactly that on
my latop:
& then on my phone:
So what did it take to create?
How I got the idea
I've known about BrowserSync for a long time but I didn't really appreciate how great it is until I started using 11ty a lot! Whenever I'm working on this very website I would see this:
So my first thought was to go to the BrowserSync API docs — sure enough, it's designed to be scripted in addition to being run from the command line 🎉!
Writing the actual script
Writing step 1: a nice template
Here all I really did was paste & modify a version of the docs API sample:
#!/usr/bin/env node
// require the module as normal
let bs = require("browser-sync").create();
// .init starts the server
bs.init({
server: "." // NOTE: I use the current working directory instead of './app'
});
// Now call methods on bs instead of the
// main browserSync module export
bs.reload("*"); // NOTE: Here I chose to reload everything
This got the basic BrowserSync features running, so yay!
Next, step 2: a dash of QR code
The next easiest piece was taking the other off-the-shelf dependency —
qrcode-terminal
— off the
proverbial shelf like so:
#!/usr/bin/env node
// require the module as normal
let bs = require("browser-sync").create();
let qrcode = require('qrcode-terminal');
// .init starts the server
bs.init({
server: "."
});
/*
* NOTE: Here we're just adding a text QR code to prove it's possible
*/
qrcode.generate('lorem ipsum', {small: true});
// Now call methods on bs instead of the
// main browserSync module export
bs.reload("*"); // NOTE: Here I chose to reload everything`
This little script indeed starts a BrowserSync server right after outputting the
QR code for lorem ipsum
:
Last, but not least, step 3: putting it all together
Finally, I knew I needed to fill in the QR code text with the actual external URL. I saved it for last because I was pretty sure this would take the most work 😬.
There's a note on the docs page
about using "a tool like dev-ip
" (italics mine) to
find the correct IP address, which I thought was interesting but I ignored when
I first read it.
First I tried to figure out where external
was coming
from. I cloned the Browser Sync repo &
ripgrep-ed the
browser-sync
lib
directory.
Running rg external
in that directory looked like this:
& that lead me to look at utils.js
. This sent me on a brief detour into
xip
but once I got past the magic[1] of that I noticed the
line above references
getHostIp
. Finally, it turned out to be this
line
that shows you where the external IP comes from: the first member of the array
that dev-ip
returns. Giants standing
on the shoulders of giants! If we add that in to our little script we get:
#!/usr/bin/env node
let qrcode = require('qrcode-terminal');
let bs = require("browser-sync").create();
let devIp = require('dev-ip');
// .init starts the server
bs.init({
server: "."
});
/*
* NOTE: We're assuming the port is 3000, this isn't a safe assumption though
*/
qrcode.generate(`http://${devIp()[0]}:3000`, {small: true});
// Now call methods on bs instead of the
// main browserSync module export
bs.reload("*"); // NOTE: Here I chose to reload everything`
Indeed, this is the bones of what bsqr
is right now. There's a little more
sugar to option flags, error checking & such, but it was a relatively straight forward
process to get from this script to the first published version.
The Future
While this was a relatively simple utility because a lot of the hard work is done by other utilities I hope this post helps you understand how easy it is to make useful scripts with JavaScript! In the future I also hope to write this as a BrowserSync plugin so that people can get the QR code functionality without the limitations of my utility.
I hope you'll try bsqr
& if you'd like to submit issues, feature requests, or
even a PR the GitHub repo is always waiting for your help!.
it's really just a suffix that acts as a very clever DNS resolver ↩︎