Jump to Table of Contents

Creating YUI Modules

In this example we will show you our recommended approach to creating your own YUI modules.

What is a module?

A YUI module can be described as "any code that can be separated to run on its own". Many times, this code can be reusable in different ways.

How is a module defined?

YUI.add() is a static method that registers a reusable module—essentially, it adds a module to the set of modules available to be attached to a YUI instance via the use() method.

Defining a reusable YUI module is as simple as providing a name and a callback function to YUI.add().

YUI.add('my-module', function (Y) {
   // Write your module code here, and make your module available on the Y
   // object if desired.
   Y.MyModule = {
       sayHello: function () {
           console.log('Hello!');
       }
   };
});

Note that there are no parentheses after YUI when calling YUI.add() as there are when calling YUI().use(). This is because add() is a static method of the global YUI object, not a method on a specific YUI instance. Modules are registered globally via add() and are later attached to a specific YUI instance via use().

The add() method accepts two optional arguments after the callback function: a module version string and a config object. The most useful option in the config object is requires, which allows you to specify an array of other YUI modules that your module requires. YUI will then be sure to load these dependencies before executing your module.

YUI.add('my-module', function (Y) {
   // ...
}, '0.0.1', {
    requires: ['node', 'event']
});

After your module has been added via YUI.add(), you can specify its name in a use() call to attach it to a YUI instance.

YUI().use('my-module', function (Y) {
    // The Y instance here is the same Y instance that was passed into
    // my-module's add() callback, so the Y.MyModule object that was created
    // there is now available here as well.
    Y.MyModule.sayHello();
});

A module's add() callback isn't executed until that module is attached to a YUI instance via use(). Each time a module is attached via use(), the module's add() callback will be executed, and will receive as an argument the same YUI instance that will later be passed to the use() callback.

How does YUI know about a module?

YUI gives you a few options on how to tell it about your modules. The simpliest way is to include your modules on the page after the YUI seed file.

Local Modules

If all of your modules are wrapped in a valid YUI.add call, YUI will know about them just because they are on the page. The calls to YUI.add tell the YUI seed all that it needs to know about your modules and registers them with the system.

<script src="/path/to/yui-min.js"></script>
<script src="/path/to/my/module1.js"></script>
<script src="/path/to/my/module2.js"></script>
<script src="/path/to/my/module3.js"></script>

Once available, they can be used as you would expect:

YUI().use('module1', 'module2', 'module3', function(Y) {

});

Configured Modules

The local use case may not be good for you if you have several modules that you would like YUI to know about. In this case, you would want to tell YUI about your modules so that it can fetch them when they are required.

To do this, you need to use one of the various ways to configure YUI and tell it about your modules.

In this example, we will use the YUI.GlobalConfig to tell all YUI instances about our modules (Note: this must come after the YUI seed file in the page's source):

YUI.GlobalConfig = {
    modules: {
        module1: '/path/to/my/module1.js',
        module2: '/path/to/my/module2.js',
        module3: {
            fullpath: '/path/to/my/module3.js',
            requires: [ 'node', 'event', 'dd', 'yql']
        }
    }
};

Now that we have told YUI about our modules, we can simply use them:

YUI().use('module1', 'module2', 'module3', function(Y) {

});

The advantage of this approach is that we now have all of our modules information in one simple location that can be created by a build script and will be much easier to maintain.

Structured Modules

In larger projects, you may need to structure your modules in a common way if you have multiple developers working on the code. In this case, you can follow YUI's model on this and structure your code to get the most use out of Loader.

When creating a module like the ones above, you can create your built files like this:

ourmodules/
    module1/
        module1.js
        module1-min.js
        module1-debug.js
        module1-coverage.js
    module2/
        module2.js
        module2-min.js
        module2-debug.js
        module2-coverage.js
    module3/
        module3.js
        module3-min.js
        module3-debug.js
        module3-coverage.js

Now that your files are structed in a parsable manner, Loader can handle them without much of a configuration.

Local Static Files

YUI.GlobalConfig: {
    groups: {
        ourmodules: {
            base: '/ourmodules/',
            modules: {
                module1: {},
                module2: {},
                module3: {
                    requires: [ 'node', 'event', 'dd', 'yql']
                }
            }
        }
    }
};

By default, when Loader is attempting to fetch a static module, it will create a url using a few config options: base and the modulename that was requested.

/<base>/<modulename>/<modulename>(-<filter>).js
/ourmodules/module1/module1.js
/ourmodules/module2/module2.js

Now when you use your modules, Loader will find them and load them dynamically.

Using a ComboHandler

There are several combo handlers for different languages, so we won't discuss them here. Basically a combo handler is an entry point on your server that accepts a query string of a list of files. The server then combines all those files and returns the output. This allows you to ask for multiple files but only use a few HTTP requests to fetch them.

YUI has always had this support built in for it's core files, but you can have this with your modules too. Configuring YUI to use a custom combo handler is extremely easy, let's modify the above example to use a combo server that's located here: /my-combo

YUI.GlobalConfig: {
    groups: {
        ourmodules: {
            base: '/ourmodules/',
            combine: true,
            comboBase: '/my-combo?',
            comboSep: ';', //Defaults to &
            root: '',
            modules: {
                module1: {},
                module2: {},
                module3: {
                    requires: [ 'node', 'event', 'dd', 'yql']
                }
            }
        }
    }
};

When Loader is attempting to fetch a set of combined modules, it will create a url using these config options: root, comboBase, comboSep and the <modulename>'s that were requested.

<comboBase><root><modulename>/<modulename>(-<filter>).js<comboSep><root><modulename>/<modulename>(-<filter>).js
/my-combo?module1/module1.js&amp;module2/module2.js;/module2/module3.js

Now you have a flexible, scalable and dynamic module loading system that will let you build large scale applications or simple websites.

Shifter

If you want to dynamically create the -min, -debug, -coverage files inside your project, take a look at our Node.js based build system tool called Shifter.

Custom Aliases

YUI uses aliases under the hood as module "shortcuts", for example, when you use "node" Loader doesn't fetch a node.js file, it actually fetches several files under the hood that make up the "node" alias. You can use this too with your custom modules:

YUI.GlobalConfig = {
    groups: {
        ourmodules: {
            modules: {
                all: {
                    use: [ 'module1', 'module2', 'module3' ]
                },
                foo: {
                    requires: [ 'all' ]
                }
            }
        }
    }
};

This will create an alias called all for the modules: 'module1', 'module2', 'module3'. You can then use that module as you normally would. You can even use that module as a requirement for other modules.

YUI().use('all');
    //or
YUI().use('foo');