Frontend JS workflow - Gulp 4 and the like

Post on 21-Apr-2017

1.142 views 4 download

Transcript of Frontend JS workflow - Gulp 4 and the like

Frontend workflow

Gulp 4 and the likeBy / Damien Seguin @dmnsgn

FRENCH Creative Developer at UNIT9, Rockstar

/ GitHub Twitter

What are the common components of our current workflow?

What is new in Gulp 4?

Are task-runner still relevant? What to expect in 2016?

What are the commoncomponents in our current

workflow?Package manager

Module system/syntax

Module loader/bundler

Task runner / Build system

Package ManagerHandles dependencies

Package: a set of files that are bundled togetherand can be installed and removed as a group.

Centralisation (one database/registry to pull data from)

Installation/uninstallation (one command to execute)

Upgrade (versionning)

Link Dependencies (avoid dependency hell)

Two main competitors

Bower: "A package manager for the web"

: "npm is the package manager for JavaScript"

but also JSPM

Bower

$ bower install underscore#1.0.3

$ bower install backbone # with underscore as a dependency

Unable to find a suitable version for underscore, please choose one: 1) underscore#1.0.3 which resolved to 1.0.3 and is required by bowerexample 2) underscore#>=1.7.0 which resolved to 1.8.3 and is required by backbone#1.2.3

. └── bower_components ├── backbone └── underscore

$ npm install -g bower

$ npm install underscore@1.0.3

$ npm install backbone # with underscore as a dependency

. └── node_modules ├── backbone │   └── node_modules │   └── underscore └── underscore

$ npm uninstall underscore $ npm dedupe underscore@1.8.3 node_modules/backbone/node_modules/underscore -> node_modules/underscore

npm and front-end packagingnpm as a front-end package manager by @kewah

npm is only for CommonJS!npm is only for server-side JavaScript!

JSPM

Dynamically loads ES6 modules in browsers and NodeJSSystemJS:

: list of packages and their endpointRegistry

$ jspm install npm:react

$ jspm install github:angular/bower-angular

$ jspm install react

"react": "npm:react",

. ├── config.js ├── jspm_packages └── package.json

Other attempts

JamJS

DuoJS

VoloJS

Component

Ender

...

Module system/syntaxDefines a way of writing modular code

No built-in support in JS for modules

ES6 to the rescue

ES5 GlobalAMDCommonJSES6 modules

ES5

Module pattern (MusicPlayer.js)

var MusicPlayerModule = (function() {

var currentTrack = undefined;

return { play: function(track) { currentTrack = ajax.getTrackObject(); }, getTitle: function() { return currentTrack.title; } };

})();

Usage

<script src="scripts/MusicPlayer.js"></script>

// Get track and play it var trackId = 'EOUyefhfeaku'; MusicPlayerModule.play(trackId);

// Get module state: currentTrack MusicPlayerModule.getTitle();

AMD

Module (MusicPlayer.js)

define('MusicPlayerModule', ['ajax'], function (requester) {

var currentTrack = undefined;

return { play: function(track) { currentTrack = requester.getTrackObject(); }, getTitle: function() { return currentTrack.title; } }; });

Import (main.js)

define('main', ['MusicPlayerModule'], function (MusicPlayer) { var trackId = 'EOUyefhfeaku';

MusicPlayer.play(trackId);

MusicPlayer.getTitle(); });

CommonJS

Module (MusicPlayer.js)

var requester = require('../utils/ajax.js');

var currentTrack = undefined;

exports.play = function(track) { currentTrack = requester.getTrackObject(); }; exports.getTitle = function() { return currentTrack.title; }

Import (main.js)

const MusicPlayer = require('./MusicPlayer.js');

var trackId = 'EOUyefhfeaku';

MusicPlayer.play(trackId);

MusicPlayer.getTitle();

ES6

Module (MusicPlayer.js)

import requester from '../utils/ajax.js';

let currentTrack = undefined;

export function play(track) { currentTrack = requester.getTrackObject(); } export function getTitle() { return currentTrack.title; }

Import (main.js)

import { play, getTitle } from './MusicPlayer.js';

var trackId = 'EOUyefhfeaku';

play(trackId);

getTitle();

Module loader/bundlerShip modules to the browser

Modules Implementation status: in development

Interpreted? More Compiled/Transpiled

Bundle to a single JS file

