GRUNT!
I have seen the future of javascript builds, and it is
Grunt. Please,
please,
please disregard my
earlier post on building with maven; it just doesn't make sense to use such a heavyweight Java-based tool for building javascript projects.
Grunt is rightfully taking the Javascript world by storm. It's got a great community around it, as shown by the wealth of plugins available. It runs on
Node, allows for package management via
npm, and lets you start working with Javascript in a way which makes us stodgy old enterprise Java guys breathe slightly easier ;-) (more posts on that soon!).
Steal Builds, with Grunt
So naturally I'd like to share how I've gotten steal builds to work with my gruntfile. There is a grunt-steal plugin, but it hasn't yet been updated for grunt 0.4, and steal builds don't run on node anyways. (but, excitingly enough, I've heard rumors that steal will run in node sooner rather than later!)
Here are the npm packages I'm using for my steal build:
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-shell');
grunt.loadNpmTasks('grunt-exists');
My approach is to copy all of the source files into a target/ folder (ala maven!) so I can apply the package version where appropriate, and build the production.{css | js} files outside of the src dir.
Here's where I copy the source files. Note that I'm only including HTML in the phase where I am replacing the project.version and steal environment variables. As with maven, if you filter binary files it will corrupt them.
copy: {
build: {
files: {
'target/': ['src/**/*.html']
},
options: {
processContent: function (content) {
return content.replace(/env\s*: 'development', \/\/ live/g, "env: 'production',").replace(/\$\{project\.version\}/g, grunt.template.process('<%= pkg.version %>'));
}
}
},
build_other: {
files: {
'target/': ['src/**/*', 'src/**/.*', '!src/**/*.html']
}
}
},
Here's my corresponding HTML snippets which get replaced:
<head>
<link href="app/production-${project.version}.css" rel="stylesheet">
</head>
<body>
<script>
(function() {
steal = {
env: 'development', // live
startFile: 'app',
executed: ['app/production.css'],
production: 'app/production-${project.version}.js'
};
}());
</script>
<script src="steal/steal.js"></script>
</body>
Note also in the build_other phase, that I'm explicitly including src/**/* and src/**/.*, so that my .htaccess file comes along for the ride.
Next, I'm using the shell plugin to run the steal build, the same way as you would manually from the command line. First, I need to make the js script executable, then I run it using the target dir as the base working dir.
shell: {
chmodjs: {
command: 'chmod 755 target/src/js'
},
steal_app: {
command: './js app/scripts/build.js',
options: {
stdout: true,
stderr: true,
failOnError: true,
execOptions: {
cwd: 'target/src'
}
}
}
And voila! You now have a target/src dir with a deployable web application. My last step is to verify that our production files were built successfully.
exists: {
prod_files: [
'target/src/app/production.js',
'target/src/app/production.css',
]
}
Also note that I haven't physically changed the name of the production.js file to production-{project.version}.js. I'm using a neat .htaccess trick from
html5boilerplate to do my filename fingerprinting in apache. This works because I've set no-cache for *.html.
<IfModule mod_rewrite.c>
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)\-\d+\.\d+\.\d+\.(js|css|png|jpg|gif)$ $1.$2 [L]
</IfModule>
I'm going to go into more detail on integrating steal with
jasmine tests, and exploring other grunt goodies, in (near!) future posts.