Grunt, Coffeescript, Sass, and Haml Project

This post builds off of code that was created in the previous post. The complete source code for this blog post is available on github with accompanying demo.

In the last post we created a Gruntfile that ran a web server for developing web applications. That was just the first building block in our rocking web development setup. Now we’re going to introduce modern tools into the development setup. Javascript, CSS, and HTML are fine but over the past 5 years there has been some real innovation in “front-end” web development. CoffeeScript, Sass, and HAML are three excellent technologies that will increase your developer happiness.

If you aren’t familiar with these languages you may be thinking of them only as syntactic sugar. Well, you better sit down, not only do you get syntactic sugar, you get all sorts of other good stuff. CoffeeScript (for loop syntax, trailing conditionals, the “existential” operator (question mark – ?), switch statements, variable hoisting) brings so much to the table. Perhaps the best thing about CoffeeScript is that it actually has the power to make Javascript fun to write… crazy, right?

Sass (mixins, variables, nesting, partials) is almost a must-have for CSS development. Sass has a couple of competitors that are supposed to be quite good; like Less or SCSS. But for our purposes Sass rocks. Haml is primarily syntactic sugar, but it is so sugary delicious that you’ll forget how you wore the < and > of your keyboard.

Both Sass and Haml require Ruby, version greater than or equal to 1.9.1. Type ruby -v from the command line to determine what version of Ruby you have. If you need to upgrade Ruby (ruby -v returned a version less than 1.9.1) then upgrade all the way to 2.1.

Once you have Ruby installed you have to install the sass and haml gems.

gem install sass
gem install haml

Also CoffeeScript requires that the coffee-script compiler is installed npm install -g coffee-script and you’re ready to roll

Let’s get started

First we’re going to change the directory structure to match the convention of more modern web apps. Setup a structure like this:

├── Gruntfile.js
├── README.md
├── app
│   ├── coffeescript
│   └── stylesheets
├── index.html
├── node_modules
├── package.json
└── public

The coffeescript files will go in app/coffeescript and the sass files will go in app/stylesheets. The compiled files will end up the public directory.

Next add the dependencies to your package.json. We’ll start with CoffeeScript. There are different packages for compiling CoffeeScript with Grunt, I like the grunt-contrib projects so we’ll use grunt-contrib-coffee. Add grunt-contrib-clean and grunt-contrib-coffee to your package.json. grunt-contrib-clean is a nice task for cleaning out your assets before building.

package.json
{
"name": "my-first-grunt-app",
"version": "0.1.0",
"repository": {
"type": "git",
"url": "https://github.com/hoitomt/grunt-setup"
},
"dependencies": {},
"devDependencies": {
"grunt": "~0.4.2",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-connect": "~0.6.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-coffee": "~0.9.0"
}
}

Run npm install and update Gruntfile.js with the coffee task and watcher:

// Gruntfile.js
module.exports = function(grunt){
grunt.initConfig({
connect: {
server: {
options: {
base: './',
port: '4000',
host: '*'
}
}
},
clean: {
build: {
src: ['public/javascripts', 'public/stylesheets']
}
},
coffee: {
compile: {
options: {
bare: true
},
files: [{
expand: true,
cwd: 'app/coffeescript',
src: ['**/*.coffee', '**/*.js'],
dest: 'public/javascripts',
ext: '.js'
}]
}
},
watch: {
js: {
files: ['app/coffeescript/**/*.coffee'],
tasks: ['coffee']
}
}
});

grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-coffee');

grunt.registerTask('build', ['clean', 'coffee']);
grunt.registerTask('default', ['build', 'connect', 'watch']);
};

This is the entire Gruntfile.js. It’s a lot to look at but I wanted to point out a few things. First a clean task and a coffee task have been added. The clean task will remove all files from the specified directories (public/javascripts and public/stylesheets). The coffee task will compile all files with a .coffee extension in the app/coffeescript directory. It will put the compiled files into the dest directory with the ext extension, in this case it is public/javascripts with the .js extension.

