as a better Grunt?

Automate your What is the front-end development workflow ? fully!
Jacek Tomaszewski

Front-end development Workflow(by @kristories)

Setup

  • Download
  • Scaffold

Develop

  • Watch
  • Lint
  • Compile
  • Test
  • Livereload

Build

  • Compile
  • Test
  • Concat
  • Minify
  • Deploy

A little bit of history

of front-end development workflow

2011

Setup phase = download & scaffold ...

<head>
  <link type="text/css" rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.16.custom.css" /> 
  <link type="text/css" rel="stylesheet" href="css/jquery.lightbox-0.5.css" /> 
  <link type="text/css" rel="stylesheet" href="css/main.css" />
  
  <script type="text/javascript" src="js/jquery-1.6.2.min.js"></script>
  <script type="text/javascript" src="js/jquery-ui-1.8.16.custom.min.js"></script>
  <script type="text/javascript" src="js/jquery.jplayer.min.js"></script>
  <script type="text/javascript" src="js/jquery.lightbox-0.5.js"></script>
  <script type="text/javascript" src="js/jquery.cycle.all.min.js"></script>
  <script type="text/javascript" src="js/jquery.mousewheel.js"></script>
  <script type="text/javascript" src="js/jquery.jscrollpane.min.js"></script>
  <script type="text/javascript" src="js/jquery.lightbox-0.5.js"></script>
  <script type="text/javascript" src="js/main.js"></script>
<head/>

In 2011, we didn't need to compile anything
Minify and concatenation? We can do it manually
Deployment? Via FTP.
Testing? Waat? It works. If not, I test it manually.

Finally, in 2012, we met these guys:

BowerYeomanGrunt

Bower

to handle project's dependencies

$ npm install -g bower

$ bower install --save jquery
$ bower install --save jquery-ui
$ bower install --save jplayer
$ bower install --save lightbox
$ bower install --save jquery.cycle
$ bower install --save jquery.mousewheel
$ bower install --save jquery.jscrollpane

Yeoman

to bootstrap and scaffold projects

$ npm install -g yo generator-bespoke
$ yo bespoke

[?] What is the title of your presentation? Hello World
[?] Would you like to use a pre-made theme? Yes
[?] Would you like bullet lists? Yes
...
   create gulpfile.js
   create README.md
   create .bowerrc
   create src/index.jade
   create src/scripts/main.js
...

I'm all done. 
Run `gulp serve` to run HTTP server on localhost:8080 and open your presentation!

Grunt

to automate development

$ grunt

Running "jshint" (jshint) task
>> 47 file lint free.

Running "qunit" (qunit) task
Testing test/tiny-pubsub.html....OK
>> 4 assertions passed (23ms)

Running "clean:dist" (clean) task
Cleaning "dist"...OK

Running "concat:dist" (concat) task
File "dist/ba-tiny-pubsub.js" created.

Running "uglify:dist" (uglify) task
File "dist/ba-tiny-pubsub.min.js" created.
Compressed size: 119 bytes gzipped (185 bytes minified).

Done, without errors.

Frontend workflow today

$ yo webapp
$ bower install --save angular
$ bower install --save angular-resource

// gruntfile.js
grunt.initConfig({
  concat: {
    dist: {
      src: [
        'vendor/bower/angular/angular.js', 
        'vendor/bower/angular-resource/angular-resource.js'
      ],
      dest: 'dist/js/vendor.js'
    }
  }
});

Grunt plugins allow us to:

  • minify js, css, http files
  • compile other syntaxes (coffeescript, sass, jade, stylus, etc.)
  • hint for errors
  • watch files for changes
  • run a HTTP server
  • run tests
  • add license info
  • compile HTML templates into JS
  • use sourcemaps
  • compress images
  • move/create somes files, directories, symlinks
  • deploy to server

This was fine.

But brought as to ..

... big bunch of spaghetti code.


        

Grunt's issues

What if we could write tasks as stream-based commands on unix?

image by: contra/gulp

Example of Unix commands

  1. Find all files in this directory
  2. Get only those with word "grunt"
  3. Delete them
$ find . | grep -l "grunt" | xargs -I {} rm -rf {}

Example of "Imaginary front-end command"

  1. Find all *.coffee files,
  2. Compile them to JS,
  3. Send through ngMinify and UglifyJS (using sourcemaps),
  4. Concat into one file,
  5. Finally save into one final file: "dist/js/main.js".
$ find src -name '*.coffee' | coffee | ng-minify | \
  uglify --sourcemaps=true | concat > dist/js/main.js
  

Grunt

Configuration over code

  • Configure plugins separately
  • You have to define exact order of tasks
  • Tasks can be run concurrently, if you configure grunt-concurrent plugin

Gulp

Code over configuration

  • Manually write your tasks, using a stream or promise
  • Gulp will take care of your tasks dependencies itself
  • Tasks are run concurrently

Gulp task example (source)

gulp.task('scripts', function() {
  return gulp.src('src/scripts/**/*.js') // <-- Read from FS
    .pipe(jshint('.jshintrc'))
    .pipe(concat('main.js'))
    .pipe(gulp.dest('dist/assets/js')) // <-- Write to FS
    .pipe(rename({suffix: '.min'}))
    .pipe(uglify())
    .pipe(gulp.dest('dist/assets/js')) // <-- Write to FS
    .pipe(livereload(server));
});

Gulp API

is just four functions

That's it!

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

gulp.task("name", ["dependency-task"], function(done) {
  // do sth ...
  done();
});
gulp.task("build", ["compile-js", "compile-css"]);
gulp.task("default", ["build", "server", "watch"]);

gulp.src(glob)

gulp.src(["app/js/**/*.js", "assets/js/**/*.js"])
gulp.task("validate-js", function(){
  return gulp.src("app/js/**/*.js")
    .pipe(jshint());
});

gulp.dest(glob)

gulp.dest("./dist/js/")
gulp.task("compile-js", function(){
  return gulp.src("app/js/**/*.js")
    .pipe(concat("app.js"))
    .pipe(gulp.dest("./dist/js/"));
});

gulp.watch(glob, tasks)

gulp.task("watch", function(){
  gulp.watch("app/js/**/*.js", ["validate-js", "compile-js"]);
});

How to start?

  1. Install Node & Gulp

    http://nodejs.org/http://gulpjs.com/
  2. Install some Gulp plugins

    http://gulpjs.com/plugins/
  3. Create gulpfile.js and code it

  4. Profit!
# Enter the project's directory
$ cd myapp

# Create package.json file (if nonexistent yet)
$ npm init

# Install gulp & its' plugins locally
# (and add them to package.json)
$ npm install --save-dev gulp gulp-sass gulp-coffee

# Code the gulpfile.js file (but learn Gulp's API first)
$ vim gulpfile.js

# Finally, run a gulp's task!
$ gulp build
var gulp = require("gulp");

gulp.task("build", function(){
  // TODO
});

gulp.task("watch", function(){
  // TODO  
});

gulp.task("default", ["build", "watch"]);

gulpfile.js

var paths = {
  scripts: "app/js/**/*.coffee",
  stylesheets: "app/css/**/*.sass"
};

gulp.task("build", ["compile-js", "compile-css"]);

gulp.task("watch", function(){
  gulp.watch(paths.scripts, ["compile-js"]);
  gulp.watch(paths.stylesheets, ["compile-css"]);
});

gulpfile.js

var sass = require("gulp-sass");

gulp.task("compile-css", function(){
  return gulp.src(paths.stylesheets)
    .pipe(sass())
    .pipe(gulp.dest("build/css/"));
});

gulpfile.js

var coffee = require("gulp-coffee");

gulp.task("compile-js", function(){
  return gulp.src(paths.scripts)
    .pipe(coffee())
    .pipe(gulp.dest("build/js/"));
});

gulpfile.js

Live demo

jtomaszewski/ionic-cordova-gulp-seed

So why it's better than Grunt?

As the gulpjs.com says:

  • Speed - tasks run concurrently
  • Efficiency - no unnecessary temporary files
  • Simplicity - Gulpfile is much cleaner

Gulp-Links

Gulp-Links

Thank you!

Questions?