After I set up Medium, the next thing I did was start writing code and unit tests. And I will write about unit tests in a couple of posts, but I want to jump ahead one stage, to a build system, because that was something that required workarounds almost from the beginning and turns out to be easy to set up if you know how.
Because, of course, if you’re using CoffeeScript and SCSS, you need a preprocessing stage to turn them into something that a browser is happy with. If you have a single CoffeeScript source file, then running the coffee
command is not too crazy, but what if you have multiple source files? You don’t want to run coffee
on each of them individually, and you don’t want to have to load each of the outputs individually into your HTML file (or at least I don’t!). The coffee
command actually has a --join
argument to handle this, so you can certainly work around this manually, but this is definitely getting to the stage where a C programmer would say “I would have written a short Makefile by now”.
In JavaScript land, though, you probably don’t want to use Make; there are various options for build tools, and the one I chose (which seems to be the most common?) is Grunt. To get started with it, you actually want to install the grunt-cli
package globally instead of putting it in your package.json
file:
npm install -g grunt-cli
This makes the grunt
command available, but the smarts are all in the grunt
package plus whatever plugins you use. Those you install via npm install --save-dev
; a good place to start is
npm install --save-dev grunt grunt-contrib-coffee grunt-contrib-sass
Grunt’s configuration file isn’t in some custom language, it uses an internal JavaScript DSL for configuration. And you can configure it in CoffeeScript, too, which is of course what I did. So here’s a basic Gruntfile.coffee
:
module.exports = (grunt) -> grunt.initConfig { pkg: grunt.file.readJSON('package.json') coffee: compile: files: 'js/medium.js': 'coffee/*.coffee' options: join: true sass: dist: files: 'css/medium.css': 'scss/medium.scss' } grunt.loadNpmTasks('grunt-contrib-coffee') grunt.loadNpmTasks('grunt-contrib-sass') grunt.registerTask('default', ['coffee', 'sass'])
Pretty self-explanatory. (I have a bunch of CoffeeScript source files but only one SCSS file; eventually I may have multiple SCSS files, but even then I should be able to use includes to get a single entry point.) And, with that in place, I just type grunt
and it builds medium.js
and medium.css
.
Of course, it does raise the question of how all those CoffeeScript files get combined into a single JavaScript file and what to do if you want to have control over that combining; I’ll explain that in my next post. But for now, this works as long as there aren’t load-time dependencies between your CoffeeScript files, and it outputs a single JavaScript file to load from your HTML.
I actually prefer not to have to manually type grunt
each time I want to rebuild: I like to have Grunt watch for changes and build things every time I save. To get this to work, install the grunt-contrib-watch
package and add a block like this to the initConfig
section of Gruntfile.coffee
:
watch: coffee: files: 'coffee/*.coffee' tasks: ['coffee'] options: spawn: false sass: files: 'scss/*.scss' tasks: ['sass'] options: spawn: false
Also, make sure to add grunt-contrib-watch
in the loadNpmTasks
section. If you do this, then you can type grunt watch
in one of your shell windows and it will rebuild whenever the appropriate files change. And yeah, it’s a bit unfortunate that you have to specify the file globs twice, but only a bit; if that really bothers you, I guess save those file globs in variables? (We are, after all, writing in a real programming language here.)
There’s one further change that
Post Revisions:
This post has not been revised since publication.
[…] one problem with the way I first set up my build system for Medium: I had no control over how the CoffeeScript files were ordered. In languages with […]
6/10/2014 @ 9:41 pm