Skip to content

Instantly share code, notes, and snippets.

@vcarel
Last active December 5, 2016 15:35
Show Gist options
  • Save vcarel/9110294 to your computer and use it in GitHub Desktop.
Save vcarel/9110294 to your computer and use it in GitHub Desktop.
Bootstrap for static websites with Grunt and Webmake (featuring Stylus, cssmin, htmlmin, jshint...)
{
"curly": true,
"eqeqeq": true,
"immed": true,
"latedef": false,
"newcap": true,
"noarg": true,
"sub": true,
"undef": true,
"unused": true,
"boss": true,
"eqnull": true,
"node": true,
"globals": {"window": false, "document": false}
}

Getting started

As a prerequisite, the latest version of node.js must be installed. For Ubuntu users, the following link may help : http://doc.ubuntu-fr.org/nodejs.

Then, from the project's root folder:

npm install
sudo npm install -g grunt-cli
grunt

That's Grunt which builds the project's distributables. It is configured to work in 3 steps :

  1. compile : generates HTML, JS and CSS files and copy the images into build/compile
  2. minify : minifies / uglifies everything into build/minify
  3. dist : creates a zip archive into the dist folder

By default, Grunt executes the minify task.

While developping, grunt watch will re-execute any needed task on the fly when the source code is modified.

Need real sources to start from ?

Here is an example : https://github.com/vcarel/client-side-app-example

Have fun !

Under the hood

There's no HTML. Instead, there's a main.mustache which is rendered as main.html.

There no CSS as well. We use the Stylus preprocessor to make them.

There are JS files, but they are organised as modules. And, at the end, there's only 1 JS file in the bundle. That's the job of Webmake to assemble those. Its syntax is pretty close to node.js, so it should be of no surprise.

How to make a release

Step 1. Ensure that all the code is committed and actually works.

Step 2. Remove "-SNAPSHOT" from the version number in the package.json file. e.g.:

"version": "1.0.0-SNAPSHOT"

becomes

"version": "1.0.0"

Step 3. Commit the change with a message like version: 1.0.0 and create a tag with the same version number. e.g.:

git tag 1.0.0

That's a good time to make the distributable archive :

grunt dist

Step 4. Increase the version number and put the "-SNAPSHOT" back again. Commit the result.

Step 5. Push the changes along with the tags :

git push --tags
module.exports = function (grunt) {
var pkg = grunt.file.readJSON('package.json');
var dirs = {
compile: 'build/compile',
minify: 'build/minify',
ressource: 'ressource_' + pkg.version,
dist: 'dist'
};
grunt.initConfig({
dirs: dirs,
jshint: {
gruntfile: {
options: {
jshintrc: '.jshintrc'
},
src: 'Gruntfile.js'
},
js: {
options: {
jshintrc: 'src/js/.jshintrc'
},
src: 'src/js/**/*.js'
}
},
clean: {
compile: dirs.compile,
minify: dirs.minify
},
watch: {
options: {
interrupt: true,
atBegin: true
},
gruntfile: {
files: 'Gruntfile.js',
tasks: ['jshint:gruntfile']
},
js: {
files: 'src/js/**',
tasks: ['jshint:js', 'webmake', 'uglify']
},
stylus: {
files: 'src/styl/**',
tasks: ['stylus', 'cssmin']
},
mustache_render: {
files: 'src/*.mustache',
tasks: ['mustache_render', 'htmlmin']
},
copy: {
files: 'src/img/**',
tasks: ['copy']
}
},
webmake: {
compile: {
files: {
'<%= dirs.compile %>/<%= dirs.ressource %>/main.js': 'src/js/main.js'
}
}
},
stylus: {
compile: {
options: {
paths: ['src/styl'],
urlfunc: 'embedurl',
'include css': true
},
files: {
'<%= dirs.compile %>/<%= dirs.ressource %>/css/main.css': 'src/styl/main.styl'
}
}
},
mustache_render: {
compile: {
files: [{
data: {
css_dir: dirs.ressource + '/css',
img_dir: dirs.ressource + '/img',
js_dir: dirs.ressource
},
template: 'src/main.mustache',
dest: dirs.compile + '/main.html'
}]
}
},
uglify: {
minify: {
files: {
'<%= dirs.minify %>/<%= dirs.ressource %>/main.js': dirs.compile + '/' + dirs.ressource + '/main.js'
}
}
},
cssmin: {
minify: {
expand: false,
src: dirs.compile + '/' + dirs.ressource + '/css/main.css',
dest: dirs.minify + '/' + dirs.ressource + '/css/main.css'
}
},
htmlmin: {
minify: {
options: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
},
files: {
'<%= dirs.minify %>/main.html': dirs.compile + '/main.html',
}
}
},
copy: {
compile: {
files: [{
expand: true,
cwd: 'src',
src: 'img/**',
dest: dirs.compile + '/' + dirs.ressource
}]
},
minify: {
expand: true,
cwd: dirs.compile + '/' + dirs.ressource,
src: 'img/**',
dest: dirs.minify + '/' + dirs.ressource
}
},
compress: {
dist: {
options: {
archive: dirs.dist + '/' + pkg.name + '_' + pkg.version + '.zip'
},
files: [{
expand: true,
cwd: dirs.minify,
src: '**'
}]
}
}
});
// All
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-watch');
// Compile
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-webmake');
grunt.loadNpmTasks('grunt-contrib-stylus');
grunt.loadNpmTasks('grunt-mustache-render');
// Minify
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-contrib-copy');
// Dist
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.registerTask('compile', ['clean:compile', 'jshint:js', 'webmake', 'stylus', 'mustache_render', 'copy:compile']);
grunt.registerTask('minify', ['compile', 'clean:minify', 'uglify', 'cssmin', 'htmlmin', 'copy:minify']);
grunt.registerTask('dist', ['minify', 'compress:dist']);
grunt.registerTask('default', ['jshint:gruntfile', 'minify']);
};
console.log('JS bundle loaded');
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
<link href="{{{ css_dir }}}/main.css" media="all" rel="stylesheet" type="text/css"/>
</head>
<body>
<div class='flex-center'>
<h1>Rock'n roll</h1>
<h2>
CSS target location : {{{ css_dir }}}
<br>Images target location : {{{ img_dir }}}
<br>Javascript target location : {{{ js_dir }}}
</h2>
</div>
<script type="text/javascript" charset="UTF-8" src="{{{ js_dir }}}/main.js"></script>
</body>
</html>
{
"name": "my_app",
"version": "0.1.0-SNAPSHOT",
"engines": {
"node": ">= 0.8.0"
},
"scripts": {
"test": "grunt jshint"
},
"devDependencies": {
"grunt-contrib-jshint": ">= 0.8.0",
"grunt-contrib-watch": ">= 0.5.3",
"grunt-contrib-clean": ">= 0.4.0",
"grunt-contrib-stylus": ">=0.12.0",
"grunt-mustache-render": ">=1.2.3",
"grunt-contrib-htmlmin": ">=0.1.3",
"grunt-contrib-cssmin": ">=0.7.0",
"grunt-contrib-copy": ">=0.5.0",
"grunt-contrib-compress": ">=0.6.0",
"grunt-webmake": ">=0.1.2",
"grunt-contrib-uglify": ">=0.3.2",
"grunt": ">= 0.4.0"
}
}
@jpcaruana
Copy link

great !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment