Jekyll: Use Grunt instead of --watch option
I'm using Jekyll for building my site. When I write a new entry, I have executed jekyll --auto
. But I noticed that ruby consumes up to 25% of CPU on my quad core PC.
This problem is caused by directory_watcher
module. It executes File::Stat()
on all files under the given directory every one second! It works fine when the number of files is small, but as it grow up, it begins wasting our CPU.
For example, my site has over 600+ posts by which Jekyll generates 900 files. And more, there are 5000 files under .git
dir. Suprisingly, Jekyll ~0.12 watches all files under the current directory, which means that it execute stat to these 6,500 files every one second.
Jekyll 1.0's jekyll build --watch
doesn't watch .git
and _site
directory. Despite of such improvement, ruby still uses up to 10% of CPU.
So I decided to use Grunt instead of using --watch
(or --auto
) option.
Grunt config files
I'm use following versions:
- Jekyll 1.0.3
- Grunt 0.4.1
grunt-shell-spawn
plugingrunt-contrib-watch
plugin
At first, I used grunt-jekyll
instead of grunt-shell-spawn
, but grunt-jekyll
plugin doesn't show Jekyll's output until it exits.
See Getting started - Grunt to setup Grunt.
Here is my package.json
and Gruntfile.js
.
package.json
{
"name": "tech-ni",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-shell-spawn": "~0.2.4",
"grunt-contrib-watch": "~0.4.4"
}
}
Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
shell: {
jekyll: {
command: 'jekyll build',
options: {
async: false
}
}
},
watch: {
jekyll: {
files: ['_posts/**/*.md', '_layout/*.html', '_includes/*.html'],
tasks: ['shell:jekyll']
}
}
});
grunt.loadNpmTasks('grunt-shell-spawn');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['shell:jekyll']);
};
(If you're using Jekyll ~0.12, replace jekyll build
to jekyll
)
How to Use
By entering grunt watch
, it starts watching files. When some posts or HTML files are modified, grunt executes jekyll build
.
But CPU usage is not 0%. It turns out that grunt-contrib-watch
doesn't use native watch API -- fs.watch()
. It uses gaze
module which execute fs.statSync()
periodically.
Oh!! Nothing changes...
It seems that fs.watch()
had many problems such as not reporting filenames on mac and executing fs.stat()
became popular. Oh, hell!