Skip to content

Workflow with Yeoman, Grunt and Bower

Béla Varga edited this page Jan 13, 2014 · 28 revisions

image

Yeoman

image

Preparation

Installation (MacOSX)

Check installation

  curl -L get.yeoman.io | bash
  ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
  brew install git optipng jpeg-turbo phantomjs
  brew link jpeg-turbo
  • Compass >= 0.12.1
  gem update --system
  gem install compass
  • Yeoman
  npm install yeoman -g

Check installation again

  curl -L get.yeoman.io | bash

Installationspath

  /usr/local/lib/node_modules/yeoman

Grunt

image

Grunt Tasks

  • clean Wipe the previous build dirs
  • concat Concatenate files
  • copy Copies the whole staging(intermediate/) folder to output (publish/) one.
  • css Concatenates, replaces @imports and minifies the CSS files
  • dom DOM-based build system
  • html Basic to aggressive HTML minification (html:buildkit, html:basics, html:compress)
  • img Optimizes .png/.jpg images using OptiPNG/JPEGtran
  • mkdirs Prepares the build dirs
  • min Minify files with Uglify.js
  • rev Automate the hash renames of assets filename
  • rjs AMD Build with rjs
  • usemin Replaces references to non-minified scripts / stylesheets
  • usemin-handler A special task which uses the build block HTML comments in markup to get back the list of files to handle, and initialize the grunt configuration appropriately, and automatically.
  • manifest Creates manifest file

Example 01 - Install Grunt

Installation

//wrong way npm install -g grunt
npm install -g grunt-cli

Preparation and run grunt

mkdir example01
cd example01
grunt // Fatal error: Unable to find local grunt.

Install grunt locally and run again

npm install grunt
grunt // Fatal error: Unable to find Gruntfile.

Create Gruntfile.js

module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({});

  // A very basic default task.
  grunt.registerTask('default', 'Log some stuff.', function() {
    grunt.log.write('Logging some stuff...').ok();
  });

};

or with grunt init https://github.com/gruntjs/grunt-init-gruntfile

grunt init:gruntfile

Remove your local grunt and add package.json

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.0",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.2.7"
  }
}

Install it again ;)

npm install

Example 02 - Initial example jshint, concat and uglify

Add Grunt Modules to package.json

{
  "name": "my-project-name",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "~0.4.0",
    "grunt-contrib-jshint": "~0.7.2",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.2.7"
  }
}
  • JSHint for style checking and code analysis.
  • Concat for file concatenation (for modules).
  • JSUglify for minifying javascript files.

More Informations:

http://www.jshint.com/blog/jshint-3-plans/ https://github.com/mdevils/node-jscs https://github.com/mishoo/UglifyJS2 Change your grunt file

// Project configuration.
/*global module:false*/
module.exports = function(grunt) {

  // Project configuration.
  grunt.initConfig({

    jshint: {
      src: {
        options: {
          curly: false,
          undef: true
        },
        files: {
          src: ['app/src/app.js', 'app/src/lib.js']
        }
      }
    },

    concat: {
      build: {
        options: {
          separator: ';',
        },
        src: ['app/src/jquery.js' ,'app/src/app.js', 'app/src/lib.js'],
        dest: 'app/build/app.js'
      }
    },

    uglify: {
      build: {
        files: { 'app/build/app.min.js': [ 'app/build/app.js' ] }
      }
    }

  });

  // Load task modules.
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');

  // Default task.
  grunt.registerTask('default', 'jshint');

};

Run grunt

grunt // jshint task is logging errors

Fix JSHint problems with adding globals to options

globals: {
  'window': true,
  'jQuery': true
}

Add more task to the default task

grunt.registerTask('default', ['jshint', 'concat', 'uglify']);

Change task names:

grunt.registerTask('default', ['test']); // grunt
grunt.registerTask('test', ['jshint']); // grunt test
grunt.registerTask('build', ['test', 'concat', 'uglify']); // grunt build

Example 03 Static web server

Install new module

npm install grunt-contrib-connect --save-dev

Update Gruntfile

connect: {
  server: {
    options: {
      port: 9001,
      base: 'app'
    }
  }
}
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.registerTask('server', 'connect');

Run grunt

grunt // server is down after task finished

Change options to run server

keepalive: true

Add option to automatically open browser at server start

open: true

Example 04 File watching and Live reload

Example 05 Qunit and PhantomJS

Example 06 Multi / Async task

Example 07 Grunt task and helper

   grunt.registerHelper('index', function(target, file) {
        var f = grunt.file;
        var contents = f.read(file.src);
        if(contents) {
            contents = contents.replace(new RegExp('', 'g'), '$2');
            f.write(file.dest, contents);
            console.log('Created ' + file.dest, target);
        }
    });

Example 08 - Build own grunt plugin

  1. Install grunt-init with npm install -g grunt-init
  2. Run grunt-init gruntplugin in an empty directory.
  3. Run npm install to prepare the development environment.
  4. Author your plugin.
