Preface
Goal: Put the whole stuff together using grunt task runner.
Have you ever got tired of saving, and running render command, and then go back to text editor, and doing this over and over again? This is why we need automation.
pug
has built in tools to run this automation, and so does other tools.
But when in comes to work with many tools, this won’t be sufficient.
Imagine you have three tools, and you have to run,
three different tools, in three different terminal.
This would be exhausting.
As your project grown, you might need other tools,
you can utilize task runner such as gulp
or grunt
.
Source Examples
You can obtain source examples here:
Automation
Grunt is simply an automation.
Suppose that you want to build a website without framework,
nor SSG, nor fancy backend using Pug+Stylus+Coffeescript
stack.
grunt
can watch each component, and compile into vanilla.
-
Generate
HTML
fromPug
. -
Render
CSS
fromStylus
. -
Compile
Javascript
fromCoffeescript
In short, grunt
can watch the whole stack at once.
Downside
Grunt is an old tool.
The task runner
is belong to the past
for conservative web development.
Use bundler
for modern web development instead,
such as webpack
, roller
or parcel
.
Both task runner
and bundler
might takes some configuration effort.
If you desire vanilla, grunt
is still your choice.
Grunt
Tips: Do not get initimidated by long config, once you understans, you can reuse in other project.
Here I represent an automation example on how to build Gruntfile.js
.
Official Documentation
Install
$ npm install -g grunt
Since in this project, we are dealing with Pug+Stylus+Coffeescript
,
we need to install each contrib
.
We also need to the contrib-watch
.
You can also install locally per project, instead of global install.
$ npm install grunt --save-dev
$ npm install grunt-contrib-pug --save-dev
$ npm install grunt-contrib-stylus --save-dev
$ npm install grunt-contrib-coffee --save-dev
$ npm install grunt-contrib-watch --save-dev
Or in package.json
{
...
"devDependencies": {
"grunt": "^1.0.1",
"grunt-contrib-pug": "^2.0.0",
"grunt-contrib-stylus": "^1.2.0",
"grunt-contrib-coffee": "^2.1.0",
"grunt-contrib-watch": "^1.1.0"
}
}
And do $ npm install
as a starter before you begin your project.
Gruntfile.js
You also need to a configuration file named Gruntfile.js
.
$ touch Gruntfile.js
As an overview,
the grunt
configuration skeleton,
for our little project,
is shown as below:
module.exports = function(grunt) {
// configure the tasks
let config = {
// Pug
pug: {
...
},
// Stylus
stylus: {
...
},
// Coffeescript
coffee: {
...
},
// Watch Files
watch: {
pug: {
...
},
stylus: {
...
},
coffee: {
...
}
}
};
grunt.initConfig(config);
// load the tasks
...
// define the tasks
grunt.registerTask('default', [
'pug', 'stylus', 'coffee', 'watch'
] );
};
Open your favorite text editor and start writing configuration.
Pug Case
Consider our last alert.pug
.
We are going to compile it using grunt
.
One Pug to One HTML
Have a look at using this configuration below:
module.exports = function(grunt) {
// configure the tasks
let config = {
// Pug
pug: {
compile: {
options: {
pretty: true
},
files: {
'html/alert.html': 'pug/alert.pug' // 1:1 compile
}
}
}
};
grunt.initConfig(config);
// load the tasks
grunt.loadNpmTasks('grunt-contrib-pug');
// define the tasks
grunt.registerTask('default', [
'pug'
] );
};
With this configuration, we can run:
$ grunt pug
Or simply the use the default
task
$ grunt
This will compile pug/alert.pug
to pug/alert.html
.
Filters
How about pug/alert-include-source.pug
to pug/alert-include-source.html
?
Allright, this should be as simple as changing line below:
files: {
'html/alert-include-source.html': 'pug/alert-include-source.pug'
}
Let’s see what’s happened!
To solve the situation we just need to to add both filters:
- to render
stylus
and, - to compile
coffescript
.
options: {
pretty: true,
filters: {
'stylus': function(block) {
return require('stylus').render(block);
},
'coffee-script': function(block) {
return require('coffeescript').compile(block);
},
},
},
files: {
'html/alertinclude-source.html': 'pug/alert-include-source.pug'
}
Now the grunt
should running fine.
Compile Many Pug at Once
What good is automation if we cannot run many task at once?
Doing this is simply as changing files
configuration as below:
files: [ {
cwd: "pug",
src: "*.pug",
dest: "html",
expand: true,
ext: ".html"
} ]
This will compile pug/*.pug
into its representative in html/*.html
.
As you can see in figure above, now the grunt compile three files.
Watch
This the most automation task from grunt
.
The watch configuration is simple:
// Watch Files
watch: {
pug: {
files: ['pug/**/*'],
tasks: ['pug'],
options: {
interrupt: false,
spawn: false
}
}
To complete the code, is to simply alter the configuration as below:
module.exports = function(grunt) {
// configure the tasks
let config = {
// Pug
pug: {
...
},
// Watch Files
watch: {
...
}
};
grunt.initConfig(config);
// load the tasks
grunt.loadNpmTasks('grunt-contrib-pug');
grunt.loadNpmTasks('grunt-contrib-watch');
// define the tasks
grunt.registerTask('default', [
'pug', 'watch'
] );
};
Now the default task will run pug task
, then run watch task
.
Alter one, or two pug
files, to see the watch task
result.
Complete Case
As I said earlier, we use this stack Pug+Stylus+Coffeescript
.
So these stack should be completely covered by Gruntfile.js
configuration.
Stylus
The stylus part is simply one file style.styl
, so we just need this:
// Stylus
stylus: {
compile: {
options: {
compress: false
},
files: {
'css/style.css': 'css/style.styl' // 1:1 compile
}
}
},
Do not forget to watch
// Watch Files
watch: {
stylus: {
files: ['css/style.styl'],
tasks: ['stylus']
}
}
And load task.
grunt.loadNpmTasks('grunt-contrib-stylus');
Coffeescript
The coffeescript part is also simply one file script.coffee
.
// Coffeescript
coffee: {
compile: {
files: {
'js/script.js': 'js/script.coffee' // 1:1 compile
}
}
},
Also, do not forget to watch
// Watch Files
watch: {
coffee: {
files: ['js/script.coffee'],
tasks: ['coffee']
}
}
And also load task.
grunt.loadNpmTasks('grunt-contrib-coffee');
Complete Configuration
Finally we need to register all tasks.
// define the tasks
grunt.registerTask('default', [
'pug', 'stylus', 'coffee', 'watch'
] );
Summary
As a conclusion, the complete configuration code is shown as below:
module.exports = function(grunt) {
// configure the tasks
let config = {
// Pug
pug: {
compile: {
options: {
data: {
debug: false
},
pretty: true,
filters: {
'stylus': function(block) {
return require('stylus').render(block);
},
'coffee-script': function(block) {
return require('coffeescript').compile(block);
},
},
},
files: [ {
cwd: "pug",
src: "*.pug",
dest: "html",
expand: true,
ext: ".html"
} ]
}
},
// Stylus
stylus: {
compile: {
options: {
compress: false
},
files: {
'css/style.css': 'css/style.styl' // 1:1 compile
}
}
},
// Coffeescript
coffee: {
compile: {
files: {
'js/script.js': 'js/script.coffee' // 1:1 compile
}
}
},
// Watch Files
watch: {
pug: {
files: ['pug/**/*'],
tasks: ['pug'],
options: {
interrupt: false,
spawn: false
}
},
stylus: {
files: ['css/style.styl'],
tasks: ['stylus']
},
coffee: {
files: ['js/script.coffee'],
tasks: ['coffee']
}
}
};
grunt.initConfig(config);
// load the tasks
grunt.loadNpmTasks('grunt-contrib-pug');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-contrib-coffee');
grunt.loadNpmTasks('grunt-contrib-watch');
// define the tasks
grunt.registerTask('default', [
'pug', 'stylus', 'coffee', 'watch'
] );
};
What’s Next?
Consider continue reading [ Bundler - Webpack - Part One ].