RequireJSBrowserifyWebpackJSPM & SystemJS ( )Rollup

ES6 Module Loader

> Bullet point comparison

RequireJS

ProsSyntax: mostly AMD (CommonJS support not ideal)-Works in the browser directly-RequireJS optimizer to bundle/minify-

ConsNot suited for server side/client application-Some cryptic error messages-

Browserify

ProsSyntax: CommonJS (+ deAMDify & debowerify)-Backed up by the huge npm community-Client-side and server-side modules-Shim Node.js modules-

(deAMDify translate AMD modules to Node-style modules automatically)

- List of transforms

ConsNeed a tool to watch and bundle ( )- watchifyTightly bound to Node.js ecosystem-

Webpack

ProsSyntax: CommonJS and AMD-Doesn't try to be compatible with node.js at all costs-Client-side and server-side modules-

(UglifyJsPlugin)- List of pluginsCons

Configuration over convention-More features in its core (less modular) but also a pro-

> browserify for webpack users

JSPM featuring SystemJS

ProsSyntax: - ES6, CommonJS, AMD...Compliant with the ES6 modules specification and future-friendly-No bundling needed (like RequireJS) = no watcher-Bundling optional-

(similar to Webpack loaders plugins)- List of pluginsCons

Needs better performances-...but doesn't matter in dev since bundling is not needed-

Rollup

ProsSyntax: - ES6, CommonJS, AMD...... but can only optimize ES6 modules-

- live-code inclusion aka tree-shaking- List of plugins

ConsYet another module bundler-...but will probably be integrated into - SystemJS

> rollup-starter-project

Rollup tree shakingutils.js

var foo = {}; foo.a = 1;

var bar = {}; bar.b = 2;

export { foo, bar };

main.js

import { foo, bar } from './utils.js';

console.log( foo.a );

bundle.js

'use strict';

var foo = {}; foo.a = 1;

console.log( foo.a );

Task runner / Build systemAutomate

gruntgulp

Grunt

Gulp

Other attempts

Broccoli

Brunch

FlyJS

...

State of affairsTask Runner Usage

Gulp 44%

Grunt 28%

Others 9%

No task runner 20%

Source: The State of Front-End Tooling – 2015

What is new in Gulp 4?What is Gulp?

Changes in Gulp 4

What is Gulp?Gulp StreamGulp APIGulp CLI

Gulp: The streaming build system.

Grunt: multiple file read/write (requires src and dest)Gulp: streams src.pipe(b).pipe(c).pipe(d)

Gulp API

.src() and .dest()

