Slide 1

Slide 1 text

Advanced JavaScript Build Pipelines with Gulp.js

Slide 2

Slide 2 text

Let's talk a short bit about the JS build tool revolution

Slide 3

Slide 3 text

HTML CSS JavaScript

Slide 4

Slide 4 text

Sass CoffeeScript LESS P o s t C S S H A M L J a d e U g l i f y E S 6 R e a c t J S B r o w s e r i f y A n g u l a r J S E m b e r C S S M i n J S L i n t ESHint ImageOptim Mocha Jasmine TypeScript

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

1530 lines of code original Ant tasks used: concat — copy — delete — mkdir

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

Maybe Java isn't the right tool? Java is to JavaScript what
 Alf is to Gandalf

Slide 12

Slide 12 text

Grunt started a boom

Slide 13

Slide 13 text

Gruntfiles get long

Slide 14

Slide 14 text

Grunt tasks get slow: lots of reads and writes

Slide 15

Slide 15 text

One unmanageable horde of (sometimes) low quality plug-ins

Slide 16

Slide 16 text

And then came Gulp

Slide 17

Slide 17 text

React is in love with Webpack

Slide 18

Slide 18 text

Ember.js digs Broccoli.js

Slide 19

Slide 19 text

Ember.js digs Broccoli.js

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

So… every community seems to have its preference Stick with your communities preference!

Slide 22

Slide 22 text

For all the rest? Gulp might be a good, very good option

Slide 23

Slide 23 text

Disclaimer

Slide 24

Slide 24 text

I (occasionally) contribute to Gulp

Slide 25

Slide 25 text

I'm writing a book on Gulp http://bit.ly/gulp-tool-book 39% off with 39baumgar
 coupon code!

Slide 26

Slide 26 text

gulp.src(…) gulp.dest(…) Reads files Writes files

Slide 27

Slide 27 text

gulp.src(…) .pipe(uglify()) gulp.dest(…)

Slide 28

Slide 28 text

gulp.src(…) .pipe(uglify()) gulp.dest(…) .pipe(concat())

Slide 29

Slide 29 text

task manager streaming file system file watcher

Slide 30

Slide 30 text

