A Grunt quick start with a GruntFile boilerplate
This Grunt quickstart contains the following features:
- Sass to CSS transpiling (*.scss)
- CSS compatibility optimizations and minifing
- JS concating of files and folders
- JS mangling and minifing
- Image optimizations
- Automatic file change watchers
- JS: ES5.1 support, but the GruntFile.js is written in ES6
- Sass: node-sass usage, a lot faster then Ruby Sass implementation
- (sassdoc HTML Generator)
- (Google PageSpeed analyse within shell)
Prepare System
Install NodeJS, it comes bundled with the package manager npm
.
If you use packages from packagist you also need composer, most frontend libraries are available from bower or other package systems. Bower is available through npm and will be installed in the next step.
In the boilerplate are variables for setting the path to the bower and composer path, relative from the GruntFile.js folder. You could use them for writing less code and when you have a few frameworks you need to work with, with different positions of all folders, you could simplify that with adding more custom folders like path_bower
and path_bower
.
Install Packages
Now open your shell and install the globally needed packages with npm:
npm i -g node-sass
npm i -g sassdoc
npm i -g grunt-cli
npm i -g bower
cd
to your project folder and execute:
npm init
for initializing npm in your project, this will create a package.json
, add the next code block to devDependencies
.
If you don't want to publish the project in npm at the moment, you could also just create the package.json
and add that content:
{
"devDependencies": {
"autoprefixer": "^7.1.1",
"grunt": "^1.0.1",
"grunt-concurrent": "^2.3.1",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-cssmin": "^2.0.0",
"grunt-contrib-imagemin": "^1.0.1",
"grunt-contrib-uglify": "^2.2.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-pagespeed": "^2.0.1",
"grunt-postcss": "^0.8.0",
"grunt-sass": "^2.0.0",
"imagemin-mozjpeg": "^5.1.0",
"load-grunt-tasks": "^3.5.2",
"pixrem": "^3.0.2",
"time-grunt": "^1.4.0"
}
}
Now run:
npm install
Keeping it up-to-date is easy with npm update
.
Then fetch bower packages used in this example:
Create bower.json
with this content:
{
"name": "name"
}
and run:
bower install --save-dev jQuery
bower install --save-dev normalize-css
bower install --save-dev font-awesome
GruntFile.js
The datastructure used is the same as within the PaintTheWeb repo.
Development files:
asset/
asset/js/
asset/js/manual-concat-file.js
asset/js/src/
asset/js/src/automatic-concat-file.js
asset/style/
asset/style/main.scss
asset/media/
Output Files / To CDN:
view/out/
view/out/js.js
view/out/js.min.js
view/out/style.css
view/out/style.min.css
view/out/media/
Create the GruntFile.js
with following content, configure the paths:
There should be a little bit more explanation of the logic...
module.exports = function(grunt) {
//
// Paths
//
/**
* with trailing slash
* bower_components/ (mostly)
* @type {string}
*/
let path_bower = 'bower_components/';
/**
* with trailing slash
* vendor/ (mostly)
* @type {string}
*/
let path_composer = 'vendor/';
/**
* This folder will be watched and JS concated, mangled, minified
*
* with trailing slash
* @type {string}
*/
let path_js_src_dir = 'asset/js/src/';
/**
* The folder in which the JS output should be saved
*
* with trailing slash
* @type {string}
*/
let path_js_build_dir = 'view/out/';
/**
* The main Sass file that should be transpiled, but:
*
* without extension
* @type {string}
*/
let path_sass_src_file = 'asset/style/main';
/**
* The folder where most sass files are located, will be used for the CSS file watcher
*
* with trailing slash
* @type {string}
*/
let path_sass_src_dir = 'asset/style/';
/**
* The folder in which the CSS should be saved
*
* with trailing slash
* @type {string}
*/
let path_sass_build_dir = 'view/out/';
/**
* Name of the CSS file, but:
*
* without extension
* @type {string}
*/
let path_sass_build_file = 'style';
/**
* The source image folder, will be watched and all images optimized and copied into path_img_build
*
* with trailing slash
* @type {string}
*/
let path_img_src = 'asset/media/';
/**
* The folder in which the optimized images are saved
*
* with trailing slash
* @type {string}
*/
let path_img_build = 'view/out/media/';
//
// JS concat
//
let js_concat = [
path_bower + 'jQuery/dist/jquery.min.js',
path_js_src_dir + '**/*.js'
];
//
// Options
//
/**
* imagemin level of optimization for png and dynamic (svg|gif)
* @type {number}
*/
let img_optimization_lvl = 3;
/**
* imagemin level of builded image quality for jpeg and dynamic (svg|gif)
* @type {number}
*/
let img_quality_lvl = 90;
//
// Watcher
//
/**
* The more files must be scanned the longer it takes, keep the list clean!
* @type {[*]}
*/
let watch_css = [
path_sass_src_dir + '**/*.scss',
'!**/node_modules/**',
'!**/*.min.css'
];
/**
* The more files must be scanned the longer it takes, keep the list clean!
* @type {[*]}
*/
let watch_js = [
path_js_src_dir + '**/*.js',
'!**/node_modules/**',
'!**/*.min.js'
];
/**
* The more files must be scanned the longer it takes, keep the list clean!
* @type {[*]}
*/
let watch_img = [
path_img_src + '**/*.{gif,svg,png,jpg}',
];
require('time-grunt')(grunt);
require('load-grunt-tasks')(grunt);
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// JS
concat: {
dist: {
// warns when something was not found but was specified
nonull: true,
src: js_concat,
dest: path_js_build_dir + 'js.js'
}
},
uglify: {
build: {
options: {
sourceMap: true,
mangle: {
properties: true,
toplevel: false,
reserved: ['jQuery', 'jquery']
}
},
src: path_js_build_dir + 'js.js',
dest: path_js_build_dir + 'js.min.js'
}
},
// CSS
sass: {
options: {
sourceMap: true
},
dist: {
files: {
[path_sass_build_dir + path_sass_build_file + '.css']: path_sass_src_file + '.scss'
}
}
},
cssmin: {
target: {
files: [{
expand: true,
cwd: path_sass_build_dir,
src: [path_sass_build_file + '.css', '!' + path_sass_build_file + '.css.map'],
dest: path_sass_build_dir,
ext: '.min.css'
}]
}
},
postcss: {
options: {
map: false,
processors: [
require('pixrem')(), // add fallbacks for rem units
require('autoprefixer')({browsers: 'last 4 versions'})
]
},
dist: {
src: path_sass_build_dir + path_sass_build_file + '.min.css'
}
},
// Image
imagemin: {
png: {
options: {
optimizationLevel: img_optimization_lvl
},
files: [{
expand: true,
cwd: path_img_src,
src: ['**/*.png'],
dest: path_img_build
}]
},
jpg: {
options: {
quality: img_quality_lvl,
progressive: true,
use: [require('imagemin-mozjpeg')()]
},
files: [{
expand: true,
cwd: path_img_src,
src: ['**/*.jpg'],
dest: path_img_build
}]
},
dynamic: {
options: {
optimizationLevel: img_optimization_lvl,
quality: img_quality_lvl,
svgoPlugins: [{removeViewBox: false}]
},
files: [{
expand: true,
cwd: path_img_src,
src: ['**/*.{gif,svg}'],
dest: path_img_build
}]
}
},
// Multi Tasking
concurrent: {
image: ['imagemin:png', 'imagemin:jpg', 'imagemin:dynamic'],
build: [['js'], ['css'], 'concurrent:image']
},
// JS and CSS/Sass file watcher
watch: {
css: {
files: watch_css,
tasks: ['css']
},
js: {
files: watch_js,
tasks: ['js']
},
image: {
files: watch_img,
tasks: ['image']
}
}
});
// Multi-Thread Task Runner
grunt.loadNpmTasks('grunt-concurrent');
// JS
grunt.registerTask('js', ['concat', 'uglify']);
// SASS
grunt.registerTask('css', ['sass', 'cssmin', 'postcss']);
// Images
grunt.registerTask('image', ['concurrent:image']);
// Build All
grunt.registerTask('build', ['concurrent:build']);
};
Grunt Execution
Now you could simply run grunt with the following commands. There must be the main.scss
file and some JS file in path_js_src_dir
that no error will be displayed. Images are optional.
Executes the Sass -> CSS task:
grunt css
Executes the JS task:
grunt js
Executes the Image task:
grunt image
Executes Sass/CSS, JS and Images at once:
grunt build
File Watcher
A File watcher will run the specified task/s when a file has been changed, you could simply start one with
grunt watch css
if you just want to let it run when you change a Sass (*.scss) file.
Or let it watch for everything:
grunt watch
As all images will be changed everytime, depending on your deployment it could be that you should exclude images from the watchers. Also it could take a lot of time when you got a lot of pictures. So run grunt image
when you are finished with handling images.
If you use PHPStorm as IDE, you should execute from the GruntTasks panel. Right-click on the GruntFile.js -> "show Grunt Tasks". Only with this the virtual file system that PHPStorm uses will be refreshed and things like automatic upload are working.
Created | Last Update