Simple Gulp Configuration for Angular Applications
Gulp is a great build tool for web applications. In this article I am sharing some gulp scripts that I found really useful.
Installing Gulp
Node.js needs to be installed prior to installing gulp. Once Node.js is ready on your machine, run this command to install gulp:
sudo npm install --global gulp-cli
This will install the gulp command line tools globally, such that the gulp
command can be invoked throughout the computer.
Create a Gulp Project
Change directory to the project’s root.
npm init
First of all, initialize npm, set the project name, version, author, etc. After the setup, package.json
will be created.
npm install gulp --save-dev
Install gulp for the project. --save-dev
make sure that gulp is logged in package.json
as a development dependency. This is particularly useful when we need to port the project to another computer. With all the dependencies logged in package.json, we just run npm install
to get them back, instead of install each of them again, or copy the gaint node_modules folder around.
Project Structure
For an Angular.js application with gulp as the build tool, I usually setup the project directory like this:
|- project/
|- bower_components/
|- dist/
|- node_modules/
|- src/
|- css/ # vendor css
|- less/ # my less source code
|- img/
|- js/ # my javascript source code
|- vendor/ # vendor js
|- views/ # angular templates
|- index.html
|- bower.json
|- gulpfile.js
|- package.json
A simple gulpfile.js
Here comes the fun part. A basic gulpfile looks like this:
// require gulp dependencies
var gulp = require('gulp');
// declare a gulp task
gulp.task('task_name', function() {
// task pipelines
});
The tasks can be run in command line like:
gulp task_name
Define Paths
Define a path object to keep track of all the source code in the project, making it easier for reference in various gulp tasks. For one of the angularjs application, I had the following paths:
var path = {
// template markups
HTML: [
'src/*.html',
'src/views/**/*.html',
'src/views/*.html'
],
// my js source code
JS: [
'src/js/**/*.js'
'src/js/*.js',
],
// all less files (for changes monitoring purpose)
LESS_ALL: [
'src/less/*.less'
],
// main less file (others are imported in style.less)
LESS: [
'src/less/style.less'
],
// images
IMG: [
'src/img/**'
],
// vendor css
CSS: [
'src/css/*.css'
],
// vendor js
VENDOR: [
'bower_components/angular/angular.js',
'bower_components/angular-animate/angular-aria.js',
// ...and more
],
DIST: [
'./dist'
]
};
Installing Gulp Plugins
There are tons of useful gulp plugins to explore. Gulp plugins can be installed by
npm install <plugin-name> --save-dev
For example, to install gulp-connect
:
npm install gulp-connect --save-dev
Useful Gulp Tasks
Here I only introduce some gulp tasks that I found helpful in angularjs development.
Spin up a erver for dist
.
var connect = require('gulp-connect');
gulp.task('connect', function() {
connect.server({
root: 'dist',
port: 4000
});
});
Clean up distribution directory.
var connect = require('gulp-clean');
gulp.task('clean', function() {
return gulp.src('./dist/*', {force: true})
.pipe(clean());
});
Use JSLint.
var jshint = require('gulp-jshint');
gulp.task('lint', function() {
return gulp.src(path.JS)
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
});
Copy over CSS and HTML
gulp.task('css', function () {
gulp.src(path.CSS)
.pipe(gulp.dest(path.DIST + '/css'));
});
gulp.task('html', function () {
gulp.src(path.HTML, {base: 'src'})
.pipe(gulp.dest(path.DIST));
});
Compile, minify and copy Less.
var less = require('gulp-less');
var lessDependents = require('gulp-less-dependents');
var minifyCSS = require('gulp-minify-css');
gulp.task('less', function () {
gulp.src(path.LESS)
.pipe(lessDependents())
.pipe(less())
.pipe(minifyCSS())
.pipe(gulp.dest(path.DIST + '/css'));
});
Merge, uglify and copy js files.
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var ngAnnotate = require('gulp-ng-annotate');
var sourcemaps = require('gulp-sourcemaps');
gulp.task('js', function () {
gulp.src(path.JS)
.pipe(sourcemaps.init())
.pipe(concat('app.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(sourcemaps.write())
.pipe(gulp.dest(path.DIST + '/js'));
});
gulp.task('vendor', function () {
gulp.src(path.VENDOR)
.pipe(concat('vendor.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(gulp.dest(path.DIST + '/js'));
});
Compress images.
var imagemin = require('gulp-imagemin');
gulp.task('img', function(){
gulp.src(path.IMG)
.pipe(imagemin())
.pipe(gulp.dest(path.DIST + '/img'));
});
Watch changes and automate tasks.
var watch = require('gulp-watch');
gulp.task('watch', function () {
gulp.watch(path.LESS_ALL, ['less']);
gulp.watch(path.VENDOR, ['vendor']);
gulp.watch(path.JS, ['lint', 'js']);
gulp.watch(path.HTML, ['html']);
gulp.watch(path.IMG, ['img']);
});
Default task.
var all_tasks = ['lint', 'css', 'less', 'vendor', 'js', 'html', 'img'];
gulp.task('default', all_tasks);
A More Complete gulpfile.js
/* gulp dependencies */
var gulp = require('gulp');
var less = require('gulp-less');
var watch = require('gulp-watch');
var imagemin = require('gulp-imagemin');
var connect = require('gulp-connect');
var concat = require('gulp-concat');
var sourcemaps = require('gulp-sourcemaps');
var uglify = require('gulp-uglify');
var ngAnnotate = require('gulp-ng-annotate');
var minifyCSS = require('gulp-minify-css');
var lessDependents = require('gulp-less-dependents');
var clean = require('gulp-clean');
var bower = require('gulp-bower');
var concat_vendor = require('gulp-concat-vendor');
var jshint = require('gulp-jshint');
/* path def */
var path = {
HTML: [
'src/.htaccess',
'src/*.html',
'src/views/**/*.html',
'src/views/*.html',
'src/favicon.png'
],
JS: [
'src/js/*.js',
'src/js/**/*.js'
],
CSS: [
'src/css/*.css'
],
LESS: [
'src/less/style.less'
],
LESS_ALL: [
'src/less/*.less'
],
IMG: [
'src/img/**'
],
VENDOR: [
'bower_components/angular/angular.js',
'bower_components/angular-animate/angular-animate.js',
'bower_components/angular-aria/angular-aria.js',
'bower_components/angular-messages/angular-messages.js',
'bower_components/angular-sanitize/angular-sanitize.js',
'bower_components/angular-ui-router/release/angular-ui-router.js'
// ...and more
],
DIST: './dist'
};
/* spin up distribution server */
gulp.task('connect', function() {
connect.server({
root: 'dist',
port: 4005
});
});
/* clean up dist dir */
gulp.task('clean', function() {
return gulp.src('./dist/*', {force: true})
.pipe(clean());
});
/* jslint for debugging */
gulp.task('lint', function() {
return gulp.src(path.JS)
.pipe(jshint())
.pipe(jshint.reporter('default'))
.pipe(jshint.reporter('fail'));
});
/* move css */
gulp.task('css', function () {
gulp.src(path.CSS)
.pipe(gulp.dest(path.DIST + '/css'));
});
/* compile less */
gulp.task('less', function () {
gulp.src(path.LESS)
.pipe(lessDependents())
.pipe(less())
.pipe(minifyCSS())
.pipe(gulp.dest(path.DIST + '/css'));
});
/* concat and compress app scripts */
gulp.task('js', function () {
gulp.src(path.JS)
.pipe(sourcemaps.init())
.pipe(concat('app.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(sourcemaps.write())
.pipe(gulp.dest(path.DIST + '/js'));
});
/* concat vendor dependencies */
gulp.task('vendor', function () {
gulp.src(path.VENDOR)
.pipe(concat('vendor.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(gulp.dest(path.DIST + '/js'));
});
/* copy over markups */
gulp.task('html', function(){
gulp.src(path.HTML, {base: 'src'})
.pipe(gulp.dest(path.DIST));
});
/* compress images */
gulp.task('img', function(){
gulp.src(path.IMG)
.pipe(imagemin())
.pipe(gulp.dest(path.DIST + '/img'));
});
/* watch all changes */
gulp.task('watch', function () {
gulp.watch(path.LESS_ALL, ['less']);
gulp.watch(path.VENDOR, ['vendor']);
gulp.watch(path.JS, ['lint', 'js']);
gulp.watch(path.HTML, ['html']);
gulp.watch(path.IMG, ['img']);
});
/* defualt */
gulp.task('default', all_tasks);