Now create a coffeescript file to make sure everything is working

# app/new_file.coffee
alert 'Starbucks!'

Add your compiled javascript file (soon to be created) to your index.html

<!-- index.html -->


Grunt Setup
<script src="public/javascripts/new_file.js"></script>
<h1>Grunt it</h1>

Run the project now with the grunt command: grunt. You should see some output like the following in your terminal:

Running "clean:build" (clean) task
Cleaning public/javascripts...OK

Running "coffee:compile" (coffee) task

Running "connect:server" (connect) task
Started connect web server on http://localhost:4000

Running "watch" task
Waiting...

When you run grunt, a few things will happen:

  1. The web server starts on port 4000. When you visit the site an alert should display with “Starbucks!”
  2. The coffeescript file will be compiled as a javascript file into public/javascripts
  3. The coffeescript file is being watched for changes. Change the coffeescript file to alert "Dunkin Donuts!". Notice that your terminal log will display a message that your coffee file has been compiled. Reload localhost:4000 and the alert message should be changed to “Dunkin Donuts!”

Pretty cool, right? That’s the general idea with Grunt. Most of the grunt configuration is the same across libraries. Let’s add Sass support next.

package.json
"grunt-contrib-sass": "~0.7.1"

Install the dependencies npm install and update Gruntfile.js

// Gruntfile.js
...
sass: {
dist: {
files: [{
expand: true,
cwd: 'app/stylesheets',
src: ['**/*.scss'],
dest: 'public/stylesheets',
ext: '.css'
}]
}
},
...
styles: {
files: ['app/stylesheets/**/*.scss'],
tasks: ['sass']
}
...
grunt.loadNpmTasks('grunt-contrib-sass');
...
grunt.registerTask('build', ['clean', 'coffee', 'sass']);

Add a test style.scss

style.scss
p {
font-size: 12px;
}
#test {
p {
font-size: 16px;
}
}

Run the grunt command and your stylesheet will compile into public/stylesheets/style.css.

Let’s add haml now. This will require a reorganization of the project. Create an app/views directory to store your haml files. Instead of updating package.json directly, let’s use the npm shortcut for installing and updating package.json at the same time npm install grunt-contrib-haml --save-dev. Notice that grunt-contrib-haml has been added to package.json, very cool!

Now for the Gruntfile.js configuration. This looks very similar to the CoffeeScript configuration and the Sass configuration, that’s by design in Grunt. NOTE the change to the connect options, we want to look in the public directory now, not the root directory

// Gruntfile.js
...
connect: {
server: {
options: {
base: './public',
port: '4000',
host: '*'
}
}
},
...
haml: {
dist: {
files: [{
expand: true,
cwd: 'app/views',
src: ['**/*.haml'],
dest: 'public',
ext: '.html'
}]
}
},
...
haml: {
files: ['app/views/**/*.haml'],
tasks: ['haml']
}
...
grunt.loadNpmTasks('grunt-contrib-haml');
...
grunt.registerTask('build', ['clean', 'coffee', 'sass', 'haml']);
...

Create the index.haml file

# app/views/index.haml
!!! 5
%html
%head
%title= "Grunt Setup"
%link{ :href => "/stylesheets/style.css", :media => "screen", :rel => "stylesheet" }
%script{ :src => "/javascripts/new_file.js" }
%body
%h1 Grunt it Haml Style!

You can delete index.html in your root directory. Once you run grunt there will be an index.html compiled from app/views/index.haml file into the public directory.

Go ahead and fire it up with grunt. If all went according to plan you should see the index.html file in the public directory along with the coffeescript and sass files compiled in the public/javascripts and public/stylesheets directories respectively. Make sure to check the site at http://localhost:4000. Was that totally awesome or what!?

All of the source code for this project is in https://github.com/hoitomt/grunt-watchers and the app is running as a GitHub page at http://hoitomt.github.io/grunt-watchers/public/