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 loadervar $ = 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
wiredepvar 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 appdata 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-annotatebuild task.pipe($.if('*.js', $.ngAnnotate()))Can you guess where?serve the application from the build folder