gulp.src([̀${config.src}/images/**/*̀, ̀!${config.src}/images/{sprite,sprite/**}̀])

A 'glob' is a pattern that allow us to select or exclude a set of files.

=> returns a stream that we can 'pipe'

.pipe(imagemin())

=> transforms the files from the stream (minify them)

.pipe(gulp.dest(̀${config.dist}/images̀));

=> writes the minified files to the system

gulp.src([̀${config.src}/images/**/*̀, ̀!${config.src}/images/{sprite,sprite/**}̀]) .pipe(imagemin()) .pipe(gulp.dest(̀${config.dist}/images̀));

Gulp API

.task() and .watch()

gulp.task('images', function() {

return gulp.src([̀${config.src}/images/**/*̀, ̀!${config.src}/images/{sprite,sprite/**}̀ .pipe(imagemin()) .pipe(gulp.dest(̀${config.dist}/images̀));

});

=> programmatic API for task dependencies and .watch()

gulp.task('serve', ['images', 'takeABreak', 'smile'], function() { // Serve once the images, takeABreak and smile tasks have completed ... });

gulp.watch(̀${config.src}/images/**/*̀, ['images']);

=> ... and more importantly to the CLI

$ gulp images

Gulp CLI

$ gulp <task> <othertask>

$ npm install gulp-cli -g

gulp --gulpfile <gulpfile path> # will manually set path of gulpfile. gulp --cwd <dir path> # will manually set the CWD. gulp --tasks # will display the task dependency tree for the loaded gulpfile. gulp --verify # will verify plugins in package.json against the plugins blacklist

Full list of arguments

Changes in Gulp 4$ npm install -g gulpjs/gulp-cli#4.0

package.json

"devDependencies": { "gulp": "github:gulpjs/gulp#4.0", ... }

Slight API changes

New task system

See dmnsgn/gulp-frontend-boilerplate

Slight API changes

gulp.src()gulp.dest()gulp.watch()gulp.task() (new syntax) 3 arguments

gulp.parallel()gulp.series()

+ gulp.symlink(), gulp.lastRun(), gulp.tree(), gulp.registry()

gulp.series() and gulp.parallel()using run-sequence (Gulp 3)

runSequence( ['markup', 'styles', 'scripts', 'images'], ['serve', 'watch'], callback );

index.js (Gulp 4)

gulp.series( gulp.parallel(markup, 'styles', 'scripts', 'images'), gulp.parallel(serve, watch) )

New task system

Gulp 3

gulp.task('serve', ['markup', 'styles', 'scripts'], function(done) {

return browserSync.init({}, done);

});

Gulp 4 (two arguments syntax)images.js

export function optimizeImages() { ... } export function generateSpritesheet() { ... } export function generateFavicons() { ... }

gulpfile.js

import { optimizeImages, generateSpritesheet, generateFavicons } from './images'; ... gulp.task( 'images', gulp.parallel(optimizeImages, generateSpritesheet, generateFavicons) );

Gulp 4 (one argument syntax)gulpfile.js

CLI

var gulp = require('gulp'); var del = require('del');

gulp.task(function clean() { return del([̀./dist/**̀, ̀!./dist̀ ,̀!./dist/static/**̀]; });

$ gulp clean

Reusable modulesclean.js

gulpfile.js

import 'del' from 'del';

export function clean() { return del([̀./dist/**̀, ̀!./dist̀ ,̀!./dist/static/**̀]); }

import { clean } from './clean'; ... gulp.task(clean);

Are task-runner still relevant?What to expect in 2016?

npm scripts to replace task-runner

JS fatigue

 scriptsShould we stop using build tools like Gulp?

How to use npm scripts

Are npm scripts the solution?

Should we stop using build tools like Gulp?

Plugins often an existing NodeJS packageRely on global dependencies (grunt-cli, gulp-cli, broccoli-cli...)Most tasks can be achieved using basic terminal commands

wrapper around

How to use   scripts

$ npm start # default script $ npm run script-name # custom script

package.json

{ "devDependencies": { "babelify": "̂6.1.x", "browser-sync": "̂2.7.x", "browserify": "̂10.2.x", "npm-run-all": "̂1.5.0", ... }, "scripts": { "dev": "npm-run-all --parallel dev:scripts dev:styles serve", "build": "npm-run-all --parallel build:scripts build:styles rimraf dist/*.map", "clean": "rimraf dist/{*.css,*.js,*.map}", "serve": "browser-sync start --server 'dist' --files 'dist/*.css, dist/*.html, dist/*.js' --no-ui" "dev:scripts": "watchify -d src/scripts/main.js -t [babelify] -o dist/main.js", "dev:styles": "stylus src/styles/main.styl -w -o dist/ -m", "build:scripts": "browserify -d src/scripts/main.js -t [babelify] -o dist/main.js && uglifyjs dist/main.js -o dist/main.js" "build:styles": "stylus src/styles/main.styl -o dist/ -c" } }

Are npm scripts the solution?

pros

Fast configuration and buildAll config in one place: package.jsonNo extra global dependency

...& cons

Readability of the commandsCompatibility between UNIX and Windows systems

2016JavaScript fatigue

Doctor's prescription

JS fatigue

A new JS framework a dayToo many tools

2015: JavaScript tool X was useful 2016: Make X easier to install/setup/use for all newusers. Minimize dependency & configuration hell.

— Addy Osmani (@addyosmani) January 8, 2016

Controversial articles (some probably missing the point)- Stop pushing the web forward- Javascript Fatigue- State of the Union.js- Solar system of JS

Why it is not a problem if you keep the UX in mind by - The Controversial State of JavaScript Tooling Nicolás

Bevacqua (@ponyfoo) by - Why I love working with the web Remy Sharp (@rem) by - If we stand still, we go backwards Jake Archibald

(@jaffathecake)

Just because it's there, doesn't mean you mustlearn it and use it. [...] No one can be an expert inthe whole web. [...] The great thing about the web

is it doesn't hit 1.0 and stop, it's continual.

Piece of advice (my two-penn'orth)

Pick styleguidesPick the right scaffoldStick with it (at least this year)

Stay curious though