I'm not really crazy about Yeoman's grunt-usemin : I find painful the way it enforces a unique pipeline, with its preliminary useminPrepare
task and :generated
targets.
But on the project I'm working on, we made the choice to use it early on, and we're sticking with it for now. And this time we wanted to add subresource integrity (SRI) checks to our project. Here is a quick and easy way to do so, assuming you already use grunt-usemin
.
This was tested with NodeJs version 0.12.2 (versions inferior to 0.12 won't have execSync
), grunt
version 0.4.5 and grunt-usemin
version 3.1.1. We also use load-grunt-config
(v0.19.0), so the following is the exact content of grunt/usemin.js
:
var execSync = require('child_process').execSync;
var compute_sri_hash = function (filename, algo) {
algo = algo || 'sha256';
return algo + '-' + execSync('openssl dgst -' + algo + ' -binary ' + filename
+ ' | openssl enc -base64 -A');
};
module.exports = function (grunt) {
return {
html: ['grunt-target/index.html'],
options: {
assetsDirs: ['grunt-target'],
blockReplacements: {
css: function (block) {
var media = block.media ? ' media="' + block.media + '"' : '';
grunt.log.writeln('Generating SRI hash for ' + block.dest);
var sri_hash = compute_sri_hash('grunt-target/' + block.dest);
return '<link rel="stylesheet" href="' + block.dest + '"' + media
+ ' integrity="' + sri_hash + '" crossorigin="anonymous">';
},
js: function (block) {
var defer = block.defer ? 'defer ' : '';
var async = block.async ? 'async ' : '';
grunt.log.writeln('Generating SRI hash for ' + block.dest);
var sri_hash = compute_sri_hash('grunt-target/' + block.dest);
return '<script ' + defer + async + 'src="' + block.dest + '"'
+ ' integrity="' + sri_hash + '"'
+ ' crossorigin="anonymous"><\/script>';
},
nominjs: function (block) {
var defer = block.defer ? 'defer ' : '';
var async = block.async ? 'async ' : '';
grunt.log.writeln('Generating SRI hash for ' + block.dest);
var sri_hash = compute_sri_hash('grunt-target/' + block.dest);
return '<script ' + defer + async + 'src="' + block.dest + '"'
+ ' integrity="' + sri_hash + '"'
+ ' crossorigin="anonymous"><\/script>';
}
}
}
};
};
With load-grunt-tasks
, module.exports
can be either an object hash or a function. I use the later form here to gain access to grunt.log
.
Then I redefine grunt-usemin
default block replacements so that they include the SRI hash.
Note that I also define a nominjs
target : this is a useful trick so that I can define different usemin
flow steps for JS files I don't want to uglify
. I then use it like this in my index.html
:
<!-- build:nominjs compile/scripts/app.js -->
<script src="../../grunt-target/scripts/app.js"></script>
<script src="../../grunt-target/scripts/ngtemplates.js"></script>
<!-- endbuild -->
In thgis example, grunt-target/scripts/app.js
& grunt-target/scripts/ngtemplates.js
have been generated by an earlier grunt
target when the usemin
piepline kicks in.
That's it !