Affichage des articles dont le libellé est Gulp. Afficher tous les articles
Affichage des articles dont le libellé est Gulp. Afficher tous les articles

18 mai 2014

The Cult of Gulp: Independent preprocessor for Gulp

In a former post (A gulp of coffee: your gulpfile in coffeescript), I've shown how to bootstrap CoffeeScript in Gulp. There is now a far better solution than mine that does not require any additional file: Cult.

This little CLI just analyse the extension and automatically requires the right REPL. A time saver!

17 mai 2014

Famo.us gulped bumped to v0.2.0

I've just updated my sample project for building a Famo.us v0.2.0 with Gulp.

I've also added a simple example to show how you can use the HeaderFooterLayout to layout the basis of your SPA:
Look at how simple it is to set content so that it takes advantage of the complete browser's height and width. Centering content is so easy as well. No funny tricks, just a simple code that works well on mobile, tablet and regular desktop browsers.

HeaderFooterLayout = require 'famous/views/HeaderFooterLayout'
layout = new HeaderFooterLayout
  headerSize: 100
  footerSize: 50
mainContext.add  layout
layout.header.add new Surface
  content: 'Header'
  size: [undefined, 100]
  classes: ['header-surface']
  properties:
    lineHeight: '100px'
    textAlign: 'center'
layout.content.add new Surface
  content: 'Content'
  size: [undefined, undefined]
  properties:
    lineHeight: '300px'
    textAlign: 'center'
layout.footer.add new Surface
  content: 'Footer'
  size: [undefined, 50]
  classes: ['footer-surface']
  properties:
    lineHeight: '50px'
    textAlign: 'center'

13 avr. 2014

Famo.us with Gulp.js and Browserify in CoffeeScript

Introduction

Famo.us is a new framework with a new radical approach to HTML5 development. Basically, everything is treated as surfaces which are seen by the browser as accelerated virtual DOM components. This is all achieved under the JS files and not via CSS3 files via transforms and animations. Therefore, it avoids pollution of your CSS which is solely used for theming. As virtual DOM, the real HTML's DOM is not polluted either with DIVs inserted only for animation purposes. The animations and screen dynamics are kept separated in your JS files. But, it is not all that is provided out-of-the-box. Every tricks that needs to be inserted in our codes for being compatible with all mobiles, tablets and desktops have been inserted directly in the library. HTML5 starts to really shine using Famo.us.

Why an HelloWorld?

Currently, Famo.us is distributed either as source file in their Github repository or as a Yeoman generator that uses Grunt. This last option is incredibly nice and will surely be the most preferred one. It is rare enough to note it: Famo.us has done a great job towards its users. There are really few frameworks which provides so much as soon as they are launched. Plus, it has been done 'state-of-the-art' for most users who like writing plain HTML, CSS and JS. Really nice. Kudos to the Famo.us team.

But, if you have read this blog entries, you should know that I don't like writing vanilla codes. I generally prefer relying on preprocessors. So, with this little tutorial, I will show you my best moves to integrate Sass, Jade, CoffeeScript and Famo.us using Gulp.js and Browserify. Once done, it becomes easy to start playing with the great examples that Famo.us provides: Collection of Famo.us examples on Github.

I've shared all the source code of this tutorial into a little Github repository: PEM--/hellofamousgulped.

Creating the folder hierarchy

The tree of files is as following:
.
├── app
│   ├── css
│   │   └── main.sass
│   ├── index.jade
│   └── js
│       └── main.coffee
├── bower.json
├── gulpfile.coffee
├── gulpfile.js
└── package.json

To create it, use this classic command:
mkdir -p app/css app/js

Import Famo.us using Bower

Now, we import Famo.us thanks to Bower. Here is the content of my bower.json file:
{
  "name": "HelloFamousGulped",
  "version": "0.0.0",
  "authors": [
    "PEM--"
  ],
  "license": "MIT",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "famous": "git@github.com:Famous/famous.git#v0_1_1"
  }
}
Once copied, use Bower to import Famo.us and all its submodules:
bower install

Import Gulp.js and Browserify using NPM

