Rapid Prototyping in JavaScript

Twitter: [@mattdesl](twitter.com/mattdesl)

This is a brief introduction to my current workflow for small, self-contained browser demos and prototypes.

screen cast of a typical prototyping session

Many modules are easy enough to unit test and develop entirely in the console (e.g. with nodemon and tape). This post is not about those modules, but instead, about the ones that are harder to unit test, and harder to visualize in a console alone. Some examples:

 goals

The aim of this workflow is to produce browser modules that are decoupled from the larger applications that surround them, so that the code is easier to re-use and maintain across projects. We also want to make use of the npm ecosystem for faster prototyping.

Deliverable demos, usually in the form of static gh-pageslike this or the iframe below – can be used to get public feedback. We also want a simple dev script, easy for others to run after a git clone.

These do not replace unit tests, but they can be better than nothing.

touch-scroll-physics demo, touch to swipe

 npm scripts

Build tools like Gulp and Grunt are great for large applications, but for small modules and demos they tend to be overkill, and the build scripts become a maintenance burden.

For these small demos, we will use the scripts field in our package.json. This means that any build tools should use --save-dev so that others cloning our repository can get the same versions. For example:

"scripts": {
  "build": "browserify demo.js > bundle.js"
}

You can then use the npm run build command to trigger the build script.

 folder structure

When creating new modules, I typically lay it out like so:

  my-module
    .npmignore
    .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.

First, install it as a devDependency:

npm install budo --save-dev

Then include it as a script in package.json:

"scripts": {
    "start": "budo demo.js"
}

Now enter npm run start in terminal to start the development server. When you open localhost:9966, it will serve the browserified bundle to a stub index.html.

This tool uses watchify under the hood, creating a fast and efficient workflow for local browserify development.

pretty

 live reload

Unlike some other tools, budo will suspend the server request until the bundle is ready, so you will never be served an empty or stale bundle.

However, during development, it can be nice to reload the page on file changes. The --live option will inject a script tag into the served index.html, giving it support for LiveReload integration.

"scripts": {
    "start": "budo demo.js --live"
}

Now, when you save your source, it will trigger a LiveReload page refresh. Because budo injects the LiveReload script, no plugin is necessary.

 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>

If you are using budo with the --live argument, any changes to style.css will get injected into the page without destroying application state.

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 repository in a single command, using ghrepo.

npm install ghrepo -g

And then, simply run the command in your directory:

ghrepo -m "first commit"

 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.

 hot reloading

In some more advanced prototyping sessions, it may be worth setting up “hot-loading” to inject changes without destroying application state. Webpack users might already be familiar with this, and hopefully one day it will arrive in browserify.

Until then, you can explore some other experimental options:

 inspiration

Much of this workflow has evolved from beefy and wzrd.

 
239
Kudos
 
239
Kudos

Now read this

Browserify vs. Webpack

It’s amazing, and slightly terrifying, how the landscape of JavaScript and its tooling can change in the blink of an eye. But, right now, we have a pretty good thing going with npm and browserify. We get thousands of useful functions and... Continue →