$30 off During Our Annual Pro Sale. View Details »

grunt.js - automate all the things!1!!

grunt.js - automate all the things!1!!

Talk from the second stahlstadt.js meetup

http://www.meetup.com/stahlstadt-js/events/143609572/

Clemens Müller

December 11, 2013
Tweet

More Decks by Clemens Müller

Other Decks in Programming

Transcript

  1. GRUNT.JS
    Automate all the things!!1!

    View Slide

  2. SE PROBLEM
    • remove debug statements like console.log
    • concatenate and minify JS files
    • run tests, JSHint
    • compile SASS, minify images, sprites, …
    • …

    View Slide

  3. APPROACHES
    manually
    • use existing infrastructure, like Rails Asset Pipeline, …
    • Makefile, Shell Scripts
    • Ant, Maven, Jake, Rake, …

    View Slide

  4. APPROACHES
    manually
    • use existing infrastructure, like Rails Asset Pipeline, …
    • Makefile, Shell Scripts
    • Ant, Maven, Jake, Rake, …
    • Grunt

    View Slide

  5. • The JavaScript Task Runner
    • node.js
    • Configuration over scripting
    • Plugins, which are mostly wrappers around existing libs
    WHAT IS GRUNT?

    View Slide

  6. • introduced by Ben @cowboy Alman
    • gruntjs.com / @gruntjs
    • v0.4.2
    HARD FACTS

    View Slide

  7. View Slide

  8. View Slide

  9. brew install nodejs
    npm install grunt-cli --global

    View Slide

  10. GRUNTIFY A PROJECT
    • package.json
    • Gruntfile.js

    View Slide

  11. $ tree
    .
    0 directories, 0 files

    View Slide

  12. // package.json
    1 {
    2 "name": "stahlstadtjs_grunt",
    3 "version": "0.0.1",
    4 "author": "Thomas Brezina"
    5 }

    View Slide

  13. npm install grunt --save-dev

    View Slide

  14. npm install grunt --save-dev
    1 {
    2 "name": "stahlstadtjs_grunt",
    3 "version": "0.0.1",
    4 "author": "Thomas Brezina",
    5 "devDependencies": {
    6 "grunt": "~0.4.2"
    7 }
    8 }

    View Slide

  15. • Project and task configuration
    • Loading Grunt plugins and tasks
    • Custom tasks

    View Slide

  16. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 }

    View Slide

  17. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 }
    $ grunt

    View Slide

  18. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 }
    $ grunt
    Warning: Task "default" not found. Use --force to continue.
    Aborted due to warnings.

    View Slide

  19. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 grunt.registerTask("default", []);
    3 }

    View Slide

  20. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 grunt.registerTask("default", []);
    3 }
    $ grunt

    View Slide

  21. // Gruntfile.js
    1 module.exports = function(grunt) {
    2 grunt.registerTask("default", []);
    3 }
    $ grunt
    Done without errors.

    View Slide

  22. .
    !"" Gruntfile.js
    #"" package.json

    View Slide

  23. // src/app.js
    1 App = function() {}
    2 App.prototype.greet = function(name) {
    3 console.log("hello " + name)
    4 }

    View Slide

  24. .
    !"" Gruntfile.js
    !"" package.json
    #"" src
    #"" app.js

    View Slide

  25. JSHINT
    npm install grunt-contrib-jshint --save-dev

    View Slide

  26. JSHINT
    npm install grunt-contrib-jshint --save-dev
    1 {
    2 "name": "stahlstadtjs_grunt",
    3 "version": "0.0.1",
    4 "author": "Thomas Brezina",
    5 "devDependencies": {
    6 "grunt": "~0.4.2",
    7 "grunt-contrib-jshint": "~0.7.2"
    8 }
    9 }

    View Slide

  27. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 jshint: {
    4 all: ["src/*.js"]
    5 }
    6 });
    7
    8 grunt.loadNpmTask("grunt-contrib-jshint");
    9
    10 grunt.registerTask("default", ["jshint"]);
    11 }
    JSHINT

    View Slide

  28. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 jshint: {
    4 all: ["src/*.js"]
    5 }
    6 });
    7
    8 grunt.loadNpmTask("grunt-contrib-jshint");
    9
    10 grunt.registerTask("default", ["jshint"]);
    11 }
    JSHINT

    View Slide

  29. JSHINT
    $ grunt

    View Slide

  30. JSHINT
    $ grunt
    Running "jshint:all" (jshint) task
    Linting src/app.js ...ERROR
    [L3:C31] W033: Missing semicolon.
    console.log("hello " + name)
    Warning: Task "jshint:all" failed. Use --force to continue.
    Aborted due to warnings.

    View Slide

  31. // src/app.js
    1 App = function() {}
    2 App.prototype.greet = function(name) {
    3 console.log("hello " + name)
    4 }

    View Slide

  32. // src/app.js
    1 App = function() {}
    2 App.prototype.greet = function(name) {
    3 console.log("hello " + name)
    ;
    4 }

    View Slide

  33. JSHINT
    $ grunt
    Running "jshint:all" (jshint) task
    >> 1 file lint free.
    Done, without errors.

    View Slide

  34. JSHINT
    $ grunt jshint
    Running "jshint:all" (jshint) task
    >> 1 file lint free.
    Done, without errors.

    View Slide

  35. JSHINT
    $ grunt jshint
    Running "jshint:all" (jshint) task
    >> 1 file lint free.
    Done, without errors.
    ProTip™ http://jslinterrors.com/

    View Slide

  36. UGLIFY
    npm install grunt-contrib-uglify --save-dev

    View Slide

  37. UGLIFY
    npm install grunt-contrib-uglify --save-dev
    1 {
    2 "name": "stahlstadtjs_grunt",
    3 "version": "0.0.1",
    4 "author": "Thomas Brezina",
    5 "devDependencies": {
    6 "grunt": "~0.4.2",
    7 "grunt-contrib-jshint": "~0.7.2",
    8 "grunt-contrib-uglify": "~0.2.7"
    9 }
    10 }

    View Slide

  38. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 jshint: { },
    4 uglify: {
    5 debug: {
    6 src: "src/app.js",
    7 dest: "dist/app.js",
    8 options: { beautify: true }
    9 },
    10 minified: {
    11 src: "src/app.js",
    12 dest: "dist/app.min.js",
    13 options: { mangle: true, compress: true }
    14 }
    15 }
    16 });
    17
    18 grunt.registerTask("default", ["jshint", "uglify"]);
    19
    20 grunt.loadNpmTasks("grunt-contrib-uglify");
    21 grunt.loadNpmTasks("grunt-contrib-jshint");
    22 }

    View Slide

  39. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 jshint: { },
    4 uglify: {
    5 debug: {
    6 src: "src/app.js",
    7 dest: "dist/app.js",
    8 options: { beautify: true }
    9 },
    10 minified: {
    11 src: "src/app.js",
    12 dest: "dist/app.min.js",
    13 options: { mangle: true, compress: true }
    14 }
    15 }
    16 });
    17
    18 grunt.registerTask("default", ["jshint", "uglify"]);
    19
    20 grunt.loadNpmTasks("grunt-contrib-uglify");
    21 grunt.loadNpmTasks("grunt-contrib-jshint");
    22 }

    View Slide

  40. UGLIFY
    $ grunt uglify:minified
    Running "uglify: minified" (uglify) task
    File "dist/app.min.js" created.
    Done, without errors.

    View Slide

  41. UGLIFY
    $ grunt uglify:debug
    Running "uglify:debug" (uglify) task
    File "dist/app.js" created.
    Done, without errors.

    View Slide

  42. UGLIFY
    $ grunt uglify
    Running "uglify:debug" (uglify) task
    File "dist/app.js" created.
    Running "uglify:minified" (uglify) task
    File "dist/app.min.js" created.
    Done, without errors.

    View Slide

  43. UGLIFY
    .
    !"" Gruntfile.js
    !"" package.json
    #"" dist
    $ !"" app.js
    $ #"" app.min.js
    #"" src
    #"" app.js

    View Slide

  44. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 pkg: grunt.file.readJSON("package.json"),
    4 jshint: {},
    5 uglify: {
    6 debug: {
    7 src: "src/app.js",
    8 dest: "dist/app-<%= pkg.version %>.js",
    9 options: { beautify: true }
    10 },
    11 minified: {
    12 src: "src/app.js",
    13 dest: "dist/app-<%= pkg.version %>.min.js",
    14 options: { mangle: true, compress: true }
    15 }
    16 }
    17 });
    18
    19 grunt.registerTask("default", ["jshint", "uglify"]);
    20
    21 grunt.loadNpmTasks("grunt-contrib-uglify");
    22 grunt.loadNpmTasks("grunt-contrib-jshint");
    23 }

    View Slide

  45. 1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 pkg: grunt.file.readJSON("package.json"),
    4 jshint: {},
    5 uglify: {
    6 debug: {
    7 src: "src/app.js",
    8 dest: "dist/app-<%= pkg.version %>.js",
    9 options: { beautify: true }
    10 },
    11 minified: {
    12 src: "src/app.js",
    13 dest: "dist/app-<%= pkg.version %>.min.js",
    14 options: { mangle: true, compress: true }
    15 }
    16 }
    17 });
    18
    19 grunt.registerTask("default", ["jshint", "uglify"]);
    20
    21 grunt.loadNpmTasks("grunt-contrib-uglify");
    22 grunt.loadNpmTasks("grunt-contrib-jshint");
    23 }

    View Slide

  46. UGLIFY
    $ grunt uglify
    Running "uglify:debug" (uglify) task
    File "dist/app-0.0.1.js" created.
    Running "uglify:minified" (uglify) task
    File "dist/app-0.0.1.min.js" created.
    Done, without errors.

    View Slide

  47. UGLIFY
    • sourcemaps
    • banner
    • conditional compilation
    • …

    View Slide

  48. UGLIFY
    .
    !"" Gruntfile.js
    !"" package.json
    #"" dist
    $ !"" app-0.0.1.js
    $ !"" app-0.0.1.min.js
    $ !"" app.js
    $ #"" app.min.js
    #"" src
    #"" app.js

    View Slide

  49. CLEAN
    npm install grunt-contrib-clean --save-dev

    View Slide

  50. CLEAN
    npm install grunt-contrib-clean --save-dev
    1 {
    2 "name": "stahlstadtjs_grunt",
    3 "version": "0.0.1",
    4 "author": "Thomas Brezina",
    5 "devDependencies": {
    6 "gruntnt": "~0.4.2",
    7 "grunt-contrib-jshint": "~0.7.2",
    8 "grunt-contrib-uglify": "~0.2.7"
    9 "grunt-contrib-clean": "~0.5.0"
    10 }
    11 }

    View Slide

  51. CLEAN
    1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 pkg: …,
    4 jshint: { … },
    5 uglify: { … },
    6 clean: ["dist"]
    7 });
    8
    9 grunt.registerTask("default", ["jshint", "clean", "uglify"]);
    10
    11 grunt.loadNpmTasks("grunt-contrib-clean");
    12 grunt.loadNpmTasks("grunt-contrib-uglify");
    13 grunt.loadNpmTasks("grunt-contrib-jshint");
    14 }

    View Slide

  52. CLEAN
    $ grunt clean

    View Slide

  53. CLEAN
    $ grunt clean
    Running "clean:0" (clean) task
    Cleaning dist...OK
    Done, without errors.

    View Slide

  54. CLEAN
    .
    !"" Gruntfile.js
    !"" package.json
    #"" src
    #"" app.js

    View Slide

  55. ALLE 3 ZUSAMMEN
    $ grunt

    View Slide

  56. ALLE 3 ZUSAMMEN
    $ grunt
    Running "jshint:all" (jshint) task
    >> 1 file lint free.
    Running "clean:0" (clean) task
    Cleaning dist...OK
    Running "uglify:debug" (uglify) task
    File "dist/app-0.0.1.js" created.
    Running "uglify:minified" (uglify) task
    File "dist/app-0.0.1.min.js" created.
    Done, without errors.

    View Slide

  57. MOAR PLUGINS
    • currently 1931 plugins
    • almost all start with grunt-
    • grunt-contrib-* are official plugins supported by core team
    • tip top README’s

    View Slide

  58. LOAD-GRUNT-TASKS
    1 module.exports = function(grunt) {
    2 grunt.loadNpmTasks("grunt-shell");
    3 grunt.loadNpmTasks("grunt-sass");
    4 grunt.loadNpmTasks("grunt-recess");
    5 grunt.loadNpmTasks("grunt-sizediff");
    6 grunt.loadNpmTasks("grunt-svgmin");
    7 grunt.loadNpmTasks("grunt-styl");
    8 grunt.loadNpmTasks("grunt-php");
    9 grunt.loadNpmTasks("grunt-eslint");
    10 grunt.loadNpmTasks("grunt-concurrent");
    11 grunt.loadNpmTasks("grunt-bower-requirejs");
    12 }

    View Slide

  59. LOAD-GRUNT-TASKS
    1 module.exports = function(grunt) {
    2 // load all grunt tasks matching the `grunt-*` pattern
    3 require("load-grunt-tasks")(grunt);
    4 }

    View Slide

  60. GRUNT-CONTRIB-CONCAT
    1 grunt.initConfig({
    2 concat: {
    3 dist: {
    4 src: ["src/main.js", "src/**/*.js"],
    5 dest: "dist/app.js"
    6 }
    7 }
    8 });

    View Slide

  61. GRUNT-NEUTER
    // a.js
    require("b");
    new B();
    // b.js
    require("c");
    B = function() {
    new C();
    }
    // c.js
    C = function() {}
    // d.js
    D = function() {}

    View Slide

  62. GRUNT-NEUTER
    // a.js
    require("b");
    new B();
    // b.js
    require("c");
    B = function() {
    new C();
    }
    // c.js
    C = function() {}
    // d.js
    D = function() {}
    // Gruntfile.js
    neuter: {
    src: ["a.js"],
    dest: "dist/app.js"
    }
    +

    View Slide

  63. GRUNT-NEUTER
    // a.js
    require("b");
    new B();
    // b.js
    require("c");
    B = function() {
    new C();
    }
    // c.js
    C = function() {}
    // d.js
    D = function() {}
    // dist/app.js
    C = function() {}
    B = function() {
    new C();
    }
    new B();
    // Gruntfile.js
    neuter: {
    src: ["a.js"],
    dest: "dist/app.js"
    }
    + =

    View Slide

  64. GRUNT-NEUTER
    // a.js
    require("b");
    new B();
    // b.js
    require("c");
    B = function() {
    new C();
    }
    // c.js
    C = function() {}
    // d.js
    D = function() {}
    // dist/app.js
    C = function() {}
    B = function() {
    new C();
    }
    new B();
    D = function() {}
    // Gruntfile.js
    neuter: {
    src: ["a.js", “*.js”],
    dest: "dist/app.js"
    }
    + =

    View Slide

  65. GRUNT-CONTRIB-WATCH
    grunt.initConfig({
    watch: {
    gruntfile: {
    files: "Gruntfile.js",
    tasks: ["jshint:gruntfile"],
    },
    src: {
    files: ["lib/*.js", "css/**/*.scss", "!lib/dontwatch.js"],
    tasks: ["default"],
    }
    }
    });

    View Slide

  66. GRUNT-NOTIFY
    • notifications
    • useful for tasks running in background

    View Slide

  67. GRUNT-NOTIFY
    • notifications
    • useful for tasks running in background

    View Slide

  68. SASS, LESS, STYLUS, ...
    • grunt-contrib-sass
    • grunt-sass
    • grunt-contrib-less
    • grunt-contrib-stylus

    View Slide

  69. GRUNT-CONTRIB-CONNECT
    grunt.initConfig({
    connect: {
    server: {
    options: {
    port: 8000,
    base: "dist"
    }
    }
    }
    });

    View Slide

  70. GRUNT-CONTRIB-CONNECT
    grunt.initConfig({
    connect: {
    server: {
    options: {
    port: 8000,
    base: "dist"
    }
    },
    test: {
    options: {
    port: 8001,
    base: "test"
    }
    }
    }
    });

    View Slide

  71. GRUNT-CONTRIB-CONNECT
    • multiple servers
    • watch task & live-reload
    • grunt-connect-proxy
    • grunt-browser-sync

    View Slide

  72. ES6-MODULE-TRANSPILER
    • Tomorrow’s JavaScript module syntax today
    • https://github.com/joefiorini/grunt-es6-module-transpiler

    View Slide

  73. OTHER USEFUL PLUGINS
    • grunt-s3
    • grunt-release
    • grunt-parallel, grunt-newer
    • grunt-remove-logging
    • mincss, uncss, csslint
    • imagemin, spritesmith
    • responsive-images
    • qunit, mocha, jasmine,
    CasperJS, SlimerJS, ...
    • http://gruntjs.com/plugins

    View Slide

  74. CUSTOM TASKS
    1 module.exports = function(grunt) {
    2 grunt.initConfig({
    3 watch: { ... },
    4 connect: {
    5 test: { ... }
    6 }
    7 });
    8
    9 grunt.registerTask("test", ["connect:test", "watch"]);
    10 }

    View Slide

  75. WRITING YOUR OWN TASK
    1 module.exports = function(grunt) {
    2 grunt.registerTask("random", "a random number", function() {
    3 var math = require("fast-random-number-NPM-module");
    4 grunt.log.writeln( math.random() );
    5 });
    6 }

    View Slide

  76. WRITING YOUR OWN TASK
    1 module.exports = function(grunt) {
    2 grunt.registerTask("random", "a random number", function() {
    3 var math = require("fast-random-number-NPM-module");
    4 grunt.log.writeln( math.random() );
    5 });
    6 }

    View Slide

  77. View Slide

  78. 1 module.exports = function(grunt) {
    2 // load needed grunt tasks from package.json
    3 grunt.loadNpmTasks("grunt-contrib-clean");
    4 ...

    View Slide

  79. 1 module.exports = function(grunt) {
    2 // load needed grunt tasks from package.json
    3 grunt.loadNpmTasks("grunt-contrib-clean");
    4 ...
    5
    6 // configure the tasks
    7 grunt.initConfig({
    8 jshint: { ... },
    9 ...
    10 });

    View Slide

  80. 1 module.exports = function(grunt) {
    2 // load needed grunt tasks from package.json
    3 grunt.loadNpmTasks("grunt-contrib-clean");
    4 ...
    5
    6 // configure the tasks
    7 grunt.initConfig({
    8 jshint: { ... },
    9 ...
    10 });
    11
    12 // specify default and custom tasks
    13 grunt.registerTask("default", ["dist"]);
    14 grunt.registerTask("dist", ["clean", "uglify"]);
    15 grunt.registerTask("test", ["watch", "connect"]);
    16 }

    View Slide

  81. 1 module.exports = function(grunt) {
    2 // load needed grunt tasks from package.json
    3 grunt.loadNpmTasks("grunt-contrib-clean");
    4 ...
    5
    6 // configure the tasks
    7 grunt.initConfig({
    8 jshint: { ... },
    9 ...
    10 });
    11
    12 // specify default and custom tasks
    13 grunt.registerTask("default", ["dist"]);
    14 grunt.registerTask("dist", ["clean", "uglify"]);
    15 grunt.registerTask("test", ["watch", "connect"]);
    16 }

    View Slide

  82. 0.5.0
    • split configurations since Gruntfile can get big #989
    • load-grunt-tasks, load-grunt-config
    • interactive CLI #949
    • Make task file compilation more intelligent #927
    • …

    View Slide

  83. PROJECTS WHICH MAKE
    GOOD USE OF GRUNT.JS
    • https://github.com/bevacqua/unbox
    • https://github.com/stefanpenner/ember-app-kit

    View Slide

  84. LINKS
    • http://tamas.io/introduction-to-grunt/
    • http://merrickchristensen.com/articles/gruntjs-workflow.html
    • https://speakerdeck.com/ginader/let-grunt-do-the-work-focus-on-
    the-fun
    • https://speakerdeck.com/addyosmani/automating-front-end-
    workflow
    • http://blog.ponyfoo.com/2013/11/13/grunt-tips-and-tricks

    View Slide

  85. POSSIBLE NEXT TALK’S
    • by YOU?!!!!
    • split options for tasks into
    separate files
    • more examples
    • live reload, across browsers
    • requirejs, grunt-neuter
    • grunt & yeoman & bower
    • project scaffolding
    • grunt advanced patterns
    • tips’n’tricks
    • grunt in the wild

    View Slide

  86. DANK U VOOR UW
    AANDACHT.
    @pangratz
    @stahlstadtjs

    View Slide