Grunt frontend

Note

experimental

There is experimental support for a Grunt "flavored" tool chain, build on top of the Generator, acting as a frontend to the known Generator jobs and living side-by-side to the existing Generator.

Applications created with create-application.py do now contain a file called Gruntfile.js, which is the entry point for Grunt.

What?

Grunt is a task runner or build tool (comparable for example with Make, Apache Maven, Apache Ant, Rake etc.). It enables you to configure tasks which then automates common working steps for you like e.g. building, linting, testing or deploying your project. It's probably the most popular build tool in the Node.js environment right now.

Tasks are configured in a JavaScript file called Gruntfile.js which makes it really easy to get started (everybody working with qooxdoo should be familiar with JS - right?!) and enables all possibilities of JS itself (dynamic creation of configuration, use of variables and so on).

Why?

The aim is to make existing functionality available through the Grunt frontend, and see which parts of the tool chain can be replaced by Grunt plugins, be they from the npm registry or self-written. For everything else the existing Python-based tool chain will be utilized. The old build system stays untouched for the time being, so you don't have to worry.

The eventual goal is to have a qooxdoo tool chain that builds on a generic JS layer, is as powerful and sophisticated as before, but allows easier integration with and extension through third-party code, to make it easier for users to customize the tool chain in their applications, and to allow us to focus more on the qooxdoo-specific functionality and less on generic infrastructure and commodity tasks.

Installing Node.js and the Grunt CLI

The Grunt frontend requires a Node.js installation (v0.10.0 or above) and the Grunt CLI (v0.4.2 or above). However, being a frontend to the Generator you need its requirements (i.e. Python) installed too, of course.

As a qooxdoo user you currently do not need any Node.js (or npm - which gets installed along with Node.js) knowledge, it is merely a technology used internally for the tools. However, in order to harness the possibilities of Node.js and Grunt (embodied as Grunt Plugins), it makes total sense to get more familiar with Node.js, npm packages and Grunt. So you only need to know as much as you want to. :)

Verifying Grunt works

Running grunt --version in a random directory should print:

$ grunt --version
grunt-cli v0.1.11

In a directory containing a Gruntfile.js it should also print the Grunt version:

$ grunt --version
grunt-cli v0.1.11
grunt v0.4.2

Learn what's the difference between the grunt-cli and grunt itself.

Using the Generator through Grunt

Every job (i.e. source, build ...) you know from the Generator can also be run through the Grunt frontend (in Grunt lingo those are called tasks then). This ensures that you are still able to use all the Generator functionality already available. Here are some Grunt tasks and their Generator job counterparts:

Grunt Generator Comments
grunt generate.py runs the default job from the config.json
grunt source generate.py source -
grunt info generate.py info different output because of different implementation
grunt generate:info generate.py info all jobs are also available via generate:{jobName}

See also the FAQ below for important differences between Grunt and the Generator.

Grunt Plugins

Nearly all functionality Grunt offers is implemented as plugin. Grunt Plugins are basically regular npm packages with the keyword gruntplugin, which are distributed via npmjs.org. A common convention is to prefix them with "grunt-".

Use them to accomplish custom goals or even write your own ones.

The Gruntfile in detail

This is how a Gruntfile might look like after creating a new qooxdoo app:

// global conf
var common = {
  QOOXDOO_VERSION: '3.5',
  QOOXDOO_PATH: '../qooxdoo-sdk'
};

// requires
var qxConf = require(common.QOOXDOO_PATH + '/tool/grunt/config/application.js');
var qxTasks = require(common.QOOXDOO_PATH + '/tool/grunt/tasks/tasks.js');

// grunt
module.exports = function(grunt) {
  var config = {

    generator_config: {
      let: {
      }
    },

    common: common,

    /*
    myTask: {
      options: {},
      myTarget: {
        options: {}
      }
    }
    */
  };

  var mergedConf = qxConf.mergeConfig(config);
  grunt.initConfig(mergedConf);

  qxTasks.registerTasks(grunt);

  // grunt.loadNpmTasks('grunt-my-plugin');
};

The only parts specific to qooxdoo are:

  1. merging your config with qooxdoo's
  2. registering qooxdoo tasks

This will register a task for each Generator job (under the same name). The tasks may be written in Python (from the Generator) or in JavaScript. After qxTasks.registerTasks() you are free to include the Grunt plugins you like to use.

Gruntify existing apps

Basically you don't need very much to make your existing project Grunt compatible. You need:

  • Node.js and the Grunt-CLI installed as stated above.
  • a current version of the SDK, which means qooxdoo 3.5 or above.
  • a Gruntfile (file called Gruntfile.js)
  • a file called package.json

In order to get the last two files and Grunt (locally) installed:

  1. Create a new app of the same type as your existing app (via create-application.py) and then copy those two files over to your project's root dir.
  2. Run npm install in your project's root dir which installs Grunt locally to your project (this will create a dir called node_modules).

Now try grunt info - it should print out something similar to generate.py info.

FAQ

Which tasks are available?
Run grunt --help to see all registered tasks.
Will Grunt also register my newly added (and exported!) jobs from my config.json?
Yes it should, otherwise it's a bug.
How do I provide Generator options like -v?
You have to use --gargs. For example generate.py lint -v translates to grunt lint --gargs="-v"
How can I run the Generator job I have known before or why does grunt xyz differ from generate.py xyz?
This happens probably because we are registering a task (now implemented in JavaScript) under the same name as before because it should replace the former one eventually. You are always able to run former Generator jobs via grunt generate:jobName or of course with generate.py xyz. The only task which is not delegated to the Generator right now is info.