Skip to content

Creating a Simple Source Bundle & Compiling it

Ioannis Charalampidis edited this page Jun 7, 2016 · 2 revisions

In this tutorial we are going to create a source JBB Bundle (.jbbsrc) with some simple resources and then use gulp-jbb to compile it into a binary bundle. We are also going to create a profile-loader for the simple profile we created in the Encoding & Decoding User Objects tutorial, in order to load such objects from file.

Preparation

You will need Node.js 3.0 installed. Then you will need to install a few more packages:

npm install jbb gulp gulp-jbb

You should also get the profile-encode.js, profile-decode.js and specs.yaml from the previous tutorial.

Step-by-Step Guide

1. Creating a profile-loader

Before we can start working in our source bundle we will need to make sure that the compiler is aware of how to load the resources we are going to specify. For this purpose we are going to create a profile-loader, that completes the profile we defined in the previous tutorial.

The boilerplate code for the profile-loader.js is the following:

module.exports = {
   // Initialize
   initialize: function( callback, specs ) {
      callback();
   },
   // Load an object based on given specs 
   'load': function( loadClass, loadSpecs, name, callback ) {
   }
}

It's quite straightforward: The initialize function is called once the source bundle specifications are loaded and load is called every time the compiler needs to load a resource. We are going to work on the load function.

The arguments of this function are the following:

  • loadClass (string) : The name of the resource class the user requested.
  • loadSpecs (any) : The specifications passed to the loader class by the user.
  • name (string) : The name of the resource in the bundle.
  • callback (function) : A function( string error, object objects ) that should be called by the loader when the resource is ready.

For example, in our simple profile we have two objects: Objects.User and Objects.Color. Therefore the load function should look something like this:

module.exports = {
   // Load an object based on given specs 
   'load': function( loadClass, loadSpecs, name, callback ) {
      if (loadClass == "Objects.User") {
         // Load a user

      } else if (loadClass == "Objects.Color") {
         // Load a color

      }
   }
}

The loadSpecs property can be a string, and object or an array, depending on what the user passed. Usually it's a string and is the URL to the file that contains the object. For our example, we are going to load the object fields from simple JSON files. Like the following:

{
   "name": "Bob",
   "age": 42,
   "color": [ 128, 128, 255 ]
}

We are going to use fs.readFile to load our file and JSON.parse to parse it:

   fs.readFile( loadSpecs, function(err, data) {
      if (err) {
         callback(err, null);
         return;
      }

      // Parse JSON
      var data = JSON.parse(data);
      
   });

Then we are going to create the object instance and prepare it for storing it in the bundle. Since a load function can populate an arbitrary number of resources in the bundle we start by creating an object that will carry all the new objects:

   var objects = {};

The property name provide us with the hint for the name of the resource. Usually we are just creating a key with the given name were we store our object, but some other kind of loader might use this as a prefix (for instance name+'/texture', name+'/mesh' etc).

   objects[name] = new Objects.User(
         data['name'], data['age'], 
         new Objects.Color(
               data['color'][0],
               data['color'][1],
               data['color'][2]
            )
      );

We will then call the callback function to let JBB compiler know that we are ready.

      callback( null, objects );

Finally, we must return true in the code branches that we have handled the user's request to let the compiler know that our profile supports this type:

   'load': function( loadClass, loadSpecs, name, callback ) {
      if (loadClass == "Objects.User") {

         // Load a user
         fs.readFile( loadSpecs, function(err, data) {
            if (err) {
               callback(err, null);
               return;
            }

            // Parse JSON
            var data = JSON.parse(data);

            // Create object
            var objects = {};
            objects[name] = new Objects.User(
                  data['name'], data['age'], 
                  new Objects.Color(
                        data['color'][0],
                        data['color'][1],
                        data['color'][2]
                     )
               );

            // Call back
            callback( null, objects );
            return true;
            
         });

      } else if (loadClass == "Objects.Color") {

         // Load a color
         fs.readFile( loadSpecs, function(err, data) {
            if (err) {
               callback(err, null);
               return;
            }

            // Parse JSON
            var data = JSON.parse(data);

            // Create object
            var objects = {};
            objects[name] = new Objects.Color(
                        data['color'][0],
                        data['color'][1],
                        data['color'][2]
                     );

            // Call back
            callback( null, objects );
            return true;
            
         });

      }
   }

2. Creating a source bundle

The source bundle is a directory that contains all of our resources and an index file called bundle.json. Let's start by creating our 'users' bundle:

~$ mkdir users.jbbsrc

Also create a new file called bundle.json inside the folder with the following contents:

{
  "name": "users",
  "exports": {

  }
}

The bare minimum requirements for a source bundle is the name of the bundle and an exports section were we are going to define the resources this bundle exports. You can refer to the bundle.json Reference if you want more information regarding the syntax of this file.

The exports section has the following general:

  "exports": {

    "<Loader Class>": {
      "<Resrouce Name>": <Loader Specs>,
      ...
    }

  }

According to the profile-loader we created in the previous step we have two loader classes: Objects.User and Objects.Color that use the path to the file as loader specifications. This means that in order to load two users from the file we will specify:

{
  "name": "users",
  "exports": {
    "Objects.User": {
      "mike": "mike.json",
      "bob": "bob.json"
    }
  }
}

The files mike.json and bob.json must be located in the bundle directory and their path is relevant to the folder. So let's create them:

For bob.json:

{
   "name": "Bob Doe",
   "age": 46,
   "color": [ 170, 187, 204 ]
}

For mike.json:

{
   "name": "Mike Johanson",
   "age": 36,
   "color": [ 19, 255, 69 ]
}

And now we are ready to compile our bundle.

3. Compiling the bundle

To compile the bundle we are going to use the gulp-jbb plug-in for gulp. This way it's very easy to integrate this process to your existing build process.

Let's start by creating the following gulpfile.js:

var gulp 		= require('gulp');
var jbb 		= require('gulp-jbb');

// Compile source bundle
gulp.task('default', function() {
  return gulp
    .src([ "users.jbbsrc" ])
    .pipe(jbb({
      profile: "."
    }))
    .pipe(gulp.dest('.'));
});

The profile option in the jbb() pipe instructs JBB to use the profile from our working directory. The profile is the directory were the profile-encode, profile-decode and profile-load scripts are located.

We can now compile our source bundle using gulp:

~$ gulp
[10:51:40] Using gulpfile ~/gulpfile.js
[10:51:40] Starting 'default'...
[10:51:40] Finished 'default' after 53 ms
~$ ls
-rwxr-xr-x    1 user  staff   136B Jun  6 10:45 users.jbb