grunt.registerTask('my_grunt_plugin', 'Your task description goes here.', function() {

    var doneCallback = this.async();
    grunt.helper('fec:readConfig');
    grunt.helper('fec:readFolder', doneCallback);

  });
  grunt.registerHelper('fec:readConfig', function() {
    var configFile = grunt.config('my-config').value;
  });
  grunt.registerHelper('fec:readFolder', function(cb) {

    glob('./' + config.directory + '/*',function(err,dirs){
        dirs.forEach(function (dir) {
          var name = path.basename(dir);
        });
        cb();
    });

  });
  1. Run npm publish to publish the grunt plugin to npm!
  2. Or create local npm package.
cd /path/to/package.json
npm pack
cd path/to/your/project/
npm install ../path/to/your/npm/packagename-version.tgz

Set the package to real private.

"private": true

Example 09 - Build custom jquery

Enter the jquery directory and install the Node dependencies, this time without specifying a global install:

cd jquery && npm install

Make sure you have grunt installed by testing:

grunt -version

Then, to get a complete, minified (w/ Uglify.js), linted (w/ JSHint) version of jQuery, type the following:

grunt

The built version of jQuery will be put in the dist/ subdirectory, along with the minified copy and associated map file.

Bower

image

Packets are loaded from an git endpoint and then cached locally. The packages are in this folder: yeoman-folder/components/

Versions of the packages are tags in the git repository.

"modernizr": "~2.6.1"

Example 01 - Basic usage

Example 02 - Create own package with version

Create a github repository with an component.json

{
 "name": "myProject",
 "version": "1.0.0",
 "main": ["./path/to/app.css", "./path/to/app.js"],
 "dependencies": {
   "jquery": "~1.7.2"
 }
}

Register it global

bower register myawesomepackagename git://github.com/foo/bar

Example 03 - Set up own server

node_modules/bower/lib/core/source.js:line12
https://bower.herokuapp.com/packages

Move folder /bower_server to an Webserver

This url

http://localhost/bower_server/packages/

should return back this json (needs php)

[{"name":"fun","url":"[email protected]:netzzwerg/bower-fun.git"},
{"name":"core","url":"[email protected]:netzzwerg/bower-core.git"},
{"name":"jquery","url":"git://github.com/netzzwerg/bower-jquery.git"}]

and you can change the url in /fec_build/.bowerrc

{
  "directory" : "packages",
  "json" : "component.json",
  "endpoint" : "http://localhost/bower_server"
}

bower search should you show only 4 results

cd fec_build
bower search

Yeoman

image

Check installation

curl -L get.yeoman.io | bash

Build Target Profiles

These are equivalent to grunt alias except that we defined a single task and use arguments to trigger the appropriate target.

  • build no html compression, no usemin-handler task
  • usemin (default) same as build but parsing config from markup
  • text same as usemin but without image (png/jpg) optimizing
  • buildkit minor html optimizations, all html whitespace/comments maintained (todo: inline script/style minified)
  • basics same as buildkit plus minor html optimizations
  • test same as default build plus but conditionally runs compass / manifest task depending on whether or not compass / phantomjs binaries are available within the path. During the checking process, we output warning infos about missing deps. It might make sense to make it the default (img task internally does this check)

Build Tasks

  • default rjs concat css min img rev usemin manifest
  • usemin usemin-handler rjs concat css min img rev usemin manifest
  • text usemin-handler rjs concat css min rev usemin manifest
  • buildkit usemin-handler rjs concat css min img rev usemin manifest html:buildkit
  • basics usemin-handler rjs concat css min img rev usemin manifest html:basics
  • minify usemin-handler rjs concat css min img rev usemin manifest html:compress
  • test usemin-handler rjs concat css min img rev usemin manifest

Example 01 - Basic usage

Show all existing generators.

yeoman init --help 

Generate Backbone project

yeoman init backbone

Start local webserver

yeoman server

Test the application with Mocha

yeoman test

Build the application

yeoman build

Add new backbone model

yeoman init backbone:model MYDATA

Example 02 - Build own generator

Show all existing generators.

yeoman init --help 

Generator path under MacOSX

/usr/local/lib/node_modules/yeoman/node_modules/yeoman-generators/lib/generators

generator-name/all/index.js

var path = require('path'),
    util = require('util'),
    yeoman = require('../../../../');

module.exports = Generator;

function Generator() {
  yeoman.generators.Base.apply(this, arguments);
  this.appname = path.basename(process.cwd());
}

util.inherits(Generator, yeoman.generators.Base);

Generator.prototype.createIndexFile = function createIndexFile() {
  this.template('index.html', 'app/index.html');
};

Generator.prototype.createCssFile = function createCssFile() {
  this.template('bootstrap.css', 'app/bootstrap.css');
};

Example 03 - Modify build process

Change your local grunt file:

grunt.registerTask('my-task', 'my grunt task', function() {
  console.log('my task');
});
grunt.registerTask('build', 'your own build task list', function() {
  //var tasks = 'clean mkdirs usemin-handler rjs concat min css rev usemin manifest copy time';
  var tasks = 'my-task time';
  grunt.task.run(tasks);
});

Example 04 - Add own plugin with task

module.exports = function(grunt){

  grunt.loadNpmTasks('your-grunt-plugin');

  grunt.initConfig({
    // grunt config
  });

  grunt.registerTask('default', 'your-task');
};
Clone this wiki locally