Slide 1

Slide 1 text

Frontend Tooling with Grunt / Gulp / Yeoman Workshop JavaScript Days Munich, March 2014

Slide 2

Slide 2 text

https://github.com/ddprrt/tooling-workshop https://github.com/ddprrt/generator-netural https://fettblog.eu Workshop samples

Slide 3

Slide 3 text

Servus

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

@ddprrt

Slide 6

Slide 6 text

Netural

Slide 7

Slide 7 text

workingdraft.de

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

Our development stack

Slide 10

Slide 10 text

Sass LESS Stylus CoffeeScript TypeScript JSHint JSLint HAML Jade Uglify CSSMIN ImageOptim Jasmine Mocha Proxy Connections Server Deployment CI/CD

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

I should use a tool

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 17

Slide 17 text

! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !

Slide 18

Slide 18 text

! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 1530 lines of code

Slide 19

Slide 19 text

! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 1530 lines of code Original Ant tasks used:

Slide 20

Slide 20 text

! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 1530 lines of code Original Ant tasks used: concat - copy - delete - mkdir

Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

In the end, I realized that a task-based build tool with built-in, commonly used tasks was the approach that would work best for me. Unfortunately, I couldn’t find a build tool that worked the way that I wanted. So I built one. — Ben Alman, 2012

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

eco system

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

Runtime Environment JavaScript

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

package manager each module is installed via npm grunt and grunt plugins

Slide 30

Slide 30 text

grunt provides an interface to node modules grunt plugins are usually just a wrapper

Slide 31

Slide 31 text

npm install -g grunt-cli npm init touch Gruntfile.js

Slide 32

Slide 32 text

to the command line!

Slide 33

Slide 33 text

to the command line!

Slide 34

Slide 34 text

npm install --save-dev grunt-contrib-* npm install --save-dev grunt

Slide 35

Slide 35 text

• parameter --save-dev adds modules to package.json • node_modules holds all the installed modules • npm install takes package.json to install required modules

Slide 36

Slide 36 text

'use strict'; ! module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); ! grunt.initConfig({ uglify: { dist: { files: [{ 'scripts/main.min.js' : ['scripts/main.js'] }] } } }); ! grunt.registerTask('default', [ 'uglify' ]); };

Slide 37

Slide 37 text

'use strict'; ! module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); ! grunt.initConfig({ uglify: { dist: { files: [{ 'scripts/main.min.js' : ['scripts/main.js'] }] } } }); ! grunt.registerTask('default', [ 'uglify' ]); }; task

Slide 38

Slide 38 text

'use strict'; ! module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); ! grunt.initConfig({ uglify: { dist: { files: [{ 'scripts/main.min.js' : ['scripts/main.js'] }] } } }); ! grunt.registerTask('default', [ 'uglify' ]); }; task target

Slide 39

Slide 39 text

'use strict'; ! module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-uglify'); ! grunt.initConfig({ uglify: { dist: { files: [{ 'scripts/main.min.js' : ['scripts/main.js'] }] } } }); ! grunt.registerTask('default', [ 'uglify' ]); }; task target options

Slide 40

Slide 40 text

• registerTask registers a sequence of tasks! • grunt calls default task
 grunt build calls build task • initConfig takes an object of task definitions • loadNpmTasks loads one installed task
 has to be defined in package.json!

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

basic tasks

Slide 44

Slide 44 text

contrib tasks

Slide 45

Slide 45 text

Sass LESS Stylus CoffeeScript TypeScript JSHint JSLint HAML Jade Uglify CSSMIN ImageOptim Jasmine Mocha Proxy Connections Server Deployment CI/CD

Slide 46

Slide 46 text

Sass LESS Stylus CoffeeScript TypeScript JSHint JSLint HAML Jade Uglify CSSMIN ImageOptim Jasmine Mocha Proxy Connections Server Deployment CI/CD

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

connect

Slide 53

Slide 53 text

Connect Connect is a middleware framework for node, shipping with over 18 bundled middleware and a rich selection of 3rd-party middleware.

Slide 54

Slide 54 text

connect.static connect.directory connect.static connect.session

Slide 55

Slide 55 text

connect.static connect.directory Request cannot GET Response OK Response OK

Slide 56

Slide 56 text

connect: { server: { options: { port: 9009, base: '.', hostname: ‘0.0.0.0’, keepalive: true } } },

Slide 57

Slide 57 text

Problem? Keep the server alive indefinitely. Note that if this option is enabled, any tasks specified after this task will never run. By default, once grunt's tasks have completed, the web server stops. This option changes that behavior.

Slide 58

Slide 58 text

connect + watch

Slide 59

Slide 59 text

grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks(‘grunt-contrib-watch'); ! grunt.registerTask('serve', [ 'connect:server', 'watch' ]);

Slide 60

Slide 60 text

watch: { scripts: { files: ['scripts/*.js'], tasks: ['jshint', 'uglify'] }, }, …

Slide 61

Slide 61 text

connect.static connect.directory livereload Request Response OK! Response Injected

Slide 62

Slide 62 text

connect: { server: { options: { port: 9009, base: '.', hostname: ‘0.0.0.0’, keepalive: true } } },

Slide 63

Slide 63 text

connect: { server: { options: { port: 9009, base: '.', hostname: ‘0.0.0.0’, livereload: 35729, open: true } } },

Slide 64

Slide 64 text

middlewares

Slide 65

Slide 65 text

return function(req, res, next) { if(/* req fulfills some condition */) { /* do something */ res.write(stdout); res.end(); } else { next(); /* next middleware in stack */ } }

Slide 66

Slide 66 text

https://github.com/ddprrt/connect-php https://www.npmjs.org/package/connect-php http://fettblog.eu/blog/2013/11/17/the-magic-of-grunt- contrib-connect-and-how-to-run-php-with-it/

Slide 67

Slide 67 text

CI / CD

Slide 68

Slide 68 text

Yes, we can!

Slide 69

Slide 69 text

Development Distribution Server + Livereload + Watch Sass —> TMP JS Unminified Assemble —> TMP Linting

Slide 70

Slide 70 text

Development Distribution Server + Livereload + Watch Sass —> TMP JS Unminified Assemble —> TMP Linting Sass —> DIST JS Concat —> DIST + Minify Assemble —> DIST Linting Usemin

Slide 71

Slide 71 text

grunt.initConfig({ // configurable paths yeoman: { app: 'app', dist: 'dist' }, … });

Slide 72

Slide 72 text

sass: { dist: { options: { style: 'compressed' }, files: { '<%= yeoman.dist %>/styles/main.css': '<%= yeoman.app %>/styles/main.scss' } } ...

Slide 73

Slide 73 text

... server: { options: { style: ‘expanded’, debugInfo: true }, files: { ‘.tmp/styles/main.css': '<%= yeoman.app %>/styles/main.scss' } } }

Slide 74

Slide 74 text

• do not commit node_modules • run
 npm install
 grunt build • npm install just installs/updates modules which aren’t there already

Slide 75

Slide 75 text

https://github.com/ddprrt/generator-netural http://assemble.io/ http://fettblog.eu/blog/2013/09/02/using-assemble-io-with- yeoman-ios-webapp-gruntfile/

Slide 76

Slide 76 text

gruntplugins

Slide 77

Slide 77 text

• Install grunt-init with npm install -g grunt-init • Install the gruntplugin template with git clone git://github.com/gruntjs/grunt-init- gruntplugin.git ~/.grunt-init/gruntplugin • Run grunt-init gruntplugin in an empty directory.! • Run npm install to prepare the development environment.

Slide 78

Slide 78 text

No content

Slide 79

Slide 79 text

No content

Slide 80

Slide 80 text

No content

Slide 81

Slide 81 text

grunt++

Slide 82

Slide 82 text

'use strict'; ! module.exports = function(grunt) { // load all grunt tasks require('load-grunt-tasks')(grunt); ! ... ! }; npm install --save-dev load-grunt-tasks

Slide 83

Slide 83 text

'use strict'; ! module.exports = function(grunt) { require(‘matchdep’) .filterDev(‘grunt-*') .forEach(grunt.loadNpmTasks); // faster! ! ... }; npm install --save-dev matchdep

Slide 84

Slide 84 text

'use strict'; ! module.exports = function(grunt) { require(‘load-grunt-config')(grunt); ! //Config intialized … now registerTasks ... }; npm install --save-dev load-grunt-config

Slide 85

Slide 85 text

- myproject/ |-- Gruntfile.js |-- grunt/ |-- concat.js |-- uglify.js |-- imagemin.js

Slide 86

Slide 86 text

// uglify.js ! module.exports = { dist: { files: { 'dist/build.min.js': ['dist/build.js'] } } };

Slide 87

Slide 87 text

http://use-init.com/ http://www.html5rocks.com/en/tutorials/tooling/ supercharging-your-gruntfile/

Slide 88

Slide 88 text

// grunt-contrib-concurrent runs tasks parallel ! concurrent: { dist: [ 'sass', 'assemble' ], }

Slide 89

Slide 89 text

No content

Slide 90

Slide 90 text

• Stream based build system • Configuration over code • Simple API • Small idiomatic Node modules Gulp?

Slide 91

Slide 91 text

• Accessing the file system too often • Not really a “pipe” — tasks are run sequentially without being connected to the other With Grunt …

Slide 92

Slide 92 text

• The Pipe is everything • Defining one bigger “task” of smaller build steps With Gulp …

Slide 93

Slide 93 text

Gulp API gulp.task(‘taskname’, [‘deps’], fn); gulp.watch(‘files.js’, [‘tasks’], fn); gulp.src([’files.js’]); gulp.dest(’endpoint.js’);

Slide 94

Slide 94 text

No content

Slide 95

Slide 95 text

No content

Slide 96

Slide 96 text

Apples & Oranges • Code over configuration • Tasks run a sequence of methods • Methods don’t care about IO • Good amount of plugins, able to use unrelated modules • Configuration over code • Small, independent tasks, but with strong connection to IO • S**tload of plugins, a lot of them not really good • There might be a task for your need already there

Slide 97

Slide 97 text

No content

Slide 98

Slide 98 text

No content

Slide 99

Slide 99 text

node-task • https://github.com/node-task/spec • Common ground for both gulp and grunt • Uses best practices of both libraries • The only thing different will be the interface

Slide 100

Slide 100 text

yeoman

Slide 101

Slide 101 text

• yeoman takes care about project scaffolding: installing best practices and common project standards by just one click! • plenty of official and community generators are out there! • overcomes blank page angst

Slide 102

Slide 102 text

npm install -g yo npm install -g generator-webapp yo webapp this.template() Usage

Slide 103

Slide 103 text

this.prompt() this.mkdir() this.copy() this.template() API

Slide 104

Slide 104 text

workingdraft.de/159

Slide 105

Slide 105 text

workingdraft.de/152

Slide 106

Slide 106 text

workingdraft.de/78

Slide 107

Slide 107 text

No content

Slide 108

Slide 108 text

No content