No JS on your desktop without the powerful Node Package Management. To import all our required build tools and plugins, use my package.json or get inspiration from it:
{
  "name": "HelloFamousGulped",
  "version": "0.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "PEM--",
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "gulp": "^3.6.0",
    "gulp-load-plugins": "^0.5.0",
    "gulp-util": "^2.2.14",
    "gulp-autoprefixer": "0.0.6",
    "gulp-imagemin": "^0.2.0",
    "gulp-clean": "^0.2.4",
    "gulp-cache": "^0.1.3",
    "gulp-ruby-sass": "^0.4.3",
    "gulp-connect": "^2.0.5",
    "gulp-cssmin": "^0.1.4",
    "gulp-jade": "^0.5.0",
    "gulp-concat": "^2.2.0",
    "gulp-plumber": "^0.6.1",
    "gulp-watch": "^0.5.4",
    "gulp-changed": "^0.3.0",
    "gulp-bower": "^0.0.4",
    "gulp-open": "^0.2.8",
    "coffeeify": "^0.6.0",
    "browserify": "^3.41.0",
    "deamdify": "^0.1.1",
    "vinyl-source-stream": "^0.1.1",
    "debowerify": "^0.7.0",
    "uglifyify": "^2.1.1"
  },
  "engines": {
    "node": ">=0.10.0"
  }
}
Once copied and adapted to suit your needs, let NPM do its magic:
npm install

Create the build scripts

Actually as I've already blogged about it (A gulp of coffee: your gulpfile in coffeescript), I use a JS wrapper to call Gulp.js within CoffeeScript. It has the strong advantage to shorten your Gulpfile a lot. Here is the wrapper gulpufile.js:
'use strict';
require('coffee-script/register');
require('./gulpfile.coffee');

And now, the real build script gulpfile.coffee:
'use strict'
gulp       = require 'gulp'
gp         = (require 'gulp-load-plugins') lazy: false
path       = require 'path'
browserify = require 'browserify'
source     = require 'vinyl-source-stream'

# HTML
gulp.task 'html', ->
  gulp.src 'app/index.jade'
    .pipe gp.plumber()
    .pipe gp.jade locals: pageTitle: 'Famo.us built with Gulp'
    .pipe gulp.dest 'www'

# JS
gulp.task 'js', ->
  browserify
    entries: ['./app/js/main.coffee']
    extensions: ['.coffee', '.js']
  .transform 'coffeeify'
  .transform 'deamdify'
  .transform 'debowerify'
  .transform 'uglifyify'
  .bundle()
  # Pass desired file name to browserify with vinyl
  .pipe source 'main.js'
  # Start piping stream to tasks!
  .pipe gulp.dest 'www/js'

# CSS
gulp.task 'css', ->
  gulp.src 'app/css/*.sass'
    .pipe gp.plumber()
    .pipe gp.rubySass style: 'compressed', loadPath: ['bower_components', '.']
    .pipe gp.cssmin keepSpecialComments: 0
    .pipe gp.autoprefixer 'last 1 version'
    .pipe gulp.dest 'www/css'

# Images
gulp.task 'img', ->
  gulp.src ['app/img/*.jpg', 'app/img/*.png']
    .pipe gp.cache gp.imagemin
      optimizationLevel: 3
      progressive: true
      interlaced: true
    .pipe gulp.dest 'www/img'

# Clean
gulp.task 'clean', ->
  gulp.src ['www', 'tmp'], read: false
    .pipe gp.clean force: true

# Build
gulp.task 'build', ['html', 'js', 'css']

# Default task
gulp.task 'default', ['clean'], -> gulp.start 'build'

# Connect
gulp.task 'connect', ['default'], ->
  gp.connect.server
    root: 'www'
    port: 9000
    livereload: true

# Watch
gulp.task 'watch', ['connect'], ->
  gulp.watch 'app/**/*', read:false, (event) ->
    ext = path.extname event.path
    taskname = null
    reloadasset = null
    switch ext
      when '.jade'
        taskname = 'html'
        reloadasset = 'www/index.html'
      when '.sass'
        taskname = 'css'
        reloadasset = 'www/css/main.css'
      when '.coffee', '.js'
        taskname = 'js'
        reloadasset = 'www/js/main.js'
      else
        taskname = 'img'
        reloadasset = "www/img/#{path.basename event.path}"
    gulp.task 'reload', [taskname], ->
      gulp.src reloadasset
        .pipe gp.connect.reload()
    gulp.start 'reload'

Now we are ready to code!

