Gulp is a toolkit for automating painful or time-consuming tasks in your development workflow, so you can stop messing around and build something.
For this code lab use the project from the ToDo App - AngularJs codelab
$ npm install gulp-cli -g
$ npm install gulp --save-dev
$ npm install gulp-load-plugins --save-dev
This is not required but it will help to write less code
$ touch gulpfile.js
var gulp = require('gulp');
gulp.task('hello-task', function(){
console.log('Our first gulp task');
});
$ gulp hello-task
JSHint is a program that flags suspicious usage in programs written in JavaScript. The core project consists of a library itself as well as a CLI program distributed as a Node module.
$ npm install jshint gulp-jshint --save-dev
.jshintrc
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` and `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // Unused variables:
// true : all variables, last function parameter
// "vars" : all variables only
// "strict" : all variables, all function parameters
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
"varstmt" : false, // true: Disallow any var statements. Only `let` and `const` are allowed.
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"esversion" : 5, // {int} Specify the ECMAScript version to which the code must adhere.
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"noyield" : false, // true: Tolerate generator functions with no yield statement in them.
"notypeof" : false, // true: Tolerate invalid typeof operator values
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
// Custom Globals
"globals" : {} // additional predefined global variables
}
JSCS is a code style linter and formatter for your style guide
$ npm install gulp-jscs --save-dev
.jscsrc
{
"requireCurlyBraces": [ "if", "else", "for", "while", "do" ],
"requireSpaceAfterKeywords": [ "if", "else", "for", "while", "do", "switch", "return" ],
"requireSpacesInFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"disallowSpacesInFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"requireMultipleVarDecl": true,
"requireSpacesInsideObjectBrackets": "all",
"requireSpacesInsideArrayBrackets": "all",
"requireSpaceBeforeBinaryOperators": ["+", "-", "/", "*", "=", "==", "===", "!=", "!=="],
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-"],
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
"disallowKeywords": [ "with" ],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": [ "else" ],
"requireLineFeedAtFileEnd": true,
"disallowSpaceAfterObjectKeys": true,
"validateLineBreaks": "CRLF"
}
hello-task
taskgulp plugin loader
var $ = require('gulp-load-plugins')();
analyze
task
gulp.task('analyze', function(){
gulp.src(['src/**/*.js', 'gulpfile.js', '!src/bower_components/**/*.js'])
.pipe($.jshint())
.pipe($.jshint.reporter('default'))
.pipe($.jshint.reporter('fail'))
.pipe($.jscs())
.pipe($.jscs.reporter())
.pipe($.jscs.reporter('fail'));
});
$ gulp analyze
More than sure you have a lot of errors. Here is a hint to fix some of them
.jshintrc
in the Enviroment section
add"node" : true, // Node.js
.jshintrc
in the globals
object add
"globals" : { // additional predefined global variables
"angular" : true
}
$ npm install wiredep gulp-inject gulp-angular-filesort --save-dev
Inside index.html
css
references with
<!-- bower:css -->
<!-- endbower -->
<!-- inject:css -->
<!-- endinject -->
javascript
references with
<!-- bower:js -->
<!-- endbower -->
<!-- inject:js -->
<!-- endinject -->
inject
taskInside gulpfile.js
wiredep
var wiredep = require('wiredep').stream;
gulp.task('inject', function(){
return gulp.src('index.html')
.pipe(wiredep({exclude: [/jQuery/, /\/bootstrap-css\/.*\.js$/]}))
.pipe($.inject(gulp.src('src/**/*.js')))
.pipe($.inject(gulp.src('src/**/*.css')))
.pipe(gulp.dest(''));
});
task
$ gulp inject
index.html
and try to see what is wrongThere are 2 problems
css
dependency is referencedChange the inject
task
gulp.task('inject', function(){
var injectCss = gulp.src('src/*.css'),
injectJs = gulp.src('src/**/*.js')
.pipe($.angularFilesort());
return gulp.src('index.html')
.pipe(wiredep({exclude: [/jQuery/, /\/bootstrap-css\/.*\.js$/]}))
.pipe($.inject(injectJs, {relative: true}))
.pipe($.inject(injectCss, {relative: true}))
.pipe(gulp.dest(''));
});
Refresh the page and the application should work
bower
dependencies correctlySome bower
dependencies are not written correctly and they need a little correction inside the bower.json
file
Just after the dependencies
block, add
"overrides": {
"bootstrap-css": {
"main": ["css/bootstrap.css", "css/bootstrap-theme.css"]
}
}
Having live-server
is nice but it doesn't run our gulp
tasks when the code changes.
In order to achieve this we have to create another task
$ npm install gulp-server-livereload --save-dev
Before doing anything else, restructure the application by doing the following
app
inside the src
foldersrc
to app
data
folder inside the src
folderindex.html
file inside the src
folder.bowerrc
file
{
"directory": "src/vendor"
}
bower_components
folder and run$ bower install
gulpfile.js
file
"use strict";
var gulp = require('gulp'),
wiredep = require('wiredep').stream,
$ = require('gulp-load-plugins')();
gulp.task('analyze', function() {
gulp.src([ 'src/**/*.js', 'gulpfile.js', '!src/vendor/**/*.js' ])
.pipe($.jshint())
.pipe($.jshint.reporter('default'))
.pipe($.jshint.reporter('fail'))
.pipe($.jscs())
.pipe($.jscs.reporter())
.pipe($.jscs.reporter('fail'));
});
gulp.task('inject', function() {
var injectCss = gulp.src('src/*.css'),
injectJs = gulp.src('src/app/**/*.js')
.pipe($.angularFilesort());
return gulp.src('src/index.html' )
.pipe(wiredep({ exclude: [ /jQuery/, /\/bootstrap-css\/.*\.js$/ ] }))
.pipe($.inject(injectJs, { relative: true }))
.pipe($.inject(injectCss, { relative: true }))
.pipe(gulp.dest('src'));
});
serve
task
gulp.task('serve', [ 'analyze', 'inject' ] , function() {
gulp.src('src')
.pipe($.serverLivereload({
livereload: true,
open: true
}));
});
$ gulp serve
As an exercise add a file named app.css
inside the src
folder
h1 {
color:red;
}
Add more rules and watch what happens in the browser
$ npm install gulp-useref del --save-dev
build
task
gulp.task('build', ['inject', 'analyze'], function(){
return gulp.src('src/index.html')
.pipe($.useref())
.pipe(gulp.dest('build'));
});
Adapt the inject templates from index.html
by wrapping them in build templates
<!-- build:css css/vendor.css -->
<!-- bower:css -->
<!-- endbower -->
<!-- endbuild -->
<!-- build:css css/app.css -->
<!-- inject:css -->
<!-- endinject -->
<!-- endbuild -->
<!-- build:js js/vendor.js -->
<!-- bower:js -->
<!-- endbower -->
<!-- endbuild -->
<!-- build:js js/app.js -->
<!-- inject:js -->
<!-- endinject -->
<!-- endbuild -->
gulp.task('other', function() {
var fileFilter = $.filter(function (file) {
return file.stat.isFile();
});
return gulp.src([ 'src/**/*', '!src/**/*.{html,css,js}' ])
.pipe(fileFilter)
.pipe(gulp.dest('build'));
});
other
task depends on gulp-filter
package. Make sure it is presentother
task from the build
task.build
is done test the result.We need gulp-if
, gulp-uglify
and gulp-clean-css
build
task
gulp.task('build', ['inject', 'analyze'], function(){
return gulp.src('src/index.html')
.pipe($.useref())
.pipe($.if('*.js', $.uglify()))
.pipe($.if('*.css', $.cleanCss()))
.pipe(gulp.dest('build'));
});
Observe the result. Is the app still working?
gulp-ng-annotate
build
task.pipe($.if('*.js', $.ngAnnotate()))
Can you guess where?serve
the application from the build folder