Rapid Prototyping in JavaScript
Twitter: @mattdesl
In this post I’ll outline a simple workflow for quick prototyping and creative development with JavaScript. It’s the workflow I’ve been using for some years now, and it is how I build most of my libraries, demos and client projects. Some examples of libraries/projects using this workflow:
Goals #
The goals of this workflow:
- Fast development experience – when I save my code, my browser should reload
- Minimal configuration – I don’t want to spend time with configuration each time I create a new project
- Modular – I want to be able to split my application into many files and depend on third-party libraries from npm
- Easy Deployment – Deploying online should be simple and require no server management
- Modern and Extensible – The workflow should be able to easily scale up to larger projects, e.g. with modern ES2015 syntax and continuous deployment
Node.js & npm #
The first step in this workflow is to install latest Node.js (which includes npm) so that you can run node
and npm
from your terminal:
I suggest using node 8+ and npm 5+.
package.json #
Next, you need to create a folder and add a package.json
file. This will define what third-party libraries our project depends on, so that we can re-install and re-run the project many years later.
In terminal, it looks like this:
# create a folder
mkdir my-cool-app
# go into that folder
cd my-cool-app
# create a package.json
npm init
If you follow the steps (you can just hit enter a bunch of times and use the default values), you will get a new package.json
file.
folder structure #
When creating new modules and projects, I typically lay it out like so:
my-module
.gitignore
LICENSE.md
README.md
test.js
index.js
demo.js
package.json
Where test.js
is the unit test (where possible), and demo.js
is the runnable mock which will be published on gh-pages
. In some cases, the module will have its own test
or demo
folder.
There’s a few tools out there for stubbing out module folders and package.json
, like module-generator and npm init
.
bundler #
In order to use npm modules in the browser, we will need to use a bundler like browserify or webpack. Both are great tools and serve different needs; but in my experience browserify is better suited for maintaining many dozen small and framework-agnostic modules.
However, browserify
by itself is a relatively thin and low-level tool, and does not provide a smooth developer experience. Here we will explore some tools built on browserify that aim to improve the developer experience across many modules.
budo #
An easy way to get a demo up and running is to serve your demo.js
source with budo. This tool creates a local server on port 9966
, and bundles your source code on the fly. Under the hood, this is using browserify.
First, install it as a devDependency
. Development dependencies are only necessary to build/configure your project, but not required directly by your source code.
npm install budo --save-dev
Once it’s installed, create a "scripts"
field in your package.json
that looks like this:
...
"scripts": {
"start": "budo demo.js"
}
...
Now when you run npm run start
from terminal, it will launch budo on your source demo.js
file and serve a local development server. You can open http://localhost:9966/ in your browser to see your demo in action. Any require()
statements in your code will be bundled by budo and browserify, so that you can work with Node.js-style modules in the browser.
live reload #
If you specify --live
in your budo script, it will reload the browser page when you save your JavaScript file.
"scripts": {
"start": "budo demo.js --live"
}
CSS & HTML #
Eventually you will need an index.html
to publish. You might also want CSS styles, HTML content, and fonts. If you have an index.html
file in the base directory, budo will use that by default. For example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>my-module</title>
<link href='http://fonts.googleapis.com/css?family=Roboto+Slab' rel='stylesheet' type='text/css'>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
For this to work, we will need to use entry mapping to serve bundle.js
instead of demo.js
. We do this by changing the entry point, like so:
"scripts": {
"start": "budo demo.js:bundle.js --live"
}
creating a repo #
Once you’re happy with your demo, you can publish it to a new GitHub repository.
To speed this process up, I use a command-line tool ghrepo, which you can install like so:
npm install ghrepo -g
And then, simply run the command in your directory:
ghrepo
publishing #
Update Jan 2017: Nowadays I often use surge.sh for static sites, as it’s much easier than gh-pages! See here for an example.
This step is optional, but often it’s nice to deliver a runnable demo to the user so they don’t have to git clone
your repo to try it out.
Make sure you have browserify installed, and uglify-js if you plan to compress the source (optional).
npm install browserify uglify-js --save-dev
Now add your final bundle task:
"scripts": {
...
"build": "browserify demo.js | uglifyjs -cm > bundle.js"
}
Once you’ve committed your changes to master, we can set up the gh-pages
branch.
git checkout -b gh-pages
In this branch, you will need to remove bundle.js
from the gitignores. When publishing new content, it will look like this:
npm run build
git add .
git commit -m 'updating build'
git push origin gh-pages
Now if you visit your site, you should see the demo:
http://your-username.github.io/your-module/index.html
To automate this, see my ghpages shell script.