The app/index.jade, a Jade file that produces our www/index.html, is really kept as its minimum:
doctype html
html(lang="en")
  head
    meta(charset="UTF-8")
    title= pageTitle
    meta(name="viewport", content="width=device-width, maximum-scale=1, user-scalable=no")
    meta(name="mobile-web-app-capable", content="yes")
    meta(name="apple-mobile-web-app-capable", content="yes")
    meta(name="apple-mobile-web-app-status-bar-style", content="black")
    link(rel="stylesheet", href="css/main.css")
  body
    script(src="js/main.js")

It's Famo.us that will populate the DOM.

Now, just to reset our CSS and gives a nice clean look to our little app, here is the content of our app/css/main.sass, a Sass file that produces our www/css/main.css:
// From this gist: https://gist.github.com/trey/3524
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p,
blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em,
img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i,
center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption,
tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section,
summary, time, mark, audio, video
  margin: 0
  padding: 0
  border: 0
  font-size: 100%
  font: inherit
  vertical-align: baseline

article, aside, details, figcaption, figure,  footer, header, hgroup, menu,
nav, section
  display: block

body
  line-height: 1
 
ol, ul
  list-style: none
 
blockquote, q
  quotes: none
 
blockquote:before, blockquote:after,
q:before, q:after
  content: ''
  content: none
 
table
  border-collapse: collapse
  border-spacing: 0

// Import Famo.us's CSS
@import bower_components/famous/core/famous.css

// Real theming happens hereafter
$bgColor: #FF851B
$mainColor: white

html, body, .bgColor
  background-color: $bgColor 

body
  color: $mainColor
  font-family: Helvetica, Arial, sans-serif

Now comes the real fun. Our app/js/main.coffee, a CoffeeScript that produces the www/js/main.js single file is where are required all the dependencies. If you plan on splitting your app into multiple files or importing new Famo.us components or installing new dependencies thanks to Bower, Browserify will do all the heavy lifting importing and building everything into a single file. So, here is simple hello world in regular CoffeeScript:
Engine = require 'famous/core/Engine'
Surface = require 'famous/core/Surface'
Modifier = require 'famous/core/Modifier'

# Create the main context
mainContext = Engine.createContext()

outline = new Surface
  size: [200, 200]
  content: 'Hello world in Famo.us'
  classes: ['bgColor']
  properties:
    lineHeight: '200px'
    textAlign: 'center'

outlineModifier = new Modifier
  origin: [0.5, 0.5]

mainContext
  .add outlineModifier
  .add outline

If you haven't installed Gulp.js and CoffeeScript yet, install them globally:
npm -g install gulp coffee-script

It is now time to check our little hello world in Famo.us. Hit the next command:
gulp watch

Now, open your favorite browser at http://localhost:9000.As our Gulpfile is loaded with Livereload, you can edit the source code live and see all the magic brought to us thanks to Famo.us. You can even open this app with your tablet or smartphone. It works like a charm.

Conclusion

Famo.us brings a new interesting way of creating rich HTML5 apps that are almost as fast as native development without the burdens of redeveloping everything on each platform.

Though, with this new shift in code design, we have some old constraints that arise again. SEO, for instance. Rethinking RWD is another one. Still with this powerful framework in our hands, it is easy to figure out how. Solving this little issues is not a big deal when compared to the huge problems that Famo.us has solved.

15 févr. 2014

Keep your Gulpfile manageable: the Gulp plugins you need to know

CoffeeScript as your Gulpfile

Though being less in the hype today, CoffeeScript shortens the need of writing boilerplate codes (as already detailed in this post A gulp of coffee: your gulpfile in coffeescript). All you need to do is to follow these simple steps:
  1. Start by installing CoffeeScript globally:
    npm -g install coffee-script
  2. Create a regular Gulpfile.js that bootstraps Gulp into using CoffeeScript:
    require('coffee-script/register');
    // This bootstraps your Gulp's main file
    require('./Gulpfile.coffee');
  3. Now, you simply edit your Gulpfile.coffee and forget about most of the semicolon, accolades, parenthesis, ...

Load plugins, automatic loading of your plugins

Like MatchDep or grunt-load-tasks do it for NodeJS and Grunt, gulp-load-plugins leverages your package.json and automatically loads all the plugins in Gulp. Here are the steps to follow:
  1. Start by creating a regular package.json for your project:
    npm init
  2. Add locally all your required plugins and save their installation as a development dependency. Here is an example for gulp-clean:
    npm install --save-dev gulp-clean
  3. At the very beginning of your Gulpfile.coffee, load Gulp and every plugins in two lines of code:
    gulp = require 'gulp'
    gp = do require 'gulp-load-plugins'  # Load all gulp plugins
  4. Now all your plugins are loaded and accessible from the gp object. Their name matches the plugins name in camel case. Putting it more simply, for the plugin gulp-clean, you use it as gp.clean, for the plugin gulp-ruby-sass, you use is as gp.rubySass.

