grill

Grill

Grill

Grill is Node.js environment for convenient development of static front-end projects be that a RICH application or a statically generated blog. It is based on Grunt, Mincer and hope.

Grill sits in the middle between Lineman and Wintersmith. It's less opinionated and (much) more flexible than both of them. And that's exactly why it expects you to be more aware of community best-practices on your own. In fact you can think about Grill as if it was a Grunt plugin.

It can do this for you:

  • Enhance your JS and CSS assets with dependencies and includes (like Sprockets)
  • Serve assets from local development server
  • Serve greedy URLs suitable for HTML5 routing
  • Proxy requests to solve Cross-Origin issues
  • Stub backend API
  • Statically build the result
  • Prepare and deploy

And it has shortcuts for everything-else-based-on-Grunt. I.e. for tests we recommend the usage of grunt-contrib-testem that plugs into the system as-is.

Grill was developed as a building environment for Joosy Framework standalone mode. We wanted to have an ability to share the dependency system between the framework core and project utilizing it. As the result we ended up using Grill to compile even Joosy itself. But you can use it for any framework and any project since Mincer approach is really the flexible and the powerful one.

  • Grill is based on Mincer

    • Out of box it supports: CoffeeScript, Coco, JST, ECO, EJS, HAML, Jade, Less, Stylus, Sass. And it has powerful standardized API that you can easily extend with anything. Totally anything

    • Forget about laggy-buggy watchers for the development mode. Instead we serve stuff using Express.js middle-wares with on-the-fly compilation and intelligent caching

    • It mimics Sprockets. Not only it gives you incredibly powerful #= require preprocessing but also opens the door for people coming from Rails world (and be honest, Sprockets IS awesome)

  • Grill is a toolbelt not a framework

    • Flexible routing – we don't really have much conventions on placement of files. Only a small rule that helps you to organize stuff based on its content type

    • Grill stays out of your way. We don't override / reimplement / hide things like Grunt, Bower and npm from you. Instead we add a pinch of spice to that mix. Use best-practices and things you are used to in conjuction with the new stuff Grill brings in

    • Because of this it's only about 200 LoC. Small things are easier to support so you can expect Grill to be more stable

$ mkdir dummy
$ cd dummy
$ mkdir -p app/images app/javascripts app/stylesheets app/haml public
$ npm init

Now you should install proper npm packages. The total list of them depends on file formats you are going to use. Grill does not depend directly on everything so you have to manually install libraries that are required to compile what you want. In this example we use HamlCoffee, Stylus (with nib) and Coffee-Script.

$ npm install grunt grill haml-coffee stylus nib coffee-script --save-dev

And here's the look of initial Grunt config:

module.exports = function(grunt) {
  grunt.loadNpmTasks('grill')
 
  grunt.initConfig({
    grill: {
      assets: {
        root: [ 'application.*' ]
      }
    }
  })
}

In Grill you have an application folder (typically app) that consists of a set of subdirectories holding different type of assets. Like this:

|- app
|--- images
|--- haml
|--- javascripts
|--- stylesheets

You can put anything having supported extension to any of them and as the result it will get to the "output" directory (typically public). During compilation the first level of folders will be striped. So having this file tree:

|- app
|--- images
|------ test.png
|------ subdirectory
|-------- another.png
|--- haml
|------ index.haml
|--- javascripts
|------ application.coffee

would result into the following output:

|- public
|---- test.png
|---- subdirectory
|------ another.png
|---- index.html
|---- application.js

During the compilation of stylesheets and javascripts, Grill compiles only the root files (configuration option assets.root). Everything else gets compiled file to file unless it matches skip mask (configuration option assets.skip).

So try to put some HAML into app/haml/index.haml, and run grunt grill:compile. Then try to run grunt grill:server – your HAML is now compiled on-request at http://localhost:4000.

And did you notice you used Grunt directly? Go unleash the power of anything with thousands of available Grunt plugins.

... :godmode:

Runs prouction-safe bower installer. Meant for the cases when you don't want to commit bower_components to your repo but want to run in during deployment instead.

Runs development web-server on port 4000.

Runs production web-server that serves static files from public compressing them on the fly.

Statically compiles your assets into public.

Runs grunt compile:development if Node.js production environment is active. Does nothing otherwise.

Cleans up public

You can pass global data to those kind of assets that can handle it (currently HAML and Stylus).

grill: {
  config: require('./config.json')
}

When used like that it globally defines @config variable within your assets. Additionaly Grill globally sets @environment variable. It's equal to 'development' when running from development server and 'production' during static build.

grill: {
  assets: {
    paths:  ['vendor/*'],       // Adds root-level inclusion paths 
    roots:  ['application.*']   // The list of first-level JS and CSS assets 
    skip:   ['_partials/**/*']  // List of files that should not be compiled statically 
    greedy: ['url/']            // List of URLs that will respond to `url/*` unless another asset was found 
  }
}

You can't make a web-site without partials. That's why Grill bundles this...

!= @partial 'path/to/asset', {foo: 'bar'}

...into .haml kind of assets so you can use it keep your layout DRY. Also you can use the following syntax to wrap part of your page into a layout:

!= @layout 'path/to/layout.haml', {foo: 'bar'}, =>
  #page
    .goes Here

And in the end you might want to exclude that partials and layouts from compilation by using skip: ['_partials/**/*'] at assets configuration section.

Say you want to make your application work with HTML5 pushState feature from the root url. Add greedy: ['/'] to your assets configuration section. It will make development server fallback to index of resource if no another asset was found. Note that other assets will keep working – the asset having straight URL match will respond first.

There are several valid syntaxes to make development server proxy something:

grill: {
  proxy: {
    '/path/from': 'http://host.com/path/to'
  }
  // or 
  proxy: {
    src: '/path/from',
    dest: 'http://host.com/path/to'
  }
  // or  
  proxy: [{
    src: '/path/from',
    dest: 'http://host.com/path/to'
  }]
}

Use anyone you like and make it proxy!

You can directly assign middlewares to the internal Express.js server:

grill: {
  middlewaresfunction(express) {
    // make express do anything for you 
  }
}

Using this you can easily organize any kind of stubs.

We use Grill to build website of Joosy, its Guides and even Joosy itself.

If you want to see Grill in action without all the hackery, you can give Joosy a try. Go to the guides section and follow the standalone mode branch.

Copyright 2013 Boris Staal

It is free software, and may be redistributed under the terms of MIT license.