gulp.task('scripts', function() { return gulp.src(‘src/**/*.js') .pipe(uglify()) .pipe(concat('main.min.js')) .pipe(gulp.dest('dist')); }); gulp.task('default', function(done) { gulp.watch(‘src/**/*.js', gulp.parallel(‘scripts')); done(); });

Slide 31

Slide 31 text

undertaker.task('scripts', function() { return vinyl.src(‘src/**/*.js') .pipe(uglify()) .pipe(concat('main.min.js')) .pipe(vinyl.dest('dist')); }); undertaker.task('default', function(done) { chokidar.watch(‘src/**/*.js', undertaker.parallel(‘scripts')); done(); });

Slide 32

Slide 32 text

so similar … yet so different? Gulp Streams Browserify 
 Streams

Slide 33

Slide 33 text

var source = require(‘vinyl-source-stream’); var b = browserify({ entries: ['_js/main.js'] }); var bundle = function() { return b.bundle() .pipe(source(‘main.js’)) .pipe(gulp.dest('js')); } gulp.task(‘bundle’, bundle);

Slide 34

Slide 34 text

This also goes the other way around…

Slide 35

Slide 35 text

var app = express(); var router = express.Router(); router.get('/*', function(req, res) { var stream = request('http://host.to.forward.to' + req.originalUrl); stream.pipe(res); stream .pipe(source('.' + req.originalUrl)) .pipe(gulp.dest('./cachedir')); }); app.use(express.static('_site')); app.use(express.static('cachedir')); app.use(router);

Slide 36

Slide 36 text

var app = express(); var router = express.Router(); router.get('/*', function(req, res) { var stream = request('http://host.to.forward.to' + req.originalUrl); stream.pipe(res); stream .pipe(source('.' + req.originalUrl)) .pipe(gulp.dest('./cachedir')); }); app.use(express.static('_site')); app.use(express.static('cachedir')); app.use(router);

Slide 37

Slide 37 text

So start thinking streams …

Slide 38

Slide 38 text

Multiple input formats return gulp.src(‘src/**/*.coffee) .pipe(coffee()) .pipe(uglify()) .pipe(concat(‘main.js’)) .pipe(gulp.dest(‘build’))

Slide 39

Slide 39 text

Multiple input formats return gulp.src(‘src/**/*.js) .pipe(uglify()) .pipe(concat(‘main.js’)) .pipe(gulp.dest(‘build’))

Slide 40

Slide 40 text

Multiple input formats return gulp.src(‘src/**/*.js) .pipe(uglify()) .pipe(concat(‘main.js’)) .pipe(gulp.dest(‘build’)) the same!

Slide 41

Slide 41 text

Multiple input formats And actually, you just want one bundle in the end

Slide 42

Slide 42 text

What if we could reuse parts of the stream?

Slide 43

Slide 43 text

return gulp.src(‘app1/src/**/*.coffee’) .pipe(coffee()) .pipe(uglify() .pipe(concat(‘main.js’)) .pipe(gulp.dest('build'));

Slide 44

Slide 44 text

var merge = require(‘merge2’); return merge(gulp.src(‘app1/src/**/*.coffee’) .pipe(coffee()), gulp.src(‘app1/src/**/*.js’)) .pipe(uglify() .pipe(concat(‘main.js’)) .pipe(gulp.dest('build'));

Slide 45

Slide 45 text

var merge = require(‘merge2’); return gulp.src(‘app1/src/**/*.coffee’) .pipe(markdown()), .pipe(gulp.src(‘app1/src/**/*.js’), {passthrough: true}) .pipe(rename(blogFn)) .pipe(wrap(layoutStr)) .pipe(swig()) .pipe(gulp.dest('build'));

Slide 46

Slide 46 text

Multiple bundles And now we need this over and over again for all our applications…

Slide 47

Slide 47 text

var elems = [ { dir: ‘app1’, bundleName: ‘app1.min.js’ }, { dir: ‘app2’, bundleName: ‘app2.min.js’ } ];

Slide 48

Slide 48 text

var streams = elems.map(function(el) { return gulp.src(el.dir + ‘src/**/*.coffee’) .pipe(coffee()), .pipe(gulp.src(el.dir + ‘src/**/*.js’), {passthrough: true}) .pipe(uglify()) .pipe(concat(el.bundleName)) }); return merge(streams) .pipe(gulp.dest('build'));

Slide 49

Slide 49 text

Incremental builds

Slide 50

Slide 50 text

Some tasks take long gulp.src(‘scripts/*.js’) .pipe(uglify()) .pipe(gulp.dest()) .pipe(concat())

Slide 51

Slide 51 text

Some tasks take long gulp.src(‘scripts/*.js’) .pipe(uglify()) .pipe(gulp.dest()) .pipe(concat())

Slide 52

Slide 52 text

Some tasks take long gulp.src(‘scripts/*.js’) .pipe(uglify()) .pipe(gulp.dest()) .pipe(concat()) Too much is going on! Each change: Uglify all the files?

Slide 53

Slide 53 text

Some tasks take long gulp.src(‘scripts/*.js’) .pipe(uglify()) .pipe(gulp.dest()) .pipe(concat())

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

filter files that have changed

Slide 56

Slide 56 text

filter files that have changed do performance heavy operations

Slide 57

Slide 57 text

filter files that have changed do performance heavy operations remember the old files

Slide 58

Slide 58 text

filter files that have changed do performance heavy operations remember the old files and continue with the other ops

Slide 59

Slide 59 text

gulp.task('scripts', function () { return gulp.src('app/scripts/**/*.js') .pipe(cached('ugly')) .pipe(uglify()) .pipe(remember('ugly')) .pipe(concat('main.min.js')) .pipe(gulp.dest('dist/scripts')); });

Slide 60

Slide 60 text

gulp.task('scripts', function () { return gulp.src('app/scripts/**/*.js') .pipe(cached('ugly')) .pipe(uglify()) .pipe(remember('ugly')) .pipe(concat('main.min.js')) .pipe(gulp.dest('dist/scripts')); }); we use the cache to check if files have changed

Slide 61

Slide 61 text

gulp.task('scripts', function () { return gulp.src('app/scripts/**/*.js') .pipe(cached('ugly')) .pipe(uglify()) .pipe(remember('ugly')) .pipe(concat('main.min.js')) .pipe(gulp.dest('dist/scripts')); }); once we are done, we remember all the other files we stored in the cache

Slide 62

Slide 62 text

gulp.task('lint', function () { return gulp.src(‘src/**/*.js’) .pipe(jshint()) .pipe(jshint.reporter(‘default’)) .pipe(jshint.reporter(‘fail’)); });

Slide 63

Slide 63 text

gulp.task('lint', function () { return gulp.src(‘src/**/*.js’, { since: gulp.lastRun(‘lint’) }) .pipe(jshint()) .pipe(jshint.reporter(‘default’)) .pipe(jshint.reporter(‘fail’)); });

Slide 64

Slide 64 text

gulp.task('images', function () { return gulp.src(‘src/images/**/*.*’) .pipe(imagemin()) .pipe(gulp.dest(‘dist’)); });

Slide 65

Slide 65 text

gulp.task('images', function () { return gulp.src(‘src/images/**/*.*’) .pipe(newer(‘dist’)) .pipe(imagemin()) .pipe(gulp.dest(‘dist’)); });

Slide 66

Slide 66 text

When architecting Gulp build pipelines…

Slide 67

Slide 67 text

Separate the process from the content

Slide 68

Slide 68 text

Think about how the basic pipeline would look like

Slide 69

Slide 69 text

Think about how your data looks like

Slide 70

Slide 70 text

And then posh your pipes up with stream tools that change your data accordingly

Slide 71

Slide 71 text

No content

Slide 72

Slide 72 text

@ddprrt