Plumber, avoid restarting Gulp when transpiling fails

When you start your watch and transpile tasks for the first time, it happens that syntax errors abruptly ends Gulp. It forces you to go the old way, fixing the bugs, re-launching Gulp, fixing the bugs, re-launching Gulp, ... tedious, to say the least.
gulp-plumber avoids the stream to end upon error. Thus, whenever you need a transpilation, you just add gulp-plumber to your pipes. Here is a simple example that shows up how to use it.
gulp.task 'css', ->
  gulp.src 'app/css/index.sass'
    .pipe gp.watch()
    .pipe gp.plumber()    # Add Plumber just before the transpilation
    .pipe gp.rubySass()
    .pipe gulp.dest 'www/css'
Installing is made easy thanks to the automatic loading of plugins as described in the previous paragraph:
npm install --save-dev gulp-plumber

Connect, start a livereload server, open your file and a create a static server in one plugin

This plugin gulp-connect is incredibly powerful: it combines a connect server with a tiny-lr one and it opens your transpiled files. Pure voodoo style. Here is short example of its basic usage:
path  = require 'path'
# Start a webserver for static files
gulp.task 'webserver', gp.connect.server
  root: path.join __dirname, 'www'
  port: 9000
  livereload: true
  open: browser: 'Google Chrome'
Just like before, the installation is easy as pie:
npm install --save-dev gulp-connect
Now, on each watch task requiring a livereload, just add a gp.connect.reload() call to their task.
Note : BrowserSync is also a very good alternative.

Autoprefixer, forget about vendor's prefixes

Wether you use pure CSS, Less, Stylus or Sass and its compagnon Compass, you have to take care of vendor prefixes depending on the public your website or hybrid app are targeting. Doing it manually is a pure madness. But using some libraries assistance may also ends up in a tedious work, piling up a bunch of libraries that you need to add or remove depending on public adoption of next generation of operating systems and browsers.
Autoprefixer does this automatically for you. It leverages the usage statistics Caniuse (by the way, an incredible and so useful website) and adds to your CSS just the prefix that it requires. A life time saver. Using it in Gulp is incredibly easy. Here is a simple example that takes Sass as its input and produces the CSS for 99% of browsers:
# Create CSS from SASS
gulp.task 'css', ->
  gulp.src 'app/css/index.sass'
    .pipe gp.watch()
    .pipe gp.plumber()
    .pipe gp.rubySass()
    .pipe gp.autoprefixer "> 1%"  # Set Autoprefixer for 99%
    .pipe gulp.dest 'www/css'

A complete sample

To complete this article, here is one of the Gulpfile.coffee that I've used on a production website (a very basic SPA):
gulp  = require 'gulp'
# Load all gulp plugins
gp    = do require 'gulp-load-plugins'
path  = require 'path'

# Start a webserver for static files
gulp.task 'webserver', gp.connect.server
  root: path.join __dirname, 'www'
  port: 9000
  livereload: true
  open: browser: 'Google Chrome'

# Create CSS from SASS
gulp.task 'css', ->
  gulp.src 'app/css/index.sass'
    .pipe gp.watch()
    .pipe gp.plumber()
    .pipe gp.rubySass()
    .pipe gp.autoprefixer "> 1%"
    .pipe gp.cssmin keepSpecialComments: 0
    .pipe gulp.dest 'www/css'
    .pipe gp.connect.reload()

# Create HTML from Jade
gulp.task 'html', ->
  gulp.src 'app/index.jade'
    .pipe gp.watch()
    .pipe gp.plumber()
    .pipe gp.jade()
    .pipe gulp.dest 'www'
    .pipe gp.connect.reload()

# Copy font files
gulp.task 'copy_fonts', ->
  gulp.src './bower_components/font-awesome/fonts/*'
    .pipe gulp.dest 'www/fonts'

# Copy vendor JS files, concatenate them and uglify them
gulp.task 'copy_js', ->
  gulp.src [
    'bower_components/better-dom/dist/better-dom.js'
    'bower_components/better-details-polyfill/dist/better-details-polyfill.js']
    .pipe gp.concat 'better-dom-and-plugin.js'
    .pipe gp.uglify()
    .pipe gulp.dest 'www/js'

