In my previous blog I wrote about how we can use Gulp to compile SASS. This time it’s all about how we can use Gulp in Visual Studio to prepare files for a release by minifying and concatenating them. I’ve put up a project using the standard MVC 5 template for this series of posts here
- Part 1: Setting up Gulp with Visual Studio
- Part 2: Getting SASSy
- Part 3: Deployment: minification and concatenation
- Part 4: Code quality: tests and linting
- Part 5: Thoughts on Gulp
Small is beautiful
To increase the speed of a site, and to save bandwidth, we want to:
- Have as few requests for the smallest files possible
- Cache these files for as long as possible
We can achieve this by concatenating and minifying the files, then adding a hash of the files contents to the filename so they can be cached indefinitely.
There are a number of different ways of approaching minification and concatenation with Gulp, and a lot of different plugins and plugin combinations. The following is a style that has worked for me, you may need to adjust it to suit your own workflow.
Plugins
There are a number of plugins we will need:
- gulp-useref for concatenation
- gulp-uglify and gulp-minify-css for minifcation of JavaScript and CSS respectively
- gulp-rev and gulp-rev-replace for revisioning
- gulp-filter for filtering files to process them independently
Lets add these to the package.json:
{
"scripts": { "install": "echo Done" },
"devDependencies": {
"gulp": "~3.9.0",
"gulp-load-plugins": "~0.10.0",
"gulp-useref": "~1.3.0",
"gulp-uglify": "~1.2.0",
"gulp-minify-css": "~1.2.1",
"gulp-rev": "~5.1.0",
"gulp-rev-replace": "~0.4.2",
"gulp-filter": "~3.0.0"
}
}
Referencing scripts and CSS
Now we’ll want to add our CSS and JavaScript files to our pages. These are put in commented build blocks that let gulp-useref know where a section it should concatenate is. So, at the top of the page (here it’s the _Layout.cshtml page) we reference the CSS:
<!-- build:css /dist/site.css --> <link href="../Content/bootstrap.css" rel="stylesheet" /> <link href="../Content/Site.css" rel="stylesheet" /> <!-- endbuild -->
In the first comment, we define two things:
- The first part, “build:css” tells useref that this be a CSS block
- The second is the location of the concatenated file that will eventually be referenced (more on this later)
We do the same at the bottom with the JavaScript:
<!-- build:js /dist/libs.js --> <script src="../Scripts/Libraries/jquery.js"></script> <script src="../Scripts/Libraries/bootstrap.js"></script> <script src="../Scripts/Libraries/respond.js"></script> <!-- endbuild -->
We can have multiple build blocks of the same type (e.g. multiple JavaScript blocks) so we can have one for libraries (Angular, jQuery etc) which will change less frequently than the code we write.
The Gulp task
Now for the biggie – the actual task itself. I’ll put it here and go through it in more detail below:
gulp.task('minifyFilesForRelease', function () {
var cssFilter = plugins.filter('**/*.css', { restore: true });
var jsFilter = plugins.filter('**/*.js', { restore: true });
var assets = plugins.useref.assets();
gulp.src('./**/*.cshtml')
.pipe(assets)
//Process JavaScript
.pipe(jsFilter)
.pipe(plugins.uglify())
.pipe(plugins.rev())
.pipe(assets.restore())
.pipe(jsFilter.restore)
//Process CSS
.pipe(cssFilter)
.pipe(plugins.minifyCss({
keepSpecialComments: 0
}))
.pipe(plugins.rev())
.pipe(assets.restore())
.pipe(cssFilter.restore)
.pipe(plugins.useref())
.pipe(plugins.revReplace({
replaceInExtensions: ['.js', '.css', '.html', '.cshtml']
}))
.pipe(gulp.dest(function (data) {
return data.base;
}
));
});
Here’s a line by line breakdown of what’s happening:
- Line 1: Define the task
- Line 3-4: Setup some filters we will use later to separate out the CSS and JavaScript for individual processing
- Line 6: Reference the assets object of useref which will return the build blocks we setup earlier
- Line 7: Get all our cshtml files
- Line 8: Get all our build blocks in these files
- Line 11: Filter our build blocks to just the JavaScript
- Line 12: Minify the JavaScript
- Line 13: Revision the JavaScript
- Line 14-15: Remove the filters, so we have access to all our build blocks again
- Line 18: Filter our build blocks to just the CSS
- Line 19-21: Minify the CSS removing any and all comments
- Line 22: Revision CSS
- Line 23-24: Remove the filters again
- Line 26: Replace the commented, unprocessed build block in our cshtml files with the processed ones
- Line 27-29: Add the revision number to the processed files (note we need to include cshtml files here)
- Line 30-32: Replace the original cshtml files. We need to specify a destination here, and as we have views in multiple folders we can use this technique by Yiling to overwrite a file regardless of its location
Phew, there’s a lot there! If you run this task each build block in our cshtml files will be replaced with a single, minified, revisioned file.
Before:
After:
Running in Release mode
We only want to run this task when we’re putting together a release, whether that’s manual or (much better!) automated. To do this, add a PreBuildEvent that runs the Gulp task before a release build. Go to the project properties, then Build Events and add the following to “Pre-build event command line”:
if $(ConfigurationName) == Release ( cd $(ProjectDir) npm install gulp minifyFilesForRelease )
This will then run our minifying task only on a Release build.
Lastly, as we are adding the minified files to a “dist” folder that doesn’t exist during development, we’ll need to add this folder after each build. Open up the csproj for the web project, and at the very end add the following:
<Target Name="AfterBuild">
<ItemGroup>
<Content Include="dist\*.*" />
</ItemGroup>
</Target>
This will include the contents of our dist folder after each build.
Wrapping up
We now have an automated process for minifying and concenating files that doesn’t intefere with the development process. The commit for everything is here.
Happy Gulping!




Commenting Area