August 14, 2015
Speed up your Less build with Gulp
Problem
Earlier this year, we started to port a customer website from a old css-style to a more modern bootstrap-style.
To be more flexible, we switched from plain css to less (as a css-precompiler) and automated the build with grunt.
The build was very basic and just compiled the less files into css files, we didn’t use sourcemaps or minification and still the complete build takes ~11s and the watch task about 4s. This is a long time, if you only want to change some styles.
Another problem in development is, without sourcemaps for your css/less-files you always need to search for the correct less file and we got a lot of them.
Idea
- Switch the build system to gulp, because we only need to compile less
- Add sourcemaps for less at development time, so you can see the correct less file in your browsers dev-tools
- Speed up the build, by using caching in gulp
Gulp and less
We used following npm-plugins for the basic build:
gulp
gulp-utils
gulp-less
gulp-sourcemaps
del
Hint: install them via npm install -save-dev {plugin-name}
The basic build (gulpfile.js) looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// load dependencies var gulp = require('gulp'); var util = require('gulp-util'); var sourceMaps = require('gulp-sourcemaps'); var less = require('gulp-less'); var del = require('del'); // define paths-variables var paths = { cssBootstrap: 'css_generated/', lessFiles: ['less/main_bootstrap.less', 'less/main_legacy.less'] }; //define tasks gulp.task('default', ['clean', 'less', 'watch'], function () { return util.log('Gulp is running!'); }); gulp.task('watch', function () { // watch all less files for a change and than call the clean and less task gulp.watch('less/**/*.less', ['clean', 'less']); }); gulp.task('less', function () { util.log('Compile less ...'); return compileLess(); }); gulp.task('clean', function (cb) { util.log('Delete old less ...'); del([paths.cssBootstrap + '**/*'], cb); }); // define task-function function compileLess() { var s = gulp.src(paths.lessFiles); s = s.pipe(sourceMaps.init()); s = s.pipe(less()); s = s.pipe(sourceMaps.write('maps/')); return s.pipe(gulp.dest(paths.cssBootstrap)); } |
The magic happens in the compileLess()
-Method, we initialize the sourcemap, compile the less to css and save the sourcemap and the css files.
After running gulp
it takes about ~11s on my machine, without the sourcemaps ~4s.
The watch tasks takes about ~10s, that’s more than 2 times slower than grunt, but we got sourcemaps now.
Speed Up
Fortunately gulp is stream based and some genius people helped us to save a lot of time with their caching plugins.
So we added the following plugins to the build:
gulp-cached
gulp-remember
Just install them via npm and import them into your gulpfile.js
1 2 |
var cache = require('gulp-cached'); var remember = require('gulp-remember'); |
Edit the compileLess()
-Method as follows:
1 2 3 4 5 6 7 8 9 |
function compileLess() { var s = gulp.src(paths.lessFiles); s = s.pipe(sourceMaps.init()); s = s.pipe(cache('less')); s = s.pipe(less()); s = s.pipe(remember('less')); s = s.pipe(sourceMaps.write('maps/')); return s.pipe(gulp.dest(paths.cssBootstrap)); } |
The cache('less')
filters all files out of the stream, which have not changed since the last call. ‘less’ is the given name for the cache. The remember('less')
adds all previously cached files back to the stream.
The first gulp
-call still takes about the same time (~11s), but when we change a random less file, it takes less than 3s to compile the css.
Hint:
- If you don’t want to run into invalid caching values at branche changes in your SCM (git, svn, …), then use this clear-Task from the gulp-cache-Authors
Attention:
- If you use a clean task, to delete all files in the output folder and you miss the
remember('less')
task, then only the changed files are compiled and written to the output folder - If you use the
@import
-statement in less and there are changes in the imported file, the cache prohibits a new build of the parent-file in the watch task (because there are no changes in the parent-file)
Conclusion
We now have a clean less build with sourcemaps for development purposes. It works as fast as grunt with the plus of sourcemaps, so we save time at development. The build is easy to understand and maintainable, but maybe because it’s only for one purpose.
The biggest issue we see, is the caching problem with less @import
statements, a solution could be:
Build all less files within the gulp task, then concat them to one css file and add the sourcemaps.
good post
This saved me so much development time. Thank you!