# Copy vendor CSS files and minifies it
gulp.task 'copy_css', ->
  gulp.src [
    'bower_components/better-details-polyfill/dist/better-details-polyfill.css']
    .pipe gp.cssmin keepSpecialComments: 0
    .pipe gulp.dest 'www/css'

# Clean produced files
gulp.task 'clean', ->
  gulp.src ['www', 'tmp']
    .pipe gp.clean()

gulp.task 'default', [
  'copy_fonts', 'copy_js', 'copy_css'
  'css', 'html', 'webserver']
And here are the relevant part of its package.json:
{
  "name": "Blablabla",
  "version": "0.0.0",
  "description": "",
  "main": "Gulpfile.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Me",
  "license": "MIT",
  "devDependencies": {
    "gulp-cssmin": "~0.1.0",
    "gulp-ruby-sass": "~0.3.0",
    "gulp-util": "~2.2.14",
    "gulp-autoprefixer": "0.0.6",
    "gulp": "~3.5.2",
    "gulp-jade": "~0.4.1",
    "gulp-watch": "~0.5.0",
    "gulp-sass": "~0.6.0",
    "gulp-plumber": "~0.5.6",
    "gulp-clean": "~0.2.4",
    "gulp-load-plugins": "~0.3.0",
    "gulp-concat": "~2.1.7",
    "gulp-uglify": "~0.2.1",
    "gulp-connect": "~0.3.1"
  }
}

9 févr. 2014

A gulp of coffee: your gulpfile in coffeescript

Pros and cons

Gulp is  a new buildsystem for the web. It contrasts with Grunt by leveraging NodeJS's streams. Therefore, it writes no temporary file on your drive when you're performing multiple operations on your assets. Of course, it speeds up the build process, but it is not the only advantage. Thanks to the streams, when you write your tasks, it feels more like you are describing the required workflow. For me, it is much more natural.

The main drawback that I've found is its inability to directly use CoffeeScript as its main file. Streams and their piping abilities produce a ton of parenthesis obfuscating the semantic of your development with unnecessary grammar and syntax requirements.

Regaining access to CoffeeScript

Like Grunt, Gulp expect a main file named Gulpfile.js. We will use this file to bootstrap CoffeeScript and, thus, regain access to a more friendly language. Of course, the same trick could be applied to other languages such as Dart or TypeScript.

Gulpfile.js
// Note the new way of requesting CoffeeScript since 1.7.x
require('coffee-script/register');
// This bootstraps your Gulp's main file
require('./Gulpfile.coffee');

That's it.

Now, just an example that demonstrates the achieved advantage, and also, the new parenthesis free chaining of CoffeeScript 1.7. The following script transpiles Jade, minifies it, compresses your SVG, transpiles your Sass, autoprefix it and minifies it, copies the fonts used in your Sass files.

Gulpfile.coffee
# Load all required libraries.
gulp       = require 'gulp'
sass       = require 'gulp-ruby-sass'
prefix     = require 'gulp-autoprefixer'
cssmin     = require 'gulp-cssmin'
jade       = require 'gulp-jade'
minifyHTML = require 'gulp-minify-html'
svgmin     = require 'gulp-svgmin'

# Create your CSS from Sass, Autoprexif it to target 99%
#  of web browsers, minifies it.
gulp.task 'css', ->
  gulp.src 'app/css/index.sass'
    .pipe sass()
    .pipe prefix "> 1%"
    .pipe cssmin keepSpecialComments: 0
    .pipe gulp.dest 'www/css'

# Create you HTML from Jade, Adds an additional step of
#  minification for filters (like markdown) that are not
#  minified by Jade.
gulp.task 'html', ->
  gulp.src 'app/index.jade'
    .pipe jade()
    .pipe minifyHTML()
    .pipe gulp.dest 'www'

# Minify your SVG.
gulp.task 'svg', ->
  gulp.src 'app/img/*.svg'
    .pipe svgmin()
    .pipe gulp.dest 'www/img'

# Copy the fonts using streams.
gulp.task 'copy', ->
  gulp.src 'app/fonts/*'
    .pipe gulp.dest 'www/fonts'

# Default task call every tasks created so far.
gulp.task 'default', ['css', 'html', 'svg', 'copy']