Saturday, July 6, 2013

Kanso Build Processors

Today I've created a custom jQuery package for Kanso. It's not only containing a much later jQuery version but also all the jQuery extensions I normally use. My goal was to compile all of them into one single file so that applications using it don't have to contain a column of script tags just to get all the extensions in.

Initial effort

I begun by creating a new package with a `kanso.json` file like this:

{
  "name": "jquery2",
  "version": "2.0.3",
  "categories": ["utils"],
  "attachments": [
    "jquery.js"
  ],
  "maintainers": [
    {
      "name": "Arthur McFlint",
      "url": "https://github.com/arthurmcflint"
    }
  ],
  "url": "http://jquery.com/",
  "dependencies": {
    "attachments": null,
    "modules": ">=0.0.8"
  },
  "description": "Contains the jQuery core as well as some extensions used commonly used by my projects."
}

By adding the `jquery2` extension to an app it automatically adds jquery.js.

Fine Tuning

Even so the above solution works like a charm it has a massive flaw. I couldn't care less about manually maintaining one big file containing the jQuery core as well as the extensions. After a while it would be beyond repair.

Instead I added build processors and some JavaScript files executed by node when running kanso. The new `kanso.json` file looks a bit like this:

{
  "name": "nexus-jquery",
  "version": "2.0.3",
  "categories": ["utils"],
  "attachments": [
    "jquery.js"
  ],
  "maintainers": [
    {
      "name": "Arthur McFlint",
      "url": "https://github.com/arthurmcflint"
    }
  ],
  "preprocessors": {
    "merge": "build/merge"
  },
  "postprocessors": {
    "cleanup": "build/cleanup"
  },
  "url": "http://jquery.com/",
  "dependencies": {
    "attachments": null,
    "modules": ">=0.0.8"
  },
  "description": "Contains the jQuery core as well as some extensions used commonly used by my projects."
}

Whenever kanso is pushing an app to the server it first goes through the process of building the app. By adding 'hooks' in the shape of preprocessors and postprocessors it's possible to alter the standard behavior quite a bit.

First of all I've created a new file calles `build_config.json`:

{
  "jsfiles": [
    "jquery-2.0.3.js",
    "extensions/misc/jquery.ba-serializeobject.js"
  ]
}

Then I've added `build/merge.js`:

var uglify = require('uglify-js');


module.exports = function(root, path, settings, doc, callback) {

  // Read the extensions config file                                                                                                                                                                   
  var build_config = JSON.parse(fs.readFileSync(__dirname + '/../build_config.json', 'ascii'));

  // Fix pathes to js files                                                                                                                                                                            
  var jsfiles = [];
  build_config.jsfiles.forEach(function(path) {
    jsfiles.push(__dirname + '/../' + path);
  });

  // This is where the output will go to                                                                                                                                                               
  var target_file = __dirname + '/../jquery.js';

  // Init output buffer                                                                                                                                                                                
  var output = '';


  // Combine JS files into one string                                                                                                                                                                  
  jsfiles.forEach(function(path) {
    output += fs.readFileSync(path, 'ascii');
  });

  // Add newline at the end of file                                                                                                                                                                    
  output += "\n";

  // If compression is active, minify the code                                                                                                                                                         
  if (settings.jscompression) {
      output += uglify.minify(output, {fromString: true})
  }

  // Write jquery.js                                                                                                                                                                                   
  fs.writeFileSync(target_file, output);

  // Carry on :)                                                                                                                                                                                       
  callback(null, doc);
};

And of course `build/cleanup.js`:

var fs = require('fs');

module.exports = function(root, path, settings, doc, callback) {

  fs.unlinkSync(__dirname + '/../jquery.js');
  callback(null, doc);

}

To make it all work, I've finally added a `package.json` file adding `uglify-js` to the requirements and installed it with npm.

The result

As a result the code is now automatically merged from the files I've added in the `build_config.json` file. If I turn on `jscompression` in the `kanso.json` file of my main application using the jquery2 package, kanso automatically minifies the code during the build.

All in all that's a very nice result and shows how much tweaking is possible within the kanso framework. The only thing you have to be cautious about is async code. I ran into big trouble using minify or node-minify to do the job. However as long as the functions run in sync mode you're good to go.

No comments :

Post a Comment