Even More Better-er In-Browser Mockups with Lineman.js

Issue #374 of A List Apart by Garann Means is a great two-part piece on using Node.js for in-browser mockups and prototypes. In “Even Better In-Browser Mockups with Node.js” she gives a great overview for why using Node for mockups/prototypes is such a great idea. In part two—”Node At Work: A Walkthrough“—she dispenses with the theory and shows hands on how to build a mockup that you can use, using Node.js. I strongly recommend you read both articles.

Using Node.js for mockups provides quite a few key benefits, not the least of which being:

  1. Mockup built using web technologies, not Photoshop comps
  2. It works! (users/stakeholders can get their hands on it)
  3. Rapid development

But there are a few drawbacks to vanilla Node/express.js:

  1. No CSS pre-processors: SASS, LESS or Stylus
  2. No JavaScript pre-compilation languages: CoffeeScript, TypeScript
  3. Hand-wiring templating engines
  4. What about client-side frameworks: Backbone, Angular, or Ember?
  5. Node’s HTTP server (express) can be a pain
  6. Restarting the server on every file change? Blech!

Lineman.js can help!

Lineman.js

Lineman is a tool to help you build fat-client webapp projects. Mockups and Prototypes certainly fit that bill! In fact, when working on mockups and prototypes, generally speed of deployment and rapid development is a key factor. Lineman can help you scaffold out a mockup and get you up and running fast!

In addition to all the benefits that Garann discussed for why using Node is a good idea for mockups, Lineman also provides the ability to use CSS preprocessors, compile-to-JavaScript languages, and choose your own templating tool. (Lineman ships with support for LESS and SASS CSS pre-processors, Underscore and Handlebars template engines, and CoffeeScript compilation. And it’s easy to add support for your tooling of choice e.g. Stylus, TypeScript, Mustache)

Lineman eases the pain of integrating with a server-side API. You can simply stub out the API endpoints in Lineman’s config/server.js configuration file (which makes a great deliverable to the server-side team when it comes time to build out the real server endpoint). Or, if the server-side API already exists, you can tell Lineman to just proxy requests to the real API.

Further, to increase the pace of development and feedback loop, let Lineman handle recompiling your assets and restarting the server for you! And lastly, if you plan on using one of the many great client-side frameworks out there (like Backbone, Angular or Ember), Lineman has project templates ready to get you going.

“Cool Art Store” Demo, ported to Lineman

I created a fork of Garann’s original “Cool Art Store” demo project to show how easy it is to use Lineman for simple mockups. I isolated the Lineman port to a branch named ‘lineman’.

What was necessary to get Garann’s Cool Art Store demo running on Lineman?

  1. Ran $ lineman new coolartstore to get the base Lineman archetype project. (much easier than starting with a blank canvas)
  2. Moved the css, js, image, and template assets from public/ to app/. (Not strictly necessary – Lineman can be configured to look for assets anywhere you wish by using config/files.js or config/files.coffee)
  3. Converted the templates from doT.js to Handlebars (Also, not technically necessary – there are a number of doT-precompile grunt tasks, so configuring Lineman to use doT is also an option; I just hapen to prefer Handlebars.)
  4. Stripped out the XHR requests for the doT templates; Lineman pre-compiles your templates into a single JS object so you need only execute the template at runtime.
  5. Ported server.js to use Lineman’s config/server.js for defining the API endpoint stubs. This mostly boiled down to removing all the express.js setup code. (Lineman makes it easy to just define the endpoints and not deal with boilerplate.)
  6. Bonus: since Lineman runs the JavaScript through JSHint, it caught a couple of lint-warnings that were promptly fixed.
  7. Bonus: Lineman concatenates and minifies the final CSS/JS assets. If this weren’t just a mockup, we’d be one step closer to shipping!
  8. Bonus: using lineman run meant that I didn’t have to keep restarting the node server after each file modification!

Not just for Mockups

Of course, Lineman is really tailored for developing fat-client webapps for production, not just mockups in particular. So TDD your client-side application using your JavaScript test framework of choice and let Lineman run the specs/tests for you! Or integrate your test suite into your Continuous Integration build. And let Lineman help with deployment to Heroku as well!

