Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dethroning Grunt: Simple and Effective Builds with gulp.js

Jay Harris
February 10, 2014

Dethroning Grunt: Simple and Effective Builds with gulp.js

Grunt is king. It is the ubiquitous task runner used for most nodejs projects and has quickly expanded to conquer other software ecosystems. However, its kingdom is vulnerable. Grunt does not align well with many nodejs paradigms and is notorious for its harsh learning curve. Meet Gulp, the challenger in the taskrunner revolution. Gulp’s easy configuration produces an easy learning curve, and its alignment with nodejs paradigms eliminates the friction. Grab your ticket, your foam finger, and your team-colored face paint and witness the battle, the revolution, and the crowning of Gulp.

Author:
Jay Harris
Problem Solver | Arana Software
[email protected] | www.aranasoft.com
www.twitter.com/jayharris

Jay Harris

February 10, 2014
Tweet

More Decks by Jay Harris

Other Decks in Technology

Transcript

  1. #dethroningGrunt
    S I M P L E A N D E F F E C T I V E B U I L D S
    W I T H G U L P. J S
    @ j a y h a r r i s

    View Slide

  2. what is a
    taskrunner?

    View Slide

  3. View Slide

  4. that is a lot of things to do
    task runners simplify to one command

    View Slide

  5. gulp


    grunt

    View Slide

  6. focusing on five tasks

    View Slide

  7. demo code
     queenseight.com
     aranasoft/queenseight

    View Slide

  8. //  Project  Specific  Tasks
    grunt.loadNpmTasks('grunt-­‐bower-­‐task');
    grunt.loadNpmTasks('grunt-­‐coffeelint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐clean');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐coffee');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐concat');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐connect');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐copy');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐cssmin');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jade');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jshint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐less');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐uglify');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐watch');

    View Slide

  9. //  General-­‐Purpose  Tasks
    grunt.loadNpmTasks('grunt-­‐bower-­‐task');
    grunt.loadNpmTasks('grunt-­‐coffeelint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐clean');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐coffee');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐concat');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐connect');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐copy');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐cssmin');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jade');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jshint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐less');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐uglify');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐watch');

    View Slide

  10. //  13  tasks  in  all
    grunt.loadNpmTasks('grunt-­‐bower-­‐task');
    grunt.loadNpmTasks('grunt-­‐coffeelint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐clean');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐coffee');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐concat');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐connect');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐copy');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐cssmin');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jade');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jshint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐less');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐uglify');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐watch');

    View Slide

  11. timer  =  require  "grunt-­‐timer"
    fs  =  require  'fs'
    path  =  require  'path'
    server  =  require  './config/server'
    urlrouter  =  require  'urlrouter'
    module.exports  =  (grunt)  -­‐>
       timer.init  grunt
       #  Project  configuration.
       grunt.initConfig
           pkg:  grunt.file.readJSON  'package.json'
           files:
               coffee:
                   app:  "app/js/**/*.coffee"
                   generated:  "generated/js/app.coffee.js"
       
               css:
                   vendor:  "vendor/css/**/*.css"
                   app:  "app/css/**/*.css"
                   concatenated:  "generated/css/app.css"
                   minified:  "dist/css/app.css"
                   minifiedWebRelative:  "css/app.css"
                   
               img:
                   root:  "img"
               jade:
                   pages:  "**/*.jade"
                   pageRoot:  "app/pages/"
               js:
                   app:  "app/js/**/*.js"
                   vendor:  [
                       "vendor/components/jquery/jquery.js"
                       "vendor/components/underscore/underscore.js"
                       "vendor/components/angular/angular.js"
                       "vendor/js/**/*.js"
                   ]
                   concatenatedVendor:  "generated/js/vendor.js"
                   minifiedVendor:  "dist/js/vendor.js"
                   minifiedVendorWebRelative:  "js/vendor.js"
                   concatenated:  "generated/js/app.js"
                   minified:  "dist/js/app.js"
                   minifiedWebRelative:  "js/app.js"                
                   
               less:
                   app:  "app/css/app.less"
                   vendor:  "vendor/css/**/*.less"
                   generatedApp:  "generated/css/app.less.css"
                   generatedVendor:  "generated/css/vendor.less.css"
                   watch:  "app/css/**/*.less"
               webfonts:
                   root:  "fonts"
           bower:
               install:
                   options:
                       copy:  false
           coffee:
               compile:
                   files:
                       "<%=  files.coffee.generated  %>":  "<%=  files.coffee.app  %>"
                       
           coffeelint:
               app:  [
                   "<%=  files.coffee.app  %>"
               ]
           concat:
               css:
                   src:  [
                       "<%=  files.less.generatedVendor  %>"
                       "<%=  files.css.vendor  %>"
                       "<%=  files.less.generatedApp  %>"
                       "<%=  files.css.app  %>"
                   ]
                   dest:  "<%=  files.css.concatenated  %>"
               js:
                   src:  [
                       "<%=  files.coffee.generated  %>"
                       "<%=  files.js.app  %>"
                   ]
                   dest:  "<%=  files.js.concatenated  %>"
               jsVendor:
                   src:  ["<%=  files.js.vendor  %>"]
                   dest:  "<%=  files.js.concatenatedVendor  %>"
           connect:
               server:
                   options:
                       port:  8000
                       base:  'generated'
                       open:  true
                       middleware:  (connect,  options)  -­‐>
                           middlewares  =  [];
                           if  (!Array.isArray(options.base))
                               options.base  =  [options.base]
                           
                           directory  =  options.directory  ||  options.base[options.base.length  -­‐  1]
                           options.base.forEach  (base)  -­‐>
                               #  Serve  static  files.
                               middlewares.push(connect.static(base))
                           middlewares.push  urlrouter(server.drawRoutes)
                           #  Make  directory  browse-­‐able.
                           middlewares.push  connect.directory(directory)
                           middlewares
           copy:
               imagesDev:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }]
               imagesDist:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }]
               staticDev:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'generated'
                   ]
               staticDist:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'dist'
                   ]
               webfontsDev:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }]
               webfontsDist:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }]
                   
           cssmin:
               compress:
                   files:
                       "<%=  files.css.minified  %>":  "<%=  files.css.concatenated  %>"
           jade:
               dev:
                   options:
                       pretty:  true
                       data:
                           js:  "<%=  files.js.minifiedWebRelative  %>"  
                           jsVendor:  "<%=  files.js.minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "generated/"
                       ext:  ".html"
                   }]
               dist:
                   options:
                       data:
                           js:  "<%=  minifiedWebRelative  %>"
                           jsVendor:  "<%=  minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "dist/"
                       ext:  ".html"
                   }]
           jshint:
               files:  ["<%=  files.js.app  %>"]
               options:
               #  enforcing  options
                   curly:  true
                   eqeqeq:  true
                   latedef:  true
                   newcap:  true
                   noarg:  true
               #  relaxing  options
                   boss:  true
                   eqnull:  true
                   sub:  true
               #  environment/globals
                   browser:  true
                   
           less:
               options:
                   paths:  ["app/css",  "vendor/css"]
               compile:
                   files:
                       "<%=  files.less.generatedVendor  %>":  "<%=  files.less.vendor  %>"
                       "<%=  files.less.generatedApp  %>":  "<%=  files.less.app  %>"
                       
           uglify:
               options:
                   banner:  '/*!  <%=  pkg.name  %>  <%=  grunt.template.today("yyyy-­‐mm-­‐dd")  %>  */\n'
               js:
                   files:
                       "<%=  files.js.minified  %>":  "<%=  files.js.concatenated  %>"
               jsVendor:
                   files:
                       "<%=  files.js.minifiedVendor  %>":  "<%=  files.js.concatenatedVendor  %>"
           clean:
               bower:
                   src:  bowerDirectory  grunt
               js:
                   src:  "<%=  files.js.concatenated  %>"
               css:
                   src:  "<%=  files.css.concatenated  %>"
               dist:
                   src:  ["dist",  "generated"]
           watch:
               coffee:
                   files:  "<%=  files.coffee.app  %>"
                   tasks:  ["coffeelint",  "coffee",  "concat:js"]
               css:
                   files:  ["<%=  files.css.vendor  %>",  "<%=  files.css.app  %>"]
                   tasks:  ["concat:css"]
               images:
                   files:  ["app/img/**/*.*",  "vendor/img/**/*.*"]
                   tasks:  ["copy:imagesDev"]
                   
               jade:
                   files:  ["<%=  files.jade.pageRoot  %>/<%=  files.jade.pages  %>"]
                   tasks:  ["jade:dev"]
               js:
                   files:  ["<%=  files.js.vendor  %>",  "<%=  files.js.app  %>"]
                   tasks:  ["concat:js"]
               less:
                   files:  [
                       "<%=  files.less.vendor  %>"
                       "<%=  files.less.watch  %>"
                   ]
                   tasks:  ["less",  "concat:css"]
       
               lint:
                   files:  "<%=  files.js.app  %>"
                   tasks:  ["jshint"]
       
               webfonts:
                   files:  ["vendor/webfonts/**/*.*",  "vendor/components/font-­‐awesome/fonts/**/*.*"]
                   tasks:  ["copy:webfontsDev"]
                   
               livereload:
                   options:
                       livereload:  true
                   files:  "dist/**/*.*"
                   
       grunt.loadNpmTasks  'grunt-­‐bower-­‐task'
       grunt.loadNpmTasks  'grunt-­‐coffeelint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐clean'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐coffee'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐concat'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐connect'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐copy'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐cssmin'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jshint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐less'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jade'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐uglify'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐watch'
       grunt.registerTask  'default',  [
           'common'
           'dev'
       ]
       grunt.registerTask  'common',  [
           'bower'
           'coffeelint'
           'jshint'
           'coffee'
           'less'
           'concat'
           'copy:staticDev'
           'copy:imagesDev'
           'copy:webfontsDev'
           'jade:dev'
       ]
       grunt.registerTask  'dev',  [
           'connect'
           'watch'
       ]
       grunt.registerTask  'dist',  [
           'uglify'
           'cssmin'
           'copy:staticDist'
           'copy:imagesDist'
           'copy:webfontsDist'
           'jade:dist'
       ]
    bowerDirectory  =  (grunt)  -­‐>
       bowerrc  =  path.join(process.cwd(),  ".bowerrc")
       bowerConfig  =  grunt.file.readJSON(bowerrc)  unless  !fs.existsSync(bowerrc)
       bowerConfig?.directory  ||  "vendor/components"
    gruntfile.coffee

    View Slide

  12. timer  =  require  "grunt-­‐timer"
    fs  =  require  'fs'
    path  =  require  'path'
    server  =  require  './config/server'
    urlrouter  =  require  'urlrouter'
    module.exports  =  (grunt)  -­‐>
       timer.init  grunt
       #  Project  configuration.
       grunt.initConfig
           pkg:  grunt.file.readJSON  'package.json'
           files:
               coffee:
                   app:  "app/js/**/*.coffee"
                   generated:  "generated/js/app.coffee.js"
       
               css:
                   vendor:  "vendor/css/**/*.css"
                   app:  "app/css/**/*.css"
                   concatenated:  "generated/css/app.css"
                   minified:  "dist/css/app.css"
                   minifiedWebRelative:  "css/app.css"
                   
               img:
                   root:  "img"
               jade:
                   pages:  "**/*.jade"
                   pageRoot:  "app/pages/"
               js:
                   app:  "app/js/**/*.js"
                   vendor:  [
                       "vendor/components/jquery/jquery.js"
                       "vendor/components/underscore/underscore.js"
                       "vendor/components/angular/angular.js"
                       "vendor/js/**/*.js"
                   ]
                   concatenatedVendor:  "generated/js/vendor.js"
                   minifiedVendor:  "dist/js/vendor.js"
                   minifiedVendorWebRelative:  "js/vendor.js"
                   concatenated:  "generated/js/app.js"
                   minified:  "dist/js/app.js"
                   minifiedWebRelative:  "js/app.js"                
                   
               less:
                   app:  "app/css/app.less"
                   vendor:  "vendor/css/**/*.less"
                   generatedApp:  "generated/css/app.less.css"
                   generatedVendor:  "generated/css/vendor.less.css"
                   watch:  "app/css/**/*.less"
               webfonts:
                   root:  "fonts"
           bower:
               install:
                   options:
                       copy:  false
           coffee:
               compile:
                   files:
                       "<%=  files.coffee.generated  %>":  "<%=  files.coffee.app  %>"
                       
           coffeelint:
               app:  [
                   "<%=  files.coffee.app  %>"
               ]
           concat:
               css:
                   src:  [
                       "<%=  files.less.generatedVendor  %>"
                       "<%=  files.css.vendor  %>"
                       "<%=  files.less.generatedApp  %>"
                       "<%=  files.css.app  %>"
                   ]
                   dest:  "<%=  files.css.concatenated  %>"
               js:
                   src:  [
                       "<%=  files.coffee.generated  %>"
                       "<%=  files.js.app  %>"
                   ]
                   dest:  "<%=  files.js.concatenated  %>"
               jsVendor:
                   src:  ["<%=  files.js.vendor  %>"]
                   dest:  "<%=  files.js.concatenatedVendor  %>"
           connect:
               server:
                   options:
                       port:  8000
                       base:  'generated'
                       open:  true
                       middleware:  (connect,  options)  -­‐>
                           middlewares  =  [];
                           if  (!Array.isArray(options.base))
                               options.base  =  [options.base]
                           
                           directory  =  options.directory  ||  options.base[options.base.length  -­‐  1]
                           options.base.forEach  (base)  -­‐>
                               #  Serve  static  files.
                               middlewares.push(connect.static(base))
                           middlewares.push  urlrouter(server.drawRoutes)
                           #  Make  directory  browse-­‐able.
                           middlewares.push  connect.directory(directory)
                           middlewares
           copy:
               imagesDev:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }]
               imagesDist:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }]
               staticDev:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'generated'
                   ]
               staticDist:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'dist'
                   ]
               webfontsDev:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }]
               webfontsDist:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }]
                   
           cssmin:
               compress:
                   files:
                       "<%=  files.css.minified  %>":  "<%=  files.css.concatenated  %>"
           jade:
               dev:
                   options:
                       pretty:  true
                       data:
                           js:  "<%=  files.js.minifiedWebRelative  %>"  
                           jsVendor:  "<%=  files.js.minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "generated/"
                       ext:  ".html"
                   }]
               dist:
                   options:
                       data:
                           js:  "<%=  minifiedWebRelative  %>"
                           jsVendor:  "<%=  minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "dist/"
                       ext:  ".html"
                   }]
           jshint:
               files:  ["<%=  files.js.app  %>"]
               options:
               #  enforcing  options
                   curly:  true
                   eqeqeq:  true
                   latedef:  true
                   newcap:  true
                   noarg:  true
               #  relaxing  options
                   boss:  true
                   eqnull:  true
                   sub:  true
               #  environment/globals
                   browser:  true
                   
           less:
               options:
                   paths:  ["app/css",  "vendor/css"]
               compile:
                   files:
                       "<%=  files.less.generatedVendor  %>":  "<%=  files.less.vendor  %>"
                       "<%=  files.less.generatedApp  %>":  "<%=  files.less.app  %>"
                       
           uglify:
               options:
                   banner:  '/*!  <%=  pkg.name  %>  <%=  grunt.template.today("yyyy-­‐mm-­‐dd")  %>  */\n'
               js:
                   files:
                       "<%=  files.js.minified  %>":  "<%=  files.js.concatenated  %>"
               jsVendor:
                   files:
                       "<%=  files.js.minifiedVendor  %>":  "<%=  files.js.concatenatedVendor  %>"
           clean:
               bower:
                   src:  bowerDirectory  grunt
               js:
                   src:  "<%=  files.js.concatenated  %>"
               css:
                   src:  "<%=  files.css.concatenated  %>"
               dist:
                   src:  ["dist",  "generated"]
           watch:
               coffee:
                   files:  "<%=  files.coffee.app  %>"
                   tasks:  ["coffeelint",  "coffee",  "concat:js"]
               css:
                   files:  ["<%=  files.css.vendor  %>",  "<%=  files.css.app  %>"]
                   tasks:  ["concat:css"]
               images:
                   files:  ["app/img/**/*.*",  "vendor/img/**/*.*"]
                   tasks:  ["copy:imagesDev"]
                   
               jade:
                   files:  ["<%=  files.jade.pageRoot  %>/<%=  files.jade.pages  %>"]
                   tasks:  ["jade:dev"]
               js:
                   files:  ["<%=  files.js.vendor  %>",  "<%=  files.js.app  %>"]
                   tasks:  ["concat:js"]
               less:
                   files:  [
                       "<%=  files.less.vendor  %>"
                       "<%=  files.less.watch  %>"
                   ]
                   tasks:  ["less",  "concat:css"]
       
               lint:
                   files:  "<%=  files.js.app  %>"
                   tasks:  ["jshint"]
       
               webfonts:
                   files:  ["vendor/webfonts/**/*.*",  "vendor/components/font-­‐awes
                   tasks:  ["copy:webfontsDev"]
                   
               livereload:
                   options:
                       livereload:  true
                   files:  "dist/**/*.*"
                   
       grunt.loadNpmTasks  'grunt-­‐bower-­‐task'
       grunt.loadNpmTasks  'grunt-­‐coffeelint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐clean'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐coffee'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐concat'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐connect'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐copy'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐cssmin'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jshint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐less'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jade'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐uglify'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐watch'
       grunt.registerTask  'default',  [
           'common'
           'dev'
       ]
       grunt.registerTask  'common',  [
           'bower'
           'coffeelint'
           'jshint'
           'coffee'
           'less'
           'concat'
           'copy:staticDev'
           'copy:imagesDev'
           'copy:webfontsDev'
           'jade:dev'
       ]
       grunt.registerTask  'dev',  [
           'connect'
           'watch'
       ]
       grunt.registerTask  'dist',  [
           'uglify'
           'cssmin'
           'copy:staticDist'
           'copy:imagesDist'
           'copy:webfontsDist'
           'jade:dist'
       ]
    bowerDirectory  =  (grunt)  -­‐>
       bowerrc  =  path.join(process.cwd(),  ".bowerrc")
       bowerConfig  =  grunt.file.readJSON(bowerrc)  unless  !fs.existsSync(bow
       bowerConfig?.directory  ||  "vendor/components"
    gruntfile.coffee
    ├──  css
    │      ├──  app.less
    │      ├──  mixins.less
    │      └──  variables.less
    ├──  img
    │      ├──  arana-­‐software.png
    │      ├──  arana-­‐[email protected]
    │      ├──  banner-­‐lg.png
    │      ├──  banner-­‐md.png
    │      ├──  banner-­‐sm.png
    │      ├──  crown.png
    │      ├──  [email protected]
    │      ├──  [email protected]
    │      └──  wood.png
    ├──  js
    │      ├──  app.coffee
    │      ├──  controllers
    │      │      └──  board.coffee
    │      ├──  directives
    │      │      └──  board.js
    │      └──  templates
    │              └──  board.coffee
    ├──  pages
    │      └──  index.jade
    └──  static
           └──  favicon.ico

    View Slide

  13. 'use  strict';
    var  util  =  require('util');
    var  Orchestrator  =  require('orchestrator');
    var  gutil  =  require('gulp-­‐util');
    var  deprecated  =  require('deprecated');
    var  vfs  =  require('vinyl-­‐fs');
    function  Gulp(){
       Orchestrator.call(this);
    }
    util.inherits(Gulp,  Orchestrator);
    Gulp.prototype.task  =  Gulp.prototype.add;
    Gulp.prototype.run  =  function(){
       //  run()  is  deprecated  as  of  3.5  and  will  be  removed  in  4.0
       //  use  task  dependencies  instead
       //  impose  our  opinion  of  "default"  tasks  onto  orchestrator
       var  tasks  =  arguments.length  ?  arguments  :  ['default'];
       this.start.apply(this,  tasks);
    };
    Gulp.prototype.src  =  vfs.src;
    Gulp.prototype.dest  =  vfs.dest;
    Gulp.prototype.watch  =  function  (glob,  opt,  fn)  {
       if  (!fn)  {
           fn  =  opt;
           opt  =  null;
       }
       //  array  of  tasks  given
       if  (Array.isArray(fn))  {
           return  vfs.watch(glob,  opt,  function(){
               this.start.apply(this,  fn);
           }.bind(this));
       }
       return  vfs.watch(glob,  opt,  fn);
    };
    //  let  people  use  this  class  from  our  instance
    Gulp.prototype.Gulp  =  Gulp;
    //  deprecations
    deprecated.field('gulp.env  has  been  deprecated.  Use  gulp-­‐util.env  or  your  own  CLI  parser  instead.',  console.log,  
    Gulp.prototype,  'env',  gutil.env);
    Gulp.prototype.run  =  deprecated.method('gulp.run()  has  been  deprecated.  Use  task  dependencies  or  gulp.watch  task  
    triggering  instead.',  console.log,  Gulp.prototype.run);
    var  inst  =  new  Gulp();
    module.exports  =  inst;

    View Slide

  14. View Slide

  15. .src(globs[, options])

    View Slide

  16. .dest(path)

    View Slide

  17. .task(name[, deps], fn)

    View Slide

  18. .watch(glob [, options], tasks)

    View Slide

  19. .pipe(destination)

    View Slide

  20.  level up
    grok streams

    View Slide




  21. a task: read, concatenate, write

    View Slide


  22.  



    

    additional steps add overhead

    View Slide


  23.  



    

    extraneous disk I/O

    View Slide


  24.  



    

     
     
    extraneous configuration

    View Slide






  25. streams pipe from task to task

    View Slide





  26.  

    additional steps without overhead

    View Slide

  27.  level up
    the first gulp

    View Slide

  28. npm install -g gulp
    npm install -D gulp
    touch gulpfile.js

    View Slide

  29. //gulpfile.js
    var  gulp      =  require('gulp');
    var  coffee  =  require('gulp-­‐coffee');
    var  concat  =  require('gulp-­‐concat');
    var  uglify  =  require('gulp-­‐uglify');

    View Slide

  30. gulp.src('app/js/**/*.coffee')

    View Slide

  31. gulp.src('app/js/**/*.coffee')
           .pipe(gulp.dest('dist/js'));
     

    View Slide

  32. gulp.src('app/js/**/*.coffee')
           .pipe(coffee())
    .pipe(concat('app.js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/js'));
     



    View Slide

  33. gulp.src('app/js/**/*.coffee')
           .pipe(coffee())
    .pipe(concat('app.js'))
    .pipe(gulp.dest('test/js'))
    .pipe(uglify())
    .pipe(gulp.dest('dist/js'));
     


     

    View Slide

  34. gulp.task('coffee',  function()  {
       gulp.src('app/js/**/*.coffee')
               .pipe(coffee())
       .pipe(concat('app.js'))
       .pipe(gulp.dest('test/js'))
       .pipe(uglify())
       .pipe(gulp.dest('dist/js'));
    });

    View Slide

  35. gulp.task('watch',  function()  {
    gulp.watch('app/js/**/*.coffee',
                         ['coffee']);
    });

    View Slide

  36. gulp.task('default',
             ['coffee','watch']);
     


      

    View Slide

  37.  level up
    dependencies

    View Slide

  38. gulp.task('css',
                       function()  {
       gulp.src('app/css/app.less')
               .pipe(less())
       .pipe(cssmin())
       .pipe(gulp.dest('dist/css'));
    });
     


    View Slide

  39. var  bower  =  require('gulp-­‐bower');
    gulp.task('install',  function()  {
       bower();
    });

    View Slide

  40. gulp.task('css',  ['install'],
                       function()  {
       gulp.src('app/css/app.less')
               .pipe(less())
       .pipe(cssmin())
       .pipe(gulp.dest('dist/css'));
    });
     

     

    View Slide

  41. var  bower  =  require('gulp-­‐bower');
    gulp.task('install',  function()  {
       bower();
    });
    //  Doesn't  work  as  expected

    View Slide

  42. our five tasks

    View Slide










  43. expected dependency tree

    View Slide










  44. actual tree

    View Slide

  45. gulp.task('stuff',  function()  {
       doStuff();
       });

    View Slide

  46. //  Option  1:  Callback
    gulp.task('stuff',  function(done)  {
       doSyncStuff()
       done(err);
    });

    View Slide

  47. var  Q  =  require('q');
    //  Option  2:  Promise
    gulp.task('stuff',  function()  {
       var  deferred  =  Q.defer();
       doAsyncStuff(deferred.resolve);
       return  deferred.promise;
    });

    View Slide

  48. //  Option  3:  Return  stream
    gulp.task('stuff',  function()  {
       var  stream  =  doStreamStuff();
       return  stream;
    });

    View Slide

  49. gulp.task  'stuff',  ()  -­‐>
       doStreamStuff()

    View Slide

  50. #  Get  return  stream  for  free!
    gulp.task  'stuff',  ()  -­‐>
       doStreamStuff()
    gulp.task('stuff',  function()  {
       return  doStreamStuff();
       });

    View Slide

  51. var  bower  =  require('gulp-­‐bower');
    gulp.task('install',  function()  {
       bower();
    });

    View Slide

  52. var  bower  =  require('gulp-­‐bower');
    gulp.task('install',  function()  {
       return  bower();  //  Win!
    });

    View Slide

  53. //  Back  to  where  we  were
    gulp.task('css',
                       function()  {
       gulp.src('app/css/app.less')
               .pipe(less())
       .pipe(cssmin())
       .pipe(gulp.dest('dist/css'));
    });
     


    View Slide

  54. gulp.task('css',  ['install'],
                       function()  {
       gulp.src('app/css/app.less')
               .pipe(less())
       .pipe(cssmin())
       .pipe(gulp.dest('dist/css'));
    });
     



    View Slide

  55.  level up
    using coffee

    View Slide

  56. //gulpfile.js
    gulp.task('css',  ['install'],
                       function()  {
       gulp.src('app/css/app.less')
               .pipe(less())
       .pipe(cssmin())
       .pipe(gulp.dest('dist/css'));
    });
     



    View Slide

  57. #  gulpfile.coffee
    gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src  'app/css/app.less'
               .pipe  less()
       .pipe  cssmin()  
       .pipe  gulp.dest('dist/css')
     



    View Slide

  58. gulp --require coffee-script/register

    View Slide

  59. //gulpfile.js
    require('coffee-­‐script');
    require('./gulpfile.coffee');
    gulp

    more gulpfile.js

    View Slide

  60.  level up
    merging streams

    View Slide





  61.  

    View Slide








  62.  

    View Slide

  63. gulp.task  'js',  ()  -­‐>
       gulp.src  files.coffee
               .pipe  coffee()
               .pipe  concat('app.js')
               .pipe  uglify()
               .pipe  gulp.dest('dist/js')





    View Slide

  64. evtstream  =  require  'event-­‐stream'
         #  and/or
    streamq      =  require  'streamqueue'

    View Slide

  65. gulp.task  'js',  ()  -­‐>
       gulp.src(files.coffee)
               .pipe(coffee())
               .pipe  concat('app.js')
               .pipe  uglify()
               .pipe  gulp.dest('dist/js')





    View Slide

  66. gulp.task  'js',  ()  -­‐>
       evtstream.concat(
           gulp.src(files.coffee)
                   .pipe(coffee()),
           gulp.src(files.js))
               .pipe  concat('app.js')
               .pipe  uglify()
               .pipe  gulp.dest('dist/js')






    View Slide

  67. gulp.task  'js',  ()  -­‐>
       es.concat(
           gulp.src(files.coffee)
                   .pipe(coffee()),
           gulp.src(files.js))
               .pipe  concat('app.js')
               .pipe  uglify()
               .pipe  gulp.dest('dist/js')






    View Slide

  68. gulp.task  'js',  ()  -­‐>
       sq  =  streamq  {objectmode:true}
       sq.queue  gulp.src(files.coffee)
                                 .pipe(coffee()
       sq.queue  gulp.src(files.js)
       sq.done().pipe  concat('app.js')
                         .pipe  uglify()
                         .pipe  gulp.dest('dist/js')






    View Slide

  69.  level up
    error handling

    View Slide

  70. gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src  files.less
               .pipe  less()  
       .pipe  cssmin()
       .pipe  gulp.dest('dist/css')
     



    View Slide

  71. //  app.less
    .mayhem  {
       font-­‐weight:  bold;
       color:              @red;
    }
    }  //  too  many  '{'  ===  BOOM!

    View Slide

  72. [gulp]  Running  'css'...
    events.js:72
                   throw  er;  //  Unhandled  'error'
                               ^
    Error:  missing  opening  `{`  in  file  ./c



    View Slide

  73. gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src  files.less
               .pipe  less()  
       .pipe  cssmin()
       .pipe  gulp.dest('dist/css')
     



    View Slide

  74. gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src  files.less
               .pipe  less().on('error',(err)-­‐>
                   console.log(''+err)  if  err
               )  #  Ick.
       .pipe  cssmin()
       .pipe  gulp.dest('dist/css')
     


     

    View Slide

  75. plumber  =  require  'gulp-­‐plumber'
    gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src  files.less
               .pipe  plumber()  #Win!
               .pipe  less()  
       .pipe  cssmin()
       .pipe  gulp.dest('dist/css')
     


     

    View Slide

  76. [gulp]  Running  'css'...
    [gulp]  Error  in  plugin  'gulp-­‐less':
       missing  opening  `{`  in  file  app.less
    [gulp]  Finished  'css'  in  21  ms


    View Slide

  77.  level up
    master class

    View Slide

  78. gutil  =  require  'gulp-­‐util'

    View Slide

  79. gulp.src  'app/css/*.css'
       .pipe  concat('app.css')
       .pipe(if  gutil.env.dest  ==  'prod'
                   then  cssmin()
                   else  gutil.noop())
       .pipe  gulp.dest('dist/css')




     

    View Slide

  80. gulp.src  'app/css/*.css'
       .pipe  concat('app.css')
       .pipe(if  gutil.env.dest  ==  'prod'
                   then  cssmin()
                   else  gutil.noop())
       .pipe  gulp.dest('dist/css')




     

    View Slide

  81. gulp  -­‐-­‐port=8000  -­‐-­‐dest=prod
    gutil.env.port  ===  8000
    gutil.env.dest  ===  'prod'


    View Slide

  82.  echo  Total  Files:  $(    \
     find  .  -­‐type  f  -­‐print\
     |  wc  -­‐l)
    Total  Files:  1000
    echo  watch  will  vomit


    View Slide

  83.  level up
    comparison

    View Slide

  84. timer  =  require  "grunt-­‐timer"
    fs  =  require  'fs'
    path  =  require  'path'
    server  =  require  './config/server'
    urlrouter  =  require  'urlrouter'
    module.exports  =  (grunt)  -­‐>
       timer.init  grunt
       #  Project  configuration.
       grunt.initConfig
           pkg:  grunt.file.readJSON  'package.json'
           files:
               coffee:
                   app:  "app/js/**/*.coffee"
                   generated:  "generated/js/app.coffee.js"
       
               css:
                   vendor:  "vendor/css/**/*.css"
                   app:  "app/css/**/*.css"
                   concatenated:  "generated/css/app.css"
                   minified:  "dist/css/app.css"
                   minifiedWebRelative:  "css/app.css"
                   
               img:
                   root:  "img"
               jade:
                   pages:  "**/*.jade"
                   pageRoot:  "app/pages/"
               js:
                   app:  "app/js/**/*.js"
                   vendor:  [
                       "vendor/components/jquery/jquery.js"
                       "vendor/components/underscore/underscore.js"
                       "vendor/components/angular/angular.js"
                       "vendor/js/**/*.js"
                   ]
                   concatenatedVendor:  "generated/js/vendor.js"
                   minifiedVendor:  "dist/js/vendor.js"
                   minifiedVendorWebRelative:  "js/vendor.js"
                   concatenated:  "generated/js/app.js"
                   minified:  "dist/js/app.js"
                   minifiedWebRelative:  "js/app.js"                
                   
               less:
                   app:  "app/css/app.less"
                   vendor:  "vendor/css/**/*.less"
                   generatedApp:  "generated/css/app.less.css"
                   generatedVendor:  "generated/css/vendor.less.css"
                   watch:  "app/css/**/*.less"
               webfonts:
                   root:  "fonts"
           bower:
               install:
                   options:
                       copy:  false
           coffee:
               compile:
                   files:
                       "<%=  files.coffee.generated  %>":  "<%=  files.coffee.app  %>"
                       
           coffeelint:
               app:  [
                   "<%=  files.coffee.app  %>"
               ]
           concat:
               css:
                   src:  [
                       "<%=  files.less.generatedVendor  %>"
                       "<%=  files.css.vendor  %>"
                       "<%=  files.less.generatedApp  %>"
                       "<%=  files.css.app  %>"
                   ]
                   dest:  "<%=  files.css.concatenated  %>"
               js:
                   src:  [
                       "<%=  files.coffee.generated  %>"
                       "<%=  files.js.app  %>"
                   ]
                   dest:  "<%=  files.js.concatenated  %>"
               jsVendor:
                   src:  ["<%=  files.js.vendor  %>"]
                   dest:  "<%=  files.js.concatenatedVendor  %>"
           connect:
               server:
                   options:
                       port:  8000
                       base:  'generated'
                       open:  true
                       middleware:  (connect,  options)  -­‐>
                           middlewares  =  [];
                           if  (!Array.isArray(options.base))
                               options.base  =  [options.base]
                           
                           directory  =  options.directory  ||  options.base[options.base.length  -­‐  1]
                           options.base.forEach  (base)  -­‐>
                               #  Serve  static  files.
                               middlewares.push(connect.static(base))
                           middlewares.push  urlrouter(server.drawRoutes)
                           #  Make  directory  browse-­‐able.
                           middlewares.push  connect.directory(directory)
                           middlewares
           copy:
               imagesDev:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "generated/<%=  files.img.root  %>/"
                   }]
               imagesDist:
                   files:  [{
                       expand:  true
                       cwd:  "app/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/img/"
                       src:  "**"
                       dest:  "dist/<%=  files.img.root  %>/"
                   }]
               staticDev:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'generated'
                   ]
               staticDist:
                   files:  [
                       expand:  true
                       cwd:  "app/static"
                       src:  "**"
                       dest:  'dist'
                   ]
               webfontsDev:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "generated/<%=  files.webfonts.root  %>/"
                   }]
               webfontsDist:
                   files:  [{
                       expand:  true
                       cwd:  "vendor/webfonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }
                   {
                       expand:  true
                       cwd:  "vendor/components/font-­‐awesome/fonts/"
                       src:  "**"
                       dest:  "dist/<%=  files.webfonts.root  %>/"
                   }]
                   
           cssmin:
               compress:
                   files:
                       "<%=  files.css.minified  %>":  "<%=  files.css.concatenated  %>"
           jade:
               dev:
                   options:
                       pretty:  true
                       data:
                           js:  "<%=  files.js.minifiedWebRelative  %>"  
                           jsVendor:  "<%=  files.js.minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "generated/"
                       ext:  ".html"
                   }]
               dist:
                   options:
                       data:
                           js:  "<%=  minifiedWebRelative  %>"
                           jsVendor:  "<%=  minifiedVendorWebRelative  %>"
                           css:  "<%=  files.css.minifiedWebRelative  %>"
                           pkg:  "<%=  pkg  %>"
                   files:  [{
                       expand:  true
                       src:  "<%=  files.jade.pages  %>"
                       cwd:  "<%=  files.jade.pageRoot  %>"
                       dest:  "dist/"
                       ext:  ".html"
                   }]
           jshint:
               files:  ["<%=  files.js.app  %>"]
               options:
               #  enforcing  options
                   curly:  true
                   eqeqeq:  true
                   latedef:  true
                   newcap:  true
                   noarg:  true
               #  relaxing  options
                   boss:  true
                   eqnull:  true
                   sub:  true
               #  environment/globals
                   browser:  true
                   
           less:
               options:
                   paths:  ["app/css",  "vendor/css"]
               compile:
                   files:
                       "<%=  files.less.generatedVendor  %>":  "<%=  files.less.vendor  %>"
                       "<%=  files.less.generatedApp  %>":  "<%=  files.less.app  %>"
                       
           uglify:
               options:
                   banner:  '/*!  <%=  pkg.name  %>  <%=  grunt.template.today("yyyy-­‐mm-­‐dd")  %>  */\n'
               js:
                   files:
                       "<%=  files.js.minified  %>":  "<%=  files.js.concatenated  %>"
               jsVendor:
                   files:
                       "<%=  files.js.minifiedVendor  %>":  "<%=  files.js.concatenatedVendor  %>"
           clean:
               bower:
                   src:  bowerDirectory  grunt
               js:
                   src:  "<%=  files.js.concatenated  %>"
               css:
                   src:  "<%=  files.css.concatenated  %>"
               dist:
                   src:  ["dist",  "generated"]
           watch:
               coffee:
                   files:  "<%=  files.coffee.app  %>"
                   tasks:  ["coffeelint",  "coffee",  "concat:js"]
               css:
                   files:  ["<%=  files.css.vendor  %>",  "<%=  files.css.app  %>"]
                   tasks:  ["concat:css"]
               images:
                   files:  ["app/img/**/*.*",  "vendor/img/**/*.*"]
                   tasks:  ["copy:imagesDev"]
                   
               jade:
                   files:  ["<%=  files.jade.pageRoot  %>/<%=  files.jade.pages  %>"]
                   tasks:  ["jade:dev"]
               js:
                   files:  ["<%=  files.js.vendor  %>",  "<%=  files.js.app  %>"]
                   tasks:  ["concat:js"]
               less:
                   files:  [
                       "<%=  files.less.vendor  %>"
                       "<%=  files.less.watch  %>"
                   ]
                   tasks:  ["less",  "concat:css"]
       
               lint:
                   files:  "<%=  files.js.app  %>"
                   tasks:  ["jshint"]
       
               webfonts:
                   files:  ["vendor/webfonts/**/*.*",  "vendor/components/font-­‐awesome/fonts/**/*.*"]
                   tasks:  ["copy:webfontsDev"]
                   
               livereload:
                   options:
                       livereload:  true
                   files:  "dist/**/*.*"
                   
       grunt.loadNpmTasks  'grunt-­‐bower-­‐task'
       grunt.loadNpmTasks  'grunt-­‐coffeelint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐clean'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐coffee'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐concat'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐connect'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐copy'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐cssmin'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jshint'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐less'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐jade'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐uglify'
       grunt.loadNpmTasks  'grunt-­‐contrib-­‐watch'
       grunt.registerTask  'default',  [
           'common'
           'dev'
       ]
       grunt.registerTask  'common',  [
           'bower'
           'coffeelint'
           'jshint'
           'coffee'
           'less'
           'concat'
           'copy:staticDev'
           'copy:imagesDev'
           'copy:webfontsDev'
           'jade:dev'
       ]
       grunt.registerTask  'dev',  [
           'connect'
           'watch'
       ]
       grunt.registerTask  'dist',  [
           'uglify'
           'cssmin'
           'copy:staticDist'
           'copy:imagesDist'
           'copy:webfontsDist'
           'jade:dist'
       ]
    bowerDirectory  =  (grunt)  -­‐>
       bowerrc  =  path.join(process.cwd(),  ".bowerrc")
       bowerConfig  =  grunt.file.readJSON(bowerrc)  unless  !fs.existsSync(bowerrc)
       bowerConfig?.directory  ||  "vendor/components"
    gruntfile.coffee

    View Slide

  85. gulp  =  require  'gulp'
    gutil  =  require  'gulp-­‐util'
    fs  =  require  'fs'
    path  =  require  'path'
    bower  =  require  'gulp-­‐bower'
    clean  =  require  'gulp-­‐clean'
    coffee  =  require  'gulp-­‐coffee'
    coffeelint  =  require  'gulp-­‐coffeelint'
    concat  =  require  'gulp-­‐concat'
    jade  =  require  'gulp-­‐jade'
    jslint  =  require  'gulp-­‐jshint'
    jslintReporter  =  require  'jshint-­‐stylish'
    less  =  require  'gulp-­‐less'
    cssmin  =  require  'gulp-­‐minify-­‐css'
    connect  =  require  'gulp-­‐connect'
    uglify  =  require  'gulp-­‐uglify'
    es  =  require  'event-­‐stream'
    pkg  =  require  './package.json'
    server  =  require  './config/server'
    urlrouter  =  require  'urlrouter'
    output  =
       css:            'css/app.css'
       jsApp:        'js/app.js'
       jsVendor:  'js/vendor.js'
    files  =
       coffee:      'app/js/**/*.coffee'
       img:            'app/img/**/*.*'
       static:      'app/static/**/*.*'
       webfonts:  [
           'vendor/webfonts/**/*.*'
           'vendor/components/font-­‐awesome/fonts/**/*.*'
       ]
       jade:          'app/pages/**/*.jade'
       js:
           app:        ['app/js/**/*.js']
           vendor:  [
               'vendor/components/jquery/jquery.min.js'
               'vendor/components/underscore/underscore-­‐min.js'
               'vendor/components/angular/angular.min.js'
               'vendor/js/**/*.js'
           ]
       less:
           app:        'app/css/app.less'
           watch:    [
               'app/css/**'
               'vendor/components/bootstrap/less/**'
           ]
    config  =
       jshint:
           #  enforcing  options
           curly:  true
           eqeqeq:  true
           latedef:  true
           newcap:  true
           noarg:  true
           #  relaxing  options
           boss:  true
           eqnull:  true
           sub:  true
           #  environment/globals
           browser:  true
       jade:
           pretty:  true
           data:
               js:  output.jsApp
               jsVendor:  output.jsVendor
               css:  output.css
               pkg:  pkg
       server:
           port:  8000
           base:  'generated'
           livereload:  true
           open:  true
           middleware:  (connect,  options)  -­‐>
               middlewares  =  [];
               if  (!Array.isArray(options.base))
                   options.base  =  [options.base]
       
               directory  =  options.directory  ||  options.base[options.base.length  -­‐  1]
               options.base.forEach  (base)  -­‐>
                   #  Serve  static  files.
                   middlewares.push(connect.static(base))
       
               middlewares.push  urlrouter(server.drawRoutes)
               #  Make  directory  browse-­‐able.
               middlewares.push  connect.directory(directory)
               middlewares
    gulp.task  'default',  ['lint','build']
    gulp.task  'run',  ['lint','build','server','watch']
    gulp.task  'build',  [
           'install'
           'js'
           'css'
           'jade'
           'copy'
       ]
    gulp.task  'install',  ()  -­‐>
       bower()
    gulp.task  'lint',  ['coffeelint','jslint']
    gulp.task  'coffeelint',  ()  -­‐>
       gulp.src(files.coffee)
           .pipe(coffeelint())
           .pipe(coffeelint.reporter())
    gulp.task  'jslint',  ()  -­‐>
       gulp.src(files.js.app)
           .pipe(jslint(config.jshint))
           .pipe(jslint.reporter(jslintReporter))
       
    gulp.task  'jade',  ()  -­‐>
       gulp.src(files.jade)
           .pipe(jade(config.jade))
           .pipe(gulp.dest('./generated'))
           .pipe(gulp.dest('./dist'))
    gulp.task  'jsApp',  ()  -­‐>
       es.concat(
               gulp.src(files.coffee).pipe(coffee()),
               gulp.src(files.js.app)
           ).pipe(concat(output.jsApp))
           .pipe(gulp.dest('./generated'))
           .pipe(uglify())
           .pipe(gulp.dest('./dist'))
       
    gulp.task  'jsVendor',  ['install'],  ()  -­‐>
       gulp.src(files.js.vendor)
           .pipe(concat(output.jsVendor))
           .pipe(gulp.dest('./generated'))
           .pipe(gulp.dest('./dist'))
       
    gulp.task  'js',  ['jsApp','jsVendor']
       
    gulp.task  'css',  ['install'],  ()  -­‐>
       gulp.src(files.less.app)
           .pipe(plumber())
           .pipe(less())
           .pipe(concat(output.css))
           .pipe(gulp.dest('./generated'))
           .pipe(cssmin())
           .pipe(gulp.dest('./dist'))
       
    gulp.task  'clean',  ()  -­‐>
       gulp.src(['./dist','./generated',  bowerDirectory()])
           .pipe(clean())
    gulp.task  'copy',  ['install'],  ()  -­‐>
       es.concat(
           gulp.src(files.img)
               .pipe(gulp.dest('./generated/img'))
               .pipe(gulp.dest('./dist/img')),
           gulp.src(files.static)
               .pipe(gulp.dest('./generated/'))
               .pipe(gulp.dest('./dist/')),
           gulp.src(files.webfonts)
               .pipe(gulp.dest('./generated/fonts'))
               .pipe(gulp.dest('./dist/fonts'))
       )
    gulp.task  'watch',  ()  -­‐>
       gulp.watch  files.coffee,            ['coffeelint','jsApp']
       gulp.watch  files.js.app,            ['jslint','jsApp']
       gulp.watch  files.js.vendor,      ['jsVendor']
       gulp.watch  files.jade.pages,    ['jade']
       gulp.watch  files.less.watch,    ['css']
       gulp.watch  [files.img,  files.webfonts,  files.static],    ['copy']
       
    gulp.task  'server',  ['build'],  connect.server(config.server)
    bowerDirectory  =  ()  -­‐>
       bowerpath  =  path.join(process.cwd(),  ".bowerrc")
       bowerrc  =  fs.readFileSync(bowerpath)  unless  !fs.existsSync  bowerpath  
       bowerConfig  =  JSON.parse(bowerrc)  if  bowerrc?
       bowerConfig?.directory  ||  "vendor/components"
    gulpfile.coffee

    View Slide

  86. grunt.loadNpmTasks('grunt-­‐bower-­‐task');
    grunt.loadNpmTasks('grunt-­‐coffeelint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐clean');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐coffee');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐concat');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐connect');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐copy');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐cssmin');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jade');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐jshint');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐less');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐uglify');
    grunt.loadNpmTasks('grunt-­‐contrib-­‐watch');

    View Slide

  87. bower            =  require  'gulp-­‐bower'
    coffeelint  =  require  'gulp-­‐coffeelint'
    clean            =  require  'gulp-­‐clean'
    coffee          =  require  'gulp-­‐coffee'
    concat          =  require  'gulp-­‐concat'
    connect        =  require  'gulp-­‐connect'
       #          copy  included  via  .dest
    cssmin          =  require  'gulp-­‐minify-­‐css'
    jade              =  require  'gulp-­‐jade'
    jslint          =  require  'gulp-­‐jshint'
    less              =  require  'gulp-­‐less'
    uglify          =  require  'gulp-­‐uglify'
       #        watch  included  via  .watch

    View Slide



  88. git  co  grunt
    time  grunt  >  /dev/null
    real  0m5.114s
    user  0m4.802s
    sys    0m0.214s

    View Slide

  89. git  co  gulp
    time  gulp  >  /dev/null
    real  0m2.811s
    user  0m2.601s
    sys    0m0.241s




    git  co  grunt
    time  grunt  >  /dev/null
    real  0m5.114s
    user  0m4.802s
    sys    0m0.214s

    View Slide

  90.   

    View Slide

  91. learn more
     gulpjs.com
     gulpjs/gulp
     gulpjs

    View Slide

  92. demo code
     queenseight.com
     aranasoft/queenseight
     git checkout grunt
     git checkout gulp

    View Slide

  93. npm install gulp
    N E X T S T E P S

    View Slide

  94. dethrone grunt

    View Slide

  95. [email protected]
    @ j a y h a r r i s
    # d e t h r o n i n g G r u n t

    View Slide

  96. thank you
    @ j a y h a r r i s
    # d e t h r o n i n g G r u n t

    View Slide