Supporting TypeScript in Lineman.js

Lineman is a great tool for building client-side applications. Out of the box, it supports CoffeeScript, LESS, SASS, and (of course) JavaScript and CSS. But because Lineman is built on Grunt, it is easy to add any grunt task to the Lineman toolchain. Today, let’s see how we would add TypeScript support to a Lineman project.

Here’s a rundown of the necessary steps:

  1. Add the typescript grunt task to your project
  2. Configure Lineman to load the grunt-typescript task and provide configuration for the task itself
  3. Add a TypeScript file to be compiled
  4. Add the TypeScript task to Lineman’s build/run processes
  5. Add typescript-generated javascript as a source for the concat task

Let’s start with a fresh Lineman project.

$ lineman new ts-test
$ cd ts-test

Add Dependency

Add the grunt-typescript NPM module as a dependency in your project by adding it to package.json.

"dependencies": {
  "lineman": "~0.7.1",
  "grunt-typescript": "0.1.6"
},

Running npm install should leave you with grunt-typescript and lineman under node_modules. At this point, the grunt-typescript task is available to Lineman, but isn’t being loaded, so Grunt isn’t aware of it. Verify this by running lineman grunt typescript. You should get:

Warning: Task "typescript" not found. Use --force to continue.
Aborted due to warnings.

Load the Task

Configure Lineman to load the grunt-typescript NPM module by adding it to the loadNpmTasks array in config/application.js. Lineman will invoke Grunt’s loadNpmTasks method on each task in this array. We must also provide the task with its associated config block. This should be the same configuration block that would normally be added to Gruntfile.js.

loadNpmTasks: ['grunt-typescript'],

typescript: {
  compile: {
    src: 'app/js/**/*.ts',
    dest: 'generated/js/app.ts.js'
  }
}

Now running lineman grunt typescript should give you:

Running "configure" task

Running "typescript:compile" (typescript) task
js: 0 files, map: 0 files, declaration: 0 files

Done, without errors.

Add some TypeScript

Create a new file app/js/greeter.ts. This will be the TypeScript source that is compiled into JavaScript. If you remember our typescript config block specifies the source as ‘app/js/**/*.ts’, so any filename with a ‘.ts’ extension (within any subdirectory) will be compiled into the same JavaScript file. (For more info, refer to grunt-typescript‘s configuration.)

function Greeter(greeting: string) {
  this.greeting = greeting;
}

Now running lineman grunt typescript should give you:

Running "configure" task

Running "typescript:compile" (typescript) task
File ts/generated/js/app.ts.js created.
js: 1 file, map: 0 files, declaration: 0 files

Done, without errors.

Now we’ve got the task working independently from the Lineman build process. For any arbitrary grunt task, this would be sufficient; simply run lineman grunt . However, if we run lineman build, the typescript task will not be run. Let’s add it to the Lineman build process.

Compile during Lineman build

You can add grunt tasks to either the beginning or the end of the Lineman build process using prependTasks or appendTasks, respectively. Since we want the generated JavaScript that the TypeScript compiler emits to also be concatenated and minified with the rest of the app, we need the typescript task to run before the other Lineman tasks. We also want this task to run in both dev and dist phases, so we’ll add the typescript task to prependTasks:common array in config/application.js.

prependTasks: {
  common: ['typescript']
},

Now we’ve got the task running as part of the Lineman build process. However, the typescript-generated javascript is not being concatenated/minified with the rest of our application. We need to add the typescript-generated javascript to the concat task’s source list.

Concatenate/Minify

There are a number of different ways to do this. One of which, is to simply modify the concat-task configuration block in config/application.js.

// save off the current config object instead of directly exposing it via module.exports
var config = require(process.env['LINEMAN_MAIN']).config.extend('application', {
... // this remains untouched
}

// add the typescript-generated js as a concat source
config.concat.js.src.push('generated/js/app.ts.js');

// export the modified config
module.exports = config;

Now when we run lineman build our Greeter function is compiled from TypeScript into JavaScript, concatenated along with the rest of the application JavaScript (‘generated/js/app.js’) and minified (‘dist/